Jake Vanderwerf
2026-08-27 f0645f805ecdd19cf5ed9842f9e6b69a86dbcdeb
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;
   }
   /**