From 2127b1bdd73ecd2423e443992da4b442f5a3c1a3 Mon Sep 17 00:00:00 2001
From: Jake Vanderwerf <get@jakevanderwerf.ca>
Date: Wed, 04 Feb 2026 21:19:25 +0000
Subject: [PATCH] =Major overhaul of MetaManager.php -> Meta.php and RestRouteManager.php -> Rest.php. Seems to work for JakeVan

---
 inc/managers/RoleManager.php |  349 ++++++++++++++++++++++++++++++++++++++++++++++++++++++---
 1 files changed, 328 insertions(+), 21 deletions(-)

diff --git a/inc/managers/RoleManager.php b/inc/managers/RoleManager.php
index b575a04..d4a3988 100644
--- a/inc/managers/RoleManager.php
+++ b/inc/managers/RoleManager.php
@@ -17,10 +17,26 @@
     {
        $this->roles = array_keys(JVB_USER);
 	   $this->content = array_map(function($content) {
-		   return strtolower($content['plural']);
+		   return strtolower($content['plural']??$content['singular'].'s');
 	   },JVB_CONTENT);
+	   add_action('set_user_role', [$this, 'updateRoles'], 10, 3);
     }
 
+	public function updateRoles(int $userID, string $role, array $oldRoles):void
+	{
+		if (doing_action('set_user_role') > 1) {
+			return;
+		}
+		$temp = jvbNoBase($role);
+		if (array_key_exists($temp, JVB_USER)) {
+			$user = get_userdata($userID);
+			if (!$user) {
+				return;
+			}
+			$this->reset($user);
+			$this->setUserAs($user, $temp);
+		}
+	}
 
     /**
      * @param WP_User $user
@@ -140,7 +156,6 @@
     /**
      * @param WP_User $user
      * @param string $type
-     * @param bool $add
      *
      * @return void
      */
@@ -356,22 +371,29 @@
 		}
 	}
 
+	/**
+	 * @param string $content
+	 * @return array|string[]
+	 * Note: must match what is created in PostTypeRegistrar.php::register
+	 */
 	protected function getCapabilities(string $content):array
 	{
 		$content = jvbNoBase($content);
 		if (!$this->isValidContentType($content)) {
 			return [];
 		}
+
 		$plural = $this->getContentPlural($content);
+
 		return [
-			'edit_' . $plural,
-			'delete_' . $plural,
-			'read_' . $plural,
-			'edit_published_' . $plural,
-			'delete_published_' . $plural,
-			'edit_private_' . $plural,
-			'delete_private_' . $plural,
-			'publish_' . $plural,
+			"edit_{$content}",
+			"read_{$content}",
+			"delete_{$content}",
+			"edit_{$plural}",
+			"edit_others_{$plural}",
+			"publish_{$plural}",
+			"read_private_{$plural}",
+			"edit_{$plural}",
 		];
 	}
 	protected function getOthersCapabilities(string $content):array
@@ -382,23 +404,32 @@
 		}
 		$plural = $this->getContentPlural($content);
 		return [
-			'edit_others_' . $plural,
-			'delete_others_' . $plural,
-			'read_private_' . $plural,
-			'edit_private_' . $plural,
-			'delete_private_' . $plural,
+			"edit_others_{$plural}",
+			"delete_others_{$plural}",
+			"read_private_{$plural}",
+			"edit_private_{$plural}",
+			"delete_private_{$plural}",
 		];
 	}
 
-	private function getContentPlural(string $content): string
+	public static function getPlural(string $content): string
+	{
+		$self = new self;
+		return $self->getContentPlural($content);
+	}
+	public function getContentPlural(string $content): string
 	{
 		$content = jvbNoBase($content);
-
-		if (array_key_exists($content, JVB_CONTENT)) {
-			return strtolower(JVB_CONTENT[$content]['plural'] ?? $content . 's');
+		$config = Features::getConfig($content);
+		$capsMap = $config['capability_type']??[];
+		if (empty($capsMap)){
+			$capsMap = [
+				$content,
+				str_replace('-', '_',sanitize_title(strtolower(JVB_CONTENT[$content]['plural']??JVB_TAXONOMY[$content]['plural'])))
+			];
+			return $capsMap[1];
 		}
-
-		return strtolower($content . 's');
+		return str_replace('-', '_', sanitize_title(strtolower($content . 's')));
 	}
 
 	public function activate(): void
@@ -407,4 +438,280 @@
 			$this->registerRole($slug, $config);
 		}
 	}
+
+	/******************************************************************
+	 * OWNABLE and MANAGABLE terms (ie: tattoo shops)
+	******************************************************************/
+	/**
+	 * Grant ownership of a content taxonomy term
+	 * Owners have full control over the term and its members
+	 *
+	 * @param int $userID User ID
+	 * @param int $termID Term ID
+	 * @param string $taxonomy Taxonomy slug (without BASE)
+	 * @return bool Success
+	 */
+	public function grantOwnership(int $userID, int $termID, string $taxonomy): bool
+	{
+		if (!get_userdata($userID) || !term_exists($termID)){
+			return false;
+		}
+		$taxonomy = jvbNoBase($taxonomy);
+
+		// Verify this is an ownable content taxonomy
+		if (!Features::forTaxonomy($taxonomy)->has('is_content') ||
+			!Features::forTaxonomy($taxonomy)->has('is_ownable')) {
+			return false;
+		}
+
+		$user = get_userdata($userID);
+		if (!$user) {
+			return false;
+		}
+
+		// Grant both ownership and management
+		$user->add_cap(BASE . 'can_own_' . $termID);
+		$user->add_cap(BASE . 'can_manage_' . $termID);
+
+		do_action(BASE . 'granted_ownership', $userID, $termID, $taxonomy);
+
+		return true;
+	}
+
+	/**
+	 * Revoke ownership of a content taxonomy term
+	 *
+	 * @param int $userID User ID
+	 * @param int $termID Term ID
+	 * @param string $taxonomy Taxonomy slug (without BASE)
+	 * @return bool Success
+	 */
+	public function revokeOwnership(int $userID, int $termID, string $taxonomy): bool
+	{
+		if (!get_userdata($userID) || !term_exists($termID)){
+			return false;
+		}
+		$taxonomy = jvbNoBase($taxonomy);
+
+		$user = get_userdata($userID);
+		if (!$user) {
+			return false;
+		}
+
+		$user->remove_cap(BASE . 'can_own_' . $termID);
+
+		do_action(BASE . 'revoked_ownership', $userID, $termID, $taxonomy);
+
+		return true;
+	}
+
+	/**
+	 * Grant management capabilities for a content taxonomy term
+	 * Managers can approve members and edit content but don't own the term
+	 *
+	 * @param int $userID User ID
+	 * @param int $termID Term ID
+	 * @param string $taxonomy Taxonomy slug (without BASE)
+	 * @return bool Success
+	 */
+	public function grantManagement(int $userID, int $termID, string $taxonomy): bool
+	{
+		if (!get_userdata($userID) || !term_exists($termID)){
+			return false;
+		}
+		$taxonomy = jvbNoBase($taxonomy);
+
+		// Verify this is an ownable content taxonomy
+		if (!Features::forTaxonomy($taxonomy)->has('is_content') ||
+			!Features::forTaxonomy($taxonomy)->has('is_ownable')) {
+			return false;
+		}
+
+		$user = get_userdata($userID);
+		if (!$user) {
+			return false;
+		}
+
+		$user->add_cap(BASE . 'can_manage_' . $termID);
+
+		do_action(BASE . 'granted_management', $userID, $termID, $taxonomy);
+
+		return true;
+	}
+
+	/**
+	 * Revoke management capabilities for a content taxonomy term
+	 *
+	 * @param int $userID User ID
+	 * @param int $termID Term ID
+	 * @param string $taxonomy Taxonomy slug (without BASE)
+	 * @return bool Success
+	 */
+	public function revokeManagement(int $userID, int $termID, string $taxonomy): bool
+	{
+		if (!get_userdata($userID) || !term_exists($termID)){
+			return false;
+		}
+		$taxonomy = jvbNoBase($taxonomy);
+
+		$user = get_userdata($userID);
+		if (!$user) {
+			return false;
+		}
+
+		$user->remove_cap(BASE . 'can_manage_' . $termID);
+
+		do_action(BASE . 'revoked_management', $userID, $termID, $taxonomy);
+
+		return true;
+	}
+
+	/**
+	 * Check if user owns a term
+	 *
+	 * @param int $userID User ID
+	 * @param int $termID Term ID
+	 * @return bool
+	 */
+	public function isOwner(int $userID, int $termID): bool
+	{
+		return user_can($userID, BASE . 'can_own_' . $termID);
+	}
+
+	/**
+	 * Check if user can manage a term (owner or manager)
+	 *
+	 * @param int $userID User ID
+	 * @param int $termID Term ID
+	 * @return bool
+	 */
+	public function isManager(int $userID, int $termID): bool
+	{
+		return user_can($userID, BASE . 'can_manage_' . $termID) ||
+			user_can($userID, BASE . 'can_own_' . $termID);
+	}
+
+	/**
+	 * Get all terms a user owns
+	 *
+	 * @param int $userID User ID
+	 * @param string|null $taxonomy Optional: filter by taxonomy
+	 * @return array Array of term IDs
+	 */
+	public function getOwnedTerms(int $userID, ?string $taxonomy = null): array
+	{
+		$user = get_userdata($userID);
+		if (!$user) {
+			return [];
+		}
+
+		$owned = [];
+		foreach ($user->allcaps as $cap => $value) {
+			if ($value && strpos($cap, BASE . 'can_own_') === 0) {
+				$termID = (int) str_replace(BASE . 'can_own_', '', $cap);
+				if ($termID) {
+					$owned[] = $termID;
+				}
+			}
+		}
+
+		// Filter by taxonomy if specified
+		if ($taxonomy && !empty($owned)) {
+			$taxonomy = jvbCheckBase($taxonomy);
+			$filtered = [];
+			foreach ($owned as $termID) {
+				$term = get_term($termID);
+				if ($term && !is_wp_error($term) && $term->taxonomy === $taxonomy) {
+					$filtered[] = $termID;
+				}
+			}
+			return $filtered;
+		}
+
+		return $owned;
+	}
+
+	/**
+	 * Get all terms a user can manage (owns or manages)
+	 *
+	 * @param int $userID User ID
+	 * @param string|null $taxonomy Optional: filter by taxonomy
+	 * @return array Array of term IDs
+	 */
+	public function getManagedTerms(int $userID, ?string $taxonomy = null): array
+	{
+		$user = get_userdata($userID);
+		if (!$user) {
+			return [];
+		}
+
+		$managed = [];
+		foreach ($user->allcaps as $cap => $value) {
+			if ($value && (strpos($cap, BASE . 'can_manage_') === 0 ||
+					strpos($cap, BASE . 'can_own_') === 0)) {
+				$termID = (int) str_replace([BASE . 'can_manage_', BASE . 'can_own_'], '', $cap);
+				if ($termID && !in_array($termID, $managed)) {
+					$managed[] = $termID;
+				}
+			}
+		}
+
+		// Filter by taxonomy if specified
+		if ($taxonomy && !empty($managed)) {
+			$taxonomy = jvbCheckBase($taxonomy);
+			$filtered = [];
+			foreach ($managed as $termID) {
+				$term = get_term($termID);
+				if ($term && !is_wp_error($term) && $term->taxonomy === $taxonomy) {
+					$filtered[] = $termID;
+				}
+			}
+			return $filtered;
+		}
+
+		return $managed;
+	}
+
+	/**
+	 * Get all ownable taxonomies
+	 *
+	 * @return array Array of taxonomy slugs
+	 */
+	public function getOwnableTaxonomies(): array
+	{
+		static $ownable = null;
+
+		if ($ownable === null) {
+			$ownable = [];
+			foreach (JVB_TAXONOMY as $taxonomy => $config) {
+				if (Features::forTaxonomy($taxonomy)->has('is_content') &&
+					Features::forTaxonomy($taxonomy)->has('is_ownable')) {
+					$ownable[] = $taxonomy;
+				}
+			}
+		}
+
+		return $ownable;
+	}
+
+	/**
+	 * Get all invitable taxonomies
+	 *
+	 * @return array Array of taxonomy slugs
+	 */
+	public function getInvitableTaxonomies(): array
+	{
+		static $invitable = null;
+
+		if ($invitable === null) {
+			$invitable = [];
+			foreach (JVB_TAXONOMY as $taxonomy => $config) {
+				if (Features::forTaxonomy($taxonomy)->has('invitable')) {
+					$invitable[] = $taxonomy;
+				}
+			}
+		}
+
+		return $invitable;
+	}
 }

--
Gitblit v1.10.0