Jake Vanderwerf
3 hours ago a17c578433ef543e220697813a9367a260df83e1
taxonomies/project.php
@@ -1,138 +1,842 @@
<?php
// /taxonomies/progress.php
function ajv_project():array
use JVBase\managers\Cache;
use JVBase\meta\Meta;
use JVBase\registrar\Registrar;
use JVBase\utility\Image;
if (!defined('ABSPATH')) {
    exit;
}
add_action('plugins_loaded', 'ajv_project',1);
//Add fields later so we can verify taxonomies/post types exist
add_action('plugins_loaded', 'ajv_project_fields', 2);
add_filter('ajv_ProjectSchemaDefault', 'ajv_project_schema');
add_filter('ajv_ProjectMetaDefault', 'ajv_project_meta');
add_filter('ajv_ProjectArchiveDefault', 'ajv_project_archive');
function ajv_project(){
    if (!class_exists('JVBase\registrar\Registrar')) {
        return;
    }
    $project = Registrar::forTerm('project', 'Project', 'Projects')
        ->setIcon('cards-three')
        ->make([
            'rewrite'   => [
                'slug'          => 'projects',
                'with_front'    => false,
            ],
            'for'    => [
                'art',
                'design',
                'development',
                'strategy',
                'writing'
            ],
            'hierarchical'  => true,
        ])
        ->setAll([
            'show_feed',
            'show_directory',
            'is_content',
        ]);
}
function ajv_project_fields():void
{
    return [
        'singular'       => 'Project',
        'plural'         => 'Projects',
        'icon'           => 'cards-three',
        'show_feed'      => true,
        'show_directory' => true,
        'approve_new'    => false,
        'rewrite'        => [
            'slug'         => 'projects',
            'with_front'   => false,
            'hierarchical' => true,
        ],
        'hierarchical'   => true,
        'for_content'    => [
            'art',
            'design',
            'development',
            'strategy',
            'writing',
        ],
        'seo' => [
            'schema' => [
                'type' => 'CreativeWork',
                'name' => '{{term_name}}',
                'description' => '{{term_description}}',
                'dateCreated' => '{{started}}',
                'keywords' => '{{target.name}}',
    if (!class_exists('JVBase\registrar\Registrar')) {
        return;
    }
    $project = Registrar::getInstance('project');
    $directory = $project->config('directory');
    $directory->setTitle('Projects');
    //$breadcrumbs = $project->config('breadcrumbs');
    //$breadcrumbs->setCrumb('project');
    $fields = $project->fields();
    $fields->addField(
        'city',
        [
            'type'          => 'selector',
            'subtype'          => 'taxonomy',
            'isReference' => true,
            'taxonomy'      => 'city',
            'label'         => 'City',
            'quickEdit' => true,
        ]
    );
    $fields->addField(
        'thumbnail',
        [
            'type'  => 'upload',
            'multiple'  => false,
            'label' => 'Featured Image',
        ]
    );
    $fields->addField(
        'summary',
        [
            'type'  => 'textarea',
            'quill' => true,
            'label' => 'Summary',
        ]
    );
    $fields->addField(
        'target',
        [
            'type'          => 'taxonomy',
            'isReference' => true,
            'taxonomy'      => 'target',
            'label'         => 'Target Audience',
            'quickEdit' => true,
        ]
    );
    $fields->addField(
        'started',
        [
            'type'  => 'date',
            'label' => 'Started',
            'quickEdit' => true,
        ]
    );
    $fields->addField(
        'ended',
        [
            'type'  => 'date',
            'label' => 'Ended',
            'quickEdit' => true,
        ]
    );
    $fields->addField(
        'budget',
        [
            'type'  => 'radio',
            'label' => 'Budget',
            'options'   => [
                'low'   => '$',
                'medium-low'=> '$$',
                'medium'    => '$$$',
                'medium-high'=> '$$$$',
                'high'  => '$$$$$',
            ]
        ]
    );
    $fields->addField(
        'timeline',
        [
            'type'  => 'text',
            'label' => 'Timeline',
            'quickEdit' => true,
        ]
    );
    $fields->addField('needs', [
        'type'       => 'repeater',
        'label'      => 'Needs',
        'fields'     => [
            'need' => [
                'type' => 'text',
                'label' => 'Need',
                'required' => true
            ],
            'meta' => [
                'title' => '{{term_name}} | Project by JakeVan',
                'description' => '{{term_description}}',
            'image' => [
                'type'  => 'upload',
                'multiple' => true,
                'label' => 'Example Image'
            ],
            'archive' => [
                'type' => 'CollectionPage',
                'name' => '{{term_name}}',
                'description' => '{{term_description}}',
            ],
        ],
        'fields'         => [
            'term_name' => [
                'label'     => 'Name',
                'type'      => 'text',
                'quickEdit' => true,
            ],
            'city' => [
                'type'          => 'taxonomy',
                'taxonomy_type' => 'reference',
                'taxonomy'      => 'city',
                'autocomplete'  => true,
                'quickEdit' => true,
                'label'         => 'City',
            ],
            'target' => [
                'type'          => 'taxonomy',
                'taxonomy_type' => 'reference',
                'taxonomy'      => 'target',
                'autocomplete'  => true,
                'quickEdit' => true,
                'label'         => 'Target Audience',
            ],
            'started'   => [
                'type'  => 'date',
                'label' => 'Started',
                'quickEdit' => true,
            ],
            'ended'     => [
                'type'  => 'date',
                'label' => 'Ended',
                'quickEdit' => true,
            ],
            'budget'    => [
                'type'  => 'radio',
                'label' => 'Budget',
                'options'   => [
                    'low'   => '$',
                    'medium-low'=> '$$',
                    'medium'    => '$$$',
                    'medium-high'=> '$$$$',
                    'high'  => '$$$$$',
                ]
            ],
            'timeline'  => [
                'type'  => 'text',
                'label' => 'Timeline',
                'quickEdit' => true,
            ],
            'url'   => [
                'type'  => 'url',
                'label' => 'Project URL',
                'quickEdit' => true,
            ],
            'description'   => [
            'fulfilled' => [
                'type'  => 'textarea',
                'quill' => true,
                'quickEdit' => true,
                'label' => 'Project Description',
            ],
            'needs' => [
                'type'  => 'repeater',
                'label' => 'Needs',
                'fields'=> [
                    'need' => [
                        'type' => 'text',
                        'label' => 'Need',
                        'required' => true
                    ],
                    'fulfilled' => [
                        'type'  => 'textarea',
                        'quill' => true,
                        'label' => 'How I fulfilled it'
                    ]
                ]
            ],
            'wants' => [
                'type'  => 'repeater',
                'label' => 'Wants',
                'fields'=> [
                    'need' => [
                        'type' => 'text',
                        'label' => 'Want',
                        'required' => true
                    ],
                    'fulfilled' => [
                        'type'  => 'textarea',
                        'quill' => true,
                        'label' => 'How I fulfilled it'
                    ]
                ]
            ],
            'common'    => [ 'wiki' ]
                'label' => 'How I fulfilled it'
            ]
        ]
    ]);
    $fields->addField('wants', [
        'type'       => 'repeater',
        'label'      => 'Wants',
        'fields'     => [
            'need' => [
                'type' => 'text',
                'label' => 'Need',
                'required' => true
            ],
            'image' => [
                'type'  => 'upload',
                'multiple' => true,
                'label' => 'Example Image'
            ],
            'fulfilled' => [
                'type'  => 'textarea',
                'quill' => true,
                'label' => 'How I fulfilled it'
            ]
        ]
    ]);
    $fields->addField(
        'forType',
        [
            'type'  => 'repeater',
            'label' => 'Person, Organization, or LocalBusiness this is for',
            'fields'    => [
                'type'  => [
                    'type'  => 'select',
                    'label' => 'Type',
                    'options'   => [
                        'JVBase\managers\SEO\render\Thing\Person'   => 'Person',
                        'JVBase\managers\SEO\render\Thing\Organization\LocalBusiness\LocalBusiness' => 'Local Business',
                        'JVBase\managers\SEO\render\Thing\Organization\Organization' => 'Organization'
                    ]
                ],
                'name'  => [
                    'type'  => 'text',
                    'label' => 'Name',
                ],
                'url'   => [
                    'type'  => 'url',
                    'label' => 'Website',
                ],
                'location'  => [
                    'type'  => 'text',
                    'label' => 'Location',
                ],
            ]
        ]
    );
    $fields->addCommon('review');
    $fields->addCommon('wiki');
}
//function ajv_project():array
//{
//    return [
//        'singular'       => 'Project',
//        'plural'         => 'Projects',
//        'icon'           => 'cards-three',
//        'show_feed'      => true,
//        'show_directory' => true,
//        'approve_new'    => false,
//        'rewrite'        => [
//            'slug'         => 'projects',
//            'with_front'   => false,
//            'hierarchical' => true,
//        ],
//        'hierarchical'   => true,
//        'for_content'    => [
//            'art',
//            'project',
//            'development',
//            'strategy',
//            'writing',
//        ],
//        'seo' => [
//            'schema' => [
//                'type' => 'CreativeWork',
//                'name' => '{{name}}',
//                'description' => '{{description}}',
//                'dateCreated' => '{{started}}',
//                'keywords' => '{{target.name}}',
//            ],
//            'meta' => [
//                'title' => '{{name}} | Project by JakeVan',
//                'description' => '{{description}}',
//            ],
//            'archive' => [
//                'type' => 'CollectionPage',
//                'name' => '{{name}}',
//                'description' => '{{description}}',
//            ],
//        ],
//        'fields'         => [
//            'term_name' => [
//                'label'     => 'Name',
//                'type'      => 'text',
//                'quickEdit' => true,
//            ],
//            'city' => [
//                'type'          => 'taxonomy',
//                'isReference' => true,
//                'taxonomy'      => 'city',
//                'autocomplete'  => true,
//                'quickEdit' => true,
//                'label'         => 'City',
//            ],
//            'target' => [
//                'type'          => 'taxonomy',
//                'isReference' => true,
//                'taxonomy'      => 'target',
//                'autocomplete'  => true,
//                'quickEdit' => true,
//                'label'         => 'Target Audience',
//            ],
//            'started'   => [
//                'type'  => 'date',
//                'label' => 'Started',
//                'quickEdit' => true,
//            ],
//            'ended'     => [
//                'type'  => 'date',
//                'label' => 'Ended',
//                'quickEdit' => true,
//            ],
//            'budget'    => [
//                'type'  => 'radio',
//                'label' => 'Budget',
//                'options'   => [
//                    'low'   => '$',
//                    'medium-low'=> '$$',
//                    'medium'    => '$$$',
//                    'medium-high'=> '$$$$',
//                    'high'  => '$$$$$',
//                ]
//            ],
//            'timeline'  => [
//                'type'  => 'text',
//                'label' => 'Timeline',
//                'quickEdit' => true,
//            ],
//            'url'   => [
//                'type'  => 'url',
//                'label' => 'Project URL',
//                'quickEdit' => true,
//            ],
//            'description'   => [
//                'type'  => 'textarea',
//                'quill' => true,
//                'quickEdit' => true,
//                'label' => 'Project Description',
//            ],
//            'needs' => [
//                'type'  => 'repeater',
//                'label' => 'Needs',
//                'fields'=> [
//                    'need' => [
//                        'type' => 'text',
//                        'label' => 'Need',
//                        'required' => true
//                    ],
//                    'fulfilled' => [
//                        'type'  => 'textarea',
//                        'quill' => true,
//                        'label' => 'How I fulfilled it'
//                    ]
//                ]
//            ],
//            'wants' => [
//                'type'  => 'repeater',
//                'label' => 'Wants',
//                'fields'=> [
//                    'need' => [
//                        'type' => 'text',
//                        'label' => 'Want',
//                        'required' => true
//                    ],
//                    'fulfilled' => [
//                        'type'  => 'textarea',
//                        'quill' => true,
//                        'label' => 'How I fulfilled it'
//                    ]
//                ]
//            ],
//
//            'common'    => [ 'wiki' ]
//        ]
//    ];
//}
function ajv_project_schema():array
{
    return [
//        'type'              => 'JVBase\managers\SEO\render\Thing\CreativeWork\CreativeWork',
        'name'              => 'Projects by JakeVan',
        'description'       => 'Each project can have multiple aspects - from Design to Strategy.',
//        'primaryImageOfPage'    => '{{thumbnail}}',
    ];
}
function ajv_project_meta():array
{
    return[
        'name' => '{{name}} | Project',
    ];
}
function ajv_project_archive(array $defaults):array
{
    return array_merge($defaults, [
        'name' => '{{name}} | Project',
        'description'   => '{{description}}',
        'primaryImageOfPage'    => '{{thumbnail}}',
        'about'             => [
            'type'              => 'JVBase\managers\SEO\render\Thing\CreativeWork\CreativeWork',
            'name'              => '{{name}}',
            'creator'           => '{{CREATOR}}',
            'sourceOrganization'=> '{{forType}}',
            'abstract'          => '{{summary}}'
        ],
    ]);
}
function ajv_project_reference_schema(array $defaults):array
{
    return $defaults;
}
function ajv_render_project_cover(int $ID):string
{
    return '';
    $background = '';
    $meta = Meta::forTerm($ID);
    $thumbnail = $meta->get('thumbnail');
    if ($thumbnail !== '') {
        $background = str_replace('<img', '<img style="object-position:center center"', jvbFormatImage((int)$thumbnail));
    }
    return $background;
}
add_filter('jvbSummaryHeader', 'ajv_project_summary_header', 11, 2);
function ajv_project_summary_header(string $return, string $type):string
{
    if ($type !== 'project') {
        return $return;
    }
    $ID = get_queried_object_id();
    $meta = Meta::forTerm($ID);
    $fields = $meta->getAll();
    $extra = 'Edmonton';
    if ($fields['city'] !== '') {
        $cities = explode(',', $fields['city']);
        $temp = [];
        foreach ($cities as $city) {
            $term = get_term($city, 'ajv_city');
            if ($term && !is_wp_error($term)) {
                $temp[] = $term->name;
            }
        }
        if (!empty($temp)) {
            $extra = jvbCommaList($temp);
        }
    }
    $extra .= '-Based Project';
    return '<small>'.$extra.'</small>'.$fields['name'].'.';
}
add_filter('jvbSummaryHeaderExtra', 'ajv_project_summary_extra', 11, 2);
function ajv_project_summary_extra(string $return, string $type):string
{
    if ($type !== 'project') {
        return $return;
    }
    return '<p>Or see <a href="'.get_home_url(null,'/projects/').'">all projects</a>.</p>';
}
add_filter('jvbBeforeSummary', 'ajv_project_before_summary', 10, 2);
function ajv_project_before_summary(string $return, string $type):string
{
    if ($type !== 'project') {
        return $return;
    }
    $meta = Meta::forTerm(get_queried_object_id());
    $fields = $meta->getAll();
    $rows = [];
    if (!empty($fields['city'])) {
        $rows[] = jvbMetaTermList($fields['city'], 'city');
    }
    if ($fields['target'] !== '') {
        $rows[] = jvbMetaTermList($fields['target'], 'target');
    }
    if ($fields['started'] !== '') {
        $rows[] = '<span>'.jvbIcon('calendar-blank').'Started:</span><time datetime="'.$fields['started'].'">'.date('F j, Y', strtotime($fields['started'])).'</time>';
    }
    if ($fields['ended'] !== '') {
        $rows[] = '<span>Ended:</span><time datetime="'.$fields['ended'].'">'.date('F j, Y', strtotime($fields['ended'])).'</time>';
    }
    if ($fields['budget'] !== '') {
        $budget = '';
        for ($i = 0; $i <= (int) $fields['budget']; $i++) {
            $budget .= '$';
        }
        $budget = match($fields['budget']) {
          'low'             => '$',
          'medium-low'      => '$$',
          'medium'          => '$$$',
          'medium-high'     => '$$$$',
          'high'            => '$$$$$'
        };
        $title = ucwords($fields['budget']);
        $rows[] = '<span>'.jvbIcon('currency-dollar-simple').'Budget:</span><span title="'.$title.'">'.$budget.'</span>';
    }
    if ($fields['timeline'] !== '') {
        $rows[] = '<span>'.jvbIcon('hourglass-medium').'Timeline:</span>'.$fields['timeline'];
    }
    $summary = '<section id="summary"><h2>At a Glance</h2>';
    if (!empty($rows)) {
        $summary .= '<ul class="summary">';
        $summary .= implode('', array_map(function ($row) {
            return '<li>'.$row.'</li>';
        }, $rows));
        $summary .= '</ul>';
    }
    if (!empty($meta->get('summary'))) {
        $summary .= '<div>'.wpautop($meta->get('summary')).'</div>';
    }
    $summary .= '</section>';
    $img = '';
    if (!empty($meta->get('thumbnail'))) {
        $data = Image::getData($meta->get('thumbnail'));
        if (!empty($data)) {
            $img = sprintf(
                '<div class="poster" style="background-image:url(\'%s\');" data-bg-img data-bg-small="%s" data-bg-medium="%s" data-bg-large="%s"></div>',
                $data['tiny'],
                $data['small'],
                $data['medium'],
                $data['large'],
            );
        }
    }
    return $summary.$img;
}
add_filter('jvbSummaryImage', 'ajv_project_summary_image', 10, 2);
function ajv_project_summary_image(string $return, string $type):string
{
    if ($type !== 'project') {
        return $return;
    }
    return '';
    $meta = Meta::forTerm(get_queried_object_id());
    $thumbnail = $meta->get('thumbnail');
    return $thumbnail === '' ? '' : jvbFormatImage((int)$thumbnail);
}
add_filter('jvbSummaryDetails', 'ajv_project_summary_details', 10, 2);
function ajv_project_summary_details(array $return, string $type):array
{
    if ($type !== 'project') {
        return $return;
    }
    $return  = [];
    $meta = Meta::forTerm(get_queried_object_id());
    $fields = $meta->getAll();
    if ($fields['summary'] !== '') {
        $return['about-this-project'] = wpautop($fields['summary']);
    }
    if (!empty($fields['needs'])) {
        $return['needs'] = ajvb_format_needs($fields['needs']);
    }
    if (!empty($fields['wants'])) {
        $return['wants'] = ajvb_format_wants($fields['wants']);
    }
    if (!empty($fields['review'])) {
        $return['review'] = $fields['review'];
    }
    return $return;
}
add_filter('jvbSummaryIsOpen', 'ajv_open_project_summary', 10, 2);
function ajv_open_project_summary(bool $isOpen, string $type):bool
{
    if ($type !== 'project') {
        return $isOpen;
    }
    return true;
}
add_filter('jvbSummaryDetailsTitle', 'ajv_project_summary_label', 10, 2);
function ajv_project_summary_label(string $title, string $type):string
{
    return 'More about this project';
}
add_filter('render_block', 'ajv_project_content', 9999, 2);
function ajv_project_content(string $OG, array $block):string {
    if ($block['blockName'] !== 'jvb/summary') {
        return $OG;
    }
    if (!is_tax(BASE.'project')) {
        return $OG;
    }
    $menu = Cache::for('project_menu');
    $ID = get_queried_object_id();
    $menu->connect('term');
    $registrar = Registrar::getInstance('project');
    $menu->flush();
    $nav =  $menu->remember($ID, function() use ($registrar, $ID) {
        $types = array_map(function ($type) {
            return jvbCheckBase($type);
        }, $registrar->registrar->for);
        $menu = [];
        foreach ($types as $type) {
            $query = new WP_Query([
                'post_type' => $type,
                'post_status' => 'publish',
                'tax_query' => [[
                    'taxonomy'  => BASE.'project',
                    'terms'  => $ID
                ]],
                'posts_per_page' => 1,
                'fields'    => 'ids',
            ]);
            if ($query->have_posts()) {
                $typeRegistrar = Registrar::getInstance($type);
                $menu[jvbNoBase($type)] = $typeRegistrar;
            }
            wp_reset_postdata();
        }
        if (!empty($menu)) {
            global $wp;
            $base = home_url( $wp->request );
            $current = home_url(add_query_arg($_GET, $wp->request));
            $class = $current === $base ? ' class="current"' : '';
            $nav = sprintf(
                '<nav class="filter-projects">Filter by: <ul><li><a href="%s"%s>All</a></li>',
                $base,
                $class
            );
            foreach ($menu as $slug => $registrar) {
                $url = add_query_arg('content', $slug, $base);
                $class = $current === $url ? ' class="current"' : '';
                $nav .= sprintf(
                    '<li><a href="%s"%s>%s%s</a></li>',
                    $url,
                    $class,
                    jvbIcon($registrar->getIcon()),
                    $registrar->getPlural(),
                );
            }
            $nav .= '</ul></nav>';
            return $nav;
        }
        return '';
    });
    global $_GET;
    $content = array_key_exists('content', $_GET) ? $_GET['content'] : 'all';
    $cache = Cache::for($content)->connect('term');
    $cache->flush();
    $page = $_GET['jpage']??1;
    $key = $cache->generateKey([
        'id'    => $ID,
        'page'  => $page
    ]);
    return $OG.$cache->remember(
        $key,
        function() use ($content, $ID, $page, $nav, $registrar) {
            $post_types = ($content === 'all') ? array_map(function ($type) { return jvbCheckBase($type); },$registrar->registrar->for) : jvbCheckBase($content);
            $args = [
                'post_type' => $post_types,
                'posts_per_page' => 20,
                'paged' => $page,
                'fields'    => 'ids',
                'tax_query' => [[
                    'taxonomy' => BASE.'project',
                    'field' => 'term_id',
                    'terms' => $ID
                ]]
            ];
            $posts = new WP_Query($args);
            $section = '<section id="project-parts" class="project-parts">
                <h2>Project Parts</h2>'.$nav;
                if (!empty($posts->posts)) {
                    $section .= '<ul class="project-list item-grid list-view">';
                   foreach ($posts->posts as $postID) {
                        $meta = Meta::forPost($postID);
                        $fields = $meta->getAll();
                        $link = get_the_permalink($postID);
                        $thumb = '';
                        if (!empty($fields['post_thumbnail'])) {
                            $thumb = sprintf(
                                '<a href="%s">%s</a>',
                                $link,
                                jvbFormatImage($fields['post_thumbnail'])
                                );
                        }
                        $published = '';
                        if (!empty($fields['post_date'])) {
                            $published = sprintf('<span>%s<time datetime="%s">%s</time></span>',
                            jvbIcon('calendar-blank'),
                            $fields['post_date'],
                            date('F j, Y', strtotime($fields['post_date']))
                            );
                        }
                        $section .= sprintf(
                            '<li class="item">%s<div class="col start w-full">
               <h3><a href="%s">%s</a></h3>
               %s
               %s
            </div></li>',
                            $thumb,
                            $link,
                            $fields['post_title'],
                            $published,
                            $fields['post_excerpt']
                        );
                    }
                    $section .= '</ul>';
                    $total = $posts->max_num_pages;
                    if ($total > 1) {
                        $big = 999999999; // need an unlikely integer
                        $format = ($content === 'all') ? '' : '&content='.$content;
                        echo paginate_links([
                            'base' => str_replace( $big, '%#%', esc_url( get_pagenum_link( $big ) ) ),
                            'format' => '?jpage=%#%'.$format,
                            'current' => $page,
                            'total' => $total
                        ]);
                    }
                } else {
                    $section .= '<p>Nothing here yet.</p>';
                }
            $section .= '</section>';
            wp_reset_postdata();
            return $section;
        }
    );
}
add_filter('jvbSummaryOnThisPage', 'ajv_project_on_this_page', 10, 2);
function ajv_project_on_this_page(array $ids, string $type):array {
    if ($type !== 'project'){
        return $ids;
    }
    $ids[] = 'project-parts';
    return $ids;
}
add_filter('jvb_content_tax_args_project', 'ajv_modify_project_args');
function ajv_modify_project_args(array $args):array
{
//    $args['meta_key'] = BASE.'started';
//    $args['meta_type'] = 'DATETIME';
//    $args['orderby'] = 'meta_value';
    return $args;
}
function ajv_render_project_content(int $termID):string
{
    $meta = Meta::forTerm($termID);
    $slug = sanitize_title($meta->get('name'));
    $inside = '';
    $postTypes = array_map(function($type) { return jvbCheckBase($type);}, Registrar::getInstance('project')->registrar->for);
    $images = ajv_get_limited_post_images([
        'post_type' => $postTypes,
        'tax_query' => [
            [
                'taxonomy'  => BASE.'project',
                'terms'     => $termID
            ]
        ]
    ], true);
    if (!empty($images)) {
        $images = sprintf(
            '<h3>Project Parts</h3><ul class="loop scroll">%s</ul>',
            implode('',array_map(function ($img) {
                return sprintf(
                    '<li>%s</li>',
                    $img
                );
            }, $images))
        );
    } else {
        $images = '';
    }
    $url = get_term_link($termID, BASE.'project')??'';
    $titleExtra = '';
        $terms = [
            'city',
            'target'
        ];
        foreach ($terms as $term) {
            if (!empty($meta->get($term))) {
                $registrar = Registrar::getInstance($term);
                $titleExtra .= '<li>'.jvbIcon($registrar->getIcon()).jvbGetTermLink($meta->get($term), BASE.$term).'</li>';
            }
        }
        if (!empty($titleExtra)) {
            $titleExtra = '<ul class="terms">'.$titleExtra.'</ul>';
        }
        $times = [
            'started',
            'ended'
        ];
        $temp = '';
        foreach ($times as $time) {
            if (!empty($meta->get($time))) {
                $theTime = strtotime($meta->get($time));
                $temp .= sprintf(
                    '<li>%s: <time datetime="%s">%s</time></li>',
                    ucfirst($time),
                    date('c', $theTime),
                    date('F j, Y', $theTime)
                );
            }
        }
        if (!empty($temp)) {
            $titleExtra .= '<ul class="term-list">'.$temp.'</ul>';
        }
    return sprintf(
        '<li id="%s">
            <div class="cover row btm x-mid">
                %s
                <div class="content">
                    <h2><a href="%s">%s</a></h2>
                    <div class="meta">
                    %s
                    </div>
                </div>
            </div>
            %s
            %s
            %s
            <a class="btn" href="%s">%sSee Full Project</a>
        </li>',
        $slug,
        jvbFormatImage((int)$meta->get('thumbnail')),
        $url,
        $meta->get('name'),
        $titleExtra,
        empty($meta->get('summary')) ? '' : wpautop($meta->get('summary')),
        $inside,
        $images,
        $url,
        jvbIcon('arrow-circle-right')
    );
}