Jake Vanderwerf
2026-08-27 f0645f805ecdd19cf5ed9842f9e6b69a86dbcdeb
=Notifications and routes minor cleanup. needs more work
5 files modified
338 ■■■■■ changed files
inc/blocks/FAQBlock.php 1 ●●●●● patch | view | raw | blame | history
inc/managers/NotificationManager.php 4 ●●●●● patch | view | raw | blame | history
inc/managers/Notifications/Notifications.php 60 ●●●●● patch | view | raw | blame | history
inc/rest/Rest.php 10 ●●●●● patch | view | raw | blame | history
inc/rest/routes/NotificationsRoutes.php 263 ●●●●● patch | view | raw | blame | history
inc/blocks/FAQBlock.php
@@ -2,6 +2,7 @@
namespace JVBase\blocks;
use JVBase\managers\Cache;
use JVBase\managers\DirectoryManager;
use JVBase\registrar\Registrar;
use WP_Block;
use WP_Query;
inc/managers/NotificationManager.php
@@ -74,6 +74,10 @@
   {
       return $this->notifications->getUserNotifications($user_id, $args);
   }
   public function countUserNotifications(int $user_id, array $args = []):int
   {
       return $this->notifications->countUserNotifications($user_id, $args);
   }
   public function markRead(int $userID, int|array $notification_id):bool
   {
inc/managers/Notifications/Notifications.php
@@ -315,24 +315,60 @@
        return true;
    }
    public function getUserNotifications(int $user_id, array $where = []):array
    protected function buildArgs(array $args):array
    {
        $gotWhere = $where;
        $allowedKeys = ['from_user','target_id', 'target_type','type','status','priority','action_taken','created_at','read_at','actioned_at','updated_at'];
        $where = array_filter($gotWhere, function($key) use ($allowedKeys) {
            return in_array($key, $allowedKeys);
        $allowed = $this->notifications->getColumnKeys();
        $where = array_filter($args, function($key) use ($allowed) {
            return in_array($key, $allowed);
        }, ARRAY_FILTER_USE_KEY);
        $notAllowed = array_diff($gotWhere, $allowedKeys);
        if (!empty($notAllowed)){
            error_log('[Notifications]::getUserNotifications Invalid where arguments, removed from request: '.print_r($notAllowed, true));
        if ($where['type'] === 'all') {
            unset($where['type']);
        }
        return $this->notifications->getMany(array_merge([
            'for_user'  => $user_id,
            'status'    => 'unread',    //default to unread, but the merge will override if different
        ], $where));
        $args['page'] = max(1, intval($args['page']??1));
        $order = wp_parse_args($args, [
            'orderby'   => 'created_at',
            'order'     => 'DESC',
            'per_page'  => 20,
            'page'      => 1,
        ]);
        $args = array_merge([
            'where' => $where,
        ],$order);
        return $args;
    }
    public function getUserNotifications(int $user_id, array $args = []):array
    {
        $args = $this->buildArgs($args);
        $args['where']['for_user'] = $user_id;
        if (!array_key_exists('status', $args['where'])) {
            $args['where']['status'] = 'unread';
        }
        return $this->notifications->getMany($args, false);
    }
    public function countUserNotifications(int $user_id, array $args = []):int
    {
        $args = $this->buildArgs($args);
        $args['where']['for_user'] = $user_id;
        if (!array_key_exists('status', $args['where'])) {
            $args['where']['status'] = 'unread';
        }
        return $this->notifications->count($args);
    }
    public function markRead(int $userID, int|array $notification_id):bool
    {
        if (is_int($notification_id)) {
inc/rest/Rest.php
@@ -491,6 +491,16 @@
        return $cache->remember($userId, fn() => (bool) get_userdata($userId));
    }
    protected function checkArray(string $value, array $search, bool $lowercase = true):string|false
    {
        if ($lowercase) {
            $value = strtolower($value);
        }
        return in_array($value, $search) ? $value : false;
    }
    /**
     * Check if term exists (cached)
     */
inc/rest/routes/NotificationsRoutes.php
@@ -1,9 +1,12 @@
<?php
namespace JVBase\rest\routes;
use JVBase\base\Site;
use JVBase\Instance;
use JVBase\managers\Approvals;
use JVBase\managers\Cache;
use JVBase\managers\CustomTable;
use JVBase\managers\NotificationManager;
use JVBase\registrar\Registrar;
use JVBase\rest\Rest;
use JVBase\rest\Route;
@@ -109,10 +112,7 @@
        $this->cacheName = 'notifications';
        $this->cacheTtl = HOUR_IN_SECONDS;
        parent::__construct();
        $this->cache = Cache::for($this->cacheName, 'user', $this->cacheTtl);
        $this->notifications = CustomTable::for('notifications');
        $this->metrics = CustomTable::for('notification_metrics');
        // Build complete type map
        $allTypes = [];
@@ -218,38 +218,9 @@
        if (!$user_id) {
            return $this->unauthorized();
        }
        $type = sanitize_text_field($request->get_param('type') ?? '');
        $status = sanitize_text_field($request->get_param('status') ?? '');
        $limit = absint($request->get_param('limit'));
        $offset = absint($request->get_param('offset'));
        $args = $this->buildParams($request);
        $cacheKey = compact('user_id', 'type', 'status', 'limit', 'offset');
        $result = $this->cache->remember($cacheKey, function() use ($user_id, $type, $status, $limit, $offset) {
            // Build where conditions
            $where = ['owner_id' => $user_id];
            if ($type) $where['type'] = $type;
            if ($status) $where['status'] = $status;
            $items = $this->notifications
                ->where($where)
                ->orderBy('created_at', 'DESC')
                ->limit($limit, $offset)
                ->getResults();
            $total = $this->notifications->where($where)->countResults();
            // Format notifications
            $formatted = array_map([$this, 'formatNotification'], $items);
            return [
                'items' => $formatted,
                'total' => $total,
                'has_more' => ($offset + $limit) < $total
            ];
        });
        return $this->success($result);
        return $this->success($this->manager->getUserNotifications($user_id, $args));
    }
    /**
@@ -261,16 +232,10 @@
        if (!$user_id) {
            return $this->unauthorized();
        }
        $type = sanitize_text_field($request->get_param('type') ?? '');
        $cacheKey = compact('user_id', 'type');
        $count = $this->cache->remember($cacheKey, function() use ($user_id, $type) {
            $where = ['owner_id' => $user_id, 'status' => 'unread'];
            if ($type) $where['type'] = $type;
            return $this->notifications->where($where)->countResults();
        });
        $args = $this->buildParams($request);
        $args['status'] = 'unread';
        $count = $this->manager->countUserNotifications($user_id, $args);
        return $this->success(['count' => $count]);
    }
@@ -394,192 +359,8 @@
        });
    }
    /**
     * Get regular notifications from the notifications table
     */
    protected function getRegularNotifications(int $user_id, array $params): array
    {
        $cacheKey = compact('user_id', 'params');
        return $this->cache->remember($cacheKey, function() use ($user_id, $params) {
            $status = $params['status'];
            $type = $params['type'];
            $limit = $params['limit'];
            $offset = $params['page'];
            // Build base query
            $where = ['owner_id' => $user_id];
            // Handle status filter
            if ($status === 'read') {
                // For multiple statuses, use raw query
                $notifications = $this->getNotificationsWithMultipleStatuses(
                    $user_id,
                    ['read', 'actioned'],
                    $type,
                    $limit,
                    $offset
                );
            } else {
                // Single status - use fluent builder
                if ($status !== 'all') {
                    $where['status'] = $status;
                }
                // Handle type filter
                if ($type !== 'all' && isset($this->typeMap[$type])) {
                    $types = $this->typeMap[$type];
                    if (!empty($types)) {
                        // Multiple types - use raw query
                        return $this->getNotificationsByTypes(
                            $user_id,
                            $types,
                            $status,
                            $limit,
                            $offset
                        );
                    }
                }
                // Simple query - use fluent builder
                $notifications = $this->notifications
                    ->where($where)
                    ->orderBy('created_at', 'DESC')
                    ->limit($limit, $offset)
                    ->getResults();
            }
            // Format notifications
            return array_map([$this, 'formatNotification'], $notifications);
        });
    }
    /**
     * Get notifications with multiple status values
     */
    protected function getNotificationsWithMultipleStatuses(
        int $user_id,
        array $statuses,
        string $type,
        int $limit,
        int $offset
    ): array {
        $placeholders = implode(',', array_fill(0, count($statuses), '%s'));
        $params = array_merge([$user_id], $statuses);
        $typeCondition = '';
        if ($type !== 'all' && isset($this->typeMap[$type])) {
            $types = $this->typeMap[$type];
            if (!empty($types)) {
                $typePlaceholders = implode(',', array_fill(0, count($types), '%s'));
                $typeCondition = "AND type IN ({$typePlaceholders})";
                $params = array_merge($params, $types);
            }
        }
        $params[] = $limit;
        $params[] = $offset;
        return $this->notifications->queryResults(
            "SELECT * FROM {table}
             WHERE owner_id = %d
             AND status IN ({$placeholders})
             {$typeCondition}
             ORDER BY created_at DESC
             LIMIT %d OFFSET %d",
            $params
        );
    }
    /**
     * Get notifications by multiple types
     */
    protected function getNotificationsByTypes(
        int $user_id,
        array $types,
        string $status,
        int $limit,
        int $offset
    ): array {
        $placeholders = implode(',', array_fill(0, count($types), '%s'));
        $params = array_merge([$user_id], $types, [$limit, $offset]);
        $statusCondition = '';
        if ($status !== 'all') {
            $statusCondition = "AND status = %s";
            array_splice($params, -2, 0, [$status]);
        }
        return $this->notifications->queryResults(
            "SELECT * FROM {table}
             WHERE owner_id = %d
             AND type IN ({$placeholders})
             {$statusCondition}
             ORDER BY created_at DESC
             LIMIT %d OFFSET %d",
            $params
        );
    }
    /**
     * Get approval notifications from the approval_requests table
     */
    protected function getApprovalNotifications(int $user_id, string $status): array
    {
        $cacheKey = compact('user_id', 'status');
        return $this->cache->remember($cacheKey, function() use ($user_id, $status) {
            if (!$this->isVerifiedUser($user_id)) {
                return [];
            }
            global $wpdb;
            $formatted = [];
            // Build status condition
            $statusCondition = "1=1";
            if ($status === 'unread') {
                $statusCondition = "a.status = 'pending'";
            } elseif ($status === 'read') {
                $statusCondition = "a.status IN ('approved', 'rejected')";
            } elseif ($status !== 'all') {
                $statusCondition = $wpdb->prepare("a.status = %s", $status);
            }
            $approvals = Registrar::withFeature('approve_new');
            foreach ($approvals as $type => $config) {
                $table = $wpdb->prefix . BASE . 'approval_' . $type . 'requests';
                $votes = $wpdb->prefix . BASE . 'approval_' . $type . 'votes';
                $approvalRequests = $wpdb->get_results(
                    $wpdb->prepare(
                        "SELECT a.*,
                        COALESCE(v.vote, 'none') as user_vote
                        FROM {$table} a
                        LEFT JOIN {$votes} v ON a.id = v.request_id AND v.user_id = %d
                        WHERE a.user_id != %d
                        AND {$statusCondition}
                        ORDER BY a.created_at DESC",
                        $user_id,
                        $user_id
                    )
                );
                // Filter out requests created by current user
                foreach ($approvalRequests as $approval) {
                    $requested_by = json_decode($approval->requested_by, true);
                    if (is_array($requested_by) && in_array($user_id, $requested_by)) {
                        continue;
                    }
                    $formatted[] = $this->formatApprovalNotification($approval);
                }
            }
            return $formatted;
        });
    }
    /**
     * Get content notifications for a user
@@ -1301,16 +1082,34 @@
    {
        $params = $request->get_params();
        return [
            'status' => in_array($params['status'] ?? '', ['all', 'unread', 'expired'])
        $args = [
            'status'    => in_array($params['status']??'unread', ['all', 'unread', 'expired'])
                ? $params['status']
                : 'unread',
            'user_id' => get_current_user_id(),
            'page' => absint($params['page'] ?? 1),
            'type' => in_array($params['type'] ?? '', array_keys($this->manager->notification_types ?? []))
            'type'      => in_array($params['type'] ?? 'all', array_keys($this->manager->notification_types ?? []))
                ? $params['type']
                : 'all',
            'orderby'   => $this->checkArray($params['orderby'], ['target_type', 'priority', 'created_at','read_at','actioned_at','action_taken','status'])??'created_at',
            'order'     => strtoupper($this->checkArray($params['order']??'DESC', ['desc','asc'])??'DESC'),
            'page'      => max(1, absint($params['page']??1)),
            'per_page'  => absint($params['per_page']??20),
        ];
        foreach ($params as $param => $value) {
            $result = match ($param) {
                'target_id'     => absint($value),
                'target_type'   => sanitize_text_field(strtolower($value)),
                'from_user'     => $this->checkUser(absint($value)),
                'priority'      => $this->checkArray($value, ['low','medium', 'high']),
                'action_taken'  => $value === 1 ? 1 : 0,
                default => false
            };
            if ($result) {
                $args[$param] = $result;
            }
        }
        return $args;
    }
    /**