cache = Cache::for('blocks', WEEK_IN_SECONDS); $this->cache->connect('post')->connect('taxonomy'); $this->cache->flush(); add_filter('pre_render_block', [$this, 'prerender'], 10, 3); add_filter('render_block', [$this, 'render'], 10, 2); add_action('init', [$this, 'registerBlockStyles']); } public function registerBlockStyles():void { do_action('jvbBlockStyles'); //Register extra block styles register_block_style( 'core/navigation', [ 'name'=>'condensed', 'label' => __('Condensed', 'jvb') ] ); register_block_style( 'core/navigation', [ 'name'=>'floating', 'label' => __('Floating', 'jvb') ] ); register_block_style( 'core/navigation', [ 'name'=>'fixed', 'label' => __('Fixed', 'jvb') ] ); register_block_style( 'core/group', [ 'name' =>'callout', 'label' => __('Callout', 'jvb') ] ); register_block_style( 'core/group', [ 'name' =>'callalt', 'label' => __('Callout Alt', 'jvb') ] ); } protected function checkMethods(?string $content, array $block, ?WP_Block $parent = null, bool $isPrerender = false):?string { $blockName = $this->sanitizeBlockName($block); $base = ($isPrerender) ? 'prerender_' : 'render_'; $method = $base.$blockName; $function = BASE.$method; if (function_exists($function)) { return $function($block, $content, $parent); } else if (method_exists($this, $method)) { return $this->$method($block, $content, $parent); } elseif (!empty($blockName) && JVB_TESTING) { if (!in_array($block['blockName'], $this->getIgnore($isPrerender))) { jvbDump('No method found for '.print_r($block['blockName'], true)); } } return $content; } protected function getIgnore(bool $isPrerender):array { //Ignore for both $base = [ 'core/null' ]; if ($isPrerender) { $base = array_merge($base, [ ]); } else { $base = array_merge($base, [ ]); } return $base; } public function prerender(?string $content, array $block, ?WP_Block $parent = null):?string { $result = $this->checkMethods($content, $block, $parent, true); return $result; } public function render(string $content, array $block):string { if ($block['blockName'] === 'jvb/feed') { // Enqueue the feed block script (it will automatically load dependencies) $this->localize_feedblock(); } if ($block['blockName'] === 'jvb/forms') { wp_enqueue_style('jvb-form'); } return $this->checkMethods($content, $block); } /*********************************** * Blocks **********************************/ /** * Common Blocks */ //For Reference: //core_form //core_form_input //core_form_submission_notification //core_form_submit_button /** * Design blocks */ public function prerender_core_button(array $block, ?string $content, ?WP_Block $parent):?string { // jvbDump($block, 'Button'); // jvbDump($parent, 'Parent'); preg_match('/href="([^"]*)"/', $block['innerHTML'], $url); preg_match('/>([^<]*)<\/a>/', $block['innerHTML'], $label); if (empty($url[1]) || empty($label[1])) { return ''; } $icon = ''; if (str_contains($url[1], 'google.com/maps')) { $icon = 'google-logo'; } if (str_contains($url[1], 'maps.apple.com')) { $icon = 'apple-logo'; } if ($icon !== '') { return sprintf( '%s Maps', $this->getClassesAndStyles($block['attrs']??[]), esc_url($url[1]), esc_html($label[1]), jvbIcon($icon) ); } return sprintf( '%s', $this->getClassesAndStyles($block['attrs']??[]), esc_url($url[1]), esc_html($label[1]) ); } public function prerender_core_buttons(array $block, ?string $content, ?WP_Block $parent):?string { // jvbDump($block, 'buttons'); // jvbDump($parent, 'Parent'); return 'getClassesAndStyles($block['attrs']??[], ['buttons','row']).'>'. $this->innerBlocks($block).''; } public function prerender_core_column(array $block, ?string $content, ?WP_Block $parent):?string { // jvbDump($block, 'column'); // jvbDump($parent, 'Parent'); $styles = (array_key_exists('attrs', $block) && array_key_exists('width', $block['attrs'])) ? ['flex-basis:'.$block['attrs']['width']] : []; return 'getClassesAndStyles($block['attrs']??[], ['col'], $styles).'>'. $this->innerBlocks($block).''; } public function prerender_core_columns(array $block, ?string $content, ?WP_Block $parent):?string { $tagName = array_key_exists('tagName', $block['attrs']) ? $block['attrs']['tagName'] : 'section'; return sprintf( '<%s%s>%s', $tagName, $this->getClassesAndStyles($block['attrs']??[], ['row nowrap']), $this->innerBlocks($block).'', $tagName ); } //core_comment_template public function prerender_core_group(array $block, ?string $content, ?WP_Block $parent):?string { // jvbDump($block, 'group'); // jvbDump($parent, 'Parent'); $tag = (array_key_exists('tagName', $block['attrs']??[])) ? $block['attrs']['tagName'] : 'div'; return sprintf( '<%s%s>%s', $tag, $tag === 'main' ? '' : $this->getClassesAndStyles($block['attrs']??[], ['group']), $this->innerBlocks($block), $tag ); } //core_home_link //core_more //core_nextpage public function prerender_core_separator(array $block, ?string $content, ?WP_Block $parent):?string { // jvbDump($block, 'separator'); // jvbDump($parent, 'Parent'); return 'getClassesAndStyles($block['attrs']??[]).'>'; } public function prerender_core_spacer(array $block, ?string $content, ?WP_Block $parent):?string { // jvbDump($block, 'spsacer'); // jvbDump($parent, 'Parent'); return 'getClassesAndStyles($block['attrs']??[], ['spacer'], ['height:2rem']). ' aria-hidden="true">'; } //core_table_of_contents //core_text_columns /** * Embed Block */ //core_embed /** * Media Blocks */ //core_audio public function prerender_core_cover(array $block, ?string $content, ?WP_Block $parent):?string { // jvbDump($block, 'cover'); // jvbDump($parent, 'Parent'); // Extract block attributes $attrs = $block['attrs'] ?:[]; $innerContent = $this->innerBlocks($block); $position = 'object-position: center;'; if (array_key_exists('focalPoint', $attrs)) { $x = (array_key_exists('x', $attrs['focalPoint'])) ? ($attrs['focalPoint']['x'] * 100).'%' : 'center'; $y = (array_key_exists('y', $attrs['focalPoint'])) ? ($attrs['focalPoint']['y'] * 100).'%' : 'center'; $position = 'object-position:'.$x.' '.$y.';'; unset($attrs['focalPoint']); } // Check for background type $backgroundType = $attrs['backgroundType'] ?? 'image'; $background = ''; $ID = false; if (array_key_exists('useFeaturedImage', $attrs)) { global $post; $ID = get_post_thumbnail_id($post->ID); } else if (array_key_exists('id', $attrs)) { $ID = (int)$attrs['id']; } if ($backgroundType === 'image' && $ID) { $background .= str_replace('image($ID)); } elseif ($backgroundType === 'video' && isset($attrs['url'])) { $background .= ''; } // Build classes and styles unset($attrs['url']); $classes = $this->getClassesAndStyles($attrs, ['cover row']); return '' . $background . '
' . $innerContent . '
'; } //core_file public function prerender_core_gallery(array $block, ?string $content, ?WP_Block $parent):?string { // jvbDump($block, 'gallery'); // jvbDump($parent, 'Parent'); return 'getClassesAndStyles($block['attrs']??[], ['gallery']).'>'. $this->innerBlocks($block,'
  • ', '
  • '). ''; } public function prerender_core_image(array $block, ?string $content, ?WP_Block $parent):?string { // jvbDump($block, 'image'); // jvbDump($parent, 'Parent'); $ID = $this->imageID('', $block); if (!$ID) { return ''; } $title = (get_the_title($ID) !== '') ? ''.get_the_title($ID).'' : ''; $caption = (wp_get_attachment_caption($ID)) ? '
    ' . $title . wp_get_attachment_caption($ID) . '
    ' : '
    ' . $title . '
    '; $size = array_key_exists('sizeSlug', $block['attrs']??[]) ? $block['attrs']['sizeSlug'] : 'large'; return 'getClassesAndStyles($block['attrs']??[]).'>'. $this->imageLink(true, $ID, 'tiny', $size) . $caption.''; } public function prerender_core_media_text(array $block, ?string $content, ?WP_Block $parent):?string { // jvbDump($block, 'media text'); // jvbDump($parent, 'Parent'); $ID = $this->imageID('', $block); $size = array_key_exists('mediaSizeSlug', $block['attrs']??[]) ? $block['attrs']['mediaSizeSlug'] : 'large'; $imgLink = ($ID) ? $this->imageLink(true, $ID, 'tiny', $size) : ''; $inner = $this->innerBlocks($block); $classes = ['media-text', 'row']; if (array_key_exists('isStackedOnMobile', $block['attrs']??[])) { $classes[] = 'nowrap'; } $content = 'getClassesAndStyles($block['attrs']??[], $classes).'>'; $content .= (array_key_exists( 'mediaPosition', $block['attrs']??[] ) && $block['attrs']['mediaPosition'] == 'right') ? '
    '.$inner.'
    '.$imgLink.'
    ' : '
    '.$imgLink.'
    '.$inner.'
    '; $content .= ''; return $content; } //core_video /** * Reusable blocks */ //core_pattern /** * Text Blocks */ //prerender_core_code //prerender_core_details //prerender_core_footnotes //prerender_core_classic public function prerender_core_heading(array $block, ?string $content, ?WP_Block $parent):?string { // jvbDump($block, 'heading'); // jvbDump($parent, 'Parent'); $level = (array_key_exists('level', $block['attrs']??[])) ? $block['attrs']['level'] : '2'; $content = $this->innerBlocks($block); $id = sanitize_title(wp_strip_all_tags($this->stripTagContents('small', $content))); return 'getClassesAndStyles($block['attrs']??[]).'>'. $content. ''; } public function prerender_core_list(array $block, ?string $content, ?WP_Block $parent):?string { // jvbDump($block, 'list'); // jvbDump($parent, 'Parent'); $tag = (array_key_exists('ordered', $block['attrs']??[])) ? 'ol' : 'ul'; $output = '<'.$tag.$this->getClassesAndStyles($block['attrs']??[]).'>'.$this->innerBlocks($block).''; return $output; } // public function prerender_core_list_item(array $block):string // { // return 'getClassesAndStyles($block['attrs']).'>'.$this->inside($block).''; // } //prerender_core_missing public function prerender_core_paragraph(array $block, ?string $content, ?WP_Block $parent):?string { // jvbDump($block, 'paragraph'); // jvbDump($parent, 'Parent'); return 'getClassesAndStyles($block['attrs']??[]).'>'. $this->innerBlocks($block). '

    '; } public function prerender_core_quote(array $block, ?string $content, ?WP_Block $parent): ?string { // jvbDump($block, 'quote'); // jvbDump($parent, 'Parent'); $innerHTML = $block['innerHTML']; // Extract cite content first $cite = $this->extractElement($innerHTML, 'cite'); $citeHtml = ($cite === '') ? '' : '— '.$cite.''; // Get the blockquote content $content = $this->innerBlocks($block); // Remove the cite element from content if it exists if ($cite !== '') { $content = $this->stripTagContents('cite', $content); } return 'getClassesAndStyles($block['attrs']??[]).'>
    '.$content.'
    '. $citeHtml. ''; } public function prerender_core_pullquote(array $block, ?string $content, ?WP_Block $parent):?string { // jvbDump($block, 'pullquote'); // jvbDump($parent, 'Parent'); $innerHTML = $block['innerHTML']; // Extract cite content first $cite = $this->extractElement($innerHTML, 'cite'); $citeHtml = ($cite === '') ? '' : '— '.$cite.''; // Get the blockquote content $content = $this->extractElement($innerHTML, 'blockquote'); // Remove the cite element from content if it exists if ($cite !== '') { $content = $this->stripTagContents('cite', $content); } $content = jvb_filter_content( $content); return 'getClassesAndStyles($block['attrs']??[], ['pull']).'>'. $content. $citeHtml. ''; } //prerender_core_table //prerender_core_verse /** * Theme Blocks */ //core_avatar //core_loginout public function prerender_core_loginout(array $block, ?string $content, ?WP_Block $parent):?string { $action = is_user_logged_in() ? 'logout' : 'login'; $attrs = $block['attrs']; $redirect = ''; if (array_key_exists('redirectToCurrent', $attrs) && $attrs['redirectToCurrent']) { global $wp; $redirect = get_home_url(null, $wp->request); } if (array_key_exists('displayLoginAsForm', $attrs) && $attrs['displayLoginAsForm']) { LoginManager::getInstance()->setAction($action); return LoginManager::getInstance()->renderLoginForm($action, $redirect, '

    Login

    '); } return sprintf( '%s', wp_login_url($redirect), $this->getClassesAndStyles($attrs), $action === 'login' ? jvbIcon('sign-in').'Log in' : jvbIcon('sign-out').'Logout' ); } //core_pattern public function prerender_core_site_logo(array $block, ?string $content, ?WP_Block $parent):?string { // jvbDump($block, 'site logo'); // jvbDump($parent, 'Parent'); $open = $close = ''; if (!is_home() && !is_front_page()) { $open = ''; $close = ''; } $img = get_theme_mod('custom_logo'); $img = $this->image($img, 'tiny', 'thumbnail'); $img = str_replace('getClassesAndStyles($block['attrs']??[]), $img); return $open.$img.$close; } //core_site_title_tagline public function prerender_core_site_title(array $block, ?string $content, ?WP_Block $parent):?string { // jvbDump($block, 'site title'); // jvbDump($parent, 'Parent'); $tag = (array_key_exists('level', $block['attrs']??[])) ? $block['attrs']['level'] : 1; $tag = ($tag == 0) ? 'p' : 'h'.$tag; $open = $close = ''; if (!is_front_page()) { $open = ''; $close = ''; } $class = ($tag === 'p') ? $this->getClassesAndStyles($block['attrs']??[], ['title']) : $this->getClassesAndStyles($block['attrs']??[]); return '<'.$tag.$class.'>'. $open. get_bloginfo('name'). $close. ''; } /** * Theme Comments Blocks */ //core_avatar //core_comment_author_name //core_comment_content //core_comment_date //core_comment_edit_link //core_comment_reply_link //core_comments //core_comments_pagination //core_comments_pagination_next //core_comments_pagination_number //core_comments_pagination_previous //core_comments_title //core_post_comments_form /** * Theme Navigation Blocks */ public function prerender_core_navigation(array $block, ?string $content, ?WP_Block $parent):?string { // jvbDump($block, 'navigation'); // jvbDump($parent, 'Parent'); $ID = (array_key_exists('ref', $block['attrs']??[])) ? $block['attrs']['ref'] : false; if (empty($block['innerBlocks']) && $ID && get_post($ID)) { $block['innerBlocks'] = parse_blocks(get_post($ID)->post_content); } $toggle = (array_key_exists('overlayMenu', $block['attrs']??[]) && $block['attrs']['overlayMenu'] == 'never') ? '': ''; $class = ($toggle === '') ? $this->getClassesAndStyles($block['attrs']??[], ['mobile']) : $this->getClassesAndStyles($block['attrs']??[]); $helpmenu = (get_the_title($ID) === 'Main') ? '' : ''; //Allows to add custom items to a menu, based on the menu name $helpmenu = apply_filters('jvbMenuExtraAfter', $helpmenu, get_the_title($ID)); $main = trim(apply_filters('jvbMenuExtra', $this->innerBlocks($block), get_the_title($ID), $block)); $main = str_starts_with($main, ''.$main.''; return ' Skip to Content ' . $toggle . $main. ''.$helpmenu; } public function prerender_core_navigation_link(array $block, ?string $content, ?WP_Block $parent):?string { // jvbDump($block, 'navigation link'); // jvbDump($parent, 'Parent'); global $wp; if (!array_key_exists('attrs', $block)) { return ''; } $url = (str_starts_with($block['attrs']['url'],'/')) ? home_url($block['attrs']['url']) : $block['attrs']['url']; $current = (home_url($wp->request.'/') == $url); $temp = $block['attrs']??[]; unset($temp['url']); $classes = ($current) ? $this->getClassesAndStyles($temp, ['current']): $this->getClassesAndStyles($temp); $aria = ''; if ($current) { $aria = ' aria-current="page"'; } $linkOpen = $this->build_navigation_link($block['attrs']??[], $aria); return ''.$linkOpen.$block['attrs']['label'].''; } public function prerender_core_navigation_submenu(array $block, ?string $content, ?WP_Block $parent):?string { // jvbDump($block, 'navigation submenu'); // jvbDump($parent, 'Parent'); global $wp; $url = (str_starts_with($block['attrs']['url'],'/')) ? home_url($block['attrs']['url']) : $block['attrs']['url']; $current = (home_url($wp->request) == $url); $temp = $block['attrs']??[]; unset($temp['url']); $classes = ($current) ? $this->getClassesAndStyles($temp, ['has-submenu', 'current']): $this->getClassesAndStyles($temp, ['has-submenu']); $aria = ''; if ($current) { $aria = ' aria-current="page"'; } $id = sanitize_title($block['attrs']['label']); $linkOpen = $this->build_navigation_link($block['attrs'], $aria); $content = ''.$linkOpen.$block['attrs']['label']. ''; return $content; } protected function build_navigation_link(array $attrs, string $aria):string { global $wp; $url =(str_starts_with($attrs['url'],'/')) ? home_url($attrs['url']) : $attrs['url']; $target = $type = $id = $label = $desc = $rel = $title = $kind = ''; foreach ($attrs as $k => $v) { switch ($k) { case 'description': $desc = $v; break; case 'rel': $rel = ' rel="'.$v.'"'; break; case 'title': $title = ' title="'.$v.'"'; break; case 'kind': $kind = $v; break; case 'type': $type = $v; break; case 'opensInNewTab': $target = ' target="'.$v.'"'; break; } } return ''; } /** * Theme Query Blocks */ //core_post_author //core_post_author_biography //core_post_author_name public function prerender_core_post_content(array $block, ?string $content, ?WP_Block $parent):?string { // jvbDump($block, 'post content'); // jvbDump($parent, 'Parent'); $tag = (array_key_exists('tagName', $block['attrs']??[])) ? $block['attrs']['tagName'] : 'main'; if ($content == '') { if(is_singular()) { global $post; $block['innerBlocks'] = parse_blocks($post->post_content); $result = $this->innerBlocks($block); }else { $result = ''; } } else { $result = $this->innerBlocks($block); } return apply_filters('jvb_post_content_output', $result, $block); } //core_post_date public function prerender_core_post_date(array $block, ?string $content, ?WP_Block $parent):?string { // jvbDump($block, 'post date'); // jvbDump($parent, 'Parent'); $postDate = get_the_date('c'); return ''; } //core_post_excerpt public function prerender_core_post_excerpt(array $block, ?string $content, ?WP_Block $parent):?string { return wpautop(get_the_excerpt()); } public function prerender_core_post_featured_image(array $block, ?string $content, ?WP_Block $parent):?string { // jvbDump($block, 'featured image'); // jvbDump($parent, 'Parent'); global $post; $ID = get_post_thumbnail_id($post->ID); $aOpen = $aClose = ''; if(!is_single($post->ID)) { $aOpen = ''; $aClose = ''; } return 'getClassesAndStyles($block['attrs']??[]).'>'.$aOpen. apply_filters('jvbCoreFeaturedImage', $this->image($ID), $post->post_type). $aClose.''; } //core_post_navigation_link public function prerender_core_post_navigation_link(array $block, ?string $content, ?WP_Block $parent):?string { $attr = $block['attrs']; $isPrevious = $attr['type']==='previous'; $title = array_key_exists('showTitle', $attr)&&$attr['showTitle']; $linkLabel = array_key_exists('linkLabel', $attr)&&$attr['linkLabel']; $label = array_key_exists('label', $attr) ? $attr['label'] : ''; $arrow = ''; if (array_key_exists('arrow', $attr)) { $dir = $isPrevious ? 'left' : 'right'; $icon = match($attr['arrow']) { 'arrow' => 'arrow-square-', 'chevron' => 'caret-circle-' }; if ($icon) { $arrow = jvbIcon($icon.$dir); } } // return $content; $linkedLabel = $unlinkedLabel = ''; if (!empty($label)) { $linkedLabel = $linkLabel ? $label : ''; $unlinkedLabel = $linkLabel ? '' : $label; } if ($title) { $linkedLabel .=' %title'; } elseif (!empty($label)) { $linkedLabel = $label; $unlinkedLabel = ''; } else { $linkedLabel = $isPrevious ? 'Previous' : 'Next'; $unlinkedLabel = ''; } $result = $isPrevious ? get_previous_post_link( $arrow.$unlinkedLabel.' %link', $linkedLabel ) : get_next_post_link( '%link '.$unlinkedLabel.$arrow, $linkedLabel ); return sprintf('%s', $this->getClassesAndStyles($attr,['row', 'nowrap']), $result ); } //core_post_template public function render_core_post_template(array $block, string $content):string { global $wp_query; $inner = ''; $block['innerBlocks'][0]['attrs']['tagName'] = 'li'; if ($wp_query->have_posts()) { while ($wp_query->have_posts()) { $wp_query->the_post(); $inner .= $this->innerBlocks($block); } wp_reset_postdata(); } return sprintf( '
      %s
    ', $inner ); } //core_post_terms public function prerender_core_post_terms(array $block, ?string $content, ?WP_Block $parent):?string { if (!array_key_exists('attrs', $block)) { return ''; } $terms = get_the_terms(get_the_ID(), $block['attrs']['term']); $out = ''; if ($terms && !is_wp_error($terms)) { $out = sprintf( '', $this->getClassesAndStyles($block['attrs'], ['term-list', 'row', 'start']) ); if (array_key_exists('prefix', $block['attrs']??[])) { $out .= sprintf( '
  • %s
  • ', $block['attrs']['prefix'] ); } foreach($terms as $term) { $out .= sprintf( '
  • ', get_term_link($term), html_entity_decode($term->name) ); } if (array_key_exists('suffix', $block['attrs'])) { $out .= sprintf( '
  • %s
  • ', $block['attrs']['suffix'] ); } $out .= ''; } return $out; } //core_post_time_to_read public function prerender_core_post_title(array $block, ?string $content, ?WP_Block $parent):?string { // jvbDump($block, 'post content'); // jvbDump($parent, 'Parent'); $open = $close = ''; if (array_key_exists('isLink', $block['attrs']??[])) { $rel = (array_key_exists('rel', $block['attrs']??[])) ? ' rel="'.$block['attrs']['rel'].'"' : ''; $target = (array_key_exists('linkTarget', $block['attrs']??[])) ? ' target="'.$block['attrs']['linkTarget'].'"' : ''; $open = ''; $close = ''; } if (is_singular(BASE.'partner')) { $open .= 'edmonton.ink partner: '; } $level = (array_key_exists('attrs', $block) && array_key_exists('level', $block['attrs'])) ? $block['attrs']['level'] : 2; return 'getClassesAndStyles($block['attrs']??[]).'>'. $open.get_the_title().$close. ''; } public function render_core_query(array $block, string $content): string { // $queryID = $block['attrs']['queryId'] ?? null; // $inherit = $block['attrs']['inherit'] ?? false; // // if ($inherit) { // global $wp_query; // $loop = $wp_query; // } else { // $args = []; // foreach (($block['attrs']['query'] ?? []) as $key => $value) { // if (empty($value)) { // continue; // } // switch ($key) { // case 'postType': // if ($value === BASE.'progress'){ // $args['post_parent'] = 0; // } // $args['post_type'] = $value; // break; // case 'perPage': // $args['posts_per_page'] = $value; // break; // case 'orderBy': // $args['orderby'] = $value; // break; // case 'taxQuery': // $taxQuery = []; // foreach ($value as $tax => $terms) { // $taxQuery[] = [ // 'taxonomy' => $tax, // 'terms' => $terms // ]; // } // if (!empty($taxQuery)) { // $args['tax_query'] = $taxQuery; // if (count($taxQuery) > 1) { // $args['tax_query']['relation'] = 'OR'; // } // } // break; // case 'sticky': // if ($value === 'ignore') { // $args['ignore_sticky_posts'] = true; // } else if ($value === 'exclude'){ // $args['post__not_in'] = get_option('sticky_posts'); // } else if ($value === 'only') { // $args['include'] = get_option('sticky_posts'); // } // break; // case 'search': // $args['s'] = $value; // break; // default: // $args[$key] = $value; // break; // // } // } // $search = 'query-' . $queryID; // foreach ($_GET as $key => $value) { // if (str_contains($key, $search)) { // $key = str_replace($search, '', $key); // if ($key === 'page') { // $args['paged'] = (int)$value; // } // } // } // $loop = new WP_Query($args); // } // // $inner = ''; // foreach ($block['innerBlocks'] as $innerBlock) { // switch ($innerBlock['blockName']) { // case 'core/post-template': // $inner .= '
    '; // if ($loop->have_posts()) { // while ($loop->have_posts()) { // $loop->the_post(); // $postType = get_post_type(); // $inner .= '
    ' . $this->innerBlocks($innerBlock) . '
    '; // } // } // $inner .= '
    '; // break; // } // } // // // Reset only after a custom query, not the main query // if (!$inherit) { // wp_reset_postdata(); // } $tagName = $block['attrs']['tagName'] ?? 'div'; // return sprintf( // '<%s class="loop">%s', // $tagName, // $this->innerBlocks($block), // $tagName // ); return $this->innerBlocks($block); } //core_query_no_results //core_query_pagination //core_query_pagination_next //core_query_pagination_numbers //core_query_pagination_previous public function prerender_core_query_title(array $block, ?string $content, ?WP_Block $parent):?string { jvbDump($block); $attr = $block['attrs']; $name = ''; $showPrefix = $attr['showPrefix']??false; $obj = get_queried_object(); if (is_tax()) { $name = $showPrefix ? $obj->label.':' : ''; $name .= $obj->name; } else if (is_post_type_archive()) { $name = $showPrefix ? 'Archive: ' : ''; $name .= $obj->label; } elseif (is_search()) { $name = 'Search results for: '.get_search_query(); } elseif (is_singular()) { $name = $obj->post_title; } $level = array_key_exists('level', $attr) ? 'h'.$attr['level'] : 'h1'; return sprintf( '<%s id="%s">%s', $level, sanitize_title($name), $name, $level ); } //core_read_more public function prerender_core_template_part(array $block, ?string $content, ?WP_Block $parent):?string { // jvbDump($block, 'template part'); // jvbDump($parent, 'Parent'); $slug = $block['attrs']['slug'] ?? null; $theme = $block['attrs']['theme'] ?? get_stylesheet(); $tag = $block['attrs']['tagName'] ?? 'div'; if (!$slug) { return $content; } // Try to get the template part post (customized via FSE) $template_part = get_block_template( "$theme//$slug", 'wp_template_part' ); if ( $template_part && ! empty( $template_part->content ) ) { $block['innerBlocks'] = parse_blocks( $template_part->content ); $before = $themeSwitch = $after = $beforeClose = ''; switch ($tag) { case 'header': $before = apply_filters('jvbAboveHeader', ''); if (!empty($before)) { $before = sprintf( '', $before ); } $themeSwitch = jvbDarkModeToggle(); $after = apply_filters('jvbBelowHeader', $after); if (!empty($after)) { $after = sprintf( '', $after ); } $after .= BreadcrumbManager::getInstance()->renderNavigation(); $beforeClose = '
    '; break; case 'footer': $before = apply_filters('jvbBeforeFooter', $before); if (!empty($before)) { $before = sprintf( '', $before ); } $beforeClose = jvbRandomFooterText(); break; } return sprintf( '%s<%s%s>%s%s%s%s', $before, $tag, $this->getClassesAndStyles($block['attrs']??[]), $themeSwitch, $this->innerBlocks($block), $beforeClose, $tag, $after ); } if (JVB_TESTING) { jvbDump('Could not create template part block for '.$block['blockName']); } return $content; // $isHeaderTemplate = ( // (array_key_exists('slug', $block['attrs']??[]) && str_contains(strtolower($block['attrs']['slug']), 'header')) || // (array_key_exists('tagName', $block['attrs']??[]) && str_contains(strtolower($block['attrs']['tagName']), 'header')) // ) ? 'header' : false; // $isFooterTemplate = ( // (array_key_exists('slug', $block['attrs']??[]) && str_contains(strtolower($block['attrs']['slug']), 'footer')) || // (array_key_exists('tagName', $block['attrs']??[]) && str_contains(strtolower($block['attrs']['tagName']), 'footer')) // ) ? 'footer' : false; // if (($isHeaderTemplate || $isFooterTemplate)) { // $innerContent = $content; // // $tag = $isHeaderTemplate ?: $isFooterTemplate ?: 'div'; // // $breadcrumbs = $themeSwitch = $afterHeader = $beforeHeader = $footerText= ''; // if ($isHeaderTemplate) { // // $beforeHeader = apply_filters('jvbAboveHeader', $beforeHeader); // if ($beforeHeader !== '') { // $beforeHeader = ''; // } // $themeSwitch = jvbDarkModeToggle(); // $breadcrumbs = BreadcrumbManager::getInstance()->renderNavigation(); // $afterHeader = apply_filters('jvbBelowHeader', $afterHeader); // // if ($afterHeader !== '') { // $afterHeader = ''; // } // $footerText = '
    //
    '; // } elseif ($isFooterTemplate) { // $beforeHeader = apply_filters('jvbBeforeFooter', ''); // if ($beforeHeader !== '') { // $beforeHeader = ''; // } // $footerText = jvbRandomFooterText(); // } // // $content = $beforeHeader.'<'.$tag.$this->getClassesAndStyles($block['attrs']??[]).'>'. // $themeSwitch . // $this->innerBlocks($block). //// $this->innerBlocks($block). //// $innerContent. // $footerText.''.$afterHeader.$breadcrumbs; // } // // return $content; } //core_term_description /** * Widgets Blocks */ //core_archives //core_calendar //core_categories //core_html //core_latest_comments //core_latest_posts //core_page_list //core_page_list_item //core_rss //core_search //core_shortcode public function prerender_core_social_link(array $block, ?string $content, ?WP_Block $parent):?string { // jvbDump($block, 'social link'); // jvbDump($parent, 'Parent'); $attrs = $block['attrs']??[]; $url = $attrs['url']??''; $service = $attrs['service']?:''; $iconName = ($service === 'bluesky') ? 'butterfly' : $service.'-logo'; $icon = jvbIcon($iconName); if (!$icon) { $icon = jvbIcon('link'); } return '
  • '.$icon.'Find us on '.ucfirst($service).'
  • '; } public function prerender_core_social_links(array $block, ?string $content, ?WP_Block $parent):?string { // jvbDump($block, 'social links'); // jvbDump($parent, 'Parent'); return '
      '.$this->innerBlocks($block).'
    '; } //core_tag_cloud /** * Extra feed block localization */ protected function localize_feedblock():void { wp_localize_script('jvb-feed-view-script', 'feedSettings', [ 'currentUser' => is_user_logged_in() ? [ 'id' => get_current_user_id() ] : null, 'from' => get_the_ID(), ]); } /*********************************** * Helpers **********************************/ public function stripTagContents(string $tag, ?string $content):string { $clean = preg_replace('/<'.$tag.'\b[^>]*>.*?<\/'.$tag.'>/is', '', $content); $clean = preg_replace('/\s+/', ' ', $clean); return trim($clean); } public function innerBlocks(array $block, string $before = '', string $after = '', bool $prerender = true):string { $content = ''; foreach ($block['innerBlocks'] as $b) { $content .= sprintf('%s%s%s', $before, render_block($b), $after ); } return $content; } public function inside(array $block, mixed $tag = false, mixed $o = false): string { $html = $o ?: trim($block['innerHTML']); if (empty($html)) { return ''; } if (preg_match('/^<(\w+)[^>]*>(.*)<\/\1>$/s', $html, $matches)) { if ($tag && strtolower($matches[1]) !== strtolower($tag)) { return $html; } return trim($matches[2]); } return $html; } /** * Extract content from a specific nested element * @param string $html The HTML to parse * @param string $tag The tag name to extract * @return string The content of the first matching element, or empty string */ protected function extractElement(string $html, string $tag): string { if (empty($html)) { return ''; } $dom = new DOMDocument(); // Suppress errors for malformed HTML libxml_use_internal_errors(true); $dom->loadHTML('' . $html, LIBXML_HTML_NOIMPLIED | LIBXML_HTML_NODEFDTD); libxml_clear_errors(); $elements = $dom->getElementsByTagName($tag); if ($elements->length === 0) { return ''; } return trim($elements->item(0)->textContent); } public function imageID(int|string $ID, array $block = []):int|false { if ($ID === '' && !empty($block)) { if (($block['blockName'] === 'core/post-featured-image' || (!array_key_exists('attrs', $block) && !array_key_exists('id', $block['attrs'])))) { $ID = get_post_thumbnail_id(); } else { if (array_key_exists('id', $block['attrs']??[])) { $ID = $block['attrs']['id']; } elseif (array_key_exists('mediaId', $block['attrs'])) { $ID = $block['attrs']['mediaId']; } } } if (!is_int($ID)) { return false; } return $ID; } public function imageLink( bool $close = false, int $ID = 0, string $start = 'tiny', string $replace = 'large', ?string $postSlug = null ):string { $image = jvbFormatImage($ID, $start, $replace, true, $postSlug); if ($close) { return $image; } $len = strlen(''); return substr_replace($image, '', -$len, $len); } public function image(string $ID = '', string $start = 'tiny', string $replace = 'large'):string { if ($ID == '') { $ID = $this->imageID($ID); } if ($ID === 0 || $ID === false) { return ''; } return jvbFormatImage($ID, $start, $replace, false); } public function sanitizeBlockName(array $block):string { if (!array_key_exists('blockName', $block) || is_null($block['blockName'])) { return ''; } return str_replace( '/', '_', str_replace( '-', '_', $block['blockName'] ) ); } /** * Replaces the outside tag of an HTML string * @param string $content * @param bool|string $tagName * @param string $classes * @param string $styles * * @return string */ public function replaceOutsideTag( string $content, bool|string $tagName = false, string $classes = '', string $styles = '' ):string { if ($tagName && !in_array($tagName, ['div', 'section', 'article', 'aside', 'main'])) { return $content; } $content = explode('>', $content); if ($classes !== '') { if (!str_contains($classes, 'class="')) { $classes = ' class="'.$classes.'"'; } } if ($styles !== '') { if (!str_contains($styles, 'style="')) { $styles = ' style="'.$styles.'"'; } } if ($tagName) { $content[0] = '<'.str_replace( '<', '', str_replace( '>', '', $tagName ) ).$classes.$styles; } else { unset($content[0]); } unset($content[array_key_last($content)]); $out = ''; foreach ($content as $c) { $out .= $c.'>'; } return $out; } protected function getClassesAndStyles( array $attrs, array $classes = [], array $styles = [] ):string { // Get styles and classes from attributes $attr_styles = $this->getInlineStyles($attrs); $attr_classes = $this->getClasses($attrs); if(array_key_exists('slug', $attrs) && $attrs['slug'] === 'footer') { $classes[] = 'col'; } // Merge with passed classes and styles $styles = array_merge($attr_styles, $styles); $classes = array_merge($attr_classes, $classes); // Build attribute strings $class_string = !empty($classes) ? ' class="' . implode(' ', $classes) . '"' : ''; $style_string = !empty($styles) ? ' style="' . implode(';', $styles) . '"' : ''; $return = trim($class_string . $style_string); return ($return=='')? '' : ' '.$return; } /** * @param string $spacing * * @return string */ protected function getPresetSpacing(string $spacing):string { return match ($spacing) { 'var:preset|spacing|20' => 1, 'var:preset|spacing|30' => 2, 'var:preset|spacing|40' => 3, 'var:preset|spacing|50' => 4, 'var:preset|spacing|60' => 5, 'var:preset|spacing|70' => 6, 'var:preset|spacing|80' => 7, default => $spacing, }; } protected function getClasses(array $attrs):array { if (empty($attrs)) { return []; } $classes = []; foreach ($attrs as $key => $value) { $class = $this->getClass($key, $value, $attrs); if (is_array($class)) { $classes = array_merge($classes, $class); } else { $classes[] = $class; } } return array_filter($classes, function ($class) { return $class!=='' && !str_starts_with($class, 'wp'); }); } protected function getClass(string $key, string|bool|array|int $value, array $attrs):string|array { switch ($key) { //Any additional classes the user adds case 'className': return match ($value) { 'is-style-floating' => 'always mobile fixed', 'is-style-fixed' => 'fixed bottom', default => str_replace('is-style-', '', $value), }; case 'contentPosition': $classes = []; $pos = explode(' ', $value); foreach($pos as $p) { switch ($p) { case 'top': $classes[] = 'a-start'; break; case 'right': $classes[] = 'end'; break; case 'bottom': $classes[] = 'a-end'; break; case 'left': $classes[] = 'start'; break; } } return implode(' ', $classes); //Layout attributes case 'layout': $classes = []; $type = 'row'; if (array_key_exists('type', $value)) { $type = 'col'; // if ($value['type'] === 'constrained') { // $classes[] = 'container col'; // } } if (array_key_exists('orientation', $value)) { $type = 'col'; if ($value['orientation'] === 'vertical') { $classes[] = 'col'; if (in_array('row', $classes)) { $index = array_search('row', $classes); unset($classes[$index]); } } }else if (array_key_exists('type', $value) && $value['type'] === 'flex') { $classes[] = 'row'; if (in_array('col', $classes)) { $index = array_search('col', $classes); unset($classes[$index]); } } //jvbDump($type); //jvbDump($value); // $check = [$value, $attrs]; // foreach ($check as $ch) { // // } if (!array_key_exists('justifyContent', $value) && !array_key_exists('contentPosition', $attrs)) { $classes[] = ($type === 'row') ? 'start' : 'a-start'; } if (array_key_exists('justifyContent', $value) && !array_key_exists('contentPosition', $attrs)) { if (in_array($value['justifyContent'], ['left', 'right','space-between'])) { // jvbDump($type); switch ($value['justifyContent']) { case 'right': $classes[] = ($type === 'row') ? 'end' : 'a-end'; break; case 'space-between': $classes[] = 'btw'; break; } } } if (array_key_exists('flexWrap', $value)) { if ($value['flexWrap'] === 'nowrap') { $classes[] = 'nowrap'; } } return implode(' ', $classes); case 'align': return !empty($value) ? 'align-'.$value : ''; case 'verticalAlignment': return !empty($value) ? 'v-align-'.$value : ''; case 'isStackedMobile': return ($value === true) ? 'stack-small' : ''; case 'justifyContent': return !empty($value) ? 'j-'.$value : ''; case 'orientation': return $value==='column' ? 'column' : ''; case 'width': case 'dimRatio': if (is_numeric($value)) { $width = match (true) { $value < 25 => '25', $value < 33 => '33', $value <= 50 => '50', $value < 66 => '66', $value < 75 => '75', default => 'full', }; switch ($key) { case 'width': return 'width-'.$width; case 'dimRatio': return 'overlay-'.$width; } } return ''; //Typography case 'textAlign': return !empty($value) ? 'text-'.$value : ''; case 'dropCap': return $value === true ? 'drop-cap' : ''; //Media case 'hasParallax': return $value === true ? 'bg-parallax' : ''; case 'isRepeated': return $value === true ? 'bg-repeat' : ''; //Style base: case 'style': $classes = []; //Margin and Padding if (array_key_exists('spacing', $value)) { foreach (['margin' => 'm', 'padding'=>'p'] as $search => $c) { if (array_key_exists($search, $value['spacing'])) { $directions = []; // Collect ONLY preset spacing values for classes foreach ($value['spacing'][$search] as $direction => $size) { $presetSize = $this->getPresetSpacing($size); if ($presetSize) { $directions[$direction] = $presetSize; } // Non-preset values are skipped here and handled by inline styles below } if (empty($directions)) { continue; } // Check what directions we have $hasTop = isset($directions['top']); $hasBottom = isset($directions['bottom']); $hasLeft = isset($directions['left']); $hasRight = isset($directions['right']); // Check if axes match $xMatch = $hasLeft && $hasRight && $directions['left'] === $directions['right']; $yMatch = $hasTop && $hasBottom && $directions['top'] === $directions['bottom']; // All 4 directions exist and match → p-3 if ($hasTop && $hasBottom && $hasLeft && $hasRight && count(array_unique($directions)) === 1) { $classes[] = $c . '-' . reset($directions); } // Both axes match → px-3 py-2 elseif ($xMatch && $yMatch) { $classes[] = $c . 'x-' . $directions['left']; $classes[] = $c . 'y-' . $directions['top']; } // Only X axis matches → px-3 (+ individual for top/bottom) elseif ($xMatch) { $classes[] = $c . 'x-' . $directions['left']; if ($hasTop) { $classes[] = $c . 't-' . $directions['top']; } if ($hasBottom) { $classes[] = $c . 'b-' . $directions['bottom']; } } // Only Y axis matches → py-3 (+ individual for left/right) elseif ($yMatch) { $classes[] = $c . 'y-' . $directions['top']; if ($hasLeft) { $classes[] = $c . 'l-' . $directions['left']; } if ($hasRight) { $classes[] = $c . 'r-' . $directions['right']; } } // No matches - individual directions else { foreach ($directions as $direction => $size) { $dir = match($direction) { 'top' => 't', 'bottom' => 'b', 'left' => 'l', 'right' => 'r', default => $direction }; $classes[] = $c . $dir . '-' . $size; } } } } } if (array_key_exists('fontSize', $value)) { if (in_array($value['fontSize'], ['small', 'large', 'extra-large', 'huge'])) { $classes[] = 'font-'.$value['fontSize']; } if (in_array('fontWeight', $value)) { $classes[] = 'text-'.$value['fontWeight']; } if (in_array('textTransform', $value)) { if (in_array($value['textTransform'], ['uppercase', 'capitalize', 'lowercase'])) { $classes[] = $value['textTransform']; } } } return implode(' ', $classes); case 'fontSize': $classes[] = 'font-'.$value; return implode(' ', $classes); case 'isStackedOnMobile': return ($value === true) ? 'stack-small' : ''; case 'width': if (is_numeric($value)) { $width = match (true) { $value < 25 => '25', $value < 33 => '33', $value <= 50 => '50', $value < 66 => '66', $value < 75 => '75', default => 'full', }; switch ($key) { case 'width': return 'width-'.$width; case 'dimRatio': return 'overlay-'.$width; } } return ''; default: $ignore = [ 'useFeaturedImage', 'opacity', 'borderColor', 'backgroundColor', 'textColor', 'minHeight', 'minHeightUnit', 'isDark', 'sizeSlug', 'isUserOverlayColor', 'customOverlayColor', 'dimRatio', 'placeholder', 'alt', 'imageFill', 'mediaSizeSlug', 'isLink', 'kind', 'label', 'type', 'id', 'url', 'label', 'shouldSyncIcon', 'rel', 'opensInNewTab', 'title', 'ref', 'overlayMenu', 'slug', 'theme', 'tagName', 'level', 'ordered', 'area', 'mediaId', 'mediaLink', 'mediaType', 'height', //maybe still need? ]; if (!is_admin() &&!in_array($key, $ignore)) { // TESTING // jvbDump($key, 'getClass'); // jvbDump($attrs); } return ''; } } protected function getInlineStyles(array $attrs):array { if (empty($attrs)) { return []; } $styles = []; foreach ($attrs as $key => $value) { $style = $this->getStyle($key, $value, $attrs); $styles = array_merge($styles, $style); } return $styles; } protected function getStyle(string $key, string|bool|array|int $value, array $attrs):array { $styles = []; switch ($key) { // Font family settings case 'fontFamily': if ($value === 'body') { $styles[] = 'font-family: "Open Sans", system-ui, -apple-system, sans-serif'; } elseif ($value === 'heading') { $styles[] = 'font-family: "Josefin Sans", system-ui, -apple-system, sans-serif'; } elseif (!empty($value)) { $styles[] = 'font-family: '.$value; } break; // Icon color (for icon blocks) case 'iconColorValue': if (!empty($value)) { $styles[] = 'color: '.$value; } break; // Minimum height settings case 'minHeight': if (!empty($value) && isset($attrs['minHeightUnit'])) { $styles[] = 'min-height: '.$value.$attrs['minHeightUnit']; } elseif (!empty($value)) { $styles[] = 'min-height: '.$value.'px'; // Default to px if no unit specified } break; // Background URL (for cover, media blocks) case 'url': if (!empty($value) && str_starts_with($value, 'http')) { $styles[] = 'background-image: url('.$value.')'; } break; // Focal point for background images case 'focalPoint': $x = (array_key_exists('x', $attrs['focalPoint'])) ? $attrs['focalPoint']['x'] * 100 : 'center'; $y = (array_key_exists('y', $attrs['focalPoint'])) ? $attrs['focalPoint']['y'] * 100 : 'center'; $styles[] = 'background-position:'.$x.' '.$y.';'; break; // Complex style object case 'style': // Border styles if (isset($value['border'])) { $border = $value['border']; if (isset($border['radius'])) { $styles[] = 'border-radius: '.$border['radius']; } if (isset($border['width'])) { $styles[] = 'border-width: '.$border['width']; } if (isset($border['style']) && isset($border['width']) && !empty($border['style'])) { $styles[] = 'border-style: '.$border['style']; } if (isset($border['color'])) { $styles[] = 'border-color: '.$border['color']; } } // Color styles if (isset($value['color'])) { $color = $value['color']; if (isset($color['background'])) { $styles[] = 'background-color: '.$color['background']; } if (isset($color['text'])) { $styles[] = 'color: '.$color['text']; } if (isset($color['gradient'])) { $styles[] = 'background: '.$color['gradient']; } } // Layout styles if (isset($value['layout'])) { foreach ($value['layout'] as $layout => $option) { switch ($layout) { case 'selfStretch': if ($option === 'fixed' && isset($value['layout']['selfStretchValue'])) { $styles[] = 'width: '.$value['layout']['selfStretchValue']; } break; } } } // Typography styles if (isset($value['typography'])) { $typography = $value['typography']; if (isset($typography['fontSize'])) { $styles[] = 'font-size: '.$typography['fontSize']; } if (isset($typography['fontWeight'])) { $styles[] = 'font-weight: '.$typography['fontWeight']; } if (isset($typography['textDecoration'])) { $styles[] = 'text-decoration: '.$typography['textDecoration']; } if (isset($typography['textTransform'])) { $styles[] = 'text-transform: '.$typography['textTransform']; } if (isset($typography['letterSpacing'])) { $styles[] = 'letter-spacing: '.$typography['letterSpacing']; } if (isset($typography['lineHeight'])) { $styles[] = 'line-height: '.$typography['lineHeight']; } } // Spacing styles if (isset($value['spacing'])) { $spacing = $value['spacing']; // Don't duplicate margin/padding that's handled by classes // Only add specific CSS values here that wouldn't work well as classes if (isset($spacing['margin'])) { foreach ($spacing['margin'] as $direction => $size) { // If not a preset value, add as inline style if (!str_contains($size, 'var:preset')) { $styles[] = 'margin-'.$direction.': '.$size; } } } if (isset($spacing['padding'])) { foreach ($spacing['padding'] as $direction => $size) { // If not a preset value, add as inline style if (!str_contains($size, 'var:preset')) { $styles[] = 'padding-'.$direction.': '.$size; } } } } break; case 'dimRatio': $ratio = (ceil($value /25) *25); $s = 'background-color: rgba(var(--base-rgb), '; switch ($ratio) { case 0: $s .= 'var(--rgb-subtle-hover));'; break; case 25: $s .= 'var(--rgb-light));'; break; case 50: $s .= 'var(--rgb-medium));'; break; default: $s .= 'var(--rgb-heavy));'; break; } $styles[] = $s; break; // Custom styles (any other attributes that need inline styling) case 'backgroundType': if ($value === 'video' && isset($attrs['backgroundUrl'])) { // Don't set a background image for videos - it will be handled by the video element } elseif (isset($attrs['backgroundUrl'])) { $styles[] = 'background-image: url('.$attrs['backgroundUrl'].')'; } break; case 'backgroundColor': case 'borderColor': case 'textColor': $type = ($key === 'backgroundColor') ? 'background-color:' : (($key === 'borderColor') ? 'border-color:' : 'color:'); $defaults = apply_filters('jvbColours', ['base', 'contrast', 'action', 'secondary']); $continue = true; foreach ($defaults as $default) { if (str_starts_with($value, $default)) { $continue = false; $styles[] = $type.'var(--'.$value.')'; } } if ($continue) { $styles[] = $type.$value; } break; // Any other attributes that need direct styling default: $ignore = [ 'useFeaturedImage', 'opacity', 'textAlign', 'minHeightUnit', 'isDark', 'isUserOverlayColor', 'contentPosition', 'sizeSlug', 'customOverlayColor', 'alt', 'placeholder', 'imageFill', 'mediaSizeSlug', 'isLink', 'kind', 'label', 'type', 'id', 'url', 'label', 'shouldSyncIcon', 'rel', 'opensInNewTab', 'title', 'ref', 'overlayMenu', 'slug', 'theme', 'tagName', 'level', 'ordered', 'area', 'className', 'fontSize', 'layout', 'align', 'mediaId', 'mediaLink', 'mediaType', 'isStackedOnMobile', 'width', 'height', // maybe still need? ]; if (!is_admin() && !in_array($key, $ignore)) { //TESTING // jvbDump($key, 'getStyle'); // jvbDump($attrs); } // No default inline styles break; } return $styles; } public function formatImage(int $ID = 0, string $start = 'tiny', string $replace = 'large'):string { if ($ID === 0) { $ID = $this->imageID($ID); } if ($ID === 0) { return ''; } return jvbFormatImage($ID, $start, $replace); } }