Jake Vanderwerf
2026-02-10 8c4279f9bbe7ec4681412865b999f2f4457d80ac
content/progress.php
@@ -1,46 +1,387 @@
<?php
// /content/art.php
// /content/progress.php
use JVBase\meta\Meta;
function altr_progress():array
{
    return [
        'singular'       => 'Before & After',
        'plural'       => 'Before & Afters',
        'dash_title'    => 'Before & Afters',
        'dash_title'    => 'Progress',
        'dash_description' => 'Manage your before and after posts',
        'breadcrumb'    => 'Before & Afters',
        'hide_single'  => false,
        'capability_type' => ['progress', 'progress'],
        'hide_children' => true,
        'is_timeline'   => true,
        'show_feed'    => true,
        'favouritable' => false,
        'karma'        => false,
        'show_directory'=> true,
        'directory_extra'=> ['goal', 'skin-type','age'],
        'hierarchical'  => true,
        'icon'         => 'arrows-left-right',
        'rewrite'      => [
            'slug'       => 'before-and-after',
            'with_front' => false,
        ],
        'sections'  => [
            'progression'   => [
                'label' => 'Progression',
            ]
        ],
        'custom_order' => [
            'number'    => [
                'label' => 'Number of Treatments',
                'icon'  => 'hash-straight',
                'for'   => 'progress',
            ]
        ],
        'seo' => [
            'schema' => [
                'type' => 'BeforeAfter',
                'name' => '{{post_title}}',
                'description' => '{{post_excerpt}}',
                'temporalCoverage' => '{{post_date}}/{{last_date}}',
                'additionalProperty' => [
                    ['name' => 'Number of sessions', 'value' => '{{number.name}}'],
                    ['name' => 'Treatment area', 'value' => '{{body-part.name}}'],
                    ['name' => 'Tattoo style', 'value' => '{{style.name}}'],
                    ['name' => 'Skin type', 'value' => '{{skin-type.name}}'],
                    ['name' => 'Goal', 'value' => '{{goal.name}}'],
                ],
                'associatedMedia' => '{{timeline_photos}}',
            ],
            'meta' => [
                'title' => '{{style.name}} {{theme.name}} Tattoo – Before & After {{number}} Laser Removal Sessions',
                'description' => 'See this {{style.name}} {{theme.name}} {{age.name}}-old tattoo before and after {{number}} laser tattoo removal treatments on the {{body-part.name}}.',
            ],
            'archive' => [
                'type' => 'CollectionPage',
                'name' => 'Tattoos Before and After Laser Tattoo Removal',
            ],
        ],
        'feed'      => [
            'single'    => [
                'pre_title' => 'Before & After Laser Tattoo Removal',
            ],
            'archive'   => [
            ],
            'config'    => [
                'is_gallery'    => false,
                'content'       => 'progress',
                'context'       => 'progress',
                'id'            => [],
                'class'         => [],
            ]
        ],
        'fields'       => [
            'post_title'     => [
                'type'  => 'text',
                'label' => 'Title',
                'quickEdit' => true,
                'section'   => 'progression',
                'for_all'   => true,
            ],
            'featured_image' => [
                'type'  => 'image',
                'label' => 'Image',
            'post_status'         => [
                'type'      => 'radio',
                'label'     => 'Status',
                'options'   => [
                    'publish'   => 'Show',
                    'draft'     => 'Hide',
                    'trash'     => 'Scrap',
                    'delete'    => 'Permanently Delete'
                ],
                'for_all'   => true,
            ],
            'gallery'        => [
                'type'  => 'gallery',
                'label' => 'Gallery',
            'post_date'  => [
                'type'      => 'date',
                'label'     => 'Date',
                'for_all'   => true,
            ],
            'price'          => [
                'type'  => 'number',
                'label' => 'Price',
            'post_thumbnail'                => [
                'type'      => 'upload',
                'label'     => 'Image',
                'quickEdit' => true,
                'section'   => 'progression',
                'for_all'   => true,
            ],
            'person' => [
                'type'  => 'taxonomy',
                'taxonomy'  => 'person',
                'autocomplete' => true,
                'label' => 'Person',
                'quickEdit' => true,
                'createNew' => true,
                'section'   => 'progression',
                'hint'      => 'Not public, just to make it easier to find'
            ],
            'body-part' => [
                'type'  => 'taxonomy',
                'taxonomy'  => 'body-part',
                'label' => 'Body Part',
                'autocomplete' => true,
                'quickEdit' => true,
                'createNew' => true,
                'section'   => 'progression'
            ],
            'goal' => [
                'type'  => 'taxonomy',
                'taxonomy'  => 'goal',
                'autocomplete' => true,
                'label' => 'Goal',
                'quickEdit' => true,
                'createNew' => true,
                'section'   => 'progression'
            ],
            'timeline' => [
                'type'  => 'taxonomy',
                'taxonomy'  => 'timeline',
                'autocomplete' => true,
                'label' => 'Timeline',
                'quickEdit' => true,
                'createNew' => true,
                'section'   => 'progression',
                'for_all'   => true,
                'hidden'    => true,
            ],
            'style' => [
                'type'  => 'taxonomy',
                'taxonomy'  => 'style',
                'autocomplete' => true,
                'label' => 'Tattoo Style',
                'quickEdit' => true,
                'createNew' => true,
                'section'   => 'progression'
            ],
            'theme' => [
                'type'  => 'taxonomy',
                'taxonomy'  => 'theme',
                'autocomplete' => true,
                'label' => 'Tattoo Theme',
                'quickEdit' => true,
                'createNew' => true,
                'section'   => 'progression'
            ],
            'skin-type' => [
                'type'  => 'taxonomy',
                'taxonomy'  => 'skin-type',
                'label' => 'Skin Type',
                'autocomplete' => true,
                'quickEdit' => true,
                'createNew' => true,
                'section'   => 'progression'
            ],
            'age' => [
                'type'  => 'taxonomy',
                'taxonomy'  => 'age',
                'autocomplete'      => true,
                'label' => 'Age of Tattoo',
                'quickEdit' => true,
                'createNew' => true,
                'section'   => 'progression'
            ],
            'number' => [
                'type'  => 'text',
                'subtype'  => 'number',
                'label' => 'Number of Treatments',
                'quickEdit' => true,
                'section'   => 'progression',
                'for_all'   => true,
                'hidden'    => true,    //auto calculated
            ],
            'post_content'   => [
                'type'  => 'textarea',
                'quill' => true,
                'label' => 'Notes'
                'label' => 'Notes',
                'section'   => 'progression',
                'for_all'   => true,
            ],
            'last_date' => [
                'type'  => 'number',
                'label' => 'Last Date',
                'hidden'    => true,
                'default'   => 0,
                'for_all'   => true,
            ]
        ],
        'single_image' => false,
        'upload_title' => 'Upload Before & Afters',
    ];
}
add_filter('jvbFeedItem', 'altr_progress_item', 10, 2);
function altr_progress_item(string $out, string $content):string
{
    if ($content !== 'progress') {
        return $out;
    }
    ob_start();
    ?>
    <div class="feed item col progress" data-timeline>
        <div class="images">
            <a>
                <span class="before-text">Before</span>
                <img data-field="before" width="300px" height="300px" sizes="(max-width: 768px) 100vw, (max-width: 1200px) 50vw, 800px" loading="lazy" decoding="async" class="before">
                <img data-field="after" width="300px" height="300px" sizes="(max-width: 768px) 100vw, (max-width: 1200px) 50vw, 800px" loading="lazy" decoding="async" class="after">
                <span class="after-text">After</span>
            </a>
        </div>
        <details>
            <summary>
                <?= jvbIcon('dots-three')?>
            </summary>
            <div class="item-info">
                <p data-field="started">Started: <time></time></p>
                <p data-field="updated">Last treated: <time></time></p>
                <p data-field="number">Total Treatments: <b></b></p>
                <ul data-field="goal" class="term-list"><?= array_key_exists('icon', JVB_TAXONOMY['goal']) ? jvbIcon(JVB_TAXONOMY['goal']['icon']) : '' ?><li><a></a></li></ul>
                <ul data-field="body-part" class="term-list"><?= array_key_exists('icon', JVB_TAXONOMY['body-part']) ? jvbIcon(JVB_TAXONOMY['body-part']['icon']) : '' ?><li><a><i></i></a></li></ul>
                <ul data-field="skin-type" class="term-list"><?= array_key_exists('icon', JVB_TAXONOMY['skin-type']) ? jvbIcon(JVB_TAXONOMY['skin-type']['icon']) : '' ?><li><a><i></i></a></li></ul>
                <ul data-field="age" class="term-list"><?= array_key_exists('icon', JVB_TAXONOMY['age']) ? jvbIcon(JVB_TAXONOMY['age']['icon']) : '' ?><li><a><i></i></a></li></ul>
                <ul data-field="style" class="term-list"><?= array_key_exists('icon', JVB_TAXONOMY['style']) ? jvbIcon(JVB_TAXONOMY['style']['icon']) : '' ?><li><a><i></i></a></li></ul>
                <ul data-field="theme" class="term-list"><?= array_key_exists('icon', JVB_TAXONOMY['theme']) ? jvbIcon(JVB_TAXONOMY['theme']['icon']) : '' ?><li><a><i></i></a></li></ul>
            </div>
        </details>
    </div>
    <?php
    return ob_get_clean();
}
add_filter('jvbCoreFeaturedImage', 'altr_progress_featured_image', 10, 2);
function altr_progress_featured_image(string $out, string $postType):string
{
    if ($postType !== BASE.'progress') {
        return $out;
    }
    $route = JVB()->routes('content');
    global $post;
    $data = $route->formatTimeline($post);
    $timeline = $data['fields']['timeline'];
    $total = count($timeline);
    if ($total === 1) {
        return $out;
    }
    $total--;
    $first = '<span class="before">Before</span>'.jvbFormatImage($timeline[0]['post_thumbnail'],'tiny','medium',false);
    $last = '<span class="after">After '.$total.' Tx</span>'.jvbFormatImage($timeline[$total]['post_thumbnail'],'tiny','medium',false);
    return $first.$last;
}
add_filter('jvb_directory_render_item', 'altr_progress_directory_item', 10, 4);
function altr_progress_directory_item(string $out, array $item, string $content, string $extra):string
{
    if ($content !== 'progress') return $out;
    $parent = (int)$item['id'];
    $children = get_children($parent);
    $before = jvbFormatImage(get_post_thumbnail_id($parent),'tiny','directory-preview',false);
    $after = '';
    if ($children) {
        $last = array_key_last($children);
        $after = jvbIcon('logo-triangle-fill').jvbFormatImage(get_post_thumbnail_id($last),'tiny','directory-preview',false);
    }
    return '<li class="row btw">
                    <a href="'.$item['url'].'" title="More about '.$item['name'].'">
                        '.$item['name'].'</a>'.$extra.'
                        <div class="image">'.$before.$after.'</div>
                </li>';
}
add_filter('jvbSEOResolveVariable', 'altr_progress_variables', 10, 6);
function altr_progress_variables(mixed $return, string $variable, ?int $ID, ?string $objectType, ?string $contentType, ?Meta $meta):mixed
{
    if ($contentType !== 'progress' || !in_array($variable, ['timeline_photos', 'last_date'])) {
        return $return;
    }
    $children = get_children([
        'post_parent'   => $ID,
        'post_status'   => 'publish',
        'order_by'      => 'date',
        'fields'        => 'ids'
    ]);
    //Insert the parent to the beginning
    array_unshift($children, $ID);
    if ($variable === 'timeline_photos') {
        return array_map(function ($item) {
            return get_post_thumbnail_id($item);
        }, $children);
    }
    // $variable === 'last_date'
    return get_the_date('c', $children[array_key_last($children)]);
}
add_action('wp_after_insert_post', 'altr_update_progress_alt_text', 999, 3);
function altr_update_progress_alt_text(int $ID, WP_Post $post, bool $update, ?WP_Post $before = null) {
    if ($post->post_type !== 'altr_progress') {
        return;
    }
    $thumbnail = get_post_thumbnail_id($ID);
    if (!$thumbnail || $thumbnail === 0) {
        return;
    }
    $meta = new Meta($ID, 'post');
    $number = $meta->get('number');
    $terms = $meta->getAll(['style', 'body-part','goal', 'skin-type', 'age', 'timeline']);
    foreach($terms as $slug => $value) {
        $terms = array_filter(
                array_map(function($term) use($slug) {
                    return get_term((int)$term, BASE.$slug);
                },explode(',', $value)
            ), function($term) {
                return $term && !is_wp_error($term);
            }
        );
        switch ($slug) {
            case 'skin-type':
                $slug = 'skinType';
                break;
            case 'body-part':
                $slug = 'bodyPart';
                break;
        }
        if (empty($terms)) {
            $$slug = '';
        } else {
            $$slug = jvbCommaList(array_map(function($term){ return $term->name; }, $terms));
        }
    }
    $title = sprintf(
      "%s | %s Before & After Laser Tattoo Removal",
        $post->post_title,
        $style
    );
    $style = ($style === '') ? $style : ' '.$style;
    $age = ($age === '') ? $age : ' '.$age.' old';
    $bodyPart = ($bodyPart === '') ? $bodyPart : ' on the '.$bodyPart;
    $skinType = ($skinType === '') ? $skinType : ' on a person with FitzPatrick Skin Type '.$skinType;
    $timeline = ($timeline === '') ? $timeline : ' '.$timeline.' after their '.$number.' treatment';
    $goal = ($goal === '') ? $goal : ' This client is hoping for '.$goal;
    $alt = sprintf(
            "A photo of a%s%s tattoo%s%s%s.%s",
        $style,
        $age,
        $bodyPart,  //on the ...
        $skinType,  //on a person with FitzPatrick Scale skin type ...
        $timeline,  //", x weeks after their %number% treatment"
        $goal     //"This client is hoping for %goal%."
    );
    wp_update_post([
        'ID'            => $thumbnail,
        'post_title'    => $title
    ]);
    update_post_meta($thumbnail, '_wp_attachment_image_alt', $alt);
}