From 2127b1bdd73ecd2423e443992da4b442f5a3c1a3 Mon Sep 17 00:00:00 2001
From: Jake Vanderwerf <get@jakevanderwerf.ca>
Date: Wed, 04 Feb 2026 21:19:25 +0000
Subject: [PATCH] =Major overhaul of MetaManager.php -> Meta.php and RestRouteManager.php -> Rest.php. Seems to work for JakeVan
---
inc/managers/queue/executors/ContentExecutor.php | 798 ++++++++++++++++++++++++--------------------------------
1 files changed, 340 insertions(+), 458 deletions(-)
diff --git a/inc/managers/queue/executors/ContentExecutor.php b/inc/managers/queue/executors/ContentExecutor.php
index cf3936d..7d4db57 100644
--- a/inc/managers/queue/executors/ContentExecutor.php
+++ b/inc/managers/queue/executors/ContentExecutor.php
@@ -1,9 +1,8 @@
<?php
namespace JVBase\managers\queue\executors;
-use JVBase\managers\CacheManager;
-use JVBase\managers\queue\{Executor, Operation, Progress, Result};
-use JVBase\meta\MetaManager;
+use JVBase\managers\queue\{Executor, Operation, Progress, Result, Storage};
+use JVBase\meta\Meta;
use JVBase\utility\Features;
use Exception;
@@ -19,34 +18,29 @@
{
private const HANDLED_TYPES = [
'content_update',
- 'batch_creation',
+// 'batch_creation',
];
private int $userId;
private string $postType;
private array $fields = [];
- private array $timelineSharedFields = [];
- private array $timelineUniqueFields = [];
public function execute(Operation $operation, Progress $progress): Result
{
+ $this->userId = $operation->userId;
+
if (!in_array($operation->type, self::HANDLED_TYPES)) {
throw new Exception("ContentExecutor cannot handle type: {$operation->type}");
}
- $this->userId = $operation->userId;
-
try {
$data = $operation->requestData;
- $result = match($operation->type) {
+ return match($operation->type) {
'content_update' => $this->processContentUpdate($operation, $data, $progress),
- 'batch_creation' => $this->processBatchCreation($operation, $data, $progress),
default => throw new Exception("Unknown type: {$operation->type}")
};
- return $result;
-
} catch (Exception $e) {
JVB()->error()->log(
'[ContentExecutor]:execute',
@@ -82,8 +76,9 @@
$results = [];
$errors = [];
-
- $updateTimelineOrder = [];
+ $timelineParents = [];
+ $timelineStatus = [];
+ $timelineSharedFields = [];
foreach ($posts as $id => $postData) {
try {
@@ -104,320 +99,109 @@
}
$this->savePostFields($newId, $postData);
- $results[$id] = ['success' => true, 'new_id' => $newId];
-
- if (Features::forContent($content)->has('is_timeline')) {
- $this->updateTimelineLatestDate($newId);
- }
-
- $progress->advance(1);
+ $results[$id] = [
+ 'success' => true,
+ 'new_id' => $newId,
+ 'processed_fields' => array_keys($postData)
+ ];
+ $progress->advance();
continue;
}
// Existing post update
if (!$this->verifyOwnership((int)$id)) {
$progress->failItem($id, 'No permission to modify this post');
+ $errors[$id] = 'No permission';
continue;
}
- // Check if this is a timeline post
- $isTimeline = Features::forContent($content)->has('is_timeline');
- if ($isTimeline) {
+ $this->savePostFields((int)$id, $postData);
+
+
+ if (Features::forContent($content)->has('is_timeline')) {
$post = get_post((int)$id);
- $parentId = $post->post_parent;
- $isParent = ($parentId === 0);
+ $parentId = $post->post_parent > 0 ? $post->post_parent : $post->ID;
+ $sharedFields = array_keys(array_filter(JVB_CONTENT[$content]['fields'], function ($field) {
+ return !array_key_exists('for_all', $field) || !$field['for_all'];
+ }));
- // Track timeline reordering only if date changed
- if (array_key_exists('post_date', $postData)) {
- $timelineRoot = $isParent ? (int)$id : $parentId;
- if (!in_array($timelineRoot, $updateTimelineOrder)) {
- $updateTimelineOrder[] = $timelineRoot;
- }
+ if (array_key_exists('post_date', $postData) && !in_array($parentId, $timelineParents)) {
+ $timelineParents[] = $parentId;
}
+ if ($parentId === $id) {
+ if (array_key_exists('post_status', $postData) && !array_key_exists($parentId, $timelineStatus)) {
+ $timelineStatus[$parentId] = $postData['post_status'];
+ }
- // Update shared fields if this is the parent
- if ($isParent) {
- $this->initTimelineFields($content);
- $sharedFieldsUpdated = array_filter($postData, function($key) {
- return in_array($key, $this->timelineSharedFields);
- }, ARRAY_FILTER_USE_KEY);
-
- if (!empty($sharedFieldsUpdated)) {
- $this->updateSharedFields((int)$id, $sharedFieldsUpdated);
+ if (count(array_intersect($sharedFields, array_keys($postData))) > 0) {
+ if (!array_key_exists($parentId, $timelineSharedFields)) {
+ $timelineSharedFields[$parentId] = [];
+ }
+ $temp = array_intersect($sharedFields, array_keys($postData));
+ $timelineSharedFields[$parentId] = array_unique(array_merge($timelineSharedFields[$parentId], $temp));
}
}
}
- $this->processPostUpdate((int)$id, $postData);
-
- if (Features::forContent($content)->has('is_timeline')
- && array_key_exists('post_date', $postData)) {
- $post = get_post((int)$id);
- $parentId = $post->post_parent === 0 ? (int)$id : $post->post_parent;
- $this->updateTimelineLatestDate($parentId);
- }
-
- $results[$id] = ['success' => true];
- $progress->advance(1);
-
- // Clear caches
- CacheManager::for($content)->clear();
- if (jvbSiteUsesFeedBlock()) {
- CacheManager::for('feed')->clear();
- }
+ $results[$id] = [
+ 'success' => true,
+ 'processed_fields' => array_keys($postData)
+ ];
+ $progress->advance();
} catch (Exception $e) {
$progress->failItem($id, $e->getMessage());
$errors[$id] = $e->getMessage();
- }
- }
- if (!empty($updateTimelineOrder)) {
- foreach ($updateTimelineOrder as $parentID) {
- $this->reorderTimelineByDate($parentID);
+ $results[$id] = [
+ 'success' => false,
+ 'error' => $e->getMessage()
+ ];
}
}
- // Send notification
- if (jvbSiteHasNotifications()) {
- JVB()->notification()->addNotification(
- $this->userId,
- 'content_update_complete',
- null,
- 'Content updates completed!'
- );
+ try {
+ if (!empty($timelineSharedFields)) {
+ $this->checkSharedFields($timelineSharedFields);
+ }
+ if (!empty($timelineStatus)) {
+ $this->handleTimelineStatusChange($timelineStatus);
+ }
+ if (!empty($timelineParents)) {
+ $this->maybeReorderTimelines($timelineParents);
+ }
+ } catch (Exception $e) {
+ $errors[] = $e->getMessage();
}
+
+ // Send notification
+// if (jvbSiteHasNotifications()) {
+// JVB()->notification()->addNotification(
+// $this->userId,
+// 'content_update_complete',
+// null,
+// 'Content updates completed!'
+// );
+// }
+
$outcome = 'success';
if (!empty($errors)) {
$outcome = count($errors) === count($posts) ? 'failed' : 'partial';
}
+
+
+
return new Result(
outcome: $outcome,
- result: $results
+ result: [
+ 'posts' => $results,
+ 'errors' => $errors,
+ 'updated_count' => count(array_filter($results, fn($r) => $r['success'] ?? false)),
+ 'failed_count' => count($errors)
+ ]
);
}
- private function processPostUpdate(int $postId, array $postData): void
- {
- $content = $postData['content'] ?? '';
-
- // Handle status changes
- if (isset($postData['post_status'])) {
- switch ($postData['post_status']) {
- case 'publish':
- if (user_can($this->userId, 'manage_options') || user_can($this->userId, 'skip_moderation')) {
- wp_update_post(['ID' => $postId, 'post_status' => 'publish']);
- }
- unset($postData['post_status']);
- break;
- case 'draft':
- wp_update_post(['ID' => $postId, 'post_status' => 'draft']);
- unset($postData['post_status']);
- break;
- case 'trash':
- wp_trash_post($postId);
- return;
- case 'delete':
- wp_delete_post($postId, true);
- return;
- }
- }
-
- // Save all fields via MetaManager (handles post fields too)
- $this->savePostFields($postId, $postData);
- }
-
- private function updateSharedFields(int $parentId, array $sharedFields): void
- {
- // Get all posts in timeline
- $children = get_posts([
- 'post_type' => get_post_type($parentId),
- 'post_parent' => $parentId,
- 'post_status' => ['publish', 'draft'],
- 'posts_per_page' => -1,
- 'fields' => 'ids'
- ]);
-
- $allPostIds = array_merge([$parentId], $children);
-
- // Apply shared fields to all posts
- foreach ($allPostIds as $timelinePostId) {
- $meta = new MetaManager($timelinePostId, 'post');
- $meta->setAll($sharedFields);
- }
- }
-
- private function reorderTimelineByDate(int $parentId): void
- {
- $parent = get_post($parentId);
- if (!$parent) return;
-
- // Get all posts in this timeline (parent + children)
- $children = get_posts([
- 'post_type' => get_post_type($parentId),
- 'post_parent' => $parentId,
- 'post_status' => ['publish', 'draft'],
- 'posts_per_page' => -1,
- 'orderby' => 'date',
- 'order' => 'ASC'
- ]);
-
- // Combine and sort by post_date
- $allPosts = array_merge([$parent], $children);
- usort($allPosts, function($a, $b) {
- return strtotime($a->post_date) <=> strtotime($b->post_date);
- });
-
- $newParent = $allPosts[0];
-
- // If parent changed, restructure
- if ($newParent->ID !== $parentId) {
- wp_update_post([
- 'ID' => $newParent->ID,
- 'post_parent' => 0,
- 'menu_order' => 0
- ]);
-
- wp_update_post([
- 'ID' => $parentId,
- 'post_parent' => $newParent->ID
- ]);
-
- foreach ($allPosts as $index => $post) {
- if ($index === 0) continue;
-
- wp_update_post([
- 'ID' => $post->ID,
- 'post_parent' => $newParent->ID,
- 'menu_order' => $index
- ]);
-
- $this->getOrCreateTerm($post->ID, (string)$index, 'number');
- }
- } else {
- // Just update menu_order
- foreach ($allPosts as $index => $post) {
- if ($index === 0) continue;
-
- wp_update_post([
- 'ID' => $post->ID,
- 'menu_order' => $index
- ]);
-
- $this->getOrCreateTerm($post->ID, (string)$index, 'number');
- }
- }
-
- // Calculate and set timeline taxonomy (time since previous post)
- $previousPost = null;
- foreach ($allPosts as $index => $post) {
- if ($index === 0) {
- // Parent post - no timeline term (it's the baseline)
- wp_set_object_terms($post->ID, [], BASE . 'timeline', false);
- $previousPost = $post;
- continue;
- }
-
- $timelineTerm = $this->calculateTimelineTerm($previousPost, $post);
- if ($timelineTerm) {
- $this->getorCreateTerm($post->ID, $timelineTerm, 'timeline');
- }
-
- $previousPost = $post;
- }
-
- $this->updateTimelineLatestDate($newParent->ID);
- }
-
- private function updateTimelineLatestDate(int $parentId): void
- {
- $parent = get_post($parentId);
- if (!$parent) return;
-
- // Get all posts in timeline
- $children = get_posts([
- 'post_type' => get_post_type($parentId),
- 'post_parent' => $parentId,
- 'post_status' => ['publish', 'draft'],
- 'posts_per_page' => -1,
- 'orderby' => 'date',
- 'order' => 'DESC', // Get newest first
- 'fields' => 'ids'
- ]);
-
- $allPostIds = array_merge([$parentId], $children);
-
- // Get all timestamps
- $timestamps = array_map(function($id) {
- $post = get_post($id);
- return $post ? strtotime($post->post_date) : 0;
- }, $allPostIds);
-
- $latestTimestamp = max($timestamps);
-
- // Store as UNIX timestamp
- update_post_meta($parentId, BASE . 'latest_date', $latestTimestamp);
- }
-
- private function calculateTimelineTerm(\WP_Post $previousPost, \WP_Post $currentPost): ?string
- {
- $previousDate = strtotime($previousPost->post_date);
- $currentDate = strtotime($currentPost->post_date);
-
- if (!$previousDate || !$currentDate || $currentDate <= $previousDate) {
- return null;
- }
-
- // Calculate difference in days
- $daysDiff = floor(($currentDate - $previousDate) / (60 * 60 * 24));
-
- // Convert to weeks
- $weeks = floor($daysDiff / 7);
-
- // If less than 16 weeks, use weeks
- if ($weeks < 16) {
- if ($weeks === 0) {
- return null; // Same week, no term
- }
- return $weeks === 1 ? '1 Week' : $weeks . ' Weeks';
- }
-
- // 16+ weeks, calculate months
- // Using actual month calculation rather than weeks/4
- $previousDateTime = new \DateTime($previousPost->post_date);
- $currentDateTime = new \DateTime($currentPost->post_date);
- $interval = $previousDateTime->diff($currentDateTime);
-
- $months = ($interval->y * 12) + $interval->m;
-
- if ($months === 0) {
- // Edge case: technically less than a full month but 16+ weeks
- return $weeks . ' Weeks';
- }
-
- return $months === 1 ? '1 Month' : $months . ' Months';
- }
-
- private function getOrCreateTerm(int $postID, string $termName, string $taxonomy): void
- {
- $taxonomy = jvbCheckBase($taxonomy);
- $term = get_term_by('name', $termName, $taxonomy);
-
- if (!$term) {
- $result = wp_insert_term($termName, $taxonomy);
- if (is_wp_error($result)) {
- return;
- }
- $termID = $result['term_id'];
- } else {
- $termID = $term->term_id;
- }
-
- if ($termID) {
- wp_set_object_terms($postID, [$termID], $taxonomy, false);
- }
- }
-
private function savePostFields(int $postId, array $postData): bool
{
$content = $postData['content'] ?? '';
@@ -431,184 +215,282 @@
return true;
}
- $meta = new MetaManager($postId, 'post');
- return $meta->setAll($allowedFields);
- }
-
- // ─────────────────────────────────────────────────────────────
- // Batch Creation
- // ─────────────────────────────────────────────────────────────
-
- private function processBatchCreation(Operation $operation, array $data, Progress $progress): Result
- {
- $this->postType = BASE . $data['content'];
-
- // Get upload results from dependency
- $uploadOpId = $operation->id . '_upload';
- $images = JVB()->queue()->get($uploadOpId)?->result ?? null;
-
- if (!$images) {
- return new Result(
- outcome: 'failed',
- result: ['message' => 'No upload results found']
- );
- }
-
- $results = [];
-
- if ($data['mode'] === 'selection') {
- $results = $this->createFromSelection($operation, $data, $images, $progress);
- } else {
- $results = $this->createFromDirect($operation, $data, $images, $progress);
- }
-
- // Clear caches
- CacheManager::for($data['content'])->clear();
- CacheManager::for('feed')->clear();
-
- return new Result(
- outcome: !empty($results) ? 'success' : 'failed',
- result: $results
- );
- }
-
- private function createFromSelection(Operation $operation, array $data, array $images, Progress $progress): array
- {
- $results = [];
-
- foreach ($images as $group => $files) {
- $settings = json_decode($data['files_data'][$group] ?? '{}');
-
- if (($settings->type ?? '') === 'group') {
- $postId = $this->createGroupPost($operation, $data, $files, $settings);
- } else {
- $postId = $this->createIndividualPosts($operation, $data, $files);
- }
-
- if ($postId) {
- $results = array_merge($results, (array)$postId);
- }
-
- $progress->advance(1);
- }
-
- return $results;
- }
-
- private function createFromDirect(Operation $operation, array $data, array $images, Progress $progress): array
- {
- $results = [];
-
- foreach ($images as $img) {
- $postId = wp_insert_post([
- 'post_type' => $this->postType,
- 'post_title' => $this->generatePostTitle($data['content']),
- 'post_status' => 'draft',
- 'post_author' => $operation->userId,
- ]);
-
- if ($postId && !is_wp_error($postId)) {
- set_post_thumbnail($postId, $img['attachment_id']);
- $results[] = $postId;
- }
-
- $progress->advance(1);
- }
-
- return $results;
- }
-
- private function createGroupPost(Operation $operation, array $data, array $files, object $settings): ?int
- {
- $featuredIndex = $settings->metadata->featuredFile ?? 0;
- $title = $settings->metadata->title ?? $this->generatePostTitle($data['content']);
-
- $postId = wp_insert_post([
- 'post_type' => $this->postType,
- 'post_title' => $title,
- 'post_status' => 'draft',
- 'post_author' => $operation->userId,
- ]);
-
- if (!$postId || is_wp_error($postId)) {
- return null;
- }
-
- // Set featured image
- set_post_thumbnail($postId, $files[$featuredIndex]['attachment_id']);
-
- // Remaining files go to gallery
- unset($files[$featuredIndex]);
- if (!empty($files)) {
- $meta = new MetaManager($postId, 'post');
- $ids = array_column($files, 'attachment_id');
- $meta->updateValue('gallery', implode(',', $ids));
- }
-
- return $postId;
- }
-
- private function createIndividualPosts(Operation $operation, array $data, array $files): array
- {
- $results = [];
-
- foreach ($files as $img) {
- $title = $this->generatePostTitle($data['content']);
- $postId = wp_insert_post([
- 'post_type' => $this->postType,
- 'post_title' => $title,
- 'post_slug' => sanitize_title($title),
- 'post_status' => 'draft',
- 'post_author' => $operation->userId,
- ]);
-
- if ($postId && !is_wp_error($postId)) {
- set_post_thumbnail($postId, $img['attachment_id']);
- $results[] = $postId;
- }
- }
-
- return $results;
+ $meta = Meta::forPost($postId);
+ $meta->setAll($allowedFields);
+ return true;
}
// ─────────────────────────────────────────────────────────────
// Helpers
// ─────────────────────────────────────────────────────────────
- private function initTimelineFields(string $content): void
- {
- $content = jvbNoBase($content);
- if (!Features::forContent($content)->has('is_timeline')) {
- return;
- }
-
- $config = Features::getConfig($content);
- $this->fields = $config['fields'] ?? [];
-
- // Shared fields (apply to all posts)
- $this->timelineSharedFields = array_keys(array_filter($this->fields, function ($field) {
- return !isset($field['for_all']) || $field['for_all'] === false;
- }));
- array_unshift($this->timelineSharedFields, 'post_thumbnail', 'post_title', 'post_status');
-
- // Unique fields (per-entry)
- $this->timelineUniqueFields = array_keys(array_filter($this->fields, function ($field) {
- return isset($field['for_all']) && $field['for_all'] === true;
- }));
- }
-
private function verifyOwnership(int $postId): bool
{
$post = get_post($postId);
return $post && (int)$post->post_author === $this->userId;
}
- private function generatePostTitle(string $content): string
+ /*************************************************************
+ * TIMELINE HELPERS
+ *************************************************************/
+ protected function maybeReorderTimelines(array $parentIDs):void {
+ foreach ($parentIDs as $parentId) {
+ try {
+ $this->maybeReorderTimeline((int)$parentId);
+ } catch (Exception $e) {
+ error_log("Timeline reorder failed for parent {$parentId}: " . $e->getMessage());
+ }
+ }
+ }
+ protected function maybeReorderTimeline(int $parentID):void
{
- $username = get_user_meta($this->userId, 'first_name', true);
- $link = get_user_meta($this->userId, BASE . 'link', true);
- $city = function_exists('jvbArtistCity') ? jvbArtistCity($link) : '';
+// clean_post_cache($parentID);
+ $parent = get_post($parentID);
+ if (!$parent) {
+ return;
+ }
- return ucfirst($content) . ' by ' . ($city ? "$city artist " : '') . $username;
+ $children = get_children([
+ 'post_parent' => $parentID,
+ 'posts_per_page' => -1,
+ 'post_status' => ['publish', 'draft'],
+ ]);
+
+
+ if (count($children) === 0) {
+ return;
+ }
+
+ $allPosts = array_merge([$parent], $children);
+
+ // Sort by post_date
+ usort($allPosts, function($a, $b) {
+ return strtotime($a->post_date) <=> strtotime($b->post_date);
+ });
+
+
+ // Check if order changed
+ $needsReorder = false;
+ foreach ($allPosts as $index => $post) {
+ if ($index === 0 && $post->ID !== $parent->ID) {
+ $needsReorder = true;
+ break;
+ }
+ if ($index > 0 && (int)$post->menu_order !== $index) {
+ $needsReorder = true;
+ break;
+ }
+ }
+
+ if (!$needsReorder) {
+ // Just recalculate timelines without reordering
+ $this->recalculateTimelines($allPosts);
+ return;
+ }
+
+ // Handle parent swap if needed
+ $newParent = $allPosts[0];
+ if ($newParent->ID !== $parent->ID) {
+ $this->swapTimelineParent($parent, $newParent, $allPosts);
+ } else {
+ // Just update menu orders and timelines
+ foreach ($allPosts as $index => $post) {
+ if ($index === 0) continue; // Skip parent
+
+ $success = jvb_update_post([
+ 'ID' => $post->ID,
+ 'post_parent' => $newParent->ID,
+ 'menu_order' => $index
+ ]);
+ }
+
+ $this->recalculateTimelines($allPosts);
+ }
+ }
+
+ private function recalculateTimelines(array $posts): void
+ {
+ $previousPost = null;
+ $latestTimestamp = 0;
+
+
+ $lastKey = array_key_last($posts);
+ foreach ($posts as $index => $post) {
+ $meta = Meta::forPost($post->ID);
+ if ($index === 0) {
+ $meta->set('timeline', '', false);
+ $previousPost = $post;
+ continue; // Parent has no timeline
+ }
+
+ // Calculate timeline from previous post
+ if ($previousPost) {
+ $timeline = $this->calculateTimeline($previousPost, $post);
+ if ($timeline) {
+ $termId = $this->getOrCreateTerm($timeline, 'timeline');
+ if ($termId) {
+ $success = $meta->set('timeline', $termId, false);
+ }
+ }
+ }
+
+ if ($lastKey === $index) {
+ $latestTimestamp = strtotime($post->post_date);
+ }
+
+ $previousPost = $post;
+ }
+
+ // Update parent's latest_date
+ if ($latestTimestamp > 0) {
+ $success = update_post_meta($posts[0]->ID, BASE . 'latest_date', $latestTimestamp);
+ }
+ }
+
+ private function calculateTimeline(\WP_POST $previous, \WP_POST $current): ?string
+ {
+ $previousDate = strtotime($previous->post_date);
+ $currentDate = strtotime($current->post_date);
+
+ if (!$previousDate || !$currentDate || $currentDate <= $previousDate) {
+ return null;
+ }
+
+ $daysDiff = floor(($currentDate - $previousDate) / (60*60*24));
+ $weeks = floor($daysDiff / 7);
+ if ($weeks === 0) {
+ return 'Less than 1 Week';
+ }
+ if ($weeks < 16) {
+ return $weeks === 1 ? '1 Week' : $weeks . ' Weeks';
+ }
+
+ $previousDateTime = new \DateTime($previous->post_date);
+ $currentDateTime = new \DateTime($current->post_date);
+
+ $interval = $previousDateTime->diff($currentDateTime);
+ $months = ($interval->y * 12) + $interval->m;
+
+ if ($months === 0) {
+ return $weeks . ' Weeks';
+ }
+
+ return ($months === 1) ? '1 Month' : $months . ' Months';
+ }
+
+ private function swapTimelineParent(\WP_Post $oldParent, \WP_Post $newParent, array $allPosts): void
+ {
+ // Swap titles and content
+ $originalTitle = $oldParent->post_title;
+ $originalSlug = $oldParent->post_name;
+ $originalContent = $oldParent->post_content;
+
+ $updateParent = jvb_update_post([
+ 'ID' => $oldParent->ID,
+ 'post_title' => 'Treatment',
+ 'post_name' => sanitize_title('Treatment ' . $newParent->ID),
+ 'post_content' => '',
+ ]);
+
+ $updateNewParent = jvb_update_post([
+ 'ID' => $newParent->ID,
+ 'post_title' => $originalTitle,
+ 'post_name' => $originalSlug,
+ 'post_content' => $originalContent,
+ 'post_parent' => 0,
+ 'menu_order' => 0
+ ]);
+
+ // Clear timeline taxonomy from new parent
+ wp_set_object_terms($newParent->ID, [], BASE . 'timeline', false);
+
+ // Update all other posts to new parent
+ foreach ($allPosts as $index => $post) {
+ if ($index === 0) continue; // Skip new parent
+
+ $title = $post->post_title;
+ if (str_starts_with($title, 'Treatment #')) {
+ $title = 'Treatment #' . $index;
+ }
+
+ $childUpdate = jvb_update_post([
+ 'ID' => $post->ID,
+ 'post_title' => $title,
+ 'post_parent' => $newParent->ID,
+ 'menu_order' => $index,
+ ]);
+ }
+
+ // Recalculate timelines for all posts
+ $this->recalculateTimelines($allPosts);
+ }
+
+ private function getOrCreateTerm(string $termName, string $taxonomy): ?int
+ {
+ $taxonomy = jvbCheckBase($taxonomy);
+ $term = get_term_by('name', $termName, $taxonomy);
+
+ if (!$term) {
+ $result = wp_insert_term($termName, $taxonomy);
+ if (is_wp_error($result)) {
+ return null;
+ }
+ return $result['term_id'];
+ }
+
+ return $term->term_id;
+ }
+
+ protected function checkSharedFields(array $fields): void
+ {
+ foreach ($fields as $parentID => $shared) {
+ $meta = Meta::forPost($parentID);
+ $values = $meta->getAll($shared);
+
+ $children = get_children([
+ 'post_parent' => $parentID,
+ 'posts_per_page' => -1,
+ 'fields' => 'ids',
+ ]);
+
+ if (empty($children)) {
+ continue;
+ }
+
+ foreach ($children as $child) {
+ $childMeta = Meta::forPost($child);
+ $result = $childMeta->setAll($values, false);
+ }
+ }
+ }
+
+ protected function handleTimelineStatusChange(array $updates):void
+ {
+ $updates = array_filter($updates, function ($status) {
+ return in_array($status, ['trash', 'delete', 'publish', 'draft']);
+ });
+
+ foreach ($updates as $parentID => $status) {
+ $children = get_children([
+ 'post_parent' => $parentID,
+ 'posts_per_page' => -1,
+ 'fields' => 'ids'
+ ]);
+ if (!empty($children)) {
+ foreach($children as $child) {
+ if ($status === 'trash') {
+ wp_trash_post($child);
+ } elseif ($status === 'delete') {
+ wp_delete_post($child, true);
+ }else {
+ jvb_update_post([
+ 'ID' => $child,
+ 'post_status' => $status
+ ]);
+ }
+
+ }
+ }
+ }
}
}
--
Gitblit v1.10.0