| | |
| | | { |
| | | // 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(); |
| | | |
| | |
| | | $config = array_merge($config, $mainConfig['feed']['config']); |
| | | } else { |
| | | $config['content'] = $content; |
| | | $config['icon'] = JVB_CONTENT[$content]['icon']??['logo-triangle']; |
| | | $config['icon'] = JVB_CONTENT[$content]['icon']??[jvbLogoIcon()]; |
| | | } |
| | | if (is_singular()) { |
| | | $config['source'] = $type->ID; |
| | |
| | | |
| | | public function render(array $attributes, string $content, WP_Block $block) |
| | | { |
| | | |
| | | $this->config = $this->buildParams($attributes); |
| | | return $this->cache->remember( |
| | | $this->config, |
| | |
| | | } |
| | | ); |
| | | } |
| | | 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 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; |
| | | } |
| | | |
| | | protected function renderBlock(): string |
| | | { |
| | | $ids = (array_key_exists('ids', $this->config) && !empty($this->config['ids'])) ? ' id="'.implode(' ',$this->config['ids']).'"' : ''; |
| | | $classes = (array_key_exists('classes', $this->config) && !empty($this->config['classes'])) ? ' class="'.implode(' ',$this->config['classes']).'"' : ''; |
| | | $source = (array_key_exists('source', $this->config)) ? ' data-source="'.$this->config['source'].'"' : ''; |
| | | $context = (array_key_exists('context', $this->config)) ? ' data-context="'.$this->config['context'].'"' : ''; |
| | | $icons = (array_key_exists('icon', $this->config)) ? ' data-icon="'.$this->config['icon'].'"' : ' data-icon="logo-triangle"'; |
| | | $gallery = (array_key_exists('is_gallery', $this->config) && $this->config['is_gallery']) ? ' data-gallery' : ''; |
| | | $content = (array_key_exists('content', $this->config)) ? ' data-content="'.implode(',',$this->config['content']).'"' : ''; |
| | | 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 ?>> |
| | |
| | | } |
| | | |
| | | $feedContent = $this->getFeedContent(); |
| | | $hasMany = count($this->config['content']) > 1; |
| | | $hasMany = count($this->getContent()) > 1; |
| | | ?> |
| | | <form class="feed-filters" data-save="feed-<?=$this->config['context']?>"> |
| | | <form class="filters" data-save="feed-<?=$this->getContext()?>"> |
| | | <?php if ($hasMany) { |
| | | //If we have multiple content, only show the content first |
| | | ?> |
| | |
| | | <span class="label">SHOWING: </span> |
| | | <?php |
| | | $labels = []; |
| | | foreach ($this->config['content'] as $i => $type) : |
| | | foreach ($this->getContent() as $i => $type) : |
| | | |
| | | $checked = $i === 0 ? ' checked' : ''; |
| | | $label = $feedContent[$type]['plural'] ?? ucfirst($type); |
| | |
| | | <?php endif; ?> |
| | | <?php if ($hasMany) { ?> |
| | | </summary> |
| | | <?php } ?> |
| | | |
| | | <?php } |
| | | if (!empty ($this->config['taxonomies'])) { |
| | | ?> |
| | | <div class="filters"> |
| | | <div class="filter-group row start"> |
| | | <span class="label">FILTER BY:</span> |
| | |
| | | 'feed-'.$tax, |
| | | $tax, |
| | | [ |
| | | 'icon' => $taxConfig['icon']??'logo-triangle', |
| | | 'icon' => $taxConfig['icon']??jvbLogoIcon(), |
| | | 'update' => '.selected-items-section .selected-items', |
| | | 'types' => $contentForTax, |
| | | 'autocomplete' => false, |
| | |
| | | </div> |
| | | </div> |
| | | </div> |
| | | |
| | | <?php } ?> |
| | | <div class="row btw nowrap"> |
| | | <div class="order-by filter-group row start w-full"> |
| | | <span class="label">ORDER BY:</span> |
| | |
| | | ?> |
| | | <div class="item-grid"> |
| | | <?php |
| | | $total = count($this->config['content']) - 1; |
| | | $total = count($this->getContent()) - 1; |
| | | for ($i = 1; $i <= 36; $i++) { |
| | | $rand = rand(0, $total); |
| | | $config = Features::getConfig($this->config['content'][$rand]); |
| | | $icon = jvbIcon($config['icon']??'logo-triangle'); |
| | | $config = Features::getConfig($this->getContent()[$rand]); |
| | | $icon = jvbIcon($config['icon']??jvbLogoIcon()); |
| | | ?> |
| | | <div class="placeholder"><?=apply_filters('jvbFeedPlaceholder', $icon) ?></div> |
| | | <?php |
| | |
| | | |
| | | protected function renderTemplates(): void |
| | | { |
| | | echo '<template class="feed-item">'.apply_filters('jvbFeedItem', '<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> |
| | | if ($this->getContent()) { |
| | | foreach ($this->getContent() as $content) { |
| | | echo $this->getDefaultTemplate($content); |
| | | } |
| | | } |
| | | |
| | | <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>', $this->config).'</template>'; |
| | | |
| | | echo '<template class="emptyState">'.apply_filters('jvbFeedEmptyState', '<div class="feed-empty-state"> |
| | | <h3>NOTHING HERE...</h3> |
| | | 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>', $this->config). '</template>'; |
| | | echo '<template class="placeholderTemplate"><div class="placeholder">'.apply_filters('jvbFeedPlaceholder', jvbIcon('logo-triangle')).'</div></template>'; |
| | | echo '<template class="placeholderTemplate"><div class="placeholder">'.apply_filters('jvbFeedPlaceholder', jvbIcon(jvbLogoIcon())).'</div></template>'; |
| | | } |
| | | |
| | | protected function getFavouritesButton(string $content):string |
| | | { |
| | | if (!Features::forSite()->has('favourites') && !Features::forContent($content)->has('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 |
| | | { |
| | | if (!Features::forSite()->has('karma') && !Features::forContent($content)->has('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 = 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']); |
| | | $fields = array_filter($fields, function($field) use($images) { |
| | | return !in_array($field, $images); |
| | | }); |
| | | $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) { |
| | | $fieldType = JVB_CONTENT[$content][$fieldName]['type']??'text'; |
| | | $fieldsTemplate .= apply_filters('jvbFeedItemField', $this->defaultFieldTemplate($fieldType, $fieldName), $content, $fieldName, $fieldType); |
| | | } |
| | | $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 |