Jake Vanderwerf
2026-03-03 772462eeca3002a1d52508aeba485aab2b4742ad
inc/managers/RoleManager.php
@@ -1,6 +1,7 @@
<?php
namespace JVBase\managers;
use JVBase\registrar\Registrar;
use JVBase\utility\Features;
use WP_User;
use WP_Role;
@@ -15,10 +16,17 @@
    public function __construct()
    {
       $this->roles = array_keys(JVB_USER);
       $this->roles = array_keys(array_map(function ($instance) {
         return $instance->slug;
      }, Registrar::getRegistered('user')));
      $this->content = array_map(function($content) {
         return strtolower($content['plural']??$content['singular'].'s');
      },JVB_CONTENT);
         $registrar = Registrar::getInstance($content);
         return strtolower(str_replace(' ', '_', $registrar->getPlural()??$registrar->getSingular().'s'));
      },array_merge(
         Registrar::getRegistered('post'),
         Registrar::getFeatured('is_content', 'term')
      ));
      add_action('set_user_role', [$this, 'updateRoles'], 10, 3);
    }
@@ -28,7 +36,8 @@
         return;
      }
      $temp = jvbNoBase($role);
      if (array_key_exists($temp, JVB_USER)) {
      $registrar = Registrar::getInstance($temp);
      if ($registrar) {
         $user = get_userdata($userID);
         if (!$user) {
            return;
@@ -56,17 +65,13 @@
   {
      $type = jvbNoBase($type);
      // Check in JVB_CONTENT array
      if (array_key_exists($type, JVB_CONTENT)) {
      $registrar = Registrar::getInstance($type);
      if ($registrar && $registrar->getType() === 'post') {
         return true;
      }
      // Check in JVB_TAXONOMY for content taxonomies
      if (array_key_exists($type, JVB_TAXONOMY)) {
         $tax_config = JVB_TAXONOMY[$type];
         if ($tax_config['is_content'] ?? false) {
            return true;
         }
      if ($registrar && $registrar->getType() === 'term' && $registrar->hasFeature('is_content')) {
         return true;
      }
      return false;
@@ -91,11 +96,11 @@
      foreach ($roles as $role) {
         $role = jvbNoBase($role);
         $config = JVB_USER[$role]??false;
         if (!$config) {
         $registrar = Registrar::getInstance($role);
         if (!$registrar) {
            return false;
         }
         foreach ($config as $type) {
         foreach ($registrar->getCreatable() as $type) {
            if (is_array($type) && in_array($content, $type)) {
               return true;
            } elseif ($content === $type) {
@@ -185,15 +190,19 @@
      if ($check) {
         return $check;
      }
      $check = JVB_USER[$role]['can_create'] ??false;
      if (!$check) {
      $registrar = Registrar::getInstance($role);
      if (!$registrar) {
         return false;
      }
      $out = [];
      foreach ($check as $types) {
         foreach ($types as $type => $content) {
            $out[$type] = $content;
      foreach ($registrar->getCreatable() as $types) {
         if (is_array($types)) {
            $out = array_merge($out, $types);
         } else {
            $out = [$types];
         }
         $out = array_unique($out);
      }
      set_transient(BASE.'role_config_'.$role, $out, MONTH_IN_SECONDS);
      return $out;
@@ -230,13 +239,17 @@
        return $link;
    }
   public function registerRole(string $slug, array $config): void
   public function registerRole(string $slug): void
   {
      $role_name = BASE . $slug;
      $display_name = $config['label'] ?? ucfirst($slug);
      $registrar = Registrar::getInstance($slug);
      if (!$registrar){
         return;
      }
      // Build capabilities for this role
      $capabilities = $this->buildRoleCapabilities($slug, $config);
      $capabilities = $this->buildRoleCapabilities($slug, $registrar);
      // Remove role first to ensure clean slate
      remove_role($role_name);
@@ -245,7 +258,7 @@
      add_role($role_name, $display_name, $capabilities);
      // Add management capabilities to administrator
      if ($config['has_dashboard'] ?? false) {
      if ($registrar->hasFeature('has_dashboard') ?? false) {
         $admin_role = get_role('administrator');
         if ($admin_role) {
            $admin_role->add_cap("manage_{$role_name}s", true);
@@ -254,7 +267,7 @@
      }
   }
   private function buildRoleCapabilities(string $slug, array $config): array
   private function buildRoleCapabilities(string $slug, Registrar $registrar): array
   {
      //Everyone can see the things
      $capabilities = [
@@ -262,24 +275,32 @@
      ];
      // Dashboard access
      if ($this->config['has_dashboard'] ?? false) {
      if ($registrar->hasFeature('has_dashboard') ?? false) {
         $capabilities['access_dashboard'] = true;
      }
      if (Features::forSite()->has('favourites') && $config['can_favourite'] ?? true) {
      if (Features::forSite()->has('favourites') && $registrar->hasFeature('can_favourite') ?? true) {
         $capabilities['can_favourite'] = true;
      }
      // Content creation capabilities
      if (!empty($config['can_create'])) {
         foreach ($config['can_create'] as $content_type) {
            $this->addContentCapabilities($capabilities, $content_type, $config);
      if (!empty($registrar->getCreatable())) {
         $content = [];
         foreach ($registrar->getCreatable() as $content_type) {
            if (is_array($content_type)) {
               $content = array_merge($content, $content_type);
            }else {
               $content[] = $content_type;
            }
         }
         foreach  ($content as $c) {
            $this->addContentCapabilities($capabilities, $c, $registrar);
         }
      }
      // Management capabilities
      if (!empty($this->config['manage_others'])) {
         foreach ($this->config['manage_others'] as $type) {
      if (!empty($registrar->getManageOthers())) {
         foreach ($registrar->getManageOthers() as $type) {
            // Skip if content type doesn't exist
            if (!$this->isValidContentType($type)) {
               error_log("Warning: User role '{$slug}' references non-existent content type '{$type}'");
@@ -298,44 +319,30 @@
   /**
    * Add content capabilities to capability array
    */
   private function addContentCapabilities(array &$capabilities, $content_type, array $config): void
   private function addContentCapabilities(array &$capabilities, $content_type, Registrar $registrar): void
   {
      if (is_array($content_type)) {
         // Handle array format for type-specific permissions
         foreach ($content_type as $sub_type => $types) {
            foreach ($types as $type) {
               if (!$this->isValidContentType($type)) {
                  error_log("Warning: Role references non-existent content type '{$type}'");
                  continue;
               }
               $this->addSingleContentCapabilities($capabilities, $type, $config);
            }
         }
      } else {
         if (!$this->isValidContentType($content_type)) {
            error_log("Warning: Role references non-existent content type '{$content_type}'");
            return;
         }
         $this->addSingleContentCapabilities($capabilities, $content_type, $config);
      if (!$this->isValidContentType($content_type)) {
         error_log("Warning: Role references non-existent content type '{$content_type}'");
         return;
      }
      $this->addSingleContentCapabilities($capabilities, $content_type, $registrar);
   }
   /**
    * Add capabilities for a single content type
    */
   private function addSingleContentCapabilities(array &$capabilities, string $type, array $config): void
   private function addSingleContentCapabilities(array &$capabilities, string $type, Registrar $registrar): void
   {
      $caps = $this->getCapabilities($type);
      foreach ($caps as $cap) {
         $capabilities[$cap] = true;
      }
      if (array_key_exists('approve_new', $config)) {
      if ($registrar->hasFeature('approve_new')) {
         $plural = $this->getContentPlural($type);
         // Publish capability depends on approval setting
         $capabilities["publish_{$plural}"] = !($config['approve_new'] ?? false);
      }
   }
   public function grantRoleCapabilities(string $role_name, string $content_slug, bool $grant = true): void
@@ -393,7 +400,6 @@
         "edit_others_{$plural}",
         "publish_{$plural}",
         "read_private_{$plural}",
         "edit_{$plural}",
      ];
   }
   protected function getOthersCapabilities(string $content):array
@@ -420,22 +426,17 @@
   public function getContentPlural(string $content): string
   {
      $content = jvbNoBase($content);
      $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];
      $registrar = Registrar::getInstance($content);
      if ($registrar && $registrar->getPlural()) {
         return str_replace(' ', '_', $registrar->getPlural());
      }
      return str_replace('-', '_', sanitize_title(strtolower($content . 's')));
      return str_replace(' ', '_', $content.'s');
   }
   public function activate(): void
   {
      foreach (JVB_USER as $slug => $config) {
         $this->registerRole($slug, $config);
      foreach (Registrar::getRegistered('user') as $role) {
         $this->registerRole($role);
      }
   }
@@ -458,9 +459,10 @@
      }
      $taxonomy = jvbNoBase($taxonomy);
      $registrar = Registrar::getInstance($taxonomy);
      // Verify this is an ownable content taxonomy
      if (!Features::forTaxonomy($taxonomy)->has('is_content') ||
         !Features::forTaxonomy($taxonomy)->has('is_ownable')) {
      if (!$registrar || !$registrar->hasFeature('is_content') ||
            !$registrar->hasFeature('is_ownable')) {
         return false;
      }
@@ -521,9 +523,10 @@
      }
      $taxonomy = jvbNoBase($taxonomy);
      $registrar = Registrar::getInstance($taxonomy);
      // Verify this is an ownable content taxonomy
      if (!Features::forTaxonomy($taxonomy)->has('is_content') ||
         !Features::forTaxonomy($taxonomy)->has('is_ownable')) {
      if (!$registrar || !$registrar->hasFeature('is_content') ||
         !$registrar->hasFeature('is_ownable')) {
         return false;
      }
@@ -682,13 +685,9 @@
      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;
            }
         }
         $ownable = array_map(function ($instance) {
            return $instance->slug;
         }, Registrar::getFeatured('is_ownable', 'term'));
      }
      return $ownable;
@@ -704,14 +703,24 @@
      static $invitable = null;
      if ($invitable === null) {
         $invitable = [];
         foreach (JVB_TAXONOMY as $taxonomy => $config) {
            if (Features::forTaxonomy($taxonomy)->has('invitable')) {
               $invitable[] = $taxonomy;
            }
         }
         $invitable = array_map(function ($instance) {
            return $instance->slug;
         }, Registrar::getFeatured('invitable', 'term'));
      }
      return $invitable;
   }
   public function getPermission(string $action, string $content, ?int $ID = null):?string
   {
      $plural = $this->getContentPlural($content);
      switch ($action) {
         case 'edit':
            if ($ID) {
               return "edit_{$content}";
            }
            return "edit_{$plural}";
      }
      return null;
   }
}