Jake Vanderwerf
2026-02-17 a24a06002081ad71a78ffeff9072725ba39cf121
inc/helpers/renderFields.php
@@ -4,7 +4,10 @@
   exit;
}
use JVBase\meta\MetaManager;
use JVBase\forms\TaxonomySelector;
use JVBase\managers\Cache;
use JVBase\meta\Form;
use JVBase\meta\Meta;
/**
 * Outputs a toggle text that visually changes based on selection, like a switch
@@ -23,12 +26,16 @@
    string $labelAfter,
    string $on,
    string $off,
    bool $hidden = false
    bool $hidden = false,
   array $data = []
):string {
    $hidden = ($hidden) ? ' hidden' : '';
   $dataset = (!empty($data)) ? implode('', array_map(function ($key, $value) {
      return ' data-'.$key.'="'.$value.'"';
   }, array_keys($data), array_values($data))) : '';
    return sprintf(
        '<div class="toggle-text" %s>
            <input value="all" name="%s" id="%s" type="checkbox">
        '<div class="toggle-text"%s>
            <input value="all" name="%s" id="%s" type="checkbox"%s>
            <label for="%s" class="row">
                       %s
                <span class="text row">
@@ -41,6 +48,7 @@
        $hidden,
        $name,
        $name,
      $dataset,
        $name,
        $labelBefore,
        $off,
@@ -51,24 +59,31 @@
/**
 * @param int $ID
 * @param MetaManager|null $meta
 *
 * @param string $type
 * @return string
 */
function jvbRenderLinks(int $ID, MetaManager|null $meta = null):string
function jvbRenderLinks(int $ID, string $type =''):string
{
    $cache = new JVBase\managers\CacheManager('bio-'.$ID, WEEK_IN_SECONDS);
    $key = 'links';
    $cached = $cache->get($key);
    $cache = Cache::for('user_links', WEEK_IN_SECONDS)->connect('post')->connect('taxonomy')->connect('user');
    $cached = $cache->get($ID);
    if ($cached) {
        return $cached;
    }
    if (!$meta) {
        $meta = jvbGetMeta($ID);
    }
   $meta = match($type){
      'post'   => Meta::forPost($ID),
      'term'   => Meta::forTerm($ID),
      'user'   => Meta::forUser($ID),
      default  => false
   };
   if (!$meta) {
      $meta = jvbGetMeta($ID);
   }
   if (!$meta) {
      return '';
   }
    $links = $meta->getValue('links');
    $links = $meta->get('links');
    $out = '';
    if (!empty($links)) {
@@ -77,41 +92,41 @@
            $url = $link['url'];
            switch (true) {
                case strpos($url, 'facebook'):
                    $i = jvbIcon('facebook');
                    $i = jvbIcon('facebook-logo');
                    break;
                case strpos($url, 'instagram'):
                    $i = jvbIcon('instagram');
                    $i = jvbIcon('instagram-logo');
                    break;
                case strpos($url, 'tiktok'):
                    $i = jvbIcon('tiktok');
                    $i = jvbIcon('tiktok-logo');
                    break;
                case strpos($url, 'x.com'):
                    $i = jvbIcon('x');
                    break;
                case strpos($url, 'youtube'):
                    $i = jvbIcon('youtube');
                    $i = jvbIcon('youtube-logo');
                    break;
                case strpos($url, 'mastadon'):
                    $i = jvbIcon('mastadon');
                    $i = jvbIcon('mastadon-logo');
                    break;
                case strpos($url, 'fediverse'):
                case strpos($url, 'bluesky'):
                    $i = jvbIcon('fediverse');
                    $i = jvbIcon('fediverse-logo');
                    break;
                case strpos($url, 'linktree'):
                    $i = jvbIcon('linktree');
                    $i = jvbIcon('linktree-logo');
                    break;
                case strpos($url, 'snapchat'):
                    $i = jvbIcon('snapchat');
                    $i = jvbIcon('snapchat-logo');
                    break;
                case strpos($url, 'twitch'):
                    $i = jvbIcon('twitch');
                    $i = jvbIcon('twitch-logo');
                    break;
                case strpos($url, 'threads'):
                    $i = jvbIcon('threads');
                    $i = jvbIcon('threads-logo');
                    break;
                case strpos($url, 'twitter'):
                    $i = jvbIcon('twitter');
                    $i = jvbIcon('twitter-logo');
                    break;
                default:
                    $i = jvbIcon('link');
@@ -122,50 +137,59 @@
        }
        $out .= '</ul>';
    }
    $cache->set($key, $out);
    $cache->set($ID, $out);
    return $out;
}
/**
 * @param int $ID
 * @param MetaManager|null $meta
 * @param string $type
 *
 * @return string
 */
function jvbRenderContactInfo(int $ID, MetaManager|null $meta = null):string
function jvbRenderContactInfo(int $ID, string $type = ''):string
{
    $cache = new JVBase\managers\CacheManager('bio-'.$ID, WEEK_IN_SECONDS);
    $key = 'contact';
//    $cached = $cache->get($key);
//    if($cached){
//        return $cached;
//    }
    if (!$meta) {
        $meta = jvbGetMeta($ID);
    }
    $cache = Cache::for('contact', WEEK_IN_SECONDS)->connect('post')->connect('taxonomy');
    $preference = $meta->getValue('public_contact');
    $cached = $cache->get($ID);
    if($cached){
        return $cached;
    }
   $meta = match($type){
      'post'   => Meta::forPost($ID),
      'term'   => Meta::forTerm($ID),
      'user'   => Meta::forUser($ID),
      default  => false
   };
   if (!$meta) {
      $meta = jvbGetMeta($ID);
   }
   if (!$meta) {
      return '';
   }
    $preference = $meta->get('public_contact');
    $preference = (is_array($preference)) ? $preference : explode(',', $preference);
    $out = '';
    if (!empty($preference)) {
        $out = '<ul class="contact">';
        $phone = $meta->getValue('phone');
        $phone = $meta->get('phone');
        foreach ($preference as $p) {
            $link = $label = false;
            switch ($p) {
                case 'text':
                    $link = 'sms:+1'.$phone.';?body='.rawurlencode('Hey! I found you on edmonton.ink, and I wanted to reach out.');
                    $label = jvbIcon('text').'<span>Text</span>';
                    $label = jvbIcon('chat').'<span>Text</span>';
                    break;
                case 'call':
                    $link = 'tel:+1'.$phone;
                    $label = jvbIcon('phone').'<span>Call</span>';
                    break;
                case 'email':
                    $link = 'mailto:'.$meta->getValue('email').'?subject='.rawurlencode('Contact from edmonton.ink').'&body='.rawurlencode('Hey,
                    $link = 'mailto:'.$meta->get('email').'?subject='.rawurlencode('Contact from edmonton.ink').'&body='.rawurlencode('Hey,
        I found you on edmonton.ink, and I wanted to reach out!');
                    $label = jvbIcon('email').'<span>Email</span>';
                    $label = jvbIcon('envelope').'<span>Email</span>';
                    break;
            }
            if ($link) {
@@ -176,29 +200,38 @@
        $out .= '</ul>';
    }
    $cache->set($key, $out);
    $cache->set($ID, $out);
    return $out;
}
/**
 * @param int $ID
 * @param MetaManager|null $meta
 * @param string $type
 * @return string
 */
function jvbRenderSpecialtyField(int $ID,  MetaManager|null $meta = null):string
function jvbRenderSpecialtyField(int $ID,  string $type = ''):string
{
    if (!$meta) {
        $meta = jvbGetMeta($ID);
    }
   $meta = match($type){
      'post'   => Meta::forPost($ID),
      'term'   => Meta::forTerm($ID),
      'user'   => Meta::forUser($ID),
      default  => false
   };
   if (!$meta) {
      $meta = jvbGetMeta($ID);
   }
   if (!$meta) {
      return '';
   }
    $out = '';
    $specialties = $meta->getValue('specialties');
    $specialties = $meta->get('specialties');
    if (!empty($specialties)) {
        foreach ($specialties as $specialty) {
            $out .= '<li><b>'.$specialty['specialty'].'</b>';
            if ($specialty['description'] !== '') {
                $out .= apply_filters('the_content', $specialty['description']);
                $out .= jvb_filter_content( $specialty['description']);
            }
            $out .= '</li>';
        }
@@ -213,17 +246,26 @@
/**
 * @param int $ID
 * @param MetaManager|null $meta
 * @param string $type = ''
 * @return string
 */
function jvbRenderAwardsField(int $ID, MetaManager|null $meta = null):string
function jvbRenderAwardsField(int $ID, string $type = ''):string
{
    if (!$meta) {
        $meta = jvbGetMeta($ID);
    }
   $meta = match($type){
      'post'   => Meta::forPost($ID),
      'term'   => Meta::forTerm($ID),
      'user'   => Meta::forUser($ID),
      default  => false
   };
   if (!$meta) {
      $meta = jvbGetMeta($ID);
   }
   if (!$meta) {
      return '';
   }
    $out = '';
    $awards = $meta->getValue('awards');
    $awards = $meta->get('awards');
    if (!empty($awards)) {
        foreach ($awards as $award) {
            $out .= '<li><b>'.$award['name'].'</b>';
@@ -241,23 +283,32 @@
/**
 * @param int $ID
 * @param MetaManager|null $meta
 * @param string $type
 * @return string
 */
function jvbRenderReviewsField(int $ID, MetaManager|null $meta = null):string
function jvbRenderReviewsField(int $ID, string $type = ''):string
{
    if (!$meta) {
        $meta = jvbGetMeta($ID);
    }
   $meta = match($type){
      'post'   => Meta::forPost($ID),
      'term'   => Meta::forTerm($ID),
      'user'   => Meta::forUser($ID),
      default  => false
   };
   if (!$meta) {
      $meta = jvbGetMeta($ID);
   }
   if (!$meta) {
      return '';
   }
    $out = '';
    $reviews = $meta->getValue('reviews');
    $reviews = $meta->get('reviews');
    if (!empty($reviews)) {
        foreach ($reviews as $review) {
            if ($review['review'] === '') {
                continue;
            }
            $out .= '<li><blockquote>'.apply_filters('the_content', $review['review']);
            $out .= '<li><blockquote>'.jvb_filter_content( $review['review']);
            if ($review['name'] !== '' || $review['rating'] !== 'none') {
                $date = $aOpen = $aClose = '';
                if ($review['url']) {
@@ -291,71 +342,125 @@
    return $out;
}
function jvbGetMeta(int $ID) {
    if (is_tax()) {
        $type = 'term';
    } elseif (is_singular()) {
        $type = 'post';
function jvbGetMeta(int $ID):Meta|false {
    if (term_exists($ID)) {
        return Meta::forTerm($ID);
    } elseif (get_post_status($ID)) {
        return Meta::forPost($ID);
    } elseif (get_userdata($ID)) {
        return Meta::forUser($ID);
    } else {
        $type = 'user';
    }
    return new JVBase\meta\MetaManager($ID, $type);
      return false;
   }
}
function jvbRenderTermList(array|bool|WP_Error $terms, string $label = '') {
function jvbRenderTermList(array|bool|WP_Error $terms, string $label = ''):string {
    if (!$terms || is_wp_error($terms) || empty($terms)) {
        return '';
    }
    $out = ($label === '') ? '' : '<h2 class="inline">'.$label.'</h2>';
    $out .= '<ul class="term-list '.jvbNoBase($terms[array_key_first($terms)]->taxonomy).'">';
    foreach ($terms as $term) {
        $out .= '<li>
            <a href="'.get_term_link($term->term_id, $term->taxonomy).'" title="'.$term->name.'">'.
                $term->name.
            '</a>
        </li>';
        $out .= '<li>'.jvbGetTermLink($term).'</li>';
    }
    $out .= '</ul>';
    return $out;
}
function jvbGetTermLink(int|WP_Term $term, string $taxonomy = ''):string
{
   if (is_int($term)){
      $term = get_term($term, jvbCheckBase($taxonomy));
      if (is_wp_error($term)) {
         return '';
      }
   }
   $cache = Cache::for($term->taxonomy.'_link')->connect('taxonomy');
   $key = $term->term_id;
   return $cache->remember(
      $key,
      function() use ($term) {
         return '<a href="'.get_term_link($term->term_id, $term->taxonomy).'" title="'.html_entity_decode($term->name).'">'.
            html_entity_decode($term->name).
         '</a>';
      }
   );
}
add_action('wp_footer', 'jvbOutputImageTemplates');
function jvbOutputImageTemplates() {
   if (wp_script_is('jvb-form')) {
      jvbInlineStyles('forms');
      ?>
      <template class="formSummary">
         <div class="form-summary">
            <h2>Success!</h2>
            <div class="message">
               <p>We're picking up what you're laying down.</p>
               <p>You'll get an email with your response. If you can't find it, check your spam.</p>
               <p>If you need to make any changes, respond to that email.</p>
               <p>You can see a summary of what you wrote below:</p>
            </div>
            <div class="summary">
               <div class="result">
                  <h3></h3>
                  <p></p>
               </div>
            </div>
         </div>
      </template>
      <?php
   }
   if (wp_script_is('jvb-uploader')) {
      ?>
      <template class="emptyGroup">
         <div class="empty-group">
         <div class="empty-group row">
            <p>Drag here to create new group!</p>
         </div>
      </template>
      <template class="uploadMeta">
         <?= jvbImageMeta() ?>
         <?= Form::renderImagePreview() ?>
      </template>
      <template class="imageGroup">
         <details class="upload-group">
            <summary class="row btw">
               <div class="group-header">
                  <div class="group-actions">
                     <button type="button" class="add-selection-to-group" title="Add selected images to this group">
                        <?= jvbIcon('add') ?>
                        Add to Group
                     </button>
                     <button type="button" class="remove-group" title="Delete group">
                        <?= jvbIcon('delete') ?>
                        Delete Group
                     </button>
         <div class="upload-group">
            <div class="group-header">
               <div class="selected">
                  <div class="field">
                     <input type="checkbox" id="select-all" name="select-all" data-selects="item-grid" data-select-all>
                     <label for="select-all">
                        Select All In Group
                     </label>
                  </div>
                  <div class="info" hidden>
                  </div>
               </div>
               <div class="group-content col">
                  <div class="item-grid group"></div>
                  <p class="hint group-count"></p>
               <div class="selection-actions">
                  <button type="button" data-action="add-to-group" title="Add selected uploads to this group">
                     <?= jvbIcon('plus-square') ?>
                     Add Here
                  </button>
                  <button type="button" data-action="delete-group" title="Delete group">
                     <?= jvbIcon('trash') ?>
                     Delete Group
                  </button>
               </div>
            </summary>
            <div class="fields"></div>
         </details>
            </div>
            <details>
               <summary class="row btw">
                  Extra Fields
               </summary>
               <div class="fields"></div>
            </details>
            <div class="group-content col">
               <p class="hint count"></p>
               <div class="item-grid group"></div>
            </div>
         </div>
      </template>
      <template class="groupActions">
         <div class="item-actions">
@@ -368,8 +473,8 @@
               </label>
            </div>
            <button type="button" class="remove" title="Remove from Group">
               <?=jvbIcon('delete')?>
            <button type="button" data-action="remove-from-group" title="Remove from Group">
               <?=jvbIcon('trash')?>
            </button>
         </div>
      </template>
@@ -379,14 +484,10 @@
               <span class="selection-count">0</span> items selected
            </div>
            <div class="selection-controls">
               <button type="button" class="create-from-selection">
                  <?= jvbIcon('add') ?>
               <button type="button" data-action="add-to-group">
                  <?= jvbIcon('plus-square') ?>
                  New Group
               </button>
               <button type="button" class="deselect-all">
                  <?= jvbIcon('close') ?>
                  Deselect All
               </button>
            </div>
         </div>
      </template>
@@ -412,89 +513,50 @@
         </div>
      </template>
      <template class="uploadItem">
         <div class="upload-item">
            <div class="preview">
               <img>
               <?php jvbRenderProgressBar('<span class="icon"></span><span class="details"></span>') ?>
               <div class="actions">
                  <input type="checkbox" class="upload-select" name="select-item" id="select-item">
                  <label for="select-item" aria-label="Select image"></label>
                  <div class="item-actions">
                     <div class="radio-button">
                        <input type="radio" class="featured btn" name="featured" id="featured" hidden>
                        <label for="featured">
                           <?=jvbIcon('star')?>
                           <?=jvbIcon('star', ['style' => 'fill'])?>
                           <span class="screen-reader-text">Set as featured image</span>
                        </label>
                     </div>
                     <button type="button" class="remove" title="Remove from Group">
                        <?=jvbIcon('delete')?>
                     </button>
                  </div>
               </div>
            </div>
            <details>
               <summary class="row btw"><?=jvbIcon('edit')?><span>Edit Image Info</span></summary>
            </details>
         </div>
         <?= Form::renderImagePreview() ?>
      </template>
      <template class="restoreNotification">
         <div class="restore-notification">
            <div class="restore-content">
         <dialog class="restore-uploads">
            <div class="wrap">
               <div class="restore-message">
                  <h4>Looks like we left things hanging</h4>
                  <p class="restore-details"></p>
                  <p class="details"></p>
                  <p class="hint">If you'd rather start over, you can clear this information.</p>
               </div>
               <div class="restore-actions">
                  <div class="selection-controls">
                     <button type="button" class="select-all-restore">Select All</button>
                     <button type="button" class="select-none-restore">Select None</button>
               <div class="selection-actions">
                  <div class="selected">
                     <div class="field">
                        <input type="checkbox" id="select-all-restore" name="select-all-restore">
                        <label for="select-all-restore">
                           Select All
                        </label>
                     </div>
                     <div class="info" hidden>
                     </div>
                  </div>
                  <div class="action-buttons">
                     <button type="button" class="restore-selected">
                        <?= jvbIcon('restore') ?>
                  <div class="m-actions row nowrap">
                     <button type="button" data-action="restore-all">
                        <?= jvbIcon('infinity') ?>
                        Restore All
                     </button>
                     <button type="button" data-action="restore">
                        <?= jvbIcon('arrow-counter-clockwise') ?>
                        Restore Selected
                     </button>
                     <button type="button" class="restart-uploads">
                        <?= jvbIcon('delete') ?>
                        Scrap Cache
                     </button>
                     <button type="button" class="dismiss-cache-check" title="Dismiss notification">
                        <?= jvbIcon('close') ?>
                        Dismiss
                     <button type="button" data-action="clear-cache" title="Clear cache and close window">
                        <?= jvbIcon('x') ?>
                        Clear Cache
                     </button>
                  </div>
               </div>
            </div>
         </div>
         </dialog>
      </template>
      <template class="restoreField">
         <div class="restore-field">
            <h3></h3>
         <details class="restore-field">
            <summary><h3><a></a></h3></summary>
            <div class="item-grid restore"></div>
         </div>
      </template>
      <template class="restoreItem">
         <label class="restore-item">
            <div class="preview">
               <img>
               <div class="image-placeholder"><svg width="40" height="40" fill="currentColor" viewBox="0 0 256 256">
                     <path d="M200,40H56A16,16,0,0,0,40,56V200a16,16,0,0,0,16,16H200a16,16,0,0,0,16-16V56A16,16,0,0,0,200,40ZM56,56H200V158.75l-26.07-26.06a16,16,0,0,0-22.63,0l-20,20-44-44a16,16,0,0,0-22.62,0L56,117.37V56Zm144,144H56V141.67l36.7-36.7,44,44a8,8,0,0,0,11.31,0l20-20L200,161.67V200ZM148,100a12,12,0,1,1,12,12A12,12,0,0,1,148,100Z"/>
                  </svg></div>
            </div>
            <div class="restore-item-info">
               <div class="name"></div>
               <div class="restore-item-meta">
               </div>
            </div>
            <div class="restore-item-controls">
               <input type="checkbox" id="restore-" class="restore-checkbox" checked>
            </div>
         </label>
         </details>
      </template>
      <template class="startOverConfirmation">
@@ -519,44 +581,26 @@
            </div>
         </dialog>
      </template>
      <template class="dragPreview">
         <div class="drag-preview">
            <div class="drag-items"><div class="drag-item"></div></div>
            <div class="drag-count" hidden></div>
         </div>
      </template>
      <?php
   }
   if (wp_script_is('jvb-selector')) {
      \JVBase\forms\TaxonomySelector::class::outputSelector();
      TaxonomySelector::class::outputSelectorModal();
   }
}
function jvbImageMeta(int|null $ID = null, string $title = '', string $alt = '', string $caption = ''):string
{
   $dataID = ($ID) ? ' data-image-id="'.$ID.'"' : '';
   $ID = ($ID) ? '-'.$ID : '';
   return '<div class="upload-meta"'.$dataID.'>
      <div class="field">
         <label for="image-title'.$ID.'">Image Title</label>
         <input type="text" id="image-title'.$ID.'" name="image-title'.$ID.'" value="'.$title.'">
      </div>
      <div class="field">
         <label for="image-alt-text'.$ID.'">Image Alt Text</label>
         <input type="text" id="image-alt-text'.$ID.'" name="image-alt-text'.$ID.'" value="'.$alt.'">
         <p class="hint">Alt text helps the visually impaired, as well as some benefits for SEO.</p>
      </div>
      <div class="field">
         <label for="image-caption'.$ID.'">Image Caption</label>
         <textarea id="image-caption'.$ID.'" name="image-caption'.$ID.'">'.$caption.'</textarea>
      </div>
      <p class="hint">These will be automatically generated if left blank.</p>
   </div>';
}
function jvbLocationLinks(array $location): string {
   if (empty($location['address'])) {
      return '';
   }
   $cache = new \JVBase\managers\CacheManager('location');
   $cache = Cache::for('locations')->connect('taxonomy');
   $key = $cache->generateKey($location);
   $cached = false;