| | |
| | | <?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\managers\CustomTable; |
| | | use WP_REST_Request; |
| | | use WP_REST_Response; |
| | |
| | | */ |
| | | 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); |
| | |
| | | |
| | | 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 |
| | | )); |
| | |
| | | |
| | | 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); |
| | |
| | | } |
| | | } |
| | | |
| | | 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); |
| | |
| | | } |
| | | } |
| | | |
| | | 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; |
| | | } |
| | | |
| | |
| | | '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([ |
| | |
| | | '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([ |
| | |
| | | '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']) |
| | |
| | | '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([ |
| | |
| | | 'role' => 'string|enum:owner,manager|required', |
| | | 'grant' => 'bool|required' |
| | | ]) |
| | | ->auth(PermissionHandler::custom([$this, 'checkOwnerPermission'])) |
| | | ->rateLimit(5); |
| | | ->auth(PermissionHandler::combine([[$this, 'checkTermPermission']])) |
| | | ->rateLimit(5) |
| | | ->register(); |
| | | } |
| | | } |
| | | |
| | |
| | | */ |
| | | 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))) { |
| | |
| | | } |
| | | |
| | | 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); |
| | | } |
| | | |
| | | /** |
| | |
| | | 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), |
| | |
| | | $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; |
| | | |
| | |
| | | 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(); |
| | | |
| | | $members = $query |
| | | ->orderBy('is_primary', 'DESC') |
| | | ->orderBy('is_primary') |
| | | ->orderBy('created_at', 'ASC') |
| | | ->limit($perPage, $offset) |
| | | ->getResults(ARRAY_A); |
| | |
| | | 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, |
| | | [ |
| | |
| | | $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; |
| | | |
| | |
| | | $total = $query->countResults(); |
| | | |
| | | $requests = $query |
| | | ->orderBy('created_date', 'DESC') |
| | | ->orderBy('created_date') |
| | | ->limit($perPage, $offset) |
| | | ->getResults(ARRAY_A); |
| | | |
| | |
| | | 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) { |
| | |
| | | |
| | | return $this->error('Failed to create request'); |
| | | } catch (Exception $e) { |
| | | $this->logError('createRequest', [ |
| | | 'error' => $e->getMessage(), |
| | | $this->logError($e->getMessage(),'createRequest', [ |
| | | 'user_id' => $userID, |
| | | 'term_id' => $termID |
| | | ]); |
| | |
| | | } |
| | | }); |
| | | |
| | | // Notify user |
| | | JVB()->notification()->addNotification( |
| | | $requestData['user_id'], |
| | | 'request_' . $status, |
| | | [ |
| | | 'term_id' => $requestData['term_id'], |
| | | 'taxonomy' => $this->taxonomy, |
| | | 'notes' => $notes |
| | | ] |
| | | ); |
| | | // Notify user TODO |
| | | // NotificationManager::getInstance()->notify( |
| | | // $requestData['user_id'], |
| | | // 'request_' . $status, |
| | | // [ |
| | | // 'term_id' => $requestData['term_id'], |
| | | // 'taxonomy' => $this->taxonomy, |
| | | // 'notes' => $notes |
| | | // ] |
| | | // ); |
| | | |
| | | $this->cache->flush(); |
| | | |
| | | return $this->success(['message' => 'Request ' . $status]); |
| | | |
| | | } catch (Exception $e) { |
| | | $this->logError('processRequest', [ |
| | | 'error' => $e->getMessage(), |
| | | $this->logError($e->getMessage(), 'processRequest', [ |
| | | 'request_id' => $requestID, |
| | | 'action' => $action |
| | | ]); |
| | |
| | | return $this->error('Invalid target user'); |
| | | } |
| | | |
| | | $roleManager = JVB()->roles(); |
| | | $roleManager = Roles::getInstance()(); |
| | | |
| | | try { |
| | | $result = match($role) { |
| | |
| | | return $this->error('Failed to update permissions'); |
| | | |
| | | } catch (Exception $e) { |
| | | $this->logError('updatePermissions', [ |
| | | 'error' => $e->getMessage(), |
| | | $this->logError($e->getMessage(),'updatePermissions', [ |
| | | 'term_id' => $termID, |
| | | 'target_user' => $targetUserID, |
| | | 'role' => $role |
| | |
| | | */ |
| | | 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( |
| | | $managerID, |
| | | $notificationType, |
| | | [ |
| | | 'user_id' => $actingUserID, |
| | | 'term_id' => $termID, |
| | | 'term_name' => $term->name ?? 'Unknown', |
| | | 'taxonomy' => $this->taxonomy |
| | | ] |
| | | ); |
| | | } |
| | | //TODO |
| | | // foreach ($managers as $managerID) { |
| | | // NotificationManager::getInstance()->addNotification( |
| | | // $managerID, |
| | | // $notificationType, |
| | | // [ |
| | | // 'user_id' => $actingUserID, |
| | | // 'term_id' => $termID, |
| | | // 'term_name' => $term->name ?? 'Unknown', |
| | | // 'taxonomy' => $this->taxonomy |
| | | // ] |
| | | // ); |
| | | // } |
| | | } |
| | | } |