Jake Vanderwerf
2026-02-06 94de71140be2d0c80bf6a2e03cb9381b37736ed5
inc/managers/queue/executors/UploadExecutor.php
@@ -74,10 +74,12 @@
      $uploader = new UploadManager();
      $processedResults = [];
      $errors = [];
      $uploadIds = [];
      $securedFiles = $data['secured_files'] ?? [];
      foreach ($securedFiles as $securedFile) {
         $uploadIds[] = $securedFile['upload_id'];
         try {
            $result = $uploader->processUpload(
               $securedFile['temp_path'],
@@ -92,32 +94,26 @@
               ]
            );
            if (!is_wp_error($result)) {
               $standardized = [
                  'attachment_id' => $result['attachment_id'],
                  'url'           => $result['url'],
                  'file'          => $result['file'],
                  'upload_id'     => $securedFile['upload_id'] ?? null,
               ];
               if ($standardized['upload_id']) {
                  $processedResults[$standardized['upload_id']] = $standardized;
               } else {
                  $processedResults[] = $standardized;
               }
               // Apply frontend metadata if provided
               if (!empty($securedFile['metadata'])) {
                  $this->applyMeta($standardized['attachment_id'], $securedFile['metadata']);
               }
               $progress->advance(1);
            } else {
               $progress->failItem($securedFile, $result->get_error_message());
            if (is_wp_error($result)) {
               $progress->failItem($securedFile['upload_id'], $result->get_error_message());
               $errors[] = $result->get_error_message();
               continue;
            }
            $processedResults[$securedFile['upload_id']] = [
               'upload_id' => $securedFile['upload_id'],
               'attachment_id' => $result['attachment_id'] ?? 0,
               'url' => $result['url'] ?? '',
               'sizes' => $result['sizes'] ?? [],
            ];
            if (!empty($securedFile['metadata'])) {
               $this->applyMeta($securedFile['attachment_id'], $securedFile['metadata']);
            }
            $progress->advance();
         } catch (Exception $e) {
            $progress->failItem($securedFile, $e->getMessage());
            $progress->failItem($securedFile['upload_id'], $e->getMessage());
            $errors[] = $e->getMessage();
         }
      }
@@ -128,14 +124,20 @@
      // Cleanup temp files
      $this->cleanupTempFiles($securedFiles, $operation->userId);
      $outcome = 'success';
      if (!empty($operation->failedItems)) {
         $outcome = count($operation->failedItems) === count($securedFiles) ? 'failed' : 'partial';
      $outcome = count($processedResults) > 0 ? 'success' : 'failed';
      if (count($processedResults) > 0 && !empty($errors)) {
         $outcome = 'partial';
      }
      return new Result(
         outcome: $outcome,
         result: $processedResults
         result: [
            'upload_ids'   => $uploadIds,
            'uploads'      => $processedResults,
            'processed_count'=> count($processedResults),
            'total_count'  => count($uploadIds),
            'errors'    => $errors
         ]
      );
   }
@@ -281,17 +283,27 @@
      }
      $uploads = [];
      $uploadIds = [];
      foreach ($dependencies as $dependency) {
         $res = JVB()->queue()->getOperationValue($dependency, 'result');
         if (empty($res)) {
            continue;
         }
         // Results are stored at root level, keyed by upload_id
         // Check if dependency result has upload_ids
         if (isset($res['upload_ids'])) {
            $uploadIds = array_merge($uploadIds, $res['upload_ids']);
         }
         // Results are stored in 'uploads', keyed by upload_id
         // Filter to only include actual upload results (arrays with attachment_id)
         foreach ($res as $key => $value) {
         foreach ($res['uploads'] as $key => $value) {
            if (is_array($value) && isset($value['attachment_id'])) {
               $uploads[$key] = $value;
               // If we didn't get upload_ids from result, track them from keys
               if (!isset($res['upload_ids']) && !in_array($key, $uploadIds)) {
                  $uploadIds[] = $key;
               }
            }
         }
      }
@@ -316,72 +328,126 @@
         return $this->processTimelineUploads($operation, $data, $progress, $all_uploads);
      }
      $user = (int)$operation->userId;
      $user = $operation->userId;
      $createdPosts = [];
      $errors = [];
      $groupMappings = [];
      $usedUploads = [];
      foreach($data['posts'] as $index => $post) {
         $progress->advance();
         $post_title = array_key_exists('post_title', $post['fields'])
            ? sanitize_text_field($post['fields']['post_title'])
            : 'New '. JVB_CONTENT[$data['content']]['singular'].' '.($index + 1);
         try {
            $groupId = $post['groupId'] ?? null;
            // Create post for this group
            $created = $this->createPostFromGroup($post, $index+1, $content, $uploads, $operation);
         $post_excerpt = array_key_exists('post_excerpt', $post['fields'])
            ? sanitize_textarea_field($post['fields']['post_excerpt'])
            : '';
            if ($created) {
               $postId = $created['ID'];
               $createdPosts[] = [
                  'post_id' => $postId,
                  'group_id' => $groupId,
               ];
         $args = [
            'post_type'    => $content,
            'post_author'  => $user,
            'post_status'  => 'draft',
            'post_title'   => $post_title,
            'post_excerpt' => $post_excerpt
         ];
         $newPostID = wp_insert_post($args);
         if ($newPostID && !is_wp_error($newPostID)) {
            $createdPosts[] = $newPostID;
            $featured_upload_id = $post['fields']['featured']??null;
            $featured_attachment_id = null;
            $gallery_attachment_ids = [];
            foreach ($post['images'] as $img) {
               $uploadId = $img['upload_id'];
               $usedUploads[] = $uploadId;
               if (array_key_exists($uploadId, $all_uploads)) {
                  $attachmentId = $all_uploads[$uploadId]['attachment_id'];
                  if ($uploadId === $featured_upload_id) {
                     $featured_attachment_id = $attachmentId;
                  } else {
                     $gallery_attachment_ids[] = $attachmentId;
                  }
               if ($groupId) {
                  $groupMappings[$groupId] = $postId;
               }
            }
            if ($featured_attachment_id) {
               set_post_thumbnail($newPostID, $featured_attachment_id);
            } elseif (!empty($gallery_attachment_ids)) {
               set_post_thumbnail($newPostID, $gallery_attachment_ids[0]);
               array_shift($gallery_attachment_ids);
            }
            if (!empty($gallery_attachment_ids)) {
               $meta = Meta::forPost($newPostID);
               $fields = jvbGetFields($content, 'post');
               foreach($fields as $name => $config) {
                  if ($config['type'] === 'gallery') {
                     $meta->set($name, implode(',', $gallery_attachment_ids));
                     break;
                  }
               }
               $usedUploads = array_merge($usedUploads, $created['usedUploads']);
               $progress->advance(1);
            }
         } catch (Exception $e) {
            $errors[] = $e->getMessage();
            $progress->failItem($index ?? 'unknown', $e->getMessage());
         }
      }
      $outcome = !empty($createdPosts) ? 'success' : 'failed';
      if (!empty($createdPosts) && !empty($errors)) {
         $outcome = 'partial';
      }
      return new Result(
         outcome: $outcome,
         result: [
            'upload_ids' => $usedUploads,
            'created_posts' => $createdPosts,
            'group_mappings' => $groupMappings,
            'post_count' => count($createdPosts),
            'processed_uploads' => count($uploads),
            'errors' => $errors,
         ]
      );
   }
   protected function createPostFromGroup(array $post, int $index, string $content, array $uploads, Operation $op):array|false
   {
      $config = JVB_CONTENT[jvbNoBase($content)]??false;
      if (!$config) {
         throw new Exception('No config found for content: '.$content.'.');
      }
      $post_title = array_key_exists('post_title', $post['fields'])
         ? sanitize_text_field($post['fields']['post_title'])
         : 'New '. $config['singular'].' '.($index + 1);
      $post_excerpt = array_key_exists('post_excerpt', $post['fields'])
         ? sanitize_textarea_field($post['fields']['post_excerpt'])
         : '';
      $ID = wp_insert_post([
         'post_type'    => $content,
         'post_author'  => $op->userId,
         'post_status'  => 'draft',
         'post_title'   => $post_title,
         'post_excerpt' => $post_excerpt,
      ]);
      if (!$ID || is_wp_error($ID)) {
         throw new Exception('Could not create post: '.$ID?->get_error_message());
      }
      $uploadIds = [];
      $featured_upload_id = $post['fields']['featured']??null;
      $featured_attachment_id = null;
      $gallery = [];
      foreach ($post['images'] as $img) {
         $uploadId = $img['upload_id'];
         if (array_key_exists($uploadId, $uploads)){
            $imgID = $uploads[$uploadId]['attachment_id'];
            if ($uploadId === $featured_upload_id) {
               $featured_attachment_id = $imgID;
            } else {
               $gallery[] = $imgID;
            }
            $uploadIds[] = $uploadId;
         }
      }
      return new Result(
         outcome: !empty($createdPosts) ? 'success' : 'failed',
         result: ['posts' => $createdPosts]
      );
      if ($featured_attachment_id) {
         set_post_thumbnail($ID, $featured_attachment_id);
      } elseif (!empty($gallery)) {
         set_post_thumbnail($ID, $gallery[0]);
         array_shift($gallery);
      }
      if (!empty($gallery)) {
         $meta = Meta::forPost($ID);
         $fields = jvbGetFields($content, 'post');
         //add images to first found gallery field
         $found = false;
         foreach ($fields as $name =>$config) {
            if ($config['type'] === 'upload' && (array_key_exists('multiple', $config) && $config['multiple'] === true)) {
               $found = true;
               $meta->set($name, implode(',', $gallery));
               break;
            }
         }
         if (!$found) {
            error_log('Could not find a gallery upload field for post '.$ID);
         }
      }
      return [
         'ID'  => $ID,
         'usedUploads' => $uploadIds
      ];
   }
   private function processTimelineUploads(Operation $operation, array $data, Progress $progress, array $uploads):Result
@@ -389,33 +455,37 @@
      $user = $operation->userId;
      $createdPosts = [];
      $usedUploads = [];
      $errors = [];
      $content = jvbCheckBase($data['content']);
      $config = Features::getConfig($content);
      $defaultTitle = 'New '.$config['singular']. ' ';
      foreach($data['posts'] as $index => $post) {
         $progress->advance();
         $title = array_key_exists('post_title', $post['fields'])
            ? sanitize_text_field($post['fields']['post_title'])
            : $defaultTitle . ($index + 1);
         try {
            $title = array_key_exists('post_title', $post['fields'])
               ? sanitize_text_field($post['fields']['post_title'])
               : $defaultTitle . ($index + 1);
         $excerpt = array_key_exists('post_excerpt', $post['fields'])
            ? sanitize_textarea_field($post['fields']['post_excerpt'])
            : '';
            $excerpt = array_key_exists('post_excerpt', $post['fields'])
               ? sanitize_textarea_field($post['fields']['post_excerpt'])
               : '';
         $args = [
            'post_type' => $content,
            'post_author'  => $user,
            'post_status'  => 'draft',
            'post_title'   => $title,
            'post_slug'    => sanitize_title($title),
            'post_excerpt' => $excerpt
         ];
            $args = [
               'post_type' => $content,
               'post_author'  => $user,
               'post_status'  => 'draft',
               'post_title'   => $title,
               'post_slug'    => sanitize_title($title),
               'post_excerpt' => $excerpt
            ];
         $parent = wp_insert_post($args);
         $progress->advance();
         if ($parent && !is_wp_error($parent)) {
            $parent = wp_insert_post($args);
            if (!$parent || is_wp_error($parent)) {
               throw new Exception('Could not create post: '.$parent->get_error_message());
            }
            $childPosts = [];
            $featured = $post['fields']['featured']??null;
@@ -423,7 +493,6 @@
            foreach ($post['images'] as $img) {
               $uploadId = $img['upload_id'];
               $usedUploads[] = $uploadId;
               if (array_key_exists($uploadId, $uploads)) {
                  $attachmentId = (int)$uploads[$uploadId]['attachment_id'];
@@ -434,33 +503,58 @@
                  }
               }
            }
            if ($featuredID) {
               $usedUploads[] = $featuredID;
               set_post_thumbnail($parent, $featuredID);
            } elseif (!empty($childPosts)) {
               set_post_thumbnail($parent, (int)$childPosts[0]);
               $usedUploads[] = (int)$childPosts[0];
               array_shift($childPosts);
            }
            $createdChildren = [];
            if (!empty($childPosts)) {
               $args['post_parent'] = $parent;
               $args['post_excerpt'] = '';
               $createdPosts[$parent] = [];
               foreach($childPosts as $i => $imgID) {
                  $treatment = $i + 1;
                  $args ['post_title'] = $title.' - Treatment #'.$treatment;
                  $child = wp_insert_post($args);
                  if ($child && !is_wp_error($child)) {
                     $createdPosts[$parent][] = $child;
                     $createdChildren = $child;
                     $usedUploads[] = $imgID;
                     set_post_thumbnail($child, $imgID);
                  }
               }
            }
            $createdPosts[] = [
               'parent' => $parent,
               'children'  => $createdChildren
            ];
            $this->updateTimelineMetadata($parent);
            $progress->advance();
         } catch (Exception $e) {
            $errors[] = $e->getMessage();
            $progress->failItem($index ?? 'unknown', $e->getMessage());
         }
      }
      $outcome = !empty($createdPosts) ? 'success' : 'failed';
      if (!empty($createdPosts) && !empty($errors)) {
         $outcome = 'partial';
      }
      return new Result(
         outcome: !empty($createdPosts) ? 'success' : 'failed',
         result: ['posts' => $createdPosts]
         outcome: $outcome,
         result: [
            'upload_ids'   => $usedUploads,
            'created_posts'   => $createdPosts,
            'post_count'   => count($createdPosts),
            'processed_uploads'  => count($uploads),
            'errors'    => $errors
         ]
      );
   }