| | |
| | | use JVBase\rest\RestRouteManager; |
| | | use JVBase\managers\CacheManager; |
| | | use JVBase\meta\MetaManager; |
| | | use JVBase\utility\Features; |
| | | use WP_Post; |
| | | use WP_Query; |
| | | use WP_Error; |
| | | use WP_REST_Request; |
| | |
| | | } |
| | | $post_type = str_replace('-', '_',jvbCheckBase($params['content'])); |
| | | |
| | | $config = (array_key_exists($params['content'], JVB_CONTENT) && !empty(JVB_CONTENT[$params['content']])) ? JVB_CONTENT[$params['content']] : []; |
| | | $config = Features::getConfig($params['content']); |
| | | |
| | | |
| | | |
| | |
| | | 'author' => $user_id, |
| | | 'post_status' => $post_status |
| | | ]; |
| | | //Only top level posts for timeline types |
| | | if (Features::forContent($post_type)->has('is_timeline')) { |
| | | $args['post_parent'] = 0; |
| | | } |
| | | |
| | | //Calendar filters |
| | | if (jvbCheck('is_calendar', $config)) { |
| | | if (Features::forContent($post_type)->has('is_calendar')) { |
| | | $args = $this->applyCalendarFilters($args, $params); |
| | | } |
| | | if (array_key_exists('taxonomies', $params)) { |
| | |
| | | $args['s'] = sanitize_text_field($params['search']); |
| | | } |
| | | |
| | | |
| | | |
| | | error_log('Content Routes final args: '.print_r($args, true)); |
| | | |
| | | $key = $this->cache->generateKey($args); |
| | | $lastModified = $this->cache->getTimestamp($key); |
| | | if ($lastModified !== false) { |
| | | $headerCheck = $this->ifModifiedSince($lastModified, $args, $request); |
| | | if (!is_null($headerCheck)) { |
| | | return $headerCheck; |
| | | } |
| | | } else { |
| | | // No timestamp yet, but we can still set ETag |
| | | $etag = '"' . md5(serialize($args)) . '"'; |
| | | header('ETag: ' . $etag); |
| | | header('Cache-Control: private, max-age=30'); |
| | | // Check HTTP cache headers with the specific content type |
| | | $content_type = $params['content'] ?? $params['type']; |
| | | $cache_check = $this->checkHeaders($request, $content_type, [ |
| | | 'filter_hash' => $key, |
| | | ]); |
| | | if ($cache_check) { |
| | | return $cache_check; |
| | | } |
| | | |
| | | |
| | | $cache = $this->cache->get($key); |
| | | $cache = false; |
| | | if ($cache) { |
| | | return new WP_REST_Response($cache); |
| | | $response = new WP_REST_Response($cache); |
| | | return $this->addCacheHeaders($response); |
| | | } |
| | | |
| | | // Run query |
| | |
| | | |
| | | $this->cache->set($key, $data); |
| | | |
| | | return new WP_REST_Response($data); |
| | | $response = new WP_REST_Response($data); |
| | | return $this->addCacheHeaders($response); |
| | | } |
| | | |
| | | /** |
| | |
| | | $results = []; |
| | | |
| | | foreach ($posts as $ID => $post_data) { |
| | | if (Features::forContent($post_data['content'])->has('is_timeline')) { |
| | | $results[$ID] =$this->processTimelinePost($ID, $post_data); |
| | | continue; |
| | | } |
| | | if (str_starts_with($ID, 'new')) { |
| | | |
| | | error_log('New post detected. Creating... with: '.print_r([ |
| | |
| | | ]; |
| | | } |
| | | |
| | | /** |
| | | * Extracts the postdata for timeline post child posts from the pseudo-repeater element |
| | | * @param int $parent_id |
| | | * @param array $post_data |
| | | * @return array|true[] |
| | | */ |
| | | protected function processTimelinePost(int $parent_id, array $post_data):array |
| | | { |
| | | if (!$this->verifyOwnership($parent_id)) { |
| | | return ['success' => false, 'message' => 'No permission']; |
| | | } |
| | | |
| | | $rows = $post_data['fields'] ?? []; |
| | | if (empty($rows)) { |
| | | return ['success' => false, 'message' => 'No data']; |
| | | } |
| | | |
| | | $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); |
| | | }, ARRAY_FILTER_USE_KEY); |
| | | |
| | | $parentMeta = new MetaManager($parent_id, 'post'); |
| | | $parentMeta->setAll($allowedFields); |
| | | |
| | | // 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 |
| | | ]); |
| | | } |
| | | // Existing child post |
| | | else { |
| | | $child_id = (int) $row_id; |
| | | |
| | | // Verify ownership via parent |
| | | if (!in_array($child_id, $existing_children)) { |
| | | continue; // Skip if not actually a child of this parent |
| | | } |
| | | |
| | | // Update menu_order (position may have changed) |
| | | wp_update_post([ |
| | | 'ID' => $child_id, |
| | | 'menu_order' => $index |
| | | ]); |
| | | } |
| | | |
| | | // 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 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]; |
| | | } |
| | | |
| | | /** |
| | | * Handle batch content creation from uploads |
| | | * @param WP_REST_Request $request |
| | |
| | | } |
| | | |
| | | /** |
| | | * @param object $post the wordpress post object |
| | | * @param WP_Post $post the wordpress post object |
| | | * |
| | | * @return array |
| | | */ |
| | | protected function prepareItem(object $post):array |
| | | protected function prepareItem(WP_Post $post, $skip = false):array |
| | | { |
| | | if (!$skip && Features::forContent($post->post_type)->has('is_timeline')) { |
| | | return $this->formatTimeline($post); |
| | | } |
| | | $this->meta = new MetaManager($post->ID, 'post'); |
| | | $data = [ |
| | | 'id' => $post->ID, |
| | |
| | | ]; |
| | | } |
| | | |
| | | $images = $this->extractImages(); |
| | | |
| | | //Extract images |
| | | |
| | | if (!empty($images)) { |
| | | $data['images'] = $images; |
| | | } |
| | | |
| | | return $data; |
| | | } |
| | | protected function extractImages():array |
| | | { |
| | | //Extract images |
| | | $images = []; |
| | | $get = []; |
| | | foreach ($this->fields as $field => $config) { |
| | | if ($config['type'] === 'gallery' || $config['type'] === 'image' || $field === 'post_thumbnail') { |
| | | foreach ($this->fields as $field => $config) { |
| | | if ($config['type'] === 'gallery' || $config['type'] === 'image' || $field === 'post_thumbnail') { |
| | | $get[] = $field; |
| | | } |
| | | } |
| | | } |
| | | } |
| | | |
| | | if (!empty($get)) { |
| | | $allImages = $this->meta->getAll($get); |
| | |
| | | } |
| | | } |
| | | } |
| | | return $images; |
| | | } |
| | | |
| | | if (!empty($images)) { |
| | | $data['images'] = $images; |
| | | } |
| | | protected function formatTimeline(WP_Post $post):array |
| | | { |
| | | $data = $this->prepareItem($post, true); |
| | | $firstRow = $data['fields']; |
| | | $firstRow['id'] = $post->ID; |
| | | $firstRow['draggable'] = false; |
| | | $fields = [$firstRow]; |
| | | |
| | | return $data; |
| | | } |
| | | $children = get_children(['post_parent' => $post->ID, 'orderby' => 'menu_order']); |
| | | $allImages = []; |
| | | |
| | | 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 |
| | | |
| | | $images = $this->extractImages(); |
| | | if (!empty($images)) { |
| | | $allImages = $allImages + $images; |
| | | } |
| | | } |
| | | |
| | | if (!empty($allImages)) { |
| | | if (!array_key_exists('images', $data)) { |
| | | $data['images'] = []; |
| | | } |
| | | $data['images'] = $data['images'] + $allImages; |
| | | } |
| | | $data['fields']['timeline'] = $fields; |
| | | return $data; |
| | | } |
| | | |
| | | /** |
| | | * Builds the taxonomy query |