<?php
|
namespace JVBase\managers\queue\executors;
|
|
use JVBase\managers\CustomTable;
|
use JVBase\managers\queue\Executor;
|
use JVBase\managers\queue\Operation;
|
use JVBase\managers\queue\Progress;
|
use JVBase\managers\queue\Result;
|
use JVBase\managers\RoleManager;
|
use JVBase\meta\Meta;
|
use JVBase\utility\Features;
|
use Exception;
|
|
if (!defined('ABSPATH')) {
|
exit;
|
}
|
|
/**
|
* Executor for content taxonomy operations (shops, studios, etc.)
|
*
|
* Handles:
|
* - Term metadata updates
|
* - Member additions (via history table)
|
* - Member removals (via history table)
|
*/
|
class ContentTermExecutor implements Executor
|
{
|
protected RoleManager $roleManager;
|
|
public function __construct()
|
{
|
$this->roleManager = new RoleManager();
|
}
|
|
public function execute(Operation $operation, Progress $progress): Result
|
{
|
// Extract taxonomy from operation type (e.g., "shop_update" -> "shop")
|
$parts = explode('_', $operation->type);
|
$taxonomy = $parts[0] ?? '';
|
|
if (!$taxonomy || !isset(JVB_TAXONOMY[$taxonomy])) {
|
return Result::fail("Invalid taxonomy: {$taxonomy}");
|
}
|
|
return match(true) {
|
str_ends_with($operation->type, '_update') => $this->processUpdate($operation, $taxonomy),
|
str_ends_with($operation->type, '_member_add') => $this->processMemberAdd($operation, $taxonomy),
|
str_ends_with($operation->type, '_member_remove') => $this->processMemberRemove($operation, $taxonomy),
|
default => Result::fail("Unknown operation type: {$operation->type}")
|
};
|
}
|
|
/**
|
* Process term metadata updates
|
*/
|
protected function processUpdate(Operation $operation, string $taxonomy): Result
|
{
|
$termID = $operation->requestData['term_id'] ?? 0;
|
$userID = $operation->userId;
|
|
if (!$termID || !term_exists($termID, jvbCheckBase($taxonomy))) {
|
return Result::fail('Invalid term ID');
|
}
|
|
// Verify permissions using RoleManager
|
if (!user_can($userID, 'manage_options') && !$this->roleManager->isManager($userID, $termID)) {
|
return Result::fail('User does not have permission to manage this ' . $taxonomy);
|
}
|
|
try {
|
$meta = Meta::forTerm($termID);
|
$data = $operation->requestData;
|
unset($data['term_id']);
|
|
// Filter to only allowed fields
|
$allowed = jvbGetFields($taxonomy, 'term');
|
$setData = array_filter(
|
$data,
|
fn($key) => array_key_exists($key, $allowed),
|
ARRAY_FILTER_USE_KEY
|
);
|
|
if (empty($setData)) {
|
return Result::fail('No valid fields to update');
|
}
|
|
// Update metadata
|
$results = $meta->setAll($setData);
|
|
if ($results) {
|
// Clear cache
|
JVB()->cache()->for($taxonomy)->delete("term_{$termID}_meta");
|
|
// Trigger any post-update actions (e.g., thumbnail generation)
|
do_action(BASE . "{$taxonomy}_updated", $termID, $userID, $setData);
|
|
return Result::success([
|
'updated_fields' => array_keys($setData),
|
'term_id' => $termID
|
]);
|
}
|
|
return Result::fail('Failed to update term metadata');
|
|
} catch (Exception $e) {
|
return Result::fail('Update error: ' . $e->getMessage());
|
}
|
}
|
|
/**
|
* Add member to term (via history table)
|
*/
|
protected function processMemberAdd(Operation $operation, string $taxonomy): Result
|
{
|
$termID = $operation->requestData['term_id'] ?? 0;
|
$targetUserID = $operation->requestData['target_user'] ?? 0;
|
$userID = $operation->userId;
|
|
if (!$termID || !term_exists($termID, jvbCheckBase($taxonomy))) {
|
return Result::fail('Invalid term ID');
|
}
|
|
if (!get_userdata($targetUserID)) {
|
return Result::fail('Invalid target user');
|
}
|
|
// Verify permissions
|
if (!user_can($userID, 'manage_options') && !$this->roleManager->isManager($userID, $termID)) {
|
return Result::fail('User does not have permission to manage this ' . $taxonomy);
|
}
|
|
// Check if tracking enabled
|
if (!Features::forTaxonomy($taxonomy)->has('track_changes')) {
|
return Result::fail('Member tracking not enabled for ' . $taxonomy);
|
}
|
|
try {
|
return $this->addMember($targetUserID, $termID, $taxonomy);
|
} catch (Exception $e) {
|
return Result::fail('Add member error: ' . $e->getMessage());
|
}
|
}
|
|
/**
|
* Remove member from term (via history table)
|
*/
|
protected function processMemberRemove(Operation $operation, string $taxonomy): Result
|
{
|
$termID = $operation->requestData['term_id'] ?? 0;
|
$targetUserID = $operation->requestData['target_user'] ?? 0;
|
$userID = $operation->userId;
|
|
if (!$termID || !term_exists($termID, jvbCheckBase($taxonomy))) {
|
return Result::fail('Invalid term ID');
|
}
|
|
if (!get_userdata($targetUserID)) {
|
return Result::fail('Invalid target user');
|
}
|
|
// Verify permissions
|
if (!user_can($userID, 'manage_options') && !$this->roleManager->isManager($userID, $termID)) {
|
return Result::fail('User does not have permission to manage this ' . $taxonomy);
|
}
|
|
try {
|
return $this->removeMember($targetUserID, $termID, $taxonomy);
|
} catch (Exception $e) {
|
return Result::fail('Remove member error: ' . $e->getMessage());
|
}
|
}
|
|
/**
|
* Add member to term with transaction support
|
*/
|
protected function addMember(int $userID, int $termID, string $taxonomy): Result
|
{
|
$config = JVB_TAXONOMY[$taxonomy] ?? [];
|
$content = $config['for_content'] ?? [];
|
|
if (empty($content)) {
|
return Result::fail('No content types configured for ' . $taxonomy);
|
}
|
|
// Get table name (e.g., "history_artist_shop")
|
$contentType = $content[0]; // Use first content type
|
$tableName = "history_{$contentType}_{$taxonomy}";
|
$table = CustomTable::for($tableName);
|
|
return $table->transaction(function($table) use ($userID, $termID, $taxonomy, $contentType) {
|
// Check if already a member
|
$existing = $table
|
->where([
|
'user_id' => $userID,
|
'term_id' => $termID,
|
'end_date' => null
|
])
|
->first();
|
|
if ($existing) {
|
return Result::success([
|
'message' => 'User is already a member',
|
'existing' => true
|
]);
|
}
|
|
// Get user's content post ID
|
$contentID = get_user_meta($userID, BASE . 'link', true);
|
if (!$contentID) {
|
throw new Exception('User profile not found');
|
}
|
|
// Verify content post exists
|
$post = get_post($contentID);
|
if (!$post || $post->post_type !== BASE . $contentType) {
|
throw new Exception('Content post not found or invalid type');
|
}
|
|
// Insert new membership
|
$result = $table->create([
|
'user_id' => $userID,
|
'content_id' => $contentID,
|
'term_id' => $termID,
|
'role' => 'member',
|
'is_primary' => 1,
|
'start_date' => current_time('mysql')
|
]);
|
|
if (!$result) {
|
throw new Exception('Failed to create membership record');
|
}
|
|
// Add taxonomy term to content post
|
$termResult = wp_set_object_terms($contentID, [$termID], jvbCheckBase($taxonomy), true);
|
|
if (is_wp_error($termResult)) {
|
throw new Exception('Failed to set taxonomy term: ' . $termResult->get_error_message());
|
}
|
|
// Clear cache
|
JVB()->cache()->for($taxonomy)->delete("{$taxonomy}_{$termID}_members");
|
|
// Notify term managers
|
$this->notifyTermManagers($termID, $userID, $taxonomy, 'member_added');
|
|
return Result::success([
|
'message' => 'Member added successfully',
|
'user_id' => $userID,
|
'term_id' => $termID
|
]);
|
});
|
}
|
|
/**
|
* Remove member from term with transaction support
|
*/
|
protected function removeMember(int $userID, int $termID, string $taxonomy): Result
|
{
|
$config = JVB_TAXONOMY[$taxonomy] ?? [];
|
$content = $config['for_content'] ?? [];
|
|
if (empty($content)) {
|
return Result::fail('No content types configured for ' . $taxonomy);
|
}
|
|
// Get table name
|
$contentType = $content[0];
|
$tableName = "history_{$contentType}_{$taxonomy}";
|
$table = CustomTable::for($tableName);
|
|
return $table->transaction(function($table) use ($userID, $termID, $taxonomy, $contentType) {
|
// Get user's content post ID
|
$contentID = get_user_meta($userID, BASE . 'link', true);
|
if (!$contentID) {
|
throw new Exception('User profile not found');
|
}
|
|
// Update membership record - set end_date
|
$updated = $table
|
->where([
|
'user_id' => $userID,
|
'term_id' => $termID,
|
'end_date' => null
|
])
|
->updateResults([
|
'end_date' => current_time('mysql'),
|
'is_primary' => 0
|
]);
|
|
if (!$updated) {
|
throw new Exception('No active membership found');
|
}
|
|
// Remove taxonomy term from content post
|
$termResult = wp_remove_object_terms($contentID, [$termID], jvbCheckBase($taxonomy));
|
|
if (is_wp_error($termResult)) {
|
throw new Exception('Failed to remove taxonomy term: ' . $termResult->get_error_message());
|
}
|
|
// Clear cache
|
JVB()->cache()->for($taxonomy)->delete("{$taxonomy}_{$termID}_members");
|
|
// Notify term managers
|
$this->notifyTermManagers($termID, $userID, $taxonomy, 'member_removed');
|
|
return Result::success([
|
'message' => 'Member removed successfully',
|
'user_id' => $userID,
|
'term_id' => $termID
|
]);
|
});
|
}
|
|
/**
|
* Notify term owners/managers about member changes
|
*/
|
protected function notifyTermManagers(int $termID, int $userID, string $taxonomy, string $notificationType): void
|
{
|
$managers = $this->roleManager->getManagedTerms($userID, $taxonomy);
|
$term = get_term($termID, jvbCheckBase($taxonomy));
|
$user = get_userdata($userID);
|
|
foreach ($managers as $managerID) {
|
JVB()->notification()->addNotification(
|
$managerID,
|
$notificationType,
|
[
|
'user_id' => $userID,
|
'user_name' => $user ? $user->display_name : 'Unknown',
|
'term_id' => $termID,
|
'term_name' => $term && !is_wp_error($term) ? $term->name : 'Unknown',
|
'taxonomy' => $taxonomy
|
]
|
);
|
}
|
}
|
}
|