From 3d25ad7de4fdbb3eba2cde2a62422e6be5d79a0a Mon Sep 17 00:00:00 2001
From: Jake Vanderwerf <get@jakevanderwerf.ca>
Date: Tue, 25 Aug 2026 22:57:22 +0000
Subject: [PATCH] =It's been a minute. I went through the Routes and associated Managers and ensured they played together well. Reworked the Cache.php, and I think I caught all the references. Moving away from JVBase() root class, to individuall classes that are Instanced. Double checked for the ABSPATH check in every file, and added index.php to every directory that didn't already have it. Also still working on the MenuBlock to work with WooCommerce for now for Northeh

---
 inc/rest/routes/ResponseRoutes.php |  402 ++++++---------------------------------------------------
 1 files changed, 43 insertions(+), 359 deletions(-)

diff --git a/inc/rest/routes/ResponseRoutes.php b/inc/rest/routes/ResponseRoutes.php
index e84c254..0e8589d 100644
--- a/inc/rest/routes/ResponseRoutes.php
+++ b/inc/rest/routes/ResponseRoutes.php
@@ -1,7 +1,12 @@
 <?php
 namespace JVBase\rest\routes;
 
+use JVBase\Instance;
 use JVBase\JVB;
+use JVBase\managers\Cache;
+use JVBase\managers\NotificationManager;
+use JVBase\managers\Notifications\Notifications;
+use JVBase\managers\Responses;
 use JVBase\rest\Rest;
 use JVBase\managers\CustomTable;
 use WP_REST_Request;
@@ -19,6 +24,7 @@
  */
 class ResponseRoutes extends Rest
 {
+	use Instance;
 	protected int $perPage = 20;
 	protected CustomTable $table;
 	protected CustomTable $karmaTable;
@@ -28,12 +34,8 @@
 		$this->cacheName = 'responses';
 		$this->cacheTtl = 1800; // 30 minutes
 		parent::__construct();
+		$this->cache = Cache::for($this->cacheName, 'response', $this->cacheTtl);
 
-		$this->table = CustomTable::for('responses');
-		$this->karmaTable = CustomTable::for('karma_response');
-
-		add_filter(BASE.'handle_bulk_operation', [$this, 'processOperation'], 10, 3);
-		add_action('deleted_user', [$this, 'handleUserDeletion'], 10, 1);
 	}
 
 	public function registerRoutes(): void
@@ -57,38 +59,15 @@
 	 */
 	public function getResponses(WP_REST_Request $request): WP_REST_Response
 	{
-		$item_id = (int) $request->get_param('item_id');
-
-		if (!$item_id) {
+		$params = $request->get_params();
+		if (!array_key_exists('item_id', $params)) {
 			return $this->error('Missing item ID', 'missing_item_id');
 		}
+		$item_id = absint($params['item_id']);
 
-		// Build query args
 		$args = $this->buildQueryArgs($request);
-		$cacheKey = $this->cache->generateKey(array_merge(['item_id' => $item_id], $args));
 
-		// Check headers for 304 Not Modified
-		$headerCheck = $this->checkHeaders($request, $cacheKey);
-		if ($headerCheck) {
-			return $headerCheck;
-		}
-
-		// Check cache
-		$cached = $this->cache->get($cacheKey);
-		if ($cached) {
-			return $this->success($cached);
-		}
-
-		// Get responses
-		$responses = $this->getItemResponse($item_id, $args);
-
-		if (is_wp_error($responses)) {
-			return $this->notFound($responses->get_error_message());
-		}
-
-		// Cache and return
-		$this->cache->set($cacheKey, $responses);
-		return $this->success($responses);
+		return $this->success(Responses::getInstance()->getAllItemResponses($item_id, $args));
 	}
 
 	/**
@@ -103,7 +82,6 @@
 			return $this->unauthorized();
 		}
 
-		$operation_id = $data['id'] ?? uniqid('response_');
 		$action = sanitize_text_field($data['action'] ?? '');
 
 		if (!in_array($action, ['create', 'delete', 'update'])) {
@@ -116,39 +94,26 @@
 				return $this->error('Missing required fields', 'missing_fields');
 			}
 		}
-
-		// Prepare data for queue
-		$queue_data = match($action) {
-			'create' => [
-				'item_id' => (int) $data['item_id'],
-				'parent_id' => isset($data['parent_id']) ? (int) $data['parent_id'] : null,
-				'response' => wp_kses_post($data['response']),
-				'content' => sanitize_text_field($data['content'])
-			],
-			'update' => [
-				'response_id' => (int) $data['response_id'],
-				'response' => isset($data['response']) ? wp_kses_post($data['response']) : null,
-				'status' => isset($data['status']) && in_array($data['status'], ['published', 'hidden', 'flagged'])
-					? $data['status']
-					: null
-			],
-			'delete' => [
-				'response_id' => (int) $data['response_id']
-			]
+		$responses = Responses::getInstance();
+		$response = match($action) {
+			'create'	=> $responses->respondTo(
+				absint($data['item_id']),
+				sanitize_text_field($data['content']),
+				$user_id,
+				wp_kses_post($data['response']),
+				isset($data['parent_id']) ? absint($data['parent_id']) : null
+			),
+			'update'	=> $responses->editResponse(
+				absint($data['response_id']),
+				wp_kses_post($data['response']??'')
+			),
+			'delete'	=> $responses->deleteResponse(absint($data['response_id']))
 		};
 
-		// Queue the operation
-		JVB()->queue()->queueOperation(
-			$action . '_response',
-			$user_id,
-			$queue_data,
-			[
-				'operation_id' => 'u' . $user_id . '_' . $operation_id,
-				'priority' => 'high'
-			]
-		);
-
-		return $this->queued($operation_id);
+		if ($response['success']) {
+			return $this->success($response);
+		}
+		return $this->error($response['message']);
 	}
 
 	/**
@@ -156,244 +121,11 @@
 	 */
 	protected function getItemResponse(int $item_id, array $args = []): array|WP_Error
 	{
-		$defaults = [
-			'page' => 1,
-			'per_page' => $this->perPage,
-			'orderby' => 'created_at',
-			'order' => 'DESC',
-			'parent_id' => null
-		];
-
-		$args = wp_parse_args($args, $defaults);
-
-		// Verify post exists
-		$post = get_post($item_id);
-		if (!$post) {
-			return new WP_Error('not_found', 'Item not found');
-		}
-
-		// Build query with karma calculations
-		$query = "
-            SELECT r.*,
-                COALESCE((SELECT COUNT(*) FROM {$this->table->getFullTableName()} WHERE parent_id = r.id), 0) as reply_count,
-                COALESCE((SELECT COUNT(*) FROM {$this->karmaTable->getFullTableName()} WHERE item_id = r.id AND vote = 'up'), 0) as upvotes,
-                COALESCE((SELECT COUNT(*) FROM {$this->karmaTable->getFullTableName()} WHERE item_id = r.id AND vote = 'down'), 0) as downvotes
-            FROM {$this->table->getFullTableName()} r
-            WHERE r.item_id = %d
-        ";
-
-		$query_args = [$item_id];
-
-		// Filter by parent_id
-		if (isset($args['parent_id'])) {
-			$query .= " AND r.parent_id " . ($args['parent_id'] === null ? "IS NULL" : "= %d");
-			if ($args['parent_id'] !== null) {
-				$query_args[] = $args['parent_id'];
-			}
-		}
-
-		// Get total count for pagination
-		$count_query = str_replace(
-			['SELECT r.*,', 'COALESCE((SELECT COUNT(*) FROM', 'as reply_count,', 'as upvotes,', 'as downvotes'],
-			['SELECT COUNT(*)', '', '', '', ''],
-			$query
-		);
-		$count_query = preg_replace('/COALESCE\([^)]+\)[^,]*,?/', '', $count_query);
-		$total_items = (int) $this->table->queryVar($count_query, $query_args);
-
-		// Add ordering
-		$order_column = in_array($args['orderby'], ['created_at', 'updated_at'])
-			? "r.{$args['orderby']}"
-			: $args['orderby'];
-
-		$query .= " ORDER BY {$order_column} {$args['order']}";
-
-		// Add pagination
-		$query .= " LIMIT %d OFFSET %d";
-		$query_args[] = $args['per_page'];
-		$query_args[] = ($args['page'] - 1) * $args['per_page'];
-
-		// Get responses
-		$responses = $this->table->queryResults($query, $query_args);
-
+		$responses = Responses::getInstance()->getAllItemResponses($item_id, $args);
 		// Format responses
-		$items = array_map([$this, 'formatItem'], $responses);
+		$responses['items'] = array_map([$this, 'formatItem'], $responses);
 
-		return [
-			'items' => $items,
-			'has_more' => $args['page'] < ceil($total_items / $args['per_page']),
-			'total_items' => $total_items,
-			'total_pages' => (int) ceil($total_items / $args['per_page'])
-		];
-	}
-
-	/**
-	 * Process queue operations
-	 */
-	public function processOperation(WP_Error|array $result, object $operation, array $data): WP_Error|array
-	{
-		if (!in_array($operation->type, ['create_response', 'update_response', 'delete_response'])) {
-			return $result;
-		}
-
-		return match($operation->type) {
-			'create_response' => $this->createResponse($operation, $data),
-			'update_response' => $this->updateResponse($data),
-			'delete_response' => $this->deleteResponse($data),
-			default => $result
-		};
-	}
-
-	/**
-	 * Create a new response
-	 */
-	protected function createResponse(object $operation, array $data): array
-	{
-		$response_id = $this->table->insert([
-			'item_id' => $data['item_id'],
-			'content' => $data['content'],
-			'user_id' => $operation->user_id,
-			'parent_id' => $data['parent_id'],
-			'response' => $data['response'],
-			'status' => 'published'
-		]);
-
-		if (!$response_id) {
-			$this->logError('Failed to insert response', [
-				'data' => $data,
-				'error' => $this->table->getLastError()
-			]);
-
-			return ['success' => false, 'result' => 'Failed to create response'];
-		}
-
-		// Send notifications
-		$this->sendNotifications($data['item_id'], $data['parent_id'], $response_id, $operation->user_id);
-
-		// Clear cache
-		$this->cache->flush();
-
-		return ['success' => true, 'result' => $response_id];
-	}
-
-	/**
-	 * Update a response
-	 */
-	protected function updateResponse(array $data): array
-	{
-		$update_data = [];
-
-		if (isset($data['response'])) {
-			$update_data['response'] = $data['response'];
-			$update_data['updated_at'] = current_time('mysql');
-		}
-
-		if (isset($data['status'])) {
-			$update_data['status'] = $data['status'];
-		}
-
-		if (empty($update_data)) {
-			return ['success' => false, 'result' => 'No fields to update'];
-		}
-
-		$updated = $this->table->update(
-			$update_data,
-			['id' => $data['response_id']]
-		);
-
-		if ($updated === false) {
-			$this->logError('Failed to update response', [
-				'data' => $data,
-				'error' => $this->table->getLastError()
-			]);
-
-			return ['success' => false, 'result' => 'Failed to update response'];
-		}
-
-		$this->cache->flush();
-		return ['success' => true, 'result' => $updated];
-	}
-
-	/**
-	 * Delete a response (or mark as deleted if it has replies)
-	 */
-	protected function deleteResponse(array $data): array
-	{
-		$response = $this->table->get(['id' => $data['response_id']]);
-
-		if (!$response) {
-			return ['success' => false, 'result' => 'Response not found'];
-		}
-
-		// Check if it has replies
-		$has_replies = $this->table->where(['parent_id' => $data['response_id']])->countResults() > 0;
-
-		if ($has_replies) {
-			// Don't delete, just mark as deleted
-			$updated = $this->table->update(
-				[
-					'response' => '[ deleted ]',
-					'status' => 'deleted',
-					'updated_at' => current_time('mysql')
-				],
-				['id' => $data['response_id']]
-			);
-
-			$this->cache->flush();
-			return ['success' => true, 'result' => $updated];
-		}
-
-		// No replies, safe to delete
-		$deleted = $this->table->delete(['id' => $data['response_id']]);
-
-		if ($deleted === false) {
-			$this->logError('Failed to delete response', [
-				'data' => $data,
-				'error' => $this->table->getLastError()
-			]);
-
-			return ['success' => false, 'result' => 'Failed to delete response'];
-		}
-
-		$this->cache->flush();
-		return ['success' => true, 'result' => $deleted];
-	}
-
-	/**
-	 * Send notifications for new responses
-	 */
-	protected function sendNotifications(int $item_id, ?int $parent_id, int $response_id, int $user_id): void
-	{
-		// Notify post author
-		$post = get_post($item_id);
-		if ($post && $post->post_author != $user_id) {
-			JVB()->notification()->addNotification(
-				$post->post_author,
-				'new_response',
-				[
-					'message' => 'Someone responded to your post',
-					'item_id' => $item_id,
-					'response_id' => $response_id
-				]
-			);
-		}
-
-		// Notify parent response author if this is a reply
-		if ($parent_id) {
-			$parent = $this->table->get(['id' => $parent_id]);
-
-			if ($parent && $parent->user_id != $user_id) {
-				JVB()->notification()->addNotification(
-					$parent->user_id,
-					'response_reply',
-					[
-						'message' => 'Someone replied to your response',
-						'item_id' => $item_id,
-						'response_id' => $response_id
-					]
-				);
-			}
-		}
+		return $responses;
 	}
 
 	/**
@@ -420,7 +152,7 @@
 		// Ordering
 		if ($request->has_param('orderby')) {
 			$orderby = $request->get_param('orderby');
-			$valid = ['created_at', 'updated_at', 'upvotes', 'downvotes'];
+			$valid = ['created_at', 'updated_at', 'upvotes', 'downvotes', 'karmic_score'];
 
 			if (in_array($orderby, $valid)) {
 				$args['orderby'] = $orderby;
@@ -438,74 +170,26 @@
 	}
 
 	/**
-	 * Get child responses recursively
-	 */
-	protected function getChildren(int $item_id, int $parent_id): array
-	{
-		return $this->getItemResponse($item_id, ['parent_id' => $parent_id]);
-	}
-
-	/**
 	 * Format response for API output
 	 */
-	protected function formatItem(object $response): array
+	protected function formatItem(array $response): array
 	{
-		$formatted = [
-			'id' => (int) $response->id,
-			'item_id' => (int) $response->item_id,
-			'parent_id' => $response->parent_id ? (int) $response->parent_id : null,
-			'response' => $response->response,
-			'status' => $response->status,
-			'created_at' => $response->created_at,
-			'updated_at' => $response->updated_at,
-			'children' => $this->getChildren($response->item_id, $response->id),
-			'upvotes' => (int) ($response->upvotes ?? 0),
-			'downvotes' => (int) ($response->downvotes ?? 0),
-			'karma' => (int) ($response->upvotes ?? 0) - (int) ($response->downvotes ?? 0),
-			'reply_count' => (int) ($response->reply_count ?? 0)
-		];
-
 		// Handle deleted users
-		if ($response->is_user_deleted) {
-			$formatted['user'] = [
+		if ($response['is_user_deleted']) {
+			$response['user'] = [
 				'id' => null,
 				'name' => '[deleted user]',
 				'is_deleted' => true
 			];
 		} else {
-			// Add artist info
-			$artist = jvbContentFromUser($response->user_id);
-			if (!empty($artist)) {
-				$formatted['artist'] = [
-					'name' => $artist['name'],
-					'url' => $artist['url'],
-					'shop' => $artist['shop'] ?? null
-				];
-			}
+			$user = get_userdata($response['user_id']);
+			$response['user'] = [
+				'id'	=> $response['user_id'],
+				'name'	=> $user->display_name,
+				'url'	=> jvbUserProfileLink($response['user_id']),
+			];
 		}
 
-		return $formatted;
-	}
-
-	/**
-	 * Handle user deletion by anonymizing responses
-	 */
-	public function handleUserDeletion(int $user_id): void
-	{
-		$updated = $this->table->update(
-			[
-				'is_user_deleted' => 1,
-				'updated_at' => current_time('mysql')
-			],
-			['user_id' => $user_id]
-		);
-
-		$this->logError(
-			'Anonymized responses for deleted user',
-			['user_id' => $user_id, 'count' => $updated],
-			'info'
-		);
-
-		$this->cache->flush();
+		return $response;
 	}
 }

--
Gitblit v1.10.0