| | |
| | | <?php |
| | | namespace JVBase\blocks; |
| | | |
| | | use JVBase\managers\CacheManager; |
| | | use JVBase\utility\Features; |
| | | use JVBase\utility\Checker; |
| | | use JVBase\managers\Cache; |
| | | use JVBase\registrar\Registrar; |
| | | use JVBase\base\Site; |
| | | use JVBase\forms\TaxonomySelector; |
| | | use WP_Block; |
| | | |
| | |
| | | |
| | | class FeedBlock |
| | | { |
| | | protected CacheManager $cache; |
| | | protected Cache $cache; |
| | | protected array $config; |
| | | protected string $path = JVB_DIR.'/build/feed'; |
| | | |
| | | public function __construct() |
| | | { |
| | | // Initialize cache with connections |
| | | $this->cache = CacheManager::for('feed_block', WEEK_IN_SECONDS); |
| | | $this->cache->clear(); |
| | | // Set up cache connections for all feed content types |
| | | $this->setupCacheConnections(); |
| | | $this->cache = Cache::for('feed_block', WEEK_IN_SECONDS); |
| | | // if (JVB_TESTING) { |
| | | // $this->cache->flush(); |
| | | // } |
| | | |
| | | add_action('init', [$this, 'registerBlock']); |
| | | } |
| | | |
| | | /** |
| | | * Set up cache connections for feed content |
| | | */ |
| | | protected function setupCacheConnections(): void |
| | | { |
| | | // Connect to all content types that show in feed |
| | | $contentTypes = Features::getTypesWithFeature('show_feed', 'content'); |
| | | foreach ($contentTypes as $type) { |
| | | CacheManager::for('feed_content')->connectTo('post', $type); |
| | | } |
| | | |
| | | // Connect to all taxonomies that show in feed |
| | | $taxonomies = Features::getTypesWithFeature('show_feed', 'taxonomy'); |
| | | foreach ($taxonomies as $tax) { |
| | | CacheManager::for('feed_taxonomy')->connectTo('taxonomy', $tax); |
| | | } |
| | | } |
| | | |
| | | public function registerBlock() |
| | | { |
| | | register_block_type($this->path, [ |
| | |
| | | |
| | | if (is_post_type_archive() || is_singular()) { |
| | | $content = is_singular() ? jvbNoBase($type->post_type) : jvbNoBase($type->name); |
| | | $mainConfig = JVB_CONTENT[$content]??false; |
| | | if ($mainConfig && array_key_exists('feed', $mainConfig) && array_key_exists('config', $mainConfig['feed'])){ |
| | | $config = array_merge($config, $mainConfig['feed']['config']); |
| | | |
| | | $registrar = Registrar::getInstance($content)??false; |
| | | if ($registrar) { |
| | | $config = array_merge($config, $registrar->getConfig('feed')); |
| | | } else { |
| | | $config['content'] = $content; |
| | | $config['icon'] = JVB_CONTENT[$content]['icon']??[jvbLogoIcon()]; |
| | | $config['icon'] = jvbDefaultIcon(); |
| | | } |
| | | if (is_singular()) { |
| | | $config['source'] = $type->ID; |
| | |
| | | $config['taxonomies'] = $this->getTaxonomies([$content]); |
| | | } elseif (is_tax()) { |
| | | $content = jvbNoBase($type->taxonomy); |
| | | $mainConfig = JVB_TAXONOMY[$content]??false; |
| | | if ($mainConfig) { |
| | | $config['content'] = $mainConfig['for_content']; |
| | | $config['context'] = $content; // ← ADD THIS |
| | | $config['taxonomies'] = $this->getTaxonomies($mainConfig['for_content']); |
| | | if (array_key_exists('feed', $mainConfig) && array_key_exists('config', $mainConfig['feed'])){ |
| | | $config = array_merge($config, $mainConfig['feed']['config']); |
| | | $registrar = Registrar::getInstance($content)??false; |
| | | if ($registrar) { |
| | | $config['content'] = $registrar->registrar->for; |
| | | $config['context'] = $content; |
| | | $config['taxonomies'] = $this->getTaxonomies($registrar->registrar->for); |
| | | if (!empty($registrar->getConfig('feed'))){ |
| | | $config = array_merge($config, $registrar->getConfig('feed')); |
| | | } |
| | | } |
| | | $config['source'] = $type->term_id; |
| | |
| | | |
| | | /** |
| | | * Get taxonomies for given content types |
| | | * Uses Checker instead of globals |
| | | |
| | | */ |
| | | protected function getTaxonomies(array $content): array |
| | | { |
| | | $checker = Checker::getInstance(); |
| | | |
| | | $taxonomies = []; |
| | | |
| | | foreach ($content as $contentType) { |
| | | $contentTaxonomies = $checker->getTaxonomiesForContent($contentType); |
| | | $registrar = Registrar::getInstance($contentType); |
| | | if (!$registrar) { |
| | | continue; |
| | | } |
| | | $contentTaxonomies = $registrar->registrar->taxonomies; |
| | | $contentTaxonomies = array_filter($contentTaxonomies, function($taxonomy) { |
| | | return array_key_exists('show_feed', JVB_TAXONOMY[$taxonomy]) && JVB_TAXONOMY[$taxonomy]['show_feed']; |
| | | return Registrar::getInstance($taxonomy)?->hasFeature('show_feed'); |
| | | }); |
| | | $taxonomies = array_merge($taxonomies, $contentTaxonomies); |
| | | } |
| | |
| | | |
| | | $this->config = $this->buildParams($attributes); |
| | | return $this->cache->remember( |
| | | $this->config, |
| | | $this->cache->generateKey($this->config), |
| | | function() { |
| | | return $this->renderBlock(); |
| | | } |
| | |
| | | echo TaxonomySelector::outputSelectorModal(); |
| | | ?> |
| | | </section> |
| | | <footer><button data-action="refresh" data-ignore><?=jvbIcon('arrows-clockwise')?><span>Hard Refresh</span></span></button></footer> |
| | | <?php |
| | | return ob_get_clean(); |
| | | } |
| | |
| | | <?php if ($hasMany) { |
| | | //If we have multiple content, only show the content first |
| | | ?> |
| | | <details class="col a-start"> |
| | | <summary class="row btw"> |
| | | <details class="col left"> |
| | | <summary class="row x-btw"> |
| | | <span class="label">SHOWING: </span> |
| | | <?php |
| | | $labels = []; |
| | |
| | | <?php } ?> |
| | | |
| | | |
| | | <?php if (Features::forSite()->has('favourites') && is_user_logged_in()) : ?> |
| | | <?php if (Site::has('favourites') && is_user_logged_in()) : ?> |
| | | <input type="checkbox" id="favourites" class="btn" name="favourites" value="on" |
| | | data-filter="favourites"> |
| | | <label for="favourites" title="Show Favourites" class="row"> |
| | |
| | | if (!empty ($this->config['taxonomies'])) { |
| | | ?> |
| | | <div class="filters"> |
| | | <div class="filter-group row start"> |
| | | <div class="filter-group row left"> |
| | | <span class="label">FILTER BY:</span> |
| | | |
| | | <?php |
| | | $checker = Checker::getInstance(); |
| | | foreach ($this->config['taxonomies'] as $tax) : |
| | | $taxConfig = JVB_TAXONOMY[$tax] ?? null; |
| | | if (!$taxConfig) continue; |
| | | $registrar = Registrar::getInstance($tax)??false; |
| | | if (!$registrar) continue; |
| | | |
| | | $contentForTax = $checker->getContentForTaxonomy($tax); |
| | | $contentForTax = $registrar->registrar->for; |
| | | $hidden = empty($contentForTax) ? ' hidden' : ''; |
| | | |
| | | $taxSelector = new TaxonomySelector( |
| | | 'feed-'.$tax, |
| | | $tax, |
| | | [ |
| | | 'icon' => $taxConfig['icon']??jvbLogoIcon(), |
| | | 'icon' => $registrar->getIcon()??jvbLogoIcon(), |
| | | 'update' => '.selected-items-section .selected-items', |
| | | 'types' => $contentForTax, |
| | | 'autocomplete' => false, |
| | |
| | | </div> |
| | | </div> |
| | | <?php } ?> |
| | | <div class="row btw nowrap"> |
| | | <div class="order-by filter-group row start w-full"> |
| | | <div class="row x-btw nowrap"> |
| | | <div class="order-by filter-group row left w-full"> |
| | | <span class="label">ORDER BY:</span> |
| | | <?php |
| | | //TODO: Get content types that can be sorted alphabetically |
| | |
| | | </label> |
| | | |
| | | <input type="radio" id="order-date" class="btn" name="orderby" value="date" data-filter="orderby" checked> |
| | | <label for="order-date" title="Order by Date" class="row"> |
| | | <label for="order-date" title="Order by Date Created" class="row"> |
| | | <?= jvbIcon('calendar', ['title' => 'Date']) ?> |
| | | <span class="label">Date</span> |
| | | <span class="label">Date Created</span> |
| | | </label> |
| | | |
| | | <input type="radio" id="order-modified" class="btn" name="orderby" value="modified" data-filter="orderby"> |
| | | <label for="order-modified" title="Order by Date Modified" class="row"> |
| | | <?= jvbIcon('clock-clockwise') ?> |
| | | <span class="label">Date Modified</span> |
| | | </label> |
| | | |
| | | <?php |
| | | $custom = []; |
| | | foreach ($this->getContent() as $content) { |
| | | $registrar = Registrar::getInstance($content)??false; |
| | | |
| | | if ($registrar && !empty($registrar->config('feed')->getCustomOrder())) { |
| | | $custom = array_merge_recursive($custom, $registrar->config('feed')->getCustomOrder()); |
| | | } |
| | | } |
| | | foreach ($custom as $slug => $conf) { |
| | | ?> |
| | | <input type="radio" id="order-<?=$slug?>" class="btn" name="orderby" value="<?=$slug?>" data-for="<?=$conf['for']?>" data-filter="orderby"> |
| | | <label for="order-<?=$slug?>" title="<?= $conf['label']?>" class="row"> |
| | | <?= jvbIcon($conf['icon']) ?> |
| | | <span class="label"><?=$conf['label']?></span> |
| | | </label> |
| | | <?php |
| | | } |
| | | $custom = implode(',', array_keys($custom)); |
| | | ?> |
| | | <input type="radio" id="order-random" class="btn" name="orderby" value="random" data-filter="orderby"> |
| | | <label for="order-random" title="Random Order" class="row"> |
| | | <?= jvbIcon('shuffle') ?> |
| | | <span class="label">Random</span> |
| | | </label> |
| | | |
| | | </div> |
| | | |
| | | <div class="order-direction filter-group row start w-full" data-for-order="date,title"> |
| | | <div class="order-direction filter-group row left w-full" data-for-order="date,modified,title<?= $custom === '' ? '' : ','.$custom?>"> |
| | | <span class="label">ORDER:</span> |
| | | <input type="radio" id="order-desc" class="btn" name="order" value="desc" data-filter="order" checked> |
| | | <label for="order-desc" title="Sort Descending (A-Z, 1-10)" class="row"> |
| | |
| | | $total = count($this->getContent()) - 1; |
| | | for ($i = 1; $i <= 36; $i++) { |
| | | $rand = rand(0, $total); |
| | | $config = Features::getConfig($this->getContent()[$rand]); |
| | | $icon = jvbIcon($config['icon']??jvbLogoIcon()); |
| | | $config = Registrar::getInstance($this->getContent()[$rand]); |
| | | $icon = jvbIcon($config->getIcon??jvbLogoIcon()); |
| | | ?> |
| | | <div class="placeholder"><?=apply_filters('jvbFeedPlaceholder', $icon) ?></div> |
| | | <?php |
| | |
| | | |
| | | protected function getFavouritesButton(string $content):string |
| | | { |
| | | if (!Features::forSite()->has('favourites') && !Features::forContent($content)->has('favouritable')) { |
| | | $registrar = Registrar::getInstance($content); |
| | | if (!$registrar || !Site::has('favourites') || !$registrar->hasFeature('favouritable')) { |
| | | return ''; |
| | | } |
| | | return '<button class="favourite" type="button" title="Add to favourites" data-action="favourite"> |
| | |
| | | } |
| | | protected function getUpvotesButton(string $content):string |
| | | { |
| | | if (!Features::forSite()->has('karma') && !Features::forContent($content)->has('karma')){ |
| | | $registrar = Registrar::getInstance($content); |
| | | if (!Site::has('karma') || !$registrar || !$registrar->hasFeature('karma')){ |
| | | return ''; |
| | | } |
| | | return '<div class="karma row"> |
| | |
| | | } |
| | | protected function getDefaultTemplate(string $content): string |
| | | { |
| | | $config = JVB_CONTENT[$content]??[]; |
| | | $hasConfig = array_key_exists('feed', $config); |
| | | $images = ($hasConfig && array_key_exists('images', $config['feed']) ? $config['feed']['images']:['post_thumbnail']); |
| | | $images = (is_array($images)) ? $images : [$images]; |
| | | $fields = ($hasConfig && array_key_exists('fields', $config['feed']) ? $config['feed']['fields']:['post_title', 'post_date']); |
| | | $config = Registrar::getInstance($content)->getConfig('feed'); |
| | | $allFields = Registrar::getFieldsFor($content); |
| | | $images = $config['images']??['post_thumbnail']; |
| | | $fields = $config['fields']??['post_title','post_date','post_excerpt']; |
| | | $fields = array_filter($fields, function($field) use($images) { |
| | | return !in_array($field, $images); |
| | | }); |
| | | $fields = array_filter($allFields, function($field) use($fields) { |
| | | return in_array($field, $fields); |
| | | }, ARRAY_FILTER_USE_KEY); |
| | | $template = '<div class="feed item col '.$content.'">'.$this->getFavouritesButton($content).$this->getUpvotesButton($content); |
| | | |
| | | //Add all defined images, but allow for filtering |
| | |
| | | |
| | | $fieldsTemplate = ''; |
| | | |
| | | foreach ($fields as $fieldName) { |
| | | $fieldType = JVB_CONTENT[$content][$fieldName]['type']??'text'; |
| | | $fieldsTemplate .= apply_filters('jvbFeedItemField', $this->defaultFieldTemplate($fieldType, $fieldName), $content, $fieldName, $fieldType); |
| | | foreach ($fields as $fieldName => $config) { |
| | | $fieldsTemplate .= apply_filters('jvbFeedItemField', $this->defaultFieldTemplate($config['type'], $fieldName), $content, $fieldName, $config['type']); |
| | | } |
| | | $template .= '<div class="item-info">'.apply_filters('jvbFeedItemFields', $fieldsTemplate, $content, $fields).'</div>'; |
| | | $template .= '</details></div>'; |