Jake Vanderwerf
2026-08-27 f0645f805ecdd19cf5ed9842f9e6b69a86dbcdeb
inc/rest/routes/ContentTermsRoutes.php
@@ -1,12 +1,17 @@
<?php
namespace JVBase\rest\routes;
use JVBase\Instance;
use JVBase\managers\Cache;
use JVBase\managers\queue\executors\ContentTermExecutor;
use JVBase\managers\queue\Queue;
use JVBase\managers\queue\TypeConfig;
use JVBase\managers\Roles;
use JVBase\registrar\Registrar;
use JVBase\rest\Rest;
use JVBase\rest\Route;
use JVBase\rest\PermissionHandler;
use JVBase\utility\Features;
use JVBase\base\Site;
use JVBase\managers\CustomTable;
use WP_REST_Request;
use WP_REST_Response;
@@ -30,21 +35,22 @@
 */
class ContentTermsRoutes extends Rest
{
   use Instance;
   protected string $taxonomy;
   protected array $config;
   protected Registrar $registrar;
   protected ?CustomTable $historyTable = null;
   protected ?CustomTable $requestsTable = null;
   public function __construct(string $taxonomy = '')
   //TODO: This needs some work. We need to move most logic into it's own manager class
   protected function __construct(string $taxonomy = '')
   {
      $this->taxonomy = jvbNoBase($taxonomy);
      if ($taxonomy && isset(JVB_TAXONOMY[$this->taxonomy])) {
         $this->config = JVB_TAXONOMY[$this->taxonomy];
      if ($taxonomy !== '' && Registrar::getInstance($this->taxonomy)) {
         $this->registrar = Registrar::getInstance($this->taxonomy);
         $this->cacheName = $this->taxonomy;
         parent::__construct();
         $this->cache = Cache::for($this->taxonomy, 'term', DAY_IN_SECONDS);
         $this->setupTables();
         $this->setupCacheConnections();
      }
      add_action('init', [$this, 'registerContentTermsExecutors'], 5);
@@ -52,16 +58,17 @@
   public function registerContentTermsExecutors():void
   {
      $registry = JVB()->queue()->registry();
      $registry = Queue::getInstance()->registry();
      $executor = new ContentTermExecutor();
      $taxonomies = Features::getTypesWithFeature('is_content', 'taxonomy');
      $taxonomies = Registrar::withFeature('is_content', 'term');
      foreach($taxonomies as $taxonomy) {
         $registry->register("{$taxonomy}_update", new TypeConfig(
            executor: $executor,
         ));
         if (Features::forTaxonomy($taxonomy)->has('track_changes')) {
         $registrar = Registrar::getInstance($taxonomy);
         if ($registrar && $registrar->hasFeature('track_changes')) {
            $registry->register("{$taxonomy}_member_add", new TypeConfig(
               executor: $executor
            ));
@@ -76,9 +83,9 @@
   protected function setupTables(): void
   {
      $content = $this->config['for_content'] ?? [];
      $content = $this->registrar->registrar->for;
      if (Features::forTaxonomy($this->taxonomy)->has('track_changes') && !empty($content)) {
      if ($this->registrar->hasFeature('track_changes') && !empty($content)) {
         foreach ($content as $contentType) {
            $tableName = "history_{$contentType}_{$this->taxonomy}";
            $this->historyTable = CustomTable::for($tableName);
@@ -86,7 +93,7 @@
         }
      }
      if (Features::forTaxonomy($this->taxonomy)->has('verify_entry') && !empty($content)) {
      if ($this->registrar->hasFeature('verify_entry') && !empty($content)) {
         foreach ($content as $contentType) {
            $tableName = "{$contentType}_{$this->taxonomy}_requests";
            $this->requestsTable = CustomTable::for($tableName);
@@ -95,16 +102,9 @@
      }
   }
   protected function setupCacheConnections(): void
   {
      $this->cache
         ->connect('taxonomy')
         ->connect('user');
   }
   public function registerRoutes(): void
   {
      if (!Features::forTaxonomy($this->taxonomy)->has('is_content')) {
      if (!$this->registrar->hasFeature('is_content')) {
         return;
      }
@@ -117,11 +117,12 @@
            'user' => 'int|required',
            'term_id' => 'int|required'
         ])
         ->auth(PermissionHandler::custom([$this, 'checkTermPermission']))
         ->rateLimit(10);
         ->auth(PermissionHandler::combine([[$this, 'checkTermPermission']]))
         ->rateLimit(10)
         ->register();
      // Member management (if track_changes enabled)
      if (Features::forTaxonomy($this->taxonomy)->has('track_changes')) {
      if ($this->registrar->hasFeature('track_changes')) {
         Route::for("{$base}/:term_id/members")
            ->get([$this, 'getMembers'])
            ->args([
@@ -139,12 +140,13 @@
               'target_user' => 'int|required',
               'action' => 'string|enum:add,remove|required'
            ])
            ->auth(PermissionHandler::custom([$this, 'checkTermPermission']))
            ->rateLimit(5);
            ->auth(PermissionHandler::combine([[$this, 'checkTermPermission']]))
            ->rateLimit(5)
            ->register();
      }
      // Membership requests (if verify_entry enabled)
      if (Features::forTaxonomy($this->taxonomy)->has('verify_entry')) {
      if ($this->registrar->hasFeature('verify_entry')) {
         Route::for("{$base}/:term_id/requests")
            ->get([$this, 'getRequests'])
            ->args([
@@ -153,8 +155,9 @@
               'status' => 'string|enum:requested,accepted,rejected,all|default:requested',
               'page' => 'int|default:1|min:1'
            ])
            ->auth(PermissionHandler::custom([$this, 'checkTermPermission']))
            ->rateLimit(20);
            ->auth(PermissionHandler::combine([[$this, 'checkTermPermission']]))
            ->rateLimit(20)
            ->register();
         Route::for("{$base}/request")
            ->post([$this, 'handleRequest'])
@@ -166,11 +169,12 @@
               'notes' => 'string'
            ])
            ->auth('verified')
            ->rateLimit(5);
            ->rateLimit(5)
            ->register();
      }
      // Ownership/management (if is_ownable enabled)
      if (Features::forTaxonomy($this->taxonomy)->has('is_ownable')) {
      if ($this->registrar->hasFeature('is_ownable')) {
         Route::for("{$base}/:term_id/permissions")
            ->post([$this, 'updatePermissions'])
            ->args([
@@ -180,8 +184,9 @@
               'role' => 'string|enum:owner,manager|required',
               'grant' => 'bool|required'
            ])
            ->auth(PermissionHandler::custom([$this, 'checkOwnerPermission']))
            ->rateLimit(5);
            ->auth(PermissionHandler::combine([[$this, 'checkTermPermission']]))
            ->rateLimit(5)
            ->register();
      }
   }
@@ -190,7 +195,7 @@
    */
   public function checkTermPermission(WP_REST_Request $request): bool
   {
      $userID = $request->get_param('user') ?? get_current_user_id();
      $userID = get_current_user_id();
      $termID = (int)$request->get_param('term_id');
      if (!$this->checkUser($userID) || !term_exists($termID, jvbCheckBase($this->taxonomy))) {
@@ -198,20 +203,20 @@
      }
      return user_can($userID, 'manage_options') ||
         JVB()->roles()->isManager($userID, $termID);
         Roles::getInstance()()->isManager($userID, $termID);
   }
   public function checkOwnerPermission(WP_REST_Request $request): bool
   {
      $userID = $request->get_param('user') ?? get_current_user_id();
      $userID = get_current_user_id();
      $termID = (int)$request->get_param('term_id');
      if (!$this->checkUser($userID) || !term_exists($termID, jvbCheckBase($this->taxonomy))) {
      if (!term_exists($termID, jvbCheckBase($this->taxonomy))) {
         return false;
      }
      return user_can($userID, 'manage_options') ||
         JVB()->roles()->isOwner($userID, $termID);
         Roles::getInstance()()->isOwner($userID, $termID);
   }
   /**
@@ -220,13 +225,13 @@
   public function updateSettings(WP_REST_Request $request): WP_REST_Response
   {
      $termID = (int)$request->get_param('term_id');
      $userID = $request->get_param('user');
      $userID = get_current_user_id();
      $data = $request->get_params();
      unset($data['user'], $data['term_id']);
      // Queue the update
      $op = JVB()->queue()->add(
      $op = Queue::getInstance()->add(
         "{$this->taxonomy}_update",
         $userID,
         array_merge(['term_id' => $termID], $data),
@@ -248,9 +253,8 @@
      $status = $request->get_param('status');
      $page = (int)$request->get_param('page');
      $cacheKey = $this->cache->generateKey(compact('termID', 'status', 'page'));
      return $this->cache->remember($cacheKey, function() use ($termID, $status, $page) {
      return $this->cache->remember(['id'=> $termID, 'status' => $status, 'page' => $page], function() use ($termID, $status, $page) {
         $perPage = 20;
         $offset = ($page - 1) * $perPage;
@@ -259,7 +263,7 @@
         if ($status === 'active') {
            $query = $query->where(['end_date' => null]);
         } elseif ($status === 'inactive') {
            $query = $query->whereNotNull('end_date');
            $query = $query->where(['end_date'=> ['NOT NULL', null]]);
         }
         $total = $query->countResults();
@@ -294,16 +298,12 @@
   public function manageMember(WP_REST_Request $request): WP_REST_Response
   {
      $action = $request->get_param('action');
      $userID = $request->get_param('user');
      $userID = get_current_user_id();
      $termID = (int)$request->get_param('term_id');
      $targetUserID = (int)$request->get_param('target_user');
      if (!$this->checkUser($targetUserID)) {
         return $this->error('Invalid target user');
      }
      // Queue the operation
      $op = JVB()->queue()->add(
      $op = Queue::getInstance()->add(
         "{$this->taxonomy}_member_{$action}",
         $userID,
         [
@@ -325,9 +325,7 @@
      $status = $request->get_param('status');
      $page = (int)$request->get_param('page');
      $cacheKey = $this->cache->generateKey(compact('termID', 'status', 'page'));
      return $this->cache->remember($cacheKey, function() use ($termID, $status, $page) {
      return $this->cache->remember(['id'=>$termID, 'status' => $status,'page' => $page], function() use ($termID, $status, $page) {
         $perPage = 20;
         $offset = ($page - 1) * $perPage;
@@ -360,7 +358,7 @@
   public function handleRequest(WP_REST_Request $request): WP_REST_Response
   {
      $action = $request->get_param('action');
      $userID = $request->get_param('user');
      $userID = get_current_user_id();
      $termID = (int)$request->get_param('term_id');
      return match($action) {
@@ -466,7 +464,7 @@
         });
         // Notify user
         JVB()->notification()->addNotification(
         NotificationManager::getInstance()->addNotification(
            $requestData['user_id'],
            'request_' . $status,
            [
@@ -505,7 +503,7 @@
         return $this->error('Invalid target user');
      }
      $roleManager = JVB()->roles();
      $roleManager = Roles::getInstance()();
      try {
         $result = match($role) {
@@ -545,13 +543,13 @@
    */
   protected function notifyTermManagers(int $termID, int $actingUserID, string $notificationType): void
   {
      $roleManager = JVB()->roles();
      $roleManager = Roles::getInstance()();
      $managers = $roleManager->getManagedTerms($actingUserID, $this->taxonomy);
      $term = get_term($termID, jvbCheckBase($this->taxonomy));
      foreach ($managers as $managerID) {
         JVB()->notification()->addNotification(
         NotificationManager::getInstance()->addNotification(
            $managerID,
            $notificationType,
            [