From a24a06002081ad71a78ffeff9072725ba39cf121 Mon Sep 17 00:00:00 2001
From: Jake Vanderwerf <get@jakevanderwerf.ca>
Date: Tue, 17 Feb 2026 20:05:31 +0000
Subject: [PATCH] =minor changes, particularly around the JVB_CHILD_URL pattern

---
 inc/managers/NotificationManager.php |  382 +++++++++++++++++++++++++-----------------------------
 1 files changed, 179 insertions(+), 203 deletions(-)

diff --git a/inc/managers/NotificationManager.php b/inc/managers/NotificationManager.php
index 1554ad6..5a02499 100644
--- a/inc/managers/NotificationManager.php
+++ b/inc/managers/NotificationManager.php
@@ -20,7 +20,11 @@
  */
 class NotificationManager
 {
-    protected object $cache;
+    protected Cache $userCache; //the individual notifications
+    protected Cache $contentCache;	//the 'shared' notifications on new content that has been created
+	protected Cache $artistsCache;
+	protected Cache $favouritesCache;
+	protected Cache $followerCache;
     protected string $campaign;
 	protected string $table = BASE.'notifications';
 	protected string $contentTable = BASE.'notifications_content';
@@ -139,7 +143,11 @@
      */
     public function __construct()
     {
-        $this->cache = CacheManager::for('notifications', WEEK_IN_SECONDS);
+        $this->userCache = Cache::for('userNotifications', WEEK_IN_SECONDS);
+        $this->contentCache = Cache::for('contentNotifications', WEEK_IN_SECONDS)->connect('post', true)->connect('taxonomy', true);
+        $this->artistsCache = Cache::for('artist', WEEK_IN_SECONDS)->connect('post');
+        $this->favouritesCache = Cache::for('favouritedUsers', WEEK_IN_SECONDS)->connect('favourites');
+        $this->followerCache = Cache::for('totalFollowers', WEEK_IN_SECONDS)->connect('favourites');
 
         // Add filter for bulk operation handling
         add_filter(BASE . 'handle_bulk_operation', [ $this, 'processOperation' ], 10, 3);
@@ -364,7 +372,7 @@
      */
     public function notifyVerifiedArtists(string $type, int|null $action_user_id = null, string $message = '', int|null $target_id = null, string|null $target_type = null, array|null $context = null):bool|WP_Error
     {
-        $artists = $this->getVerifiedArtists();
+        $artists = $this->getVerified('artist');
         return $this->addNotification($artists, $type, $action_user_id, $message, $target_id, $target_type, $context);
     }
     /**
@@ -381,7 +389,7 @@
      */
     public function notifyVerifiedPartners(string $type, int|null $action_user_id = null, string $message = '', int|null $target_id = null, string|null $target_type = null, array|null $context = null):bool|WP_Error
     {
-        $artists = $this->getVerifiedPartners();
+        $artists = $this->getVerified('partner');
         return $this->addNotification($artists, $type, $action_user_id, $message, $target_id, $target_type, $context);
     }
     /**
@@ -398,7 +406,7 @@
      */
     public function notifyEnthusiasts(string $type, int|null $action_user_id = null, string $message = '', int|null $target_id = null, string|null $target_type = null, array|null $context = null):bool|WP_Error
     {
-        $artists = $this->getEnthusiasts();
+        $artists = $this->getUserIDs('enthusiast');
         return $this->addNotification($artists, $type, $action_user_id, $message, $target_id, $target_type, $context);
     }
     /**
@@ -415,7 +423,7 @@
      */
     public function notifyEveryone(string $type, int|null $action_user_id = null, string $message = '', int|null $target_id = null, string|null $target_type = null, array|null $context = null):bool|WP_Error
     {
-        $artists = $this->getEveryone();
+        $artists = $this->getUserIDs(array_keys(JVB_USER));
         return $this->addNotification($artists, $type, $action_user_id, $message, $target_id, $target_type, $context);
     }
 
@@ -1043,46 +1051,47 @@
      *
      * @return string HTML email content
      */
-    protected function generateDigestContent(WP_User $user, string $frequency, array $notifications, array $content_updates):string
-    {
-        $content = sprintf('<p>Hey %s,</p>', $user->first_name ?: $user->display_name);
+	protected function generateDigestContent(WP_User $user, string $frequency, array $notifications, array $content_updates):string
+	{
+		$content = sprintf('<p>Hey %s,</p>', $user->first_name ?: $user->display_name);
 
-        // Intro text based on frequency
-        switch ($frequency) {
-            case 'daily':
-                $content .= '<p>Here\'s what happened in Edmonton\'s tattoo scene today:</p>';
-                break;
-            case 'weekly':
-                $content .= '<p>Here\'s what you missed in Edmonton\'s tattoo scene this week:</p>';
-                break;
-            case 'monthly':
-                $content .= sprintf('<p>Here\'s your monthly roundup of what happened in %s in Edmonton\'s tattoo scene:</p>', date('F'));
-                break;
-        }
+		// Intro text based on frequency
+		switch ($frequency) {
+			case 'daily':
+				$content .= '<p>Here\'s what happened in Edmonton\'s tattoo scene today:</p>';
+				break;
+			case 'weekly':
+				$content .= '<p>Here\'s what you missed in Edmonton\'s tattoo scene this week:</p>';
+				break;
+			case 'monthly':
+				$content .= sprintf('<p>Here\'s your monthly roundup of what happened in %s in Edmonton\'s tattoo scene:</p>', date('F'));
+				break;
+		}
 
-        // Process artist content updates - the most visually interesting part
-        $content .= $this->generateContentUpdatesSection($content_updates);
+		// Process artist content updates - the most visually interesting part
+		$content .= $this->generateContentUpdatesSection($content_updates);
 
-        // Process regular notifications
-        if (!empty($notifications)) {
-            $content .= $this->generateNotificationsSection($notifications);
-        }
+		// Process regular notifications
+		if (!empty($notifications)) {
+			$content .= $this->generateNotificationsSection($notifications);
+		}
 
-        // Add footer content
-        $content .= '<div class="divider"></div>';
-        $content .= sprintf(
-            '<p>You\'re receiving this %s digest because you follow artists on edmonton.ink. ' .
-            'You can <a href="%s" class="text-link">adjust your notification settings</a> at any time.</p>',
-            $frequency,
-            esc_url(add_query_arg([
-                'utm_source'   => 'email',
-                'utm_medium'   => 'digest',
-                'utm_campaign' => $this->campaign
-            ], site_url('/dash/settings/')))
-        );
+		// Add footer content
+		$content .= JVB()->email()->divider();
+		$settings_url = add_query_arg([
+			'utm_source'   => 'email',
+			'utm_medium'   => 'digest',
+			'utm_campaign' => $this->campaign
+		], site_url('/dash/settings/'));
 
-        return $content;
-    }
+		$content .= sprintf(
+			'<p>You\'re receiving this %s digest because you follow artists on edmonton.ink. You can %s at any time.</p>',
+			$frequency,
+			'<a href="' . esc_url($settings_url) . '">adjust your notification settings</a>'
+		);
+
+		return $content;
+	}
 
     /**
      * Generate HTML section for content updates
@@ -1099,7 +1108,7 @@
         }
 
         $content = '';
-        $cache   = CacheManager::for('digest_content', HOUR_IN_SECONDS * 6); // Cache for 6 hours
+        $cache   = Cache::for('digest_content', HOUR_IN_SECONDS * 6); // Cache for 6 hours
 
         // Group updates by artist
         $updates_by_artist = [];
@@ -1234,46 +1243,39 @@
      *
      * @return string HTML content
      */
-    protected function generateNotificationsSection(array $notifications):string
-    {
-        if (empty($notifications)) {
-            return '';
-        }
+	protected function generateNotificationsSection(array $notifications):string
+	{
+		if (empty($notifications)) {
+			return '';
+		}
 
-        $content = '<h3>Other Updates</h3>';
-        $content .= '<ul style="padding-left: 20px;">';
+		$items = [];
 
-        // Group notifications by type
-        $by_type = [];
-        foreach ($notifications as $notification) {
-            if (!isset($by_type[$notification->type])) {
-                $by_type[ $notification->type ] = [];
-            }
-            $by_type[ $notification->type ][] = $notification;
-        }
+		// Group notifications by type
+		$by_type = [];
+		foreach ($notifications as $notification) {
+			if (!isset($by_type[$notification->type])) {
+				$by_type[$notification->type] = [];
+			}
+			$by_type[$notification->type][] = $notification;
+		}
 
-        // Process each type
-        foreach ($by_type as $type => $type_notifications) {
-            $config = $this->notification_types[ $type ] ?? [];
-            $icon   = $config['icon'] ?? 'info';
+		// Process each type
+		foreach ($by_type as $type => $type_notifications) {
+			foreach ($type_notifications as $notification) {
+				$message = $notification->message;
+				if (empty($message)) {
+					$message = $this->generateNotificationMessage($notification);
+				}
 
-            foreach ($type_notifications as $notification) {
-                $message = $notification->message;
-                if (empty($message)) {
-                    $message = $this->generateNotificationMessage($notification);
-                }
+				if (!empty($message)) {
+					$items[] = ['label' => '', 'value' => $message];
+				}
+			}
+		}
 
-                if (!empty($message)) {
-                    $content .= sprintf('<li>%s</li>', $message);
-                }
-            }
-        }
-
-        $content .= '</ul>';
-        $content .= '<div class="divider"></div>';
-
-        return $content;
-    }
+		return JVB()->email()->table($items, 'Other Updates');
+	}
 
     /**
      * Generate a message for a notification when none is provided
@@ -1525,19 +1527,16 @@
     protected function getArtistData(int $user_id):array|false
     {
         // Try to get from cache
-        $cache_key = "artist_data_{$user_id}";
-        $cached = $this->cache->get($cache_key);
+		$artist_id = get_user_meta($user_id, BASE . 'link', true);
+		if (!$artist_id || $artist_id === '') {
+			return false;
+		}
+        $cached = $this->artistsCache->get($artist_id);
 
         if ($cached !== false) {
             return $cached;
         }
 
-        // Get artist post ID from user meta
-        $artist_id = get_user_meta($user_id, BASE . 'link', true);
-        if (!$artist_id) {
-            return false;
-        }
-
         // Get basic artist data
         $artist_post = get_post($artist_id);
         if (!$artist_post) {
@@ -1554,7 +1553,7 @@
         ];
 
         // Cache the result
-        $this->cache->set($cache_key, $data, null,'artists');
+        $this->artistsCache->set($artist_id, $data);
 
         return $data;
     }
@@ -1566,19 +1565,25 @@
      */
     protected function getFollowedArtists(int $user_id):array
     {
-        global $wpdb;
-        $favourites_table = $wpdb->prefix . BASE . 'favourites';
+		return $this->favouritesCache->remember(
+			$user_id,
+			function() use ($user_id) {
+				global $wpdb;
+				$favourites_table = $wpdb->prefix . BASE . 'favourites';
 
-        // Get artists this user has favourited
-        return $wpdb->get_col($wpdb->prepare(
-            "SELECT f.target_id
-         FROM {$favourites_table} f
-         JOIN {$wpdb->posts} p ON f.target_id = p.ID
-         WHERE f.user_id = %d
-         AND f.type = 'artist'
-         AND p.post_status = 'publish'",
-            $user_id
-        ));
+				// Get artists this user has favourited
+				return $wpdb->get_col($wpdb->prepare(
+				"SELECT f.target_id
+					 FROM {$favourites_table} f
+					 JOIN {$wpdb->posts} p ON f.target_id = p.ID
+					 WHERE f.user_id = %d
+					 AND f.type = 'artist'
+					 AND p.post_status = 'publish'",
+					$user_id
+				));
+			}
+		);
+
     }
 
     /**
@@ -1588,15 +1593,21 @@
      */
     protected function getFollowerCount(int $artist_id):int
     {
-        global $wpdb;
-        $favourites_table = $wpdb->prefix . BASE . 'favourites';
+		return $this->followerCache->remember(
+			$artist_id,
+			function() use ($artist_id) {
+				global $wpdb;
+				$favourites_table = $wpdb->prefix . BASE . 'favourites';
 
-        return $wpdb->get_var($wpdb->prepare(
-            "SELECT COUNT(DISTINCT user_id)
-         FROM {$favourites_table}
-         WHERE target_id = %d AND type = 'artist'",
-            $artist_id
-        ));
+				return $wpdb->get_var($wpdb->prepare(
+					"SELECT COUNT(DISTINCT user_id)
+					 FROM {$favourites_table}
+					 WHERE target_id = %d AND type = 'artist'",
+					$artist_id
+				));
+			}
+		);
+
     }
 
     /**
@@ -1604,27 +1615,16 @@
      *
      * @return string
      */
-    protected function pluralize(string $word):string
+    protected function pluralize(string $content):string
     {
-        $irregular = [
-            'tattoo' => 'tattoos',
-            'piercing' => 'piercings',
-            'artwork' => 'artwork',
-            'news' => 'news',
-            'offer' => 'offers',
-            'event' => 'events'
-        ];
-
-        if (isset($irregular[$word])) {
-            return $irregular[$word];
-        }
-
-        // Simple pluralization rules
-        if (str_ends_with($word, 'y')) {
-            return substr($word, 0, -1) . 'ies';
-        }
-
-        return $word . 's';
+        if (array_key_exists($content, JVB_CONTENT)) {
+			return JVB_CONTENT[$content]['plural'];
+		} elseif (array_key_exists($content, JVB_TAXONOMY)) {
+			return JVB_TAXONOMY[$content]['plural'];
+		} elseif (array_key_exists($content, JVB_USER)) {
+			return JVB_USER[$content]['plural'];
+		}
+		return $content;
     }
 
     /**
@@ -1634,9 +1634,8 @@
      */
     protected function clearNotificationCache(int $user_id):void
     {
-
-        $this->cache->delete("user_{$user_id}_notifications_", 'notifications_' . $user_id);
-        $this->cache->delete("user_{$user_id}_content_notifications_", 'notifications_' . $user_id);
+		$this->userCache->forget($user_id);
+		$this->contentCache->forget($user_id);
     }
 
     /**
@@ -1703,78 +1702,56 @@
     /**
      * @return array
      */
-    protected function getVerifiedArtists():array
+    protected function getVerified(string|array $userRoles):array
     {
-        $artists = $this->cache->get('verified_artists');
-        if ($artists) {
-            return $artists;
-        }
+		$userRoles = $this->checkRoles($userRoles);
 
-        $artists = get_users([
-            'role'          => BASE.'artist',
-            'capability'    => 'skip_moderation',
-            'fields'        => 'ID'
-        ]);
-
-        $this->cache->set('verified_artists', $artists);
-        return $artists;
+		if (empty($userRoles)) {
+			return [];
+		}
+		$cache = Cache::for('verifiedUsers', DAY_IN_SECONDS)->connect('user',true);
+		return $cache->remember(
+			'verified',
+			function() use ($userRoles) {
+				return get_users([
+					'role'          => $userRoles,
+					'capability'    => 'skip_moderation',
+					'fields'        => 'ID'
+				]);
+			}
+		);
     }
 
-    /**
-     * @return array
-     */
-    protected function getVerifiedPartners():array
-    {
-        $partners = $this->cache->get('verified_partners');
-        if ($partners) {
-            return $partners;
-        }
+	protected function getUserIDs(array|string $roles):array
+	{
+		$roles = $this->checkRoles($roles);
+		if (empty($roles)) {
+			return [];
+		}
+		$cache = Cache::for('everyone', DAY_IN_SECONDS)->connect('user', true);
+		return $cache->remember(
+			$cache->generateKey($roles),
+			function() use ($roles) {
+				return get_users([
+					'role'	=> $roles,
+					'fields'	=> 'ID'
+				]);
+			}
+		);
+	}
 
-        $partners = get_users([
-            'role'          => BASE.'partner',
-            'capability'    => 'skip_moderation',
-            'fields'        => 'ID'
-        ]);
+	protected function checkRoles(string|array $roles):array
+	{
+		if (!is_array($roles)) {
+			$roles = explode(',',$roles);
+		}
 
-        $this->cache->set('verified_partners', $partners);
-        return $partners;
-    }
-
-    /**
-     * @return array
-     */
-    protected function getEnthusiasts():array
-    {
-        $enthusiasts = $this->cache->get('enthusiasts');
-        if ($enthusiasts) {
-            return $enthusiasts;
-        }
-
-        $enthusiasts = get_users([
-            'role'          => BASE.'enthusiast',
-            'fields'        => 'ID'
-        ]);
-
-        $this->cache->set('enthusiasts', $enthusiasts);
-        return $enthusiasts;
-    }
-
-    /**
-     * @return array
-     */
-    protected function getEveryone():array
-    {
-        $users = $this->cache->get('users');
-        if ($users) {
-            return $users;
-        }
-        $users = get_users([
-            'role__in' => [BASE.'artist', BASE.'enthusiast', BASE.'partner'],
-            'fields'    => 'ID'
-        ]);
-        $this->cache->set('users', $users);
-        return $users;
-    }
+		return array_map(function ($r) {
+			return jvbCheckBase(trim($r));
+		}, array_filter($roles, function ($r) {
+			return array_key_exists(trim($r), JVB_USER);
+		}));
+	}
 
     /**
      * @param int $userID
@@ -1783,13 +1760,12 @@
      */
     protected function checkUser(int $userID):bool
     {
-        $checked = $this->cache->get($userID, 'checked_users');
-        if ($checked) {
-            return $checked;
-        }
-        $test = (bool)get_userdata($userID);
-
-        $this->cache->set($userID, $test, null, 'checked_users');
-        return $test;
+		$cache = Cache::for('checked_users', DAY_IN_SECONDS)->connect('user', true);
+		return $cache->remember(
+			$userID,
+			function() use ($userID) {
+				return (bool)get_userdata($userID)?:null;
+			}
+		);
     }
 }

--
Gitblit v1.10.0