From 46d681c6b825d21b3f698d793c4e630c687d90ad Mon Sep 17 00:00:00 2001
From: Jake Vanderwerf <get@jakevanderwerf.ca>
Date: Thu, 21 May 2026 21:41:53 +0000
Subject: [PATCH] =Major CustomBlocks.php overhaul, expanding block support and customization from the editor. theme.json should now be updated on new themes to set brand colours, etc. Also note: major change to .col vs .row alignment: simplifying it to .top .bottom vs the confusion of the differences for .col/.row .start and .a-start
---
inc/blocks/FeedBlock.php | 741 ++++++++++++++++++++++++++++++++++----------------------
1 files changed, 452 insertions(+), 289 deletions(-)
diff --git a/inc/blocks/FeedBlock.php b/inc/blocks/FeedBlock.php
index 83f8b1d..c6b1f0b 100644
--- a/inc/blocks/FeedBlock.php
+++ b/inc/blocks/FeedBlock.php
@@ -1,343 +1,506 @@
<?php
namespace JVBase\blocks;
-use JVBase\managers\CacheManager;
+use JVBase\managers\Cache;
+use JVBase\registrar\Registrar;
+use JVBase\base\Site;
use JVBase\forms\TaxonomySelector;
use WP_Block;
if (!defined('ABSPATH')) {
- exit; // Exit if accessed directly
+ exit;
}
class FeedBlock
{
- protected CacheManager $cache;
- protected array $config;
- protected string $path = JVB_DIR.'/build/feed';
+ protected Cache $cache;
+ protected array $config;
+ protected string $path = JVB_DIR.'/build/feed';
- public function __construct()
- {
- $this->cache = CacheManager::for('feed',WEEK_IN_SECONDS);
- add_action('init', [$this, 'registerBlock']);
- }
+ public function __construct()
+ {
+ // Initialize cache with connections
+ $this->cache = Cache::for('feed_block', WEEK_IN_SECONDS);
+// if (JVB_TESTING) {
+// $this->cache->flush();
+// }
- public function registerBlock()
- {
- register_block_type($this->path, [
- 'render_callback' => [$this, 'render']
- ]);
- }
+ add_action('init', [$this, 'registerBlock']);
+ }
- protected function buildParams(array $attributes):array
- {
+ public function registerBlock()
+ {
+ register_block_type($this->path, [
+ 'render_callback' => [$this, 'render']
+ ]);
+ }
- if (!jvbCheck('inheritQuery', $attributes)) {
- return [
- 'title' => $attributes['title'],
- 'content' => $attributes['contentTypes'],
- 'taxonomies'=> $this->getTaxonomies($attributes['contentTypes'])
- ];
- }
+ protected function buildParams(array $attributes): array
+ {
+ if (!jvbCheck('inheritQuery', $attributes)) {
+ return [
+ 'title' => $attributes['title'],
+ 'content' => $attributes['contentTypes'],
+ 'taxonomies' => $this->getTaxonomies($attributes['contentTypes'])
+ ];
+ }
+ $config = [
+ 'is_gallery' => false,
+ 'content' => '',
+ 'taxonomies' => []
+ ];
+ $type = get_queried_object();
- if (isJVBUserType()) {
- return $this->buildProfileConfig();
- } elseif (isJVBContentTax()) {
- return $this->buildShopConfig();
- } elseif (is_tax()) {
- return $this->buildTaxonomyConfig();
- }
+ if (is_post_type_archive() || is_singular()) {
+ $content = is_singular() ? jvbNoBase($type->post_type) : jvbNoBase($type->name);
- return [];
- }
+ $registrar = Registrar::getInstance($content)??false;
+ if ($registrar) {
+ $config = array_merge($config, $registrar->getConfig('feed'));
+ } else {
+ $config['content'] = $content;
+ $config['icon'] = jvbDefaultIcon();
+ }
+ if (is_singular()) {
+ $config['source'] = $type->ID;
+ }
- protected function buildProfileConfig():array
- {
- $obj = get_queried_object();
- $content = jvbGetUserContentTypes($obj->ID);
- return [
- 'is_gallery' => true,
- 'content' => $content,
- 'context' => jvbNoBase($obj->post_type),
- 'taxonomies' => $this->getTaxonomies($content)
- ];
- }
+ $config['taxonomies'] = $this->getTaxonomies([$content]);
+ } elseif (is_tax()) {
+ $content = jvbNoBase($type->taxonomy);
+ $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;
+ }
- protected function buildShopConfig():array
- {
- $type = jvbNoBase(get_queried_object()->taxonomy);
- $content = jvbContentTaxContent($type);
+ if (!is_array($config['content'])) {
+ $config['content'] = [$config['content']];
+ }
- $context = [
- 'content' => $content,
- 'taxonomies' => $this->getTaxonomies($content),
- 'context' => $type,
- ];
- unset($context['taxonomies'][array_search($type, $context['taxonomies'])]);
- return $context;
- }
+ return $config;
+ }
- protected function buildTaxonomyConfig():array
- {
- $type = jvbNoBase(get_queried_object()->taxonomy);
- $content = jvbContentTaxContent($type);
- return [
- 'content' => $content,
- 'taxonomies' => $this->getTaxonomies($content),
- 'context' => $type
- ];
- }
+ /**
+ * Get taxonomies for given content types
- protected function getTaxonomies(array $content):array
- {
- global $jvb_taxonomy_for;
- $taxonomies = [];
- foreach ($jvb_taxonomy_for as $taxonomy => $for) {
- if (array_intersect($for, $content)) {
- $taxonomies[] = $taxonomy;
- }
- }
- return $taxonomies;
- }
+ */
+ protected function getTaxonomies(array $content): array
+ {
- public function render(array $attributes, string $content, WP_Block $block)
- {
- $this->config = $this->buildParams($attributes);
+ $taxonomies = [];
+ foreach ($content as $contentType) {
+ $registrar = Registrar::getInstance($contentType);
+ if (!$registrar) {
+ continue;
+ }
+ $contentTaxonomies = $registrar->registrar->taxonomies;
+ $contentTaxonomies = array_filter($contentTaxonomies, function($taxonomy) {
+ return Registrar::getInstance($taxonomy)?->hasFeature('show_feed');
+ });
+ $taxonomies = array_merge($taxonomies, $contentTaxonomies);
+ }
+
+ return array_unique($taxonomies);
+ }
+
+ public function render(array $attributes, string $content, WP_Block $block)
+ {
+
+ $this->config = $this->buildParams($attributes);
return $this->cache->remember(
- $this->config,
+ $this->cache->generateKey($this->config),
function() {
return $this->renderBlock();
}
);
- }
+ }
+ protected function getContext():string|bool
+ {
+ return array_key_exists('context', $this->config)?$this->config['context']:false;
+ }
+ protected function getIDS():array|bool
+ {
+ return (array_key_exists('ids', $this->config) && !empty($this->config['ids'])) ? $this->config['ids'] : false;
+ }
+ protected function getClasses():array|bool
+ {
+ return array_key_exists('classes', $this->config) && !empty($this->config['classes']) ? $this->config['classes'] : false;
+ }
+ protected function getSource():string|bool
+ {
+ return array_key_exists('source', $this->config) ? $this->config['source'] : false;
+ }
- protected function renderBlock():string
- {
- $work = isJVBUserType() ? ' id="work"' : '';
+ protected function getIcon():string|bool
+ {
+ return array_key_exists('icon', $this->config) ? $this->config['icon'] : false;
+ }
+ protected function isGallery():bool
+ {
+ return (array_key_exists('is_gallery', $this->config) && $this->config['is_gallery']);
+ }
+ protected function getContent():array|bool
+ {
+ return (array_key_exists('content', $this->config) && !empty ($this->config['content'])) ? $this->config['content'] : false;
+ }
- ob_start();
- ?>
- <section<?= $work ?> class="feed-block"
- data-source="<?= get_queried_object_id(); ?>"
- <?= (array_key_exists('context', $this->config)) ? ' data-context="'.$this->config['context'].'"' : '' ?>
- <?= (array_key_exists('is_gallery', $this->config)) ? ' data-gallery="true"' : ''?>>
- <?php
- $this->renderFilters();
- $this->renderGrid();
- $this->renderLoader();
- $this->renderTemplates();
- ?>
- </section>
- <?php
- return ob_get_clean();
- }
+ protected function renderBlock(): string
+ {
+ if (is_post_type_archive(BASE.'directory')) {
+ return '';
+ }
+ $ids = ($this->getIDS()) ? ' id="'.implode(' ',$this->getIDS()).'"' : '';
+ $classes = ($this->getClasses()) ? ' class="'.implode(' ',$this->getClasses()).'"' : '';
+ $source = ($this->getSource()) ? ' data-source="'.$this->getSource().'"' : '';
+ $context = ($this->getContext()) ? ' data-context="'.$this->getContext().'"' : '';
+ $icons = ($this->getIcon()) ? ' data-icon="'.$this->getIcon().'"' : ' data-icon="'.jvbLogoIcon().'"';
+ $gallery = $this->isGallery() ? ' data-gallery' : '';
+ $content = ($this->getContent()) ? ' data-content="'.implode(',',$this->getContent()).'"' : '';
+ ob_start();
+ ?>
+ <section<?= $ids.$classes ?> class="feed-block"<?= $content.$source.$context.$gallery.$icons ?>>
+ <?php
+ $this->renderFilters();
+ $this->renderGrid();
+ $this->renderLoader();
+ $this->renderTemplates();
+ 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();
+ }
- protected function renderFilters():void
- {
- if (empty($this->config)) {
- return;
- }
- $many = count($this->config['content']) > 1;
- global $jvb_everything;
- global $jvb_taxonomy_for;
- ?>
- <form class="feed-filters" data-save="feed">
- <details>
- <summary class="row btw">
- <span class="label">SHOWING: </span>
- <?php
- $labels = [];
- foreach ($this->config['content'] as $i => $type) :
- $checked = $i === 0 ? ' checked' : '';
+ protected function renderFilters(): void
+ {
+ if (empty($this->config)) {
+ return;
+ }
- $label = $jvb_everything[$type]['plural'];
- ?>
+ $feedContent = $this->getFeedContent();
+ $hasMany = count($this->getContent()) > 1;
+ ?>
+ <form class="filters" data-save="feed-<?=$this->getContext()?>">
+ <?php if ($hasMany) {
+ //If we have multiple content, only show the content first
+ ?>
+ <details class="col left">
+ <summary class="row x-btw">
+ <span class="label">SHOWING: </span>
+ <?php
+ $labels = [];
+ foreach ($this->getContent() as $i => $type) :
- <input type="radio"
- id="filter-<?= esc_attr($type) ?>"
- class="btn"
- name="content"
- value="<?= esc_attr($type) ?>"
- <?= $checked ?>>
- <label for="filter-<?= esc_attr($type) ?>" title="Show <?= $label ?>" class="row">
- <?= jvbIcon($type, ['title'=> $label]) ?>
- <span class="screen-reader-text"><?= $label ?></span>
- </label>
-
- <?php
- $labels['filter-'.$type] = $label;
- endforeach;
- ?>
- <ul class="filter-label">
- <?php
- $i = 0;
- foreach ($labels as $id =>$label) {
- $active = $i === 0 ? ' class="active"' : '';
- ?>
- <li id="<?=$id?>"<?=$active?>>
- <?=$label?>
- </li>
- <?php
- $i++;
- }
- ?>
- </ul>
+ $checked = $i === 0 ? ' checked' : '';
+ $label = $feedContent[$type]['plural'] ?? ucfirst($type);
+ ?>
+ <input type="radio"
+ id="filter-<?= esc_attr($type) ?>"
+ class="btn"
+ name="content"
+ data-filter="content"
+ value="<?= esc_attr($type) ?>"
+ <?= $checked ?>>
+ <label for="filter-<?= esc_attr($type) ?>" title="Show <?= $label ?>" class="row">
+ <?= jvbIcon($feedContent[$type]['icon']) ?>
+ <span class="screen-reader-text"><?= $label ?></span>
+ </label>
+ <?php
+ $labels['filter-'.$type] = $label;
+ endforeach;
+ ?>
+ <ul class="filter-label">
+ <?php
+ $i = 0;
+ foreach ($labels as $id => $label) {
+ $active = $i === 0 ? ' class="active"' : '';
+ ?>
+ <li id="<?= $id ?>"<?= $active ?>>
+ <?= $label ?>
+ </li>
+ <?php
+ $i++;
+ }
+ ?>
+ </ul>
+ <?php } ?>
- <?php if (is_user_logged_in()) : ?>
- <input type="checkbox" id="favourites" class="btn" name="favourites" value="on">
- <label for="favourites" title="Show Favourites" class="row">
- <?= jvbIcon('heart', ['title' =>'Favourites']) ?>
- <span class="screen-reader-text">Show Favourites Only</span>
- </label>
- <?php endif; ?>
+ <?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">
+ <?= jvbIcon('heart').jvbIcon('heart', ['style' => 'fill']) ?>
+ <span class="screen-reader-text">Show Favourites Only</span>
+ </label>
+ <?php endif; ?>
+ <?php if ($hasMany) { ?>
+ </summary>
+ <?php }
+ if (!empty ($this->config['taxonomies'])) {
+ ?>
+ <div class="filters">
+ <div class="filter-group row left">
+ <span class="label">FILTER BY:</span>
- <?php if ($many) {
- echo '</summary>';
- } ?>
+ <?php
+ foreach ($this->config['taxonomies'] as $tax) :
+ $registrar = Registrar::getInstance($tax)??false;
+ if (!$registrar) continue;
- <div class="filters">
- <div class="filter-group">
- <span class="label">FILTER BY:</span>
+ $contentForTax = $registrar->registrar->for;
+ $hidden = empty($contentForTax) ? ' hidden' : '';
- <?php
- foreach ($jvb_taxonomy_for as $tax => $items) :
- $hidden = !in_array($tax, $this->config['taxonomies']) ? ' hidden' : '';
- if (in_array($tax, $this->config['taxonomies'])) {
- $tax = new TaxonomySelector(
- 'feed-'.$tax,
- $tax,
- [
- 'update' => '.selected-items-section .selected-items',
- 'types' => $items,
- 'hidden'=> $hidden
- ]
- );
- echo $tax->render();
- }
+ $taxSelector = new TaxonomySelector(
+ 'feed-'.$tax,
+ $tax,
+ [
+ 'icon' => $registrar->getIcon()??jvbLogoIcon(),
+ 'update' => '.selected-items-section .selected-items',
+ 'types' => $contentForTax,
+ 'autocomplete' => false,
+ 'hidden' => $hidden,
+ 'output' => 'minimal'
+ ]
+ );
+ echo $taxSelector->render();
+ endforeach;
+ ?>
+ </div>
+ <div class="selected-items-section">
+ <div class="selected-items row"></div>
+ <div class="filter-actions row">
+ <?= str_replace('class="toggle-text"', 'class="toggle-text" hidden', jvbRenderToggleTextField('match', 'Match', 'Filters', 'ALL', 'ANY', false, ['filter' => 'match'])) ?>
+ <button type="button" class="clear-filters row" hidden>
+ <?= jvbIcon('x') ?>
+ Clear All Filters
+ </button>
+ </div>
+ </div>
+ </div>
+ <?php } ?>
+ <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
+ ?>
+ <input type="radio" id="order-title" class="btn" name="orderby" value="title" data-for="artist,shop" data-filter="orderby" hidden>
+ <label for="order-title" title="Order by Name" class="row">
+ <?= jvbIcon('alphabetical') ?>
+ <span class="label">Name</span>
+ </label>
- endforeach; ?>
- </div>
- <div class="selected-items-section">
- <div class="selected-items row"></div>
- <div class="filter-actions row">
- <?= jvbRenderToggleTextField('match', 'Match', 'Filters', 'ALL', 'ANY') ?>
- <button type="button" class="clear-filters row">
- <?= jvbIcon('x', ['title' => 'Clear']) ?>
- Clear All Filters
- </button>
- </div>
- </div>
- </div>
+ <input type="radio" id="order-date" class="btn" name="orderby" value="date" data-filter="orderby" checked>
+ <label for="order-date" title="Order by Date Created" class="row">
+ <?= jvbIcon('calendar', ['title' => 'Date']) ?>
+ <span class="label">Date Created</span>
+ </label>
- <div class="filter-group">
- <div class="order-by">
- <span class="label">ORDER BY:</span>
- <input type="radio" id="order-title" class="btn" name="orderby" value="title" data-for="artist,shop" hidden>
- <label for="order-title" title="Order by Name" class="row">
- <?= jvbIcon('alphabetical') ?>
- <span class="label">Name</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>
- <input type="radio" id="order-date" class="btn" name="orderby" value="date" checked>
- <label for="order-date" title="Order by Date" class="row">
- <?= jvbIcon('calendar', ['title'=>'Date']) ?>
- <span class="label">Date</span>
- </label>
+ <?php
+ $custom = [];
+ foreach ($this->getContent() as $content) {
+ $registrar = Registrar::getInstance($content)??false;
- <input type="radio" id="order-random" class="btn" name="orderby" value="random">
- <label for="order-random" title="Random Order" class="row">
- <?= jvbIcon('shuffle') ?>
- <span class="label">Random</span>
- </label>
- </div>
+ 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 class="order-direction radio-group-label" data-for-order="date,title">
- <span class="label">ORDER:</span>
- <input type="radio" id="order-desc" class="btn" name="order" value="desc" checked>
- <label for="order-desc" title="Newest First" class="row">
- <?= jvbIcon('sort-descending') ?>
- </label>
+ </div>
- <input type="radio" id="order-asc" class="btn" name="order" value="asc">
- <label for="order-asc" title="Oldest First" class="row">
- <?= jvbIcon('sort-ascending') ?>
- </label>
- </div>
- </div>
- </details>
- </form>
- <?php
- }
+ <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">
+ <?= jvbIcon('sort-descending') ?>
+ <span class="label" >DESC (A-Z)</span>
+ </label>
- protected function renderGrid():void
- {
- ?>
- <div class="item-grid"></div>
- <?php
- }
+ <input type="radio" id="order-asc" class="btn" name="order" value="asc" data-filter="order">
+ <label for="order-asc" title="Sort Ascending (Z-A, 10-1)" class="row">
+ <?= jvbIcon('sort-ascending') ?>
+ <span class="label" >ASC (Z-A)</span>
+ </label>
+ </div>
+ </div>
+ <?php if ($hasMany) { ?>
+ </details>
+ <?php } ?>
+ </form>
+ <?php
+ }
- protected function renderLoader():void
- {
- ?>
- <button type="button" class="load-more">
- <?= jvbIcon('arrow-elbow-left-down', ['title' =>'More']) ?>
- Show Me More
- <?= jvbIcon('arrow-elbow-right-down', ['title'=> 'More']) ?>
- </button>
+ protected function renderGrid(): void
+ {
+ ?>
+ <div class="item-grid">
+ <?php
+ $total = count($this->getContent()) - 1;
+ for ($i = 1; $i <= 36; $i++) {
+ $rand = rand(0, $total);
+ $config = Registrar::getInstance($this->getContent()[$rand]);
+ $icon = jvbIcon($config->getIcon??jvbLogoIcon());
+ ?>
+ <div class="placeholder"><?=apply_filters('jvbFeedPlaceholder', $icon) ?></div>
+ <?php
+ }
+ ?>
+ </div>
+ <?php
+ }
- <?= jvbLoadingScreen() ?>
- <?php
- if (array_key_exists('is_gallery', $this->config)) {
- jvbRenderGallery();
- }
- }
+ protected function renderLoader(): void
+ {
+ ?>
+ <button type="button" class="load-more">
+ <?= jvbIcon('arrow-elbow-left-down') ?>
+ Show Me More
+ <?= jvbIcon('arrow-elbow-right-down') ?>
+ </button>
- protected function renderTemplates():void
- {
- echo '<template class="feed-item">
- <details class="item feed" data-umami-event="view_feed">
- <summary class="row btw">
- <span class="handle">DETAILS</span>
- <button class="favourite" title="Add to favourites" onclick="toggleFavourite(this)">
- '.jvbIcon('heart')
- .jvbIcon('heart', ['style'=>'fill']).'
- </button>
- <div class="feed-images">
- <a>
- <img width="300px" height="300px" loading="lazy" decoding="async">
- </a>
- </div>
- </summary>
+ <?= jvbLoadingScreen() ?>
+ <?php
+ if (array_key_exists('is_gallery', $this->config)) {
+ jvbRenderGallery();
+ }
+ }
- <div class="item-info">
- <h3><a></a></h3>
- <div class="item">
- <span class="label"></span>
- <a></a>
- <p></p>
- </div>
- <div class="item-list">
- <span class="label"></span>
- <ul>
- <li>
- <a></a>
- </li>
- </ul>
- </div>
- </div>
- </div>
- </details>
- </template>';
+ protected function renderTemplates(): void
+ {
+ if ($this->getContent()) {
+ foreach ($this->getContent() as $content) {
+ echo $this->getDefaultTemplate($content);
+ }
+ }
- echo '<template class="emptyState">
- <div class="feed-empty-state">
- <h3>NOTHING HERE...</h3>
+ echo '<template class="feedTerm"><button class="remove-term">'.jvbIcon(jvbDefaultIcon()).'<span></span>'.jvbIcon('x').'</button></template>';
+ echo '<template class="emptyState">'.apply_filters('jvbFeedEmptyState', '<div class="empty-state">
+ <h3>'.jvbIcon($this->getIcon()).'NOTHING HERE'.jvbIcon($this->getIcon()).'</h3>
<p>Try tweaking those filters a bit.</p>
- <p>Edmonton\'s got talent - let\'s find it.</p>
- </div>
- </template>';
+ </div>', $this->config). '</template>';
+ echo '<template class="placeholderTemplate"><div class="placeholder">'.apply_filters('jvbFeedPlaceholder', jvbIcon(jvbLogoIcon())).'</div></template>';
+ }
- echo '<template class="placeholderTemplate"><div class="placeholder"></div></template>';
- }
+ protected function getFavouritesButton(string $content):string
+ {
+ $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">
+ '.jvbIcon('heart')
+ .jvbIcon('heart', ['style'=>'fill']).'
+ </button>';
+ }
+ protected function getUpvotesButton(string $content):string
+ {
+ $registrar = Registrar::getInstance($content);
+ if (!Site::has('karma') || !$registrar || !$registrar->hasFeature('karma')){
+ return '';
+ }
+ return '<div class="karma row">
+ <button type="button" class="vote" data-action="upvote">
+ '.jvbIcon('arrow-fat-up')
+ .jvbIcon('arrow-fat-up', ['style'=>'fill']).
+ '</button>
+ <button type="button" class="vote" data-action="downvote">
+ '.jvbIcon('arrow-fat-down')
+ .jvbIcon('arrow-fat-down', ['style'=>'fill']).
+ '</button>
+ <span class="score"></span>
+ </div>';
+ }
+ protected function getDefaultTemplate(string $content): string
+ {
+ $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
+ $imageTemplate = '<a>';
+ foreach ($images as $image) {
+ $imageTemplate .= '<img data-field="'.$image.'" width="300px" height="300px" sizes="(max-width: 768px) 100vw, (max-width: 1200px) 50vw, 800px" loading="lazy" decoding="async">';
+ }
+ $imageTemplate .= '</a>';
+ $template .= '<div class="images">'.apply_filters('jvbFeedImages', $imageTemplate, $content, $images).'</div>';
+
+ //Output default fields, but allow for filtering
+ $template .= '<details>
+ <summary>'.apply_filters('jvbFeedItemSummary', jvbIcon('dots-three'), $content).'</summary>';
+
+ $fieldsTemplate = '';
+
+ 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>';
+
+ return '<template class="feedItem'.ucfirst($content).'">'.apply_filters('jvbFeedItem', $template, $content).'</template>';
+ }
+
+ protected function defaultFieldTemplate(string $fieldType, string $fieldName):string
+ {
+ $data = ' data-field="'.$fieldName.'"';
+ switch ($fieldName) {
+ case 'post_title':
+ return '<h3'.$data.'></h3>';
+ case 'post_date':
+ case 'post_modified':
+ return '<time'.$data.'></time>';
+ }
+ return match($fieldType) {
+ 'upload' => '<img'.$data.' width="300px" height="300px" sizes="(max-width: 768px) 100vw, (max-width: 1200px) 50vw, 800px" loading="lazy" decoding="async">',
+ 'taxonomy' => '<ul'.$data.'><li><a><i></i></a></li></ul>',
+ default => '<p'.$data.'></p>',
+ };
+ }
+ /**
+ * Get feed content using Features instead of get_option
+ * Returns array of slug => config for types that show in feed
+ */
+ public function getFeedContent(): array
+ {
+ return JVB()->routes('feed')->getFeedTypesConfig();
+ }
}
--
Gitblit v1.10.0