cache = Cache::for('feed_block', WEEK_IN_SECONDS); if (JVB_TESTING) { $this->cache->flush(); } add_action('init', [$this, 'registerBlock']); } public function registerBlock() { register_block_type($this->path, [ 'render_callback' => [$this, 'render'] ]); } public function render(array $attributes): string { if (is_post_type_archive(BASE.'directory')) { return ''; } $this->determineContent($attributes); if (empty($this->content)) { return ''; } $this->determineTaxonomies(); $classes = ''; return sprintf( '
%s%s%s%s%s
', $classes, $this->getContent(), $this->isGallery ? ' data-gallery' : '', $this->renderFiltersAndControls(), $this->renderGrid(), $this->renderTemplates(), $this->renderLoader(), TaxonomySelector::outputSelectorModal(), $this->renderActions() ); } protected function determineContent(array $attrs):void { if (array_key_exists('inheritQuery', $attrs) && $attrs['inheritQuery'] === true) { if (is_post_type_archive()) { $obj = get_queried_object(); $this->content = [jvbNoBase($obj->name)]; return; } elseif (!empty(Registrar::getProfileTypes()) && is_singular(Registrar::getProfileTypes())) { global $post; $author = $post->post_author; $role = jvbUserRole($author); $registrar = Registrar::getInstance($role); if (!$registrar) { return; } $this->content = $registrar->getCreatable(); return; } elseif (is_tax()) { $obj = get_queried_object(); $registrar = Registrar::getInstance($obj->taxonomy); if (!$registrar) { return; } if ($registrar->hasFeature('is_content')) { //example: tattoo shop, etc TODO return; } $this->content = array_map(function ($item) { return jvbNoBase($item); }, $registrar->registrar->for); return; } } // not inheriting, getting from config $this->content = $attrs['contentTypes']??[]; } protected function getContent():string { return implode(',', $this->content); } protected function determineTaxonomies():void { $taxonomies = []; $ignore = []; foreach ($this->content as $content) { $registrar = Registrar::getInstance($content); if (!$registrar) continue; $theTax = $registrar->registrar->taxonomies; foreach ($theTax as $tax) { if (!in_array($tax, $ignore) && !in_array($tax, $taxonomies)) { $taxReg = Registrar::getInstance($tax); if ($taxReg->hasFeature('show_feed')) { $taxonomies[] = $tax; } else { $ignore[] = $tax; } } } } $this->taxonomies = array_unique($taxonomies); } protected function renderFiltersAndControls():string { return sprintf( '
Filters %s %s%s%s%s%s
', $this->renderContentLabels(), $this->renderSearch(), $this->renderContent(), $this->renderFilters(), $this->renderOrderControls(), $this->renderViewControls(), jvbIcon('x') ); } protected function renderSearch():string { return sprintf( '', jvbSearch() ); } protected function renderContentLabels():string { $inside = ''; if (count($this->content) === 1) { return ''; } foreach ($this->content as $i => $type) { $active = $i === 0 ? ' class="active"' : ''; $inside .= sprintf( '
  • %s
  • ', $type, $active, Registrar::getInstance($type)->getPlural()??'' ); } return sprintf( '', $inside ); } protected function renderContent():string { $favourites = ''; if (Site::has('favourites')) { $favourites = sprintf( ' ', jvbIcon('heart'), jvbIcon('heart', ['style' => 'fill']) ); } if (count($this->content) === 1) { return empty($favourites) ? sprintf( '', implode(',', $this->content) ) : sprintf( '
    %s
    ', implode(',', $this->content), $favourites ); } $i = 0; $content = implode('', array_map(function($type) use (&$i) { $i++; $registrar = Registrar::getInstance($type); return sprintf( ' ', $type, $type, $i === 0 ? ' checked' : '', $type, $registrar->getPlural(), jvbIcon($registrar->getIcon()), $registrar->getPlural() ); }, $this->content)); return sprintf( '
    Showing: %s%s
    ', $content, $favourites ); } protected function renderFilters():string { if (empty ($this->taxonomies)) { return ''; } $inside = implode('', array_filter(array_map(function($tax) { $registrar = Registrar::getInstance($tax); if (!$registrar) return ''; $current = BASE.$this->content[0]; $contentFor = $registrar->registrar->for; $hidden = in_array($current, $contentFor) ? '' : ' hidden'; $selector = new TaxonomySelector( 'feed-'.$tax, $tax, [ 'icon' => $registrar->getIcon(), 'update'=> '.selected-items-section .selected-items', 'types' => $contentFor, 'autocomplete' => false, 'hidden' => $hidden, 'output' => 'minimal', 'search' => true ] ); return $selector->render(); }, $this->taxonomies))); return sprintf( '
    Filter By:
    %s
    %s
    ', $inside, str_replace('class="toggle-text"', 'class="toggle-text" hidden', jvbRenderToggleTextField('match', 'Match', 'Filters', 'ALL', 'ANY', false, ['filter' => 'match'])), jvbIcon('x') ); } protected function renderOrderControls():string { $orderby = [ [ 'slug' => 'title', 'icon' => 'alphabetical', 'label' => 'Name' ], [ 'slug' => 'date', 'icon' => 'calendar', 'label' => 'Date Created', ], [ 'slug' => 'date_modified', 'icon' => 'clock-clockwise', 'label' => 'Date Modified' ] ]; $custom = $this->getCustomOrdering(); $orderby = $orderby + $custom; $orderby[] = [ 'slug' => 'random', 'icon' => 'shuffle', 'label' => 'Randomly' ]; $custom = implode(',', array_map(function($ord) { return $ord['slug']; }, $custom)); $i = 0; $orderby = sprintf( '
    Order by:%s
    ', implode('', array_map(function ($by) use (&$i){ $checked = $i === 0 ? ' checked' : ''; $i++; return sprintf( ' ', $by['slug'], $by['slug'], $checked, empty($by['for']??[]) ? '' : ' data-for="'.implode($by['for']).'"', $by['slug'], $by['slug'] === 'random' ? $by['label'] : 'by '.$by['label'], jvbIcon($by['icon']), $by['label'] ); }, $orderby)) ); $order = [ [ 'slug' => 'desc', 'icon' => 'sort-descending', 'label' => 'Descending (A-Z, 1-10)' ], [ 'slug' => 'asc', 'icon' => 'sort-ascending', 'label' => 'Ascending (Z-A, 10-1)' ] ]; $i = 0; $order = sprintf( '
    Order: %s
    ', $custom === '' ? '' : ','.$custom, implode('', array_map(function ($ord) use (&$i) { $checked = $i=== 0 ? ' checked' : ''; $i++; return sprintf( ' ', $ord['slug'], $ord['slug'], $checked, $ord['slug'], $ord['label'], jvbIcon($ord['icon']), $ord['label'] ); }, $order)) ); return sprintf( '
    %s%s
    ', $orderby, $order ); } protected function getCustomOrdering():array { $custom = []; foreach ($this->content as $content) { $registrar = Registrar::getInstance($content); if (!$registrar || empty($registrar->config('feed')->getCustomOrder())) { continue; } $custom = array_merge_recursive($custom, $registrar->config('feed')->getCustomOrder()); } return $custom; } protected function renderViewControls():string { $views = [ 'grid' => ['slug' => 'grid', 'icon' => 'squares-four', 'label' => 'Grid View'], 'list' => ['slug' => 'list', 'icon' => 'rows', 'label' => 'List View'] ]; $i = 0; return sprintf( '
    Switch View:%s
    ', implode('', array_map(function ($view) use (&$i) { $checked = $i === 0 ? ' checked' : ''; $i++; return sprintf( ' ', $view['slug'], $view['slug'], $view['slug'], $checked, $view['slug'], $view['label'], jvbIcon($view['icon']), $view['label'], ); }, $views)) ); } protected function renderGrid():string { $placeholders = ''; $total = count($this->content) - 1; $icons = []; $icon = apply_filters('jvbFeedPlaceholder', ''); for ($i=1; $i<=36; $i++) { if (empty($icon)) { $rand = $total === 0 ? $total : rand(0, $total); $content = $this->content[$rand]; if (!in_array($content, $icons)) { $icons[$content] = Registrar::getInstance($content)->getIcon(); } $icon = jvbIcon($icons[$content]); } $placeholders .= sprintf( '
    %s
    ', $icon ); } return sprintf( '
    %s
    ', $placeholders ); } protected function renderLoader():string { return sprintf( ' %s%s', jvbIcon('arrow-elbow-left-down'), jvbIcon('arrow-elbow-right-down'), jvbLoadingScreen(), $this->isGallery ? jvbRenderGallery(false) : '', ); } protected function renderTemplates():string { $templates = []; foreach ($this->content as $content) { $templates[] = $this->getDefaultTemplate($content); } $templates[] = sprintf( '', jvbIcon(jvbDefaultIcon()), jvbIcon('x') ); $defaultEmptyState = sprintf( '

    %sNOTHING HERE%s

    Try tweaking those filters a bit.

    ', jvbIcon(jvbDefaultIcon()), jvbIcon(jvbDefaultIcon()), ); $emptyState = apply_filters('jvbFeedEmptyState', $defaultEmptyState, $this->content); $templates[] = sprintf( '', $emptyState ); $placeholder = apply_filters('jvbFeedPlaceholder', jvbIcon(jvbLogoIcon())); $templates[] = sprintf( '', $placeholder ); return implode('', $templates); } protected function renderActions():string { return sprintf( '', jvbIcon('arrows-clockwise') ); } public static function getFavouritesButton(string $content):string { $registrar = Registrar::getInstance($content); if (!$registrar || !Site::has('favourites') || !$registrar->hasFeature('favouritable')) { return ''; } return ''; } public static function getUpvotesButton(string $content):string { $registrar = Registrar::getInstance($content); if (!Site::has('karma') || !$registrar || !$registrar->hasFeature('karma')){ return ''; } return '
    '; } protected function getDefaultTemplate(string $content): string { $template = apply_filters('jvbFeedItem', '', $content); if (empty($template)) { $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 = sprintf( '
    %s%s', $content, self::getFavouritesButton($content), self::getUpvotesButton($content) ); //Add all defined images, but allow for filtering $imageTemplate = ''; foreach ($images as $image) { $imageTemplate .= sprintf( '', $image ); } $imageTemplate .= ''; $template .= sprintf( '
    %s
    ', apply_filters('jvbFeedImages', $imageTemplate, $content, $images) ); //Output default fields, but allow for filtering $template .= sprintf( '
    %s', apply_filters('jvbFeedItemSummary', jvbIcon('dots-three'), $content) ); $fieldsTemplate = ''; foreach ($fields as $fieldName => $config) { $fieldsTemplate .= apply_filters('jvbFeedItemField', $this->defaultFieldTemplate($config['type'], $fieldName), $content, $fieldName, $config['type']); } $template .= sprintf( '
    %s
    ', apply_filters('jvbFeedItemFields', $fieldsTemplate, $content, $fields) ); $template .= '
    '; } return sprintf( '', ucfirst($content), $template ); } protected function defaultFieldTemplate(string $fieldType, string $fieldName):string { $data = ' data-field="'.$fieldName.'"'; switch ($fieldName) { case 'post_title': return ''; case 'post_date': case 'post_modified': return ''; } return match($fieldType) { 'date','datetime','time' => '', 'upload' => '', 'taxonomy' => '
  • ', default => '

    ', }; } }