| | |
| | | namespace JVBase\rest\routes; |
| | | |
| | | use JVBase\meta\Meta; |
| | | use JVBase\registrar\Registrar; |
| | | use JVBase\rest\Rest; |
| | | use JVBase\integrations\Umami; |
| | | use JVBase\rest\Route; |
| | | use JVBase\utility\Checker; |
| | | use JVBase\utility\Features; |
| | | use JVBase\base\Site; |
| | | use WP_Query; |
| | | use WP_Post; |
| | | use WP_Term; |
| | |
| | | { |
| | | protected int $per_page = 36; |
| | | protected ?Umami $tracker = null; |
| | | protected ?Checker $checker = null; |
| | | |
| | | protected ?array $fields = null; |
| | | protected ?array $timelineSharedFields = null; |
| | | protected ?array $timelineUniqueFields = null; |
| | |
| | | |
| | | public function init():void |
| | | { |
| | | $this->checker = Checker::getInstance(); |
| | | |
| | | if (Features::hasIntegration('umami')) { |
| | | if (Site::hasIntegration('umami')) { |
| | | $this->tracker = JVB()->connect('umami'); |
| | | } |
| | | } |
| | |
| | | 'highlight' => 'string', |
| | | ]) |
| | | ->auth('public') |
| | | ->rateLimit(30, 60); |
| | | ->rateLimit(30) |
| | | ->register(); |
| | | |
| | | // Feed types endpoint |
| | | Route::for('feed/types') |
| | | ->get([$this, 'getFeedTypes']) |
| | | ->auth('public') |
| | | ->rateLimit(60, 60); |
| | | ->rateLimit() |
| | | ->register(); |
| | | } |
| | | |
| | | /** |
| | |
| | | return $this->cache->remember( |
| | | $postID, |
| | | function() use ($postID, $type, $metaType, $post, $skip) { |
| | | $config = null; |
| | | $registrar = null; |
| | | switch ($metaType) { |
| | | case 'post': |
| | | $config = JVB_CONTENT[$type]; |
| | | |
| | | $registrar = Registrar::getInstance($type); |
| | | $meta = Meta::forPost($postID); |
| | | if (!$skip && array_key_exists('is_timeline', $config) && $config['is_timeline']) { |
| | | if (!$skip && $registrar->isTimeline()) { |
| | | return $this->formatTimeline($postID, $post); |
| | | } |
| | | break; |
| | | case 'term': |
| | | |
| | | $meta = Meta::forTerm($postID); |
| | | $config = JVB_TAXONOMY[$type]; |
| | | $registrar = Registrar::getInstance($type); |
| | | break; |
| | | case 'user': |
| | | $meta = Meta::forUser($postID); |
| | | $registrar = Registrar::getInstance($type); |
| | | break; |
| | | } |
| | | if (!$config) { |
| | | if (!$registrar) { |
| | | return []; |
| | | } |
| | | $fields = $config['fields']; |
| | | $fields = $registrar->getFields(); |
| | | |
| | | //Allow custom filtering for public fields |
| | | if (array_key_exists('feed', $config) && array_key_exists('fields', $config['feed'])) { |
| | | $fields = array_filter($fields, function($field) use ($config) { |
| | | return in_array($field, $config['feed']['fields']); |
| | | if (!empty($registrar->getConfig('feed')['fields'])) { |
| | | $fields = array_filter($fields, function($field) use ($registrar) { |
| | | return in_array($field, $registrar->getConfig('feed')['fields']); |
| | | }, ARRAY_FILTER_USE_KEY); |
| | | } |
| | | |
| | |
| | | |
| | | $out['id'] = $postID; |
| | | $out['content'] = $type; |
| | | $out['icon'] = $config['icon']??jvbDefaultIcon(); |
| | | $out['icon'] = $registrar->getIcon()??jvbDefaultIcon(); |
| | | if ($out['icon'] === '') { |
| | | $out['icon'] = jvbDefaultIcon(); |
| | | } |
| | | |
| | | if ($this->tracker) { |
| | | $args = ($metaType === 'post') ? ['owner_id' => $post->post_author] : []; |
| | |
| | | |
| | | switch ($metaType) { |
| | | case 'term': |
| | | $owner = (in_array($type, jvbContentTaxonomies()) ? $meta->getValue('owner') : null); |
| | | |
| | | $owner = $registrar->hasFeature('is_content') ? $meta->get('owner') : null; |
| | | if (!is_null($owner)) { |
| | | $out['user_id'] = $owner; |
| | | } |
| | |
| | | |
| | | protected function initTimelineFields(string $content):void |
| | | { |
| | | $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); |
| | | $this->fields = $config['fields']; |
| | | $this->fields = $registrar->getFields(); |
| | | |
| | | $this->timelineSharedFields = array_keys(array_filter($this->fields, function ($field) { |
| | | if (!array_key_exists('for_all', $field) || $field['for_all'] === false){ |
| | |
| | | if (empty($value)) { |
| | | continue; |
| | | } |
| | | if (!array_key_exists($key, JVB_TAXONOMY)) { |
| | | |
| | | $registrar = Registrar::getInstance($key); |
| | | if (!$registrar || $registrar->registrar->public === false){ |
| | | continue; |
| | | } |
| | | |
| | | $taxConfig = JVB_TAXONOMY[$key]; |
| | | if (isset($taxConfig['public']) && $taxConfig['public'] === false) { |
| | | continue; |
| | | } |
| | | $terms = array_map('absint', explode(',', $value)); |
| | | $terms = array_filter($terms); // Remove 0 values |
| | | |
| | |
| | | protected function getAuthorData(WP_Post $post) |
| | | { |
| | | $author = $post->post_author; |
| | | $userLink = get_user_meta($author, BASE.'link', true); |
| | | $userLink = get_user_meta($author, BASE.'profile_link', true); |
| | | return $this->cache->remember( |
| | | $userLink, |
| | | function () use ($userLink, $author) { |
| | | $label = jvbUserRole($author); |
| | | if (array_key_exists($label, JVB_USER)) { |
| | | $label = JVB_USER[$label]['label']; |
| | | $registrar = Registrar::getInstance($label); |
| | | if ($registrar) { |
| | | $label = $registrar->getSingular(); |
| | | } else { |
| | | $label = 'Artist'; |
| | | } |
| | |
| | | |
| | | protected function getTaxonomies(int $postID, string $content): array |
| | | { |
| | | $taxonomies = jvbTaxonomiesForContent($content); |
| | | $registrar = Registrar::getInstance($content)??false; |
| | | $taxonomies = $registrar->registrar->taxonomies; |
| | | $out = []; |
| | | foreach ($taxonomies as $tax) { |
| | | $terms = get_the_terms($postID, $tax); |
| | | $t = []; |
| | | if ($terms && !is_wp_error($terms)) { |
| | | $config = jvbNoBase($tax); |
| | | $config = Registrar::getInstance($tax); |
| | | $out[] = [ |
| | | 'icon' => $config, |
| | | 'title' => JVB_TAXONOMY[$config]['plural'], |
| | | 'icon' => $config->getIcon(), |
| | | 'title' => $config->getPlural(), |
| | | 'terms' => array_map(function ($term) use ($tax, $postID, $content) { |
| | | $item = $this->cache->remember( |
| | | $term->term_id, |
| | |
| | | $data = $request->get_params(); |
| | | $args = [ |
| | | 'post_type' => (array_key_exists($data['content'], $this->buildFeedTypesConfig())) ? |
| | | BASE . $data['content'] : |
| | | BASE . array_key_first(JVB_CONTENT), |
| | | jvbCheckBase($data['content']) : null, |
| | | 'paged' => intval($data['page'] ?? 1), |
| | | 'posts_per_page' => $this->per_page, |
| | | ]; |
| | |
| | | // Extract key and value |
| | | $key = array_keys($data['highlight'])[0] ?? false; |
| | | $value = array_values($data['highlight'])[0] ?? false; |
| | | error_log('Highlighted item: ' . $key); |
| | | error_log('Highlighted item: ' . $value); |
| | | error_log('No Single Content Types: ' . print_r(jvbNoSingleContentTypes(), true)); |
| | | error_log('Page: ' . print_r($data['paged'], true)); |
| | | if (in_array($key, jvbNoSingleContentTypes()) && $value && $data['paged'] === 1) { |
| | | error_log('Formatted Highlighted item: ' . print_r($this->formatItem($value), true)); |
| | | error_log('Items: ' . print_r($items, true)); |
| | | if ($key && $data['paged'] === 1) { |
| | | array_unshift($items, $this->formatItem($value)); |
| | | error_log('Items after unshift: ' . print_r($items, true)); |
| | | } |
| | | return $items; |
| | | } |
| | |
| | | return $args; |
| | | } |
| | | |
| | | $registrar = Registrar::getInstance($context['type']); |
| | | switch (true) { |
| | | case contentIsJVBUserType($context['type']): |
| | | case $registrar->hasFeature('profile_link'): |
| | | $args['author'] = (int)get_post_meta($context['id'], BASE . 'link', true); |
| | | break; |
| | | case taxIsJVBContentTax($context['type']): |
| | | case $registrar->getType() === 'term' && $registrar->hasFeature('is_content'): |
| | | $args['post_type'] = is_array($args['post_type']) |
| | | ? $args['post_type'] |
| | | : explode(',', $args['post_type']); |
| | | |
| | | // Check if filtering global feed content |
| | | if (in_array($context['type'], jvbGlobalFeedContentTaxonomies())) { |
| | | if (in_array(jvbNoBase($context['type']), Registrar::getFeatured('is_content', 'term'))) { |
| | | // Global: show posts from any content type with this taxonomy |
| | | $for_content = JVB_TAXONOMY[$context['type']]['for_content'] ?? []; |
| | | if (empty($for_content)) { |
| | | // Fall back to any content that has this taxonomy registered |
| | | $for_content = array_keys( |
| | | array_filter( |
| | | JVB_CONTENT, |
| | | fn($c) => in_array($context['type'], $c['taxonomies'] ?? []) |
| | | ) |
| | | ); |
| | | } |
| | | $for_content = Registrar::getInstance($context['type'])->registrar->for ?? []; |
| | | |
| | | // Convert to full post types with BASE prefix |
| | | $post_types = array_map(fn($type) => BASE . $type, $for_content); |
| | | $post_types = array_map(fn($type) => jvbCheckBase($type), $for_content); |
| | | |
| | | // Filter to only show_feed content types |
| | | $show_feed_types = Features::getTypesWithFeature('show_feed', 'content'); |
| | | $show_feed_types = Registrar::getFeatured('show_feed', 'post'); |
| | | $args['post_type'] = array_intersect( |
| | | $post_types, |
| | | array_map(fn($type) => BASE . $type, $show_feed_types) |
| | | array_map(fn($type) => jvbCheckBase($type), $show_feed_types) |
| | | ); |
| | | } |
| | | |
| | |
| | | { |
| | | $postType = is_array($args['post_type']) ? $args['post_type'][0] : $args['post_type']; |
| | | $slug = jvbNoBase($postType); |
| | | |
| | | if (Features::forContent($slug)->has('is_timeline')) { |
| | | $registrar = Registrar::getInstance($slug); |
| | | if ($registrar && $registrar->hasFeature('is_timeline')) { |
| | | $args['post_parent'] = 0; |
| | | } |
| | | if (in_array($slug, Features::getTypesWithFeature('is_content', 'taxonomy'))) { |
| | | if ($registrar && $registrar->hasFeature('is_content')) { |
| | | return $this->handleContentTaxonomies($args); |
| | | } |
| | | $args['fields'] = 'ids'; |
| | |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * Get custom table fields for a taxonomy |
| | | * @param string $taxonomy Taxonomy type |
| | | * @return array Field definitions |
| | | */ |
| | | protected function getCustomTableFields(string $taxonomy): array |
| | | { |
| | | return jvbContentTaxonomiesTableFields($taxonomy)['fields'] ?? []; |
| | | } |
| | | |
| | | /** |
| | | * Get available feed types (for block editor) |
| | |
| | | public function getFeedTypes(WP_REST_Request $request): WP_REST_Response |
| | | { |
| | | // Check HTTP cache |
| | | $cache_check = $this->checkHeaders($request, ['feed_types']); |
| | | $cache_check = $this->checkHeaders($request, 'feed_types'); |
| | | if ($cache_check) { |
| | | return $cache_check; |
| | | } |
| | |
| | | */ |
| | | protected function buildFeedTypesConfig(): array |
| | | { |
| | | if (!$this->checker) { |
| | | $this->checker = Checker::getInstance(); |
| | | } |
| | | return $this->cache->remember( |
| | | 'contentTypes', |
| | | function () { |
| | | $config = []; |
| | | |
| | | // Get content types with show_feed |
| | | $contentTypes = Features::getTypesWithFeature('show_feed', 'content'); |
| | | $contentTypes = Registrar::getFeatured('show_feed', 'post'); |
| | | foreach ($contentTypes as $slug) { |
| | | $this->cache->tag('content:'.$slug); |
| | | $contentConfig = JVB_CONTENT[$slug] ?? null; |
| | | if (!$contentConfig) continue; |
| | | $registrar = Registrar::getInstance($slug); |
| | | if (!$registrar) continue; |
| | | |
| | | $config[$slug] = [ |
| | | 'type' => 'content', |
| | | 'singular' => $contentConfig['singular'] ?? ucfirst($slug), |
| | | 'plural' => $contentConfig['plural'] ?? ucfirst($slug) . 's', |
| | | 'icon' => $slug, |
| | | 'taxonomies' => $this->checker->getTaxonomiesForContent($slug), |
| | | 'singular' => $registrar->getSingular(), |
| | | 'plural' => $registrar->getPlural(), |
| | | 'icon' => $registrar->getIcon(), |
| | | 'taxonomies' => $registrar->registrar->taxonomies, |
| | | ]; |
| | | } |
| | | |
| | | // Get taxonomies with show_feed (content taxonomies) |
| | | $taxonomies = Features::getTypesWithFeature('show_feed', 'taxonomy'); |
| | | $taxonomies = Registrar::getFeatured('show_feed', 'term'); |
| | | foreach ($taxonomies as $slug) { |
| | | $taxConfig = JVB_TAXONOMY[$slug] ?? null; |
| | | if (!$taxConfig || !($taxConfig['is_content'] ?? false)) { |
| | | $registrar = Registrar::getInstance($slug); |
| | | if (!$registrar || !($registrar->hasFeature('is_content') ?? false)) { |
| | | continue; |
| | | } |
| | | |
| | |
| | | |
| | | $config[$slug] = [ |
| | | 'type' => 'taxonomy', |
| | | 'singular' => $taxConfig['singular'] ?? ucfirst($slug), |
| | | 'plural' => $taxConfig['plural'] ?? ucfirst($slug) . 's', |
| | | 'icon' => $slug, |
| | | 'singular' => $registrar->getSingular(), |
| | | 'plural' => $registrar->getPlural(), |
| | | 'icon' => $registrar->getIcon(), |
| | | 'taxonomies' => [], // Content taxonomies don't have sub-taxonomies |
| | | 'for_content' => $taxConfig['for_content'] ?? [], |
| | | 'for_content' => $registrar->registrar->for ?? [] |
| | | ]; |
| | | } |
| | | |