| | |
| | | use JVBase\managers\queue\executors\ContentExecutor; |
| | | use JVBase\managers\queue\TypeConfig; |
| | | use JVBase\meta\Meta; |
| | | use JVBase\registrar\Registrar; |
| | | use JVBase\rest\PermissionHandler; |
| | | use JVBase\rest\Response; |
| | | use JVBase\rest\Rest; |
| | | use JVBase\managers\Cache; |
| | | use JVBase\rest\Route; |
| | | use JVBase\utility\Features; |
| | | use WP_Post; |
| | | use WP_Query; |
| | | use WP_Error; |
| | | use WP_REST_Request; |
| | | use WP_REST_Response; |
| | | use Exception; |
| | | use WP_Term; |
| | | use WP_Term_Query; |
| | | |
| | | if (!defined('ABSPATH')) { |
| | | exit; // Exit if accessed directly |
| | |
| | | $this->cache->flush(); |
| | | } |
| | | $this->cache->connect('post', true); |
| | | $this->cache->connect('term', true); |
| | | add_action('init', [$this, 'registerContentExecutors'], 5); |
| | | } |
| | | |
| | |
| | | protected function initTimelineFields(string $content): void |
| | | { |
| | | $content = jvbNoBase($content); |
| | | if (!Features::forContent($content)->has('is_timeline')) { |
| | | |
| | | $config = Registrar::getInstance($content); |
| | | if (!$config || !$config->hasFeature('is_timeline')) { |
| | | return; |
| | | } |
| | | $config = Features::getConfig($content); |
| | | $this->fields = $config['fields']; |
| | | $this->fields = $config->getFields(); |
| | | |
| | | $this->timelineSharedFields = $this->getTimelineSharedFields($content); |
| | | array_unshift($this->timelineSharedFields, 'post_thumbnail'); |
| | |
| | | public function getTimelineUniqueFields(string $content): array |
| | | { |
| | | $content = jvbNoBase($content); |
| | | if (!Features::forContent($content)->has('is_timeline')) { |
| | | $registrar = Registrar::getInstance($content); |
| | | if (!$registrar || !$registrar->hasFeature('is_timeline')) { |
| | | return []; |
| | | } |
| | | $config = Features::getConfig($content); |
| | | $allFields = $config['fields']; |
| | | |
| | | $allFields = $registrar->getFields(); |
| | | |
| | | return array_keys(array_filter($allFields, function ($field) { |
| | | if (array_key_exists('for_all', $field) && $field['for_all'] === true) { |
| | |
| | | public function getTimelineSharedFields(string $content): array |
| | | { |
| | | $content = jvbNoBase($content); |
| | | if (!Features::forContent($content)->has('is_timeline')) { |
| | | $registrar = Registrar::getInstance($content); |
| | | if (!$registrar || !$registrar->hasFeature('is_timeline')) { |
| | | return []; |
| | | } |
| | | $config = Features::getConfig($content); |
| | | if (!$config || empty($config)) { |
| | | return []; |
| | | } |
| | | $allFields = $config['fields'] ?? []; |
| | | |
| | | $allFields = $registrar->getFields()??[]; |
| | | |
| | | return array_keys(array_filter($allFields, function ($field) { |
| | | if (!array_key_exists('for_all', $field) || $field['for_all'] === false) { |
| | | if (!array_key_exists('for_all', $field) || is_null($field['for_all']) || $field['for_all'] === false) { |
| | | return true; |
| | | } |
| | | return false; |
| | |
| | | unset($data['id']); |
| | | |
| | | error_log('[CONTENT]:'.print_r($data, true)); |
| | | |
| | | $queue = JVB()->queue(); |
| | | $queue->queueOperation( |
| | | 'content_update', |
| | | $user_id, |
| | | $data, |
| | | [ |
| | | 'count' => $count, |
| | | 'chunk_key' => 'posts', |
| | | 'chunk_size' => 10, |
| | | 'operation_id' => $operationId |
| | | ] |
| | | ); |
| | |
| | | public function getContent(WP_REST_Request $request): WP_REST_Response |
| | | { |
| | | $params = $request->get_params(); |
| | | error_log('getContent::params '.print_r($params, true)); |
| | | |
| | | $registrar = Registrar::getInstance($params['content']); |
| | | switch ($registrar->getType()) { |
| | | case 'term': |
| | | return $this->getTerms($request, $params, $registrar); |
| | | case 'user': |
| | | //TODO maybe do something? |
| | | break; |
| | | case 'post': |
| | | return $this->getPosts($request, $params, $registrar); |
| | | } |
| | | |
| | | return $this->error('Something went wrong, this does not appear to have a proper content type'); |
| | | } |
| | | |
| | | public function getPosts(WP_REST_Request $request, array $params, Registrar $registrar):WP_REST_Response |
| | | { |
| | | $user_id = $params['user']; |
| | | |
| | | $post_status = $params['status']; |
| | |
| | | } |
| | | $post_type = str_replace('-', '_', jvbCheckBase($params['content'])); |
| | | |
| | | |
| | | // Build query args |
| | | $args = [ |
| | | 'post_type' => $post_type, |
| | |
| | | 'author' => $user_id, |
| | | 'post_status' => $post_status |
| | | ]; |
| | | |
| | | |
| | | //Only top level posts for timeline types |
| | | if (Features::forContent($post_type)->has('is_timeline')) { |
| | | if ($registrar?->hasFeature('is_timeline')) { |
| | | $args['post_parent'] = 0; |
| | | } |
| | | |
| | | //Calendar filters |
| | | if (Features::forContent($post_type)->has('is_calendar')) { |
| | | if ($registrar?->hasFeature('is_calendar')) { |
| | | $args = $this->applyCalendarFilters($args, $params); |
| | | } |
| | | $taxonomies = array_filter($params, function ($param) { |
| | |
| | | $args['s'] = sanitize_text_field($params['search']); |
| | | } |
| | | |
| | | $key = $this->cache->generateKey($args); |
| | | $cached = $this->checkCache($key, $request); |
| | | if ($cached) { |
| | | return $cached; |
| | | } |
| | | |
| | | $this->post_type = jvbCheckBase($params['content'] ?? $params['type']); |
| | | |
| | | if (array_key_exists('s', $args)) { |
| | | $args = $this->applySearchFilters($args, $params); |
| | | } |
| | | |
| | | // Run query |
| | | $query = new WP_Query($args); |
| | | |
| | | $registrar = Registrar::getInstance($this->post_type); |
| | | $this->fields = $registrar->getFields()??[]; |
| | | $this->taxonomies = $this->getTaxonomies($this->post_type); |
| | | |
| | | $posts = array_map([$this, 'preparePost'], $query->posts); |
| | | |
| | | $data = [ |
| | | 'items' => $posts, |
| | | 'total' => $query->found_posts, |
| | | 'total_pages' => $query->max_num_pages, |
| | | 'has_more' => $args['paged']??1 < $query->max_num_pages, |
| | | ]; |
| | | |
| | | |
| | | $this->cache->set($key, $data); |
| | | |
| | | $response = Response::success($data); |
| | | return $this->addCacheHeaders($response); |
| | | } |
| | | public function getTerms(WP_REST_Request $request, array $params, Registrar $registrar):WP_REST_Response |
| | | { |
| | | // Build query args |
| | | $args = [ |
| | | 'taxonomy' => jvbCheckBase($params['content']), |
| | | 'number' => $params['per_page'] ?? 30, |
| | | 'orderby' => 'name', |
| | | 'order' => 'DESC', |
| | | 'hide_empty' => false, |
| | | ]; |
| | | $paged = $params['page']??1; |
| | | $args['page'] = $paged; |
| | | if ($paged > 1) { |
| | | $args['offset'] = ($paged-1) * $args['number']; |
| | | } |
| | | |
| | | //TODO |
| | | // if (array_key_exists('taxonomies', $params)) { |
| | | // $args = $this->applyTaxonomyFilters($args, $params); |
| | | // } |
| | | // 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 (array_key_exists('search', $params)) { |
| | | $args['s'] = sanitize_text_field($params['search']); |
| | | } |
| | | |
| | | $key = $this->cache->generateKey($args); |
| | | // Check HTTP cache headers with the specific content type |
| | |
| | | $response = Response::success($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)) { |
| | |
| | | } |
| | | |
| | | // Run query |
| | | $query = new WP_Query($args); |
| | | $query = new WP_Term_Query($args); |
| | | |
| | | |
| | | $this->fields = jvbGetFields(str_replace('-', '_', $this->post_type)); |
| | | $this->taxonomies = $this->getTaxonomies($this->post_type); |
| | | $posts = array_map([$this, 'prepareItem'], $query->posts); |
| | | |
| | | $terms = $query->get_terms(); |
| | | $data = [ |
| | | 'items' => $posts, |
| | | 'total' => $query->found_posts, |
| | | 'total_pages' => $query->max_num_pages, |
| | | 'has_more' => $args['paged']??1 < $query->max_num_pages, |
| | | 'total' => 0, |
| | | 'total_pages' => 0, |
| | | 'has_more' => false |
| | | ]; |
| | | |
| | | if (!is_wp_error($terms) && !empty($terms)) |
| | | { |
| | | $total = get_terms([ |
| | | 'taxonomy' => $args['taxonomy'], |
| | | 'hide_empty' => false, |
| | | 'fields' => 'count' |
| | | ]); |
| | | $data['total'] = $total; |
| | | $data['total_pages'] = max($total/$args['number'], 1); |
| | | $data['has_more'] = ($args['page'] * $args['number']) < $total; |
| | | } else { |
| | | $terms = []; |
| | | } |
| | | |
| | | $this->fields = $registrar->getFields()??[]; |
| | | |
| | | $this->taxonomies = []; |
| | | $data['items'] =array_map([$this, 'prepareTerm'], $terms); |
| | | |
| | | $this->cache->set($key, $data); |
| | | |
| | |
| | | */ |
| | | protected function getTaxonomies(string $content): array |
| | | { |
| | | $taxonomy_for = jvbGlobalTaxonomyFor(); |
| | | $out = []; |
| | | foreach ($taxonomy_for as $tax => $postTypes) { |
| | | if (in_array($content, $postTypes)) { |
| | | $out[BASE . $tax] = [ |
| | | 'label' => JVB_CONTENT[$content]['plural'], |
| | | 'icon' => $tax, |
| | | ]; |
| | | } |
| | | $registrar = Registrar::getInstance($content); |
| | | if (!$registrar || $registrar->getType()!== 'post') { |
| | | return []; |
| | | } |
| | | $out = []; |
| | | foreach ($registrar->registrar->taxonomies as $tax) { |
| | | $taxReg = Registrar::getInstance($tax); |
| | | $out[jvbCheckBase($tax)] = [ |
| | | 'label' => $taxReg->getPlural(), |
| | | 'icon' => $taxReg->getIcon()??jvbDefaultIcon() |
| | | ]; |
| | | } |
| | | |
| | | return $out; |
| | | } |
| | | |
| | |
| | | * |
| | | * @return array |
| | | */ |
| | | protected function prepareItem(WP_Post $post, bool $skip = false, bool $fields = true): array |
| | | protected function preparePost(WP_Post $post, bool $skip = false, bool $fields = true): array |
| | | { |
| | | if (!$skip && Features::forContent($post->post_type)->has('is_timeline')) { |
| | | $registrar = Registrar::getInstance($post->post_type); |
| | | if (!$skip && $registrar && $registrar->hasFeature('is_timeline')) { |
| | | $this->initTimelineFields($post->post_type); |
| | | return $this->formatTimeline($post); |
| | | } |
| | |
| | | 'modified' => $post->post_modified, |
| | | 'thumbnail' => get_the_post_thumbnail_url($post->ID), |
| | | 'alt' => get_post_meta(get_post_thumbnail_id(), '_wp_attachment_image_alt', true), |
| | | 'icon' => $this->post_type, |
| | | 'icon' => $registrar->getIcon(), |
| | | 'taxonomies' => [], |
| | | 'fields' => $fields, |
| | | 'images' => [], |
| | | ]; |
| | | |
| | | // Add taxonomy terms |
| | | foreach ($this->taxonomies as $taxonomy => $options) { |
| | | $tax = str_replace(BASE, '', $taxonomy); |
| | | $terms = wp_get_object_terms( |
| | | $post->ID, |
| | | $taxonomy, |
| | | ['fields' => 'id=>name'] |
| | | ); |
| | | $data['taxonomies'][$tax] = [ |
| | | 'terms' => (is_wp_error($terms)) ? [] : $terms, |
| | | 'name' => $options['label'], |
| | | 'icon' => $tax |
| | | ]; |
| | | } |
| | | |
| | | $images = $this->extractImages(); |
| | | |
| | | |
| | | $images = $this->extractImages($fields, $this->meta); |
| | | if (!empty($images)) { |
| | | $data['images'] = $images; |
| | | } |
| | | |
| | | |
| | | $taxonomies = $this->extractTerms($fields, $this->meta); |
| | | if (!empty($taxonomies)) { |
| | | $data['taxonomies'] = $taxonomies; |
| | | } |
| | | return $data; |
| | | } |
| | | |
| | | protected function extractImages(array $fields = []): array |
| | | /** |
| | | * @param WP_Term $post the post object |
| | | * |
| | | * @return array |
| | | */ |
| | | protected function prepareTerm(WP_Term $post, bool $fields = true): array |
| | | { |
| | | //Extract images |
| | | $images = []; |
| | | $get = []; |
| | | $fields = (empty($fields)) ? $this->fields : $fields; |
| | | $get = $this->getUploadFields($fields); |
| | | $registrar = Registrar::getInstance($post->taxonomy); |
| | | |
| | | $this->meta = Meta::forTerm($post->term_id); |
| | | $fields = ($fields) ? $this->meta->getAll() : []; |
| | | $data = [ |
| | | 'id' => $post->term_id, |
| | | 'title' => $post->name, |
| | | 'date' => $fields['created_date']??'', |
| | | 'modified' => $fields['modified_date']??'', |
| | | 'thumbnail' => '', |
| | | 'icon' => $registrar->getIcon(), |
| | | 'taxonomies' => [], |
| | | 'fields' => $fields, |
| | | 'images' => [], |
| | | ]; |
| | | |
| | | if (!empty($get)) { |
| | | $actualGet = array_map(function($fieldName) { |
| | | return (str_contains($fieldName, ':')) ? strtok($fieldName, ':') : $fieldName; |
| | | }, $get); |
| | | $complex = array_map(function($item) { |
| | | return explode(':', $item); |
| | | },array_filter($get, function($fieldName) { |
| | | return str_contains($fieldName, ':'); |
| | | })); |
| | | |
| | | $allImages = $this->meta->getAll($actualGet); |
| | | foreach ($allImages as $k => $imgs) { |
| | | //It's a complex field |
| | | if (is_array($imgs)) { |
| | | foreach ($imgs as $row) { |
| | | foreach ($row as $fName => $fValue) { |
| | | foreach ($complex as $c) { |
| | | if (in_array($k, $c)) { |
| | | foreach ($c as $complexField) { |
| | | if ($complexField === $fName) { |
| | | $images = $this->addImages($fValue, $images); |
| | | } |
| | | } |
| | | } |
| | | } |
| | | } |
| | | } |
| | | } else { |
| | | $images = $this->addImages($imgs, $images); |
| | | } |
| | | } |
| | | } |
| | | return $images; |
| | | } |
| | | public function addImages(string $imgs, array $images):array |
| | | { |
| | | $temp = explode(',', $imgs); |
| | | foreach ($temp as $img) { |
| | | if (is_numeric($img) && !array_key_exists($img, $images)) { |
| | | $images[$img] = jvbImageData((int)$img); |
| | | } |
| | | } |
| | | return $images; |
| | | } |
| | | public function getUploadFields($fields):array |
| | | { |
| | | $get = []; |
| | | foreach ($fields as $field => $config) { |
| | | if (in_array($config['type'], ['group', 'repeater'])) { |
| | | $nestedUploads = $this->getUploadFields($config['fields']); |
| | | foreach ($nestedUploads as $nested) { |
| | | $get[] = $field.':'.$nested; |
| | | } |
| | | } elseif ($config['type'] === 'upload' || $config['type'] === 'gallery' || $config['type'] === 'image' ) { |
| | | $get[] = $field; |
| | | } |
| | | $images = $this->extractImages($fields, $this->meta); |
| | | if (!empty($images)) { |
| | | $data['images'] = $images; |
| | | } |
| | | |
| | | return $get; |
| | | $taxonomies = $this->extractTerms($fields, $this->meta); |
| | | if (!empty($taxonomies)) { |
| | | $data['taxonomies'] = $taxonomies; |
| | | } |
| | | |
| | | error_log('Term data: '.print_r($data, true)); |
| | | return $data; |
| | | } |
| | | |
| | | |
| | | public function formatTimeline(WP_Post $post): array |
| | | { |
| | | $item = $this->prepareItem($post, true, false); |
| | | $item = $this->preparePost($post, true, false); |
| | | //Step 1: Get the fields that apply to all posts |
| | | $mainMeta = Meta::forPost($post->ID); |
| | | $item['fields'] = $mainMeta->getAll($this->timelineSharedFields); |
| | |
| | | |
| | | $images[$f['post_thumbnail']] = jvbImageData((int)$f['post_thumbnail']); |
| | | } |
| | | $item['fields']['timeline'] = $subFields; |
| | | $item['fields']['timeline_gallery'] = $subFields; |
| | | $item['images'] = $item['images'] + $images; |
| | | $item['number'] = $mainMeta->get('number'); |
| | | |