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(
'',
$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(
'
Search:
%s
',
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(
'',
$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(
'%s',
$emptyState
);
$placeholder = apply_filters('jvbFeedPlaceholder', jvbIcon(jvbLogoIcon()));
$templates[] = sprintf(
'%s
',
$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(
'%s',
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 => '',
};
}
}