From ed57c386db34d8693ca75311972d0929ebe5f488 Mon Sep 17 00:00:00 2001
From: Jake Vanderwerf <get@jakevanderwerf.ca>
Date: Mon, 01 Jun 2026 22:23:19 +0000
Subject: [PATCH] =Added some more Schema classes, allowed for override of array in outputSchema for complex schema, as for timeline post types
---
inc/managers/RoleManager.php | 622 +++++++++++++++++++++++++++++++++++++++++++++++++------
1 files changed, 546 insertions(+), 76 deletions(-)
diff --git a/inc/managers/RoleManager.php b/inc/managers/RoleManager.php
index 09029f7..b275f12 100644
--- a/inc/managers/RoleManager.php
+++ b/inc/managers/RoleManager.php
@@ -1,7 +1,8 @@
<?php
namespace JVBase\managers;
-use JVBase\utility\Features;
+use JVBase\registrar\Registrar;
+use JVBase\base\Site;
use WP_User;
use WP_Role;
@@ -12,23 +13,45 @@
{
private array $roles;
private array $content;
+ private array $subTypes = [];
public function __construct()
{
- $this->roles = array_keys(JVB_USER);
+ $this->roles = 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);
+
+ $this->checkRoleSubTypes();
}
+ public function checkRoleSubTypes():void
+ {
+ foreach ($this->roles as $role) {
+ $registrar = Registrar::getInstance($role);
+ if ($registrar->getUserSubtype()){
+ $this->subTypes[jvbCheckBase($registrar->getUserSubtype())] = $registrar->getCreatable();
+ }
+ }
+ if (!empty($this->subTypes)) {
+ add_action('set_object_terms', [$this, 'maybeSwitchPermissions'],10, 6);
+ }
+ }
+
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)) {
+ $registrar = Registrar::getInstance($temp);
+ if ($registrar) {
$user = get_userdata($userID);
if (!$user) {
return;
@@ -56,17 +79,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;
@@ -90,12 +109,11 @@
$roles = array_keys($user->roles);
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 +203,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;
@@ -210,7 +232,7 @@
if (!in_array($type, $this->roles)) {
return false;
}
- $link = get_user_meta($user->ID, BASE.'link', true);
+ $link = get_user_meta($user->ID, BASE.'profile_link', true);
if ($link === '') {
$type = BASE.$type;
$name = $user->display_name;
@@ -223,20 +245,24 @@
'post_author' => $user->ID,
]);
if ($link) {
- update_user_meta($user->ID, BASE.'link', $link);
- update_post_meta($link, BASE.'link', $user->ID);
+ update_user_meta($user->ID, BASE.'profile_link', $link);
+ update_post_meta($link, BASE.'profile_link', $user->ID);
}
}
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,16 +271,16 @@
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);
- $admin_role->add_cap("edit_{$role_name}_settings", true);
+ $admin_role->add_cap("manage_{$role_name}s");
+ $admin_role->add_cap("edit_{$role_name}_settings");
}
}
}
- private function buildRoleCapabilities(string $slug, array $config): array
+ private function buildRoleCapabilities(string $slug, Registrar $registrar): array
{
//Everyone can see the things
$capabilities = [
@@ -262,24 +288,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 (Site::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 +332,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
@@ -387,13 +407,15 @@
return [
"edit_{$content}",
+ "edit_published_{$content}",
"read_{$content}",
"delete_{$content}",
+ "delete_published_{$content}",
"edit_{$plural}",
- "edit_others_{$plural}",
"publish_{$plural}",
"read_private_{$plural}",
- "edit_{$plural}",
+ "edit_private_{$plural}",
+ "delete_private_{$plural}",
];
}
protected function getOthersCapabilities(string $content):array
@@ -406,9 +428,6 @@
return [
"edit_others_{$plural}",
"delete_others_{$plural}",
- "read_private_{$plural}",
- "edit_private_{$plural}",
- "delete_private_{$plural}",
];
}
@@ -420,22 +439,473 @@
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
+ public static function activate(): void
{
- foreach (JVB_USER as $slug => $config) {
- $this->registerRole($slug, $config);
+ error_log('[RoleManager]::activate');
+ error_log('Registering roles...');
+ $instance = new self;
+ foreach (Registrar::getRegistered('user') as $role) {
+ $instance->registerRole($role);
}
+ error_log('Roles registered!');
+ error_log('Removing unneeded roles...');
+ remove_role('contributor');
+ remove_role('author');
+ remove_role('editor');
+ error_log('Roles removed!');
+
+ error_log('Adding Admin Capabilities');
+ $instance->addAdminCaps();
+ error_log('Ensuring Existing User\'s Roles...');
+ $instance->ensureRoleCaps();
+ error_log('Roles activated!');
+ }
+ protected function addAdminCaps():void
+ {
+ $users = get_users(['role' => 'administrator']);
+ foreach (array_merge(Registrar::getRegistered('post'), Registrar::getFeatured('is_content')) as $slug) {
+ $this->grantRoleCapabilities('administrator', $slug);
+ $this->grantRoleOthersCapabilities('administrator', $slug);
+
+ foreach ($users as $user) {
+ $this->grantContent($user, $slug);
+ $this->grantOthersContent($user, $slug);
+ }
+ }
+ }
+ protected function ensureRoleCaps():void
+ {
+ $roles = Registrar::getRegistered('user');
+ foreach ($roles as $role) {
+ $registrar = Registrar::getInstance($role);
+ $creatable = $registrar->getCreatable();
+ $manageable = $registrar->getManageOthers();
+ if (!empty($creatable) || !empty($manageable)) {
+ $users = get_users(['role' => jvbCheckBase($role)]);
+ foreach ($users as $user) {
+ foreach ($creatable as $slug) {
+ $this->grantContent($user, $slug);
+ }
+ foreach ($manageable as $slug) {
+ $this->grantOthersContent($user, $slug);
+ }
+ }
+ }
+ }
+ }
+
+ /******************************************************************
+ * 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);
+
+ $registrar = Registrar::getInstance($taxonomy);
+ // Verify this is an ownable content taxonomy
+ if (!$registrar || !$registrar->hasFeature('is_content') ||
+ !$registrar->hasFeature('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);
+
+ $owners = get_term_meta($termID, BASE.'owners', true);
+ if (empty($owners)) {
+ $owners = [];
+ }
+ $owners[] = $userID;
+ $owners = array_unique($owners);
+ update_term_meta($termID, BASE.'owners', $owners);
+
+
+ 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;
+ }
+
+ $owners = get_term_meta($termID, BASE.'owners', true);
+ if (empty($owners)) {
+ $owners = [];
+ }
+ if (in_array($userID, $owners)) {
+ unset($owners[array_search($userID, $owners)]);
+ }
+ $owners = array_unique($owners);
+ update_term_meta($termID, BASE.'owners', $owners);
+
+ $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);
+
+ $registrar = Registrar::getInstance($taxonomy);
+ // Verify this is an ownable content taxonomy
+ if (!$registrar || !$registrar->hasFeature('is_content') ||
+ !$registrar->hasFeature('is_ownable')) {
+ return false;
+ }
+
+ $user = get_userdata($userID);
+ if (!$user) {
+ return false;
+ }
+
+ $user->add_cap(BASE . 'can_manage_' . $termID);
+
+ $managers = get_term_meta($termID, BASE.'managers', true);
+ if (empty($managers)) {
+ $managers = [];
+ }
+ $managers[] = $userID;
+ $managers = array_unique($managers);
+ update_term_meta($termID, BASE.'managers', $managers);
+
+ 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);
+
+ $managers = get_term_meta($termID, BASE.'managers', true);
+ if (empty($managers)) {
+ $managers = [];
+ }
+ if (in_array($userID, $managers)) {
+ unset($managers[array_search($userID, $managers)]);
+ }
+ $managers = array_unique($managers);
+ update_term_meta($termID, BASE.'managers', $managers);
+
+ 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 = array_map(function ($instance) {
+ return $instance->slug;
+ }, Registrar::getFeatured('is_ownable', 'term'));
+ }
+
+ return $ownable;
+ }
+
+ /**
+ * Get all invitable taxonomies
+ *
+ * @return array Array of taxonomy slugs
+ */
+ public function getInvitableTaxonomies(): array
+ {
+ static $invitable = null;
+
+ if ($invitable === null) {
+ $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;
+ }
+
+ public function maybeSwitchPermissions(int $object_id, array $terms, array $tt_ids, string $taxonomy, bool $append, array $old_tt_ids):void
+ {
+ //This shouldn't happen, but whatever
+ if (empty($this->subTypes)) {
+ return;
+ }
+
+ if (!in_array($taxonomy, array_keys($this->subTypes))) {
+ return;
+ }
+
+ $new = array_diff($tt_ids, $old_tt_ids);
+ $old = array_diff($old_tt_ids, $tt_ids);
+
+ $userID = (int)get_post_meta($object_id, BASE.'profile_link',true);
+ if ($userID === 0) {
+ return;
+ }
+ $user = get_userdata($userID);
+ if (!$user) {
+ return;
+ }
+
+ //Revoke old first
+ if (!empty($old)) {
+ $old = array_filter(array_map(function ($id) use ($taxonomy) {
+ $termID = $this->getTermIDFromTTID($id);
+ return $this->getRootTermSlug($termID, $taxonomy);
+ }, $old));
+ foreach ($old as $slug) {
+ if (!array_key_exists($slug, $this->subTypes[$taxonomy])) {
+ error_log('[RoleManager]::maybeSwitchPermissions Could not find creatable types for role subtype '.$slug);
+ continue;
+ }
+ foreach ($this->subTypes[$taxonomy][$slug] ?? [] as $s) {
+ $this->grantContent($user, $s, false);
+ }
+ }
+ }
+
+ if (!empty($new)) {
+ $new = array_filter(array_map(function ($id) use ($taxonomy){
+ $termID = $this->getTermIDFromTTID($id);
+ return $this->getRootTermSlug($termID, $taxonomy);
+ }, $new));
+ foreach ($new as $slug) {
+ if (!array_key_exists($slug, $this->subTypes[$taxonomy])) {
+ error_log('[RoleManager]::maybeSwitchPermissions Could not find creatable types for role subtype '.$slug);
+ continue;
+ }
+ foreach ($this->subTypes[$taxonomy][$slug] ?? [] as $s) {
+ $this->grantContent($user, $s);
+ }
+ }
+ }
+ }
+
+ /**
+ * Helper function to get term_id from term_taxonomy_id
+ * @param int $tt_id
+ *
+ * @return int
+ */
+ private function getTermIDFromTTID(int $tt_id):int
+ {
+ global $wpdb;
+ return $wpdb->get_var($wpdb->prepare(
+ "SELECT term_id FROM {$wpdb->term_taxonomy} WHERE term_taxonomy_id = %d",
+ $tt_id
+ ));
+ }
+
+ private function getRootTermSlug(int $termID, string $taxonomy): string|false
+ {
+ $term = get_term($termID, $taxonomy);
+ if (!$term || is_wp_error($term)) {
+ return false;
+ }
+ while ($term->parent !== 0) {
+ $term = get_term($term->parent, $taxonomy);
+ if (!$term || is_wp_error($term)) {
+ return false;
+ }
+ }
+ return $term->slug;
}
}
--
Gitblit v1.10.0