From 747d741293e064a979d7bf6c143ef969ea6d7629 Mon Sep 17 00:00:00 2001
From: Jake Vanderwerf <get@jakevanderwerf.ca>
Date: Sun, 24 May 2026 20:49:44 +0000
Subject: [PATCH] =GMBReview block minor tweaks. Refactored ReferralManager.php and ReferralRoutes.php to utilize the manager for all logic, and CustomTable for table interactions.

---
 inc/helpers/renderFields.php |  351 ++++++++++++++++++++++++++++++++-------------------------
 1 files changed, 197 insertions(+), 154 deletions(-)

diff --git a/inc/helpers/renderFields.php b/inc/helpers/renderFields.php
index 5c806d0..150af0e 100644
--- a/inc/helpers/renderFields.php
+++ b/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']) {
@@ -268,7 +319,7 @@
                     $date = new Date('M j, Y', strtotime($review['date']));
                 }
 
-                $out .= '<cite class="row btw">';
+                $out .= '<cite class="row x-btw">';
                 if ($review['rating'] !== 'none') {
                     $out .= jvbFormatStarRating($review['rating']);
                 }
@@ -291,39 +342,78 @@
     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">
@@ -332,48 +422,48 @@
 			</div>
 		</template>
 		<template class="uploadMeta">
-			<?= jvbImageMeta() ?>
+			<?= Form::renderImagePreview() ?>
 		</template>
 		<template class="imageGroup">
 			<div class="upload-group">
 				<div class="group-header">
 					<div class="selected">
-						<div class="field">
-							<input type="checkbox" id="select-all-group" name="select-all-group">
-							<label for="select-all-group">
-								Select All
+						<div class="field checkbox">
+							<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-actions">
+					<div class="selection-actions">
 						<button type="button" data-action="add-to-group" title="Add selected uploads to this group">
-							<?= jvbIcon('add') ?>
+							<?= jvbIcon('plus-square') ?>
 							Add Here
 						</button>
 						<button type="button" data-action="delete-group" title="Delete group">
-							<?= jvbIcon('delete') ?>
+							<?= jvbIcon('trash') ?>
 							Delete Group
 						</button>
 					</div>
 				</div>
 				<details>
-					<summary class="row btw">
+					<summary class="row x-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>
-					<p class="hint group-count"></p>
 				</div>
 			</div>
 
 		</template>
 		<template class="groupActions">
 			<div class="item-actions">
-				<div class="radio-button">
+				<div class="btn">
 					<input type="radio" class="featured btn" name="featured" id="featured">
 					<label for="featured">
 						<?=jvbIcon('star')?>
@@ -383,7 +473,7 @@
 				</div>
 
 				<button type="button" data-action="remove-from-group" title="Remove from Group">
-					<?=jvbIcon('delete')?>
+					<?=jvbIcon('trash')?>
 				</button>
 			</div>
 		</template>
@@ -394,7 +484,7 @@
 				</div>
 				<div class="selection-controls">
 					<button type="button" data-action="add-to-group">
-						<?= jvbIcon('add') ?>
+						<?= jvbIcon('plus-square') ?>
 						New Group
 					</button>
 				</div>
@@ -422,46 +512,19 @@
 			</div>
 		</template>
 		<template class="uploadItem">
-			<div class="item upload">
-				<div class="preview">
-					<?php jvbRenderProgressBar('',true) ?>
-					<input type="checkbox" class="upload-select" name="select-item" id="select-item">
-					<label for="select-item" aria-label="Select image">
-						<img>
-						<video></video>
-						<span></span>
-					</label>
-					<div class="item-actions row btw">
-						<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" data-action="delete-upload" title="Remove from Group">
-							<?=jvbIcon('delete')?>
-						</button>
-					</div>
-				</div>
-				<details>
-					<summary class="row btw"><?=jvbIcon('edit')?><span>Edit Image Info</span></summary>
-				</details>
-			</div>
+			<?= Form::renderImagePreview() ?>
 		</template>
 		<template class="restoreNotification">
 			<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-actions">
 						<div class="selected">
-							<div class="field">
+							<div class="field checkbox">
 								<input type="checkbox" id="select-all-restore" name="select-all-restore">
 								<label for="select-all-restore">
 									Select All
@@ -471,12 +534,16 @@
 							</div>
 						</div>
 						<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('restore') ?>
+								<?= jvbIcon('arrow-counter-clockwise') ?>
 								Restore Selected
 							</button>
 							<button type="button" data-action="clear-cache" title="Clear cache and close window">
-								<?= jvbIcon('close') ?>
+								<?= jvbIcon('x') ?>
 								Clear Cache
 							</button>
 						</div>
@@ -485,10 +552,10 @@
 			</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>
+			</details>
 		</template>
 
 		<template class="startOverConfirmation">
@@ -522,41 +589,17 @@
 		<?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;

--
Gitblit v1.10.0