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/rest/routes/VoteRoutes.php |  437 ++++++++++++++++++++++++-----------------------------
 1 files changed, 198 insertions(+), 239 deletions(-)

diff --git a/inc/rest/routes/VoteRoutes.php b/inc/rest/routes/VoteRoutes.php
index e314f46..1ddf49f 100644
--- a/inc/rest/routes/VoteRoutes.php
+++ b/inc/rest/routes/VoteRoutes.php
@@ -1,7 +1,11 @@
 <?php
 namespace JVBase\rest\routes;
-use JVBase\JVB;
-use JVBase\rest\RestRouteManager;
+
+use JVBase\managers\CustomTable;
+use JVBase\rest\Response;
+use JVBase\rest\Rest;
+use JVBase\rest\Route;
+use JVBase\utility\Features;
 use WP_REST_Request;
 use WP_REST_Response;
 use WP_Error;
@@ -10,12 +14,12 @@
 if (!defined('ABSPATH')) {
     exit; // Exit if accessed directly
 }
-class VoteRoutes extends RestRouteManager
+class VoteRoutes extends Rest
 {
     public function __construct()
     {
-        $this->cache_name = 'karma';
-        $this->cache_ttl = 86400;
+        $this->cacheName = 'karma';
+        $this->cacheTtl = DAY_IN_SECONDS;
         parent::__construct();
         add_filter(BASE.'handle_bulk_operation', [$this, 'processOperation'], 10, 3);
     }
@@ -26,18 +30,23 @@
      */
     public function registerRoutes():void
     {
-        register_rest_route($this->namespace, 'vote', [
-            [
-                'methods'  => 'POST',
-                'callback'  => [$this, 'handleVote'],
-                'permission_callback'   => [$this, 'checkPermission']
-            ],
-            [
-                'methods'   => 'GET',
-                'callback'  => [$this, 'getVotes'],
-                'permission_callback'   => [$this, 'checkPermission']
-            ]
-        ]);
+		Route::for('vote')
+			->post([$this, 'handleVote'])
+			->args([
+				'user' => 'integer|required',
+				'id' => 'string|required',
+				'item_id' => 'integer|required',
+				'content' => 'string|required',
+				'vote' => 'string|required|enum:up,down',
+			])
+			->auth('user')
+			->rateLimit(10)
+			->get([$this, 'getVotes'])
+			->args([
+				'user' => 'integer',
+			])
+			->auth('user')
+			->rateLimit(30);
     }
 
     /**
@@ -47,42 +56,45 @@
      */
     public function handleVote(WP_REST_Request $request):WP_REST_Response
     {
-        global $karma;
-        if (!array_key_exists($request->get_param('content'), $karma)) {
-            return new WP_REST_Response([
-                'success' => false,
-                'message' => __('Invalid content', 'jvb'),
-            ]);
-        }
-        $vote = $request->get_param('vote');
-        if (!$request->get_param('item_id') || !in_array($vote, ['up', 'down'])) {
-            return new WP_REST_Response([
-                'success' => false,
-                'message' => __('Invalid item or vote attempt', 'jvb'),
-            ]);
+        $content = sanitize_text_field($request->get_param('content')??'');
+		if ((!Features::forContent($content)->has('karma') && !Features::forTaxonomy($content)->has('karma') && !Features::forUser($content)->has('karma'))) {
+			return Response::validationError(['message' => __('Invalid content', 'jvb')]);
+		}
+
+        $vote = sanitize_text_field($request->get_param('vote')??'');
+		$itemID = absint($request->get_param('item_id')??0);
+        if ($itemID === 0 || !in_array($vote, ['up', 'down'])) {
+			return Response::validationError(['message' => __('Invalid item or vote attempt', 'jvb')]);
         }
 
-        //cursory sanitization
-        $user = (int) $request->get_param('user');
-        if (!$this->userCheck($user)) {
-            return new WP_REST_Response([
-                'success' => false,
-                'message' => __('User doesn\'t match. Bot?', 'jvb'),
-            ]);
-        }
+		$user = absint($request->get_param('user'));
+		if (!$this->userCheck($user)) {
+			return Response::validationError(['message' => __('User doesn\'t match. Bot?', 'jvb')]);
+		}
+
         $operation = sanitize_text_field($request->get_param('id'));
 
+		$type = match(true) {
+			array_key_exists($content, JVB_CONTENT) => 'post',
+			array_key_exists($content, JVB_TAXONOMY) => 'term',
+			array_key_exists($content, JVB_USER) => 'user',
+			default => false
+		};
+		if (!$type) {
+			return Response::validationError(['message' => __('Invalid content type', 'jvb')]);
+		}
+
         $data = [
             'user'      => $user,
-            'item_id'   => (int) $request->get_param('item_id'),
-            'content'   => sanitize_text_field($request->get_param('content')),
-            'vote'      => sanitize_text_field($vote),
+            'item_id'   => $itemID,
+            'content'   => $content,
+			'type'		=> $type,
+            'vote'      => $vote,
         ];
 
         error_log('Final Vote Data: '.print_r($data, true));
         error_log('Operation: '.print_r($operation, true));
-        $queue = JVB()->queue();
-        $queue->queueOperation(
+        $operationID = JVB()->queue()->add(
             'karmic',
             $user,
             $data,
@@ -91,33 +103,30 @@
                 'operation_id'  => $operation,
             ]
         );
-
-        return new WP_REST_Response([
-            'success' => true,
-            'message' => __('Operation queued', 'jvb'),
-            'operation_id' => $operation
-        ]);
+		return $this->queued($operationID['operation_id']);
     }
 
-    /**
-     * @param WP_Error|array $result
-     * @param object $operation
-     * @param array $data
-     *
-     * @return WP_Error|array
-     */
+	/**
+	 * @param WP_Error|array $result
+	 * @param object $operation
+	 * @param array $data
+	 *
+	 * @return WP_Error|array
+	 * @throws Exception
+	 */
     public function processOperation(WP_Error|array $result, object $operation, array $data):WP_Error|array
     {
         if ($operation->type !== 'karmic') {
             return $result;
         }
-        // Get parameters from request
-        global $karma;
 
         //Check if item exists
-        $item = ($karma[$data['content']] === 'term') ?
-            get_term($data['item_id'], BASE.$data['content']) :
-            get_post($data['item_id']);
+		$item = match ($data['type']) {
+			'post' 	=> get_post($data['item_id']),
+			'term'	=> get_term($data['item_id'], jvbCheckBase($data['content'])),
+			'user'	=> get_userdata($data['item_id']),
+			default => false
+		};
         if (!$item || is_wp_error($item)) {
             return [
                 'success' => false,
@@ -125,122 +134,79 @@
             ];
         }
 
-        global $wpdb;
-        $table_name = $wpdb->prefix . BASE . 'karma_' . $data['content'];
-        $key = $data['user'];
-        error_log('Processing: '.print_r($data, true));
-        // Check if user has already voted on this post
-        $existing_vote = $wpdb->get_var(
-            $wpdb->prepare(
-                "SELECT vote FROM {$table_name} WHERE item_id = %d AND user_id = %d",
-                $data['item_id'],
-                $data['user']
-            )
-        );
+		$table = CustomTable::for('karma_' . $data['content']);
 
-        // Begin transaction for data integrity
-        $wpdb->query('START TRANSACTION');
+		return $table->transaction(function($table) use ($data) {
+			// Check existing vote
+			$existing = $table->where([
+				'item_id' => $data['item_id'],
+				'user_id' => $data['user']
+			])->first();
 
-        try {
-            // Initialize response data
-            $response_data = [
-                'item_id' => $data['item_id'],
-                'previous_vote' => $existing_vote,
-                'new_vote' => $data['vote'],
-                'updated' => false
-            ];
+			$existing_vote = $existing->vote ?? null;
+			$new_vote = $data['vote'];
 
-            error_log('Existing: '.print_r($existing_vote, true));
-            error_log('New: '.print_r($data['vote'], true));
+			// No previous vote - insert new
+			if ($existing_vote === null) {
+				$inserted = $table->create([
+					'item_id' => $data['item_id'],
+					'user_id' => $data['user'],
+					'vote' => $new_vote,
+				]);
 
-            // If user hasn't voted before
-            if ($existing_vote === null) {
-                // Insert new vote
-                $inserted = $wpdb->insert(
-                    $table_name,
-                    [
-                        'item_id' => $data['item_id'],
-                        'user_id' => $data['user'],
-                        'vote' => $data['vote'],
-                        'date' => current_time('mysql')
-                    ],
-                    ['%d', '%d', '%s', '%s']
-                );
+				if (!$inserted) {
+					throw new Exception('Failed to record vote');
+				}
 
-                if (!$inserted) {
-                    throw new Exception('Failed to record vote');
-                }
+				$this->updateVoteCount($data['content'], $data['type'], $data['item_id'], $new_vote, 1);
+				$this->cache->invalidate($data['user']);
 
-                // Increment the appropriate vote counter
-                $this->updateVoteCount($data['content'], $data['item_id'], $data['vote'], 1);
+				return [
+					'success' => true,
+					'result' => __('Vote recorded', 'jvb'),
+				];
+			}
 
-                $response_data['updated'] = true;
-                $this->cache->invalidate($key);
-            } elseif ($existing_vote !== $data['vote']) {
-                // If user is changing their vote
-                // Update existing vote
-                $updated = $wpdb->update(
-                    $table_name,
-                    [
-                        'vote'  => $data['vote'],
-                    ],
-                    [
+			// Changing vote
+			if ($existing_vote !== $new_vote) {
+				$updated = $table->where([
+					'item_id' => $data['item_id'],
+					'user_id' => $data['user']
+				])->updateResults(['vote' => $new_vote]);
 
-                        'item_id' => $data['item_id'],
-                        'user_id' => $data['user'],
-                    ],
-                    ['%s'],
-                    ['%d', '%d']
-                );
+				if (!$updated) {
+					throw new Exception('Failed to update vote');
+				}
 
-                if (!$updated) {
-                    throw new Exception('Failed to update vote');
-                }
+				// Decrement old, increment new
+				$this->updateVoteCount($data['content'], $data['type'], $data['item_id'], $existing_vote, -1);
+				$this->updateVoteCount($data['content'], $data['type'], $data['item_id'], $new_vote, 1);
+				$this->cache->invalidate($data['user']);
 
-                $this->updateVoteCount($data['content'], $data['item_id'], $existing_vote, -1);
+				return [
+					'success' => true,
+					'result' => __('Vote updated', 'jvb'),
+				];
+			}
 
-                // Increment new vote type
-                $this->updateVoteCount($data['content'], $data['item_id'], $data['vote'], 1);
+			// Toggle off - remove vote
+			$deleted = $table->where([
+				'item_id' => $data['item_id'],
+				'user_id' => $data['user']
+			])->deleteResults();
 
-                $response_data['updated'] = true;
-                $this->cache->invalidate($key);
-            } else {
-                // If user is clicking the same vote again (toggle off)
-                // Remove the vote
-                $deleted = $wpdb->delete(
-                    $table_name,
-                    [
-                        'item_id' => $data['item_id'],
-                        'user_id' => $data['user']
-                    ],
-                    ['%d', '%d']
-                );
+			if (!$deleted) {
+				throw new Exception('Failed to remove vote');
+			}
 
-                if (!$deleted) {
-                    throw new Exception('Failed to remove vote');
-                }
+			$this->updateVoteCount($data['content'], $data['type'], $data['item_id'], $existing_vote, -1);
+			$this->cache->delete($data['user']);
 
-                // Decrement the vote counter
-                $this->updateVoteCount($data['content'], $data['item_id'], $data['vote'], -1);
-
-                $response_data['new_vote'] = null;
-                $response_data['updated'] = true;
-                $this->cache->invalidate($key);
-            }
-
-            $wpdb->query('COMMIT');
-
-            return [
-                'success'   => true,
-                'result'   => __('Vote handled', 'jvb'),
-            ];
-        } catch (Exception $e) {
-            $wpdb->query('ROLLBACK');
-            return [
-                'success'   => false,
-                'result'   => $e->getMessage()
-            ];
-        }
+			return [
+				'success' => true,
+				'result' => __('Vote removed', 'jvb'),
+			];
+		});
     }
 
     /**
@@ -251,51 +217,52 @@
      *
      * @return void
      */
-    protected function updateVoteCount(string $content, int $ID, string $vote, int $value):void
+    protected function updateVoteCount(string $content, string $type, int $ID, string $vote, int $value):void
     {
-        global $karma;
+		$key = ($vote === 'down') ? BASE.'downvotes' : BASE.'upvotes';
 
-        $key = ($vote==='down') ? BASE.'downvotes' : BASE.'upvotes';
+		switch ($type) {
+			case 'post':
+				$old = (int) get_post_meta($ID, $key, true);
+				$new = max(0, $old + $value);
+				update_post_meta($ID, $key, $new);
 
-        switch ($karma[$content]) {
-            case 'post':
-                $old = (int) get_post_meta($ID, $key, true);
-                $new = max(0, $old + $value);
-                update_post_meta($ID, $key, $new);
-                $up = (int) get_post_meta($ID, BASE.'upvotes', true);
-                $down = (int) get_post_meta($ID, BASE.'downvotes', true);
-                update_post_meta($ID, BASE.'karma', $up - $down);
-                break;
-            case 'term':
-                $old = (int) get_term_meta($ID, $key, true);
-                $new = max(0, $old + $value);
-                update_term_meta($ID, $key, $new);
-                $up = (int) get_term_meta($ID, BASE.'upvotes', true);
-                $down = (int) get_term_meta($ID, BASE.'downvotes', true);
-                update_term_meta($ID, BASE.'karma', $up - $down);
-                break;
-            case 'user':
-                $old = (int) get_user_meta($ID, $key, true);
-                $new = max(0, $old + $value);
-                update_user_meta($ID, $key, $new);
-                $up = (int) get_user_meta($ID, BASE.'upvotes', true);
-                $down = (int) get_user_meta($ID, BASE.'downvotes', true);
-                update_user_meta($ID, BASE.'karma', $up - $down);
-                break;
-            case 'response':
-                // Direct table update for responses
-                global $wpdb;
-                $table = $wpdb->prefix . BASE . 'responses';
+				$up = (int) get_post_meta($ID, BASE.'upvotes', true);
+				$down = (int) get_post_meta($ID, BASE.'downvotes', true);
+				update_post_meta($ID, BASE.'karma', $up - $down);
+				break;
 
-                // Update vote count
-                $field = str_replace(BASE, '', $key);
-                $wpdb->query($wpdb->prepare(
-                    "UPDATE $table SET $field = GREATEST(0, $field + %d), karma = (upvotes - downvotes) WHERE id = %d",
-                    $value,
-                    $ID
-                ));
-                break;
-        }
+			case 'term':
+				$old = (int) get_term_meta($ID, $key, true);
+				$new = max(0, $old + $value);
+				update_term_meta($ID, $key, $new);
+
+				$up = (int) get_term_meta($ID, BASE.'upvotes', true);
+				$down = (int) get_term_meta($ID, BASE.'downvotes', true);
+				update_term_meta($ID, BASE.'karma', $up - $down);
+				break;
+
+			case 'user':
+				$old = (int) get_user_meta($ID, $key, true);
+				$new = max(0, $old + $value);
+				update_user_meta($ID, $key, $new);
+
+				$up = (int) get_user_meta($ID, BASE.'upvotes', true);
+				$down = (int) get_user_meta($ID, BASE.'downvotes', true);
+				update_user_meta($ID, BASE.'karma', $up - $down);
+				break;
+
+			case 'response':
+				$field = str_replace(BASE, '', $key);
+				CustomTable::for('responses')->query(
+					"UPDATE {table}
+					 SET $field = GREATEST(0, $field + %d),
+					     karma = (upvotes - downvotes)
+					 WHERE id = %d",
+					[$value, $ID]
+				);
+				break;
+		}
     }
 
     /**
@@ -303,47 +270,39 @@
      *
      * @return WP_REST_Response
      */
-    public function getVotes(WP_REST_Request $request):WP_REST_Response
-    {
-        $user = $request->get_param('user')??get_current_user_id();
-        $cache = $this->cache->get($user);
-        if ($cache) {
-            return new WP_REST_Response($cache);
-        }
+	public function getVotes(WP_REST_Request $request): WP_REST_Response
+	{
+		$user = absint($request->get_param('user') ?? get_current_user_id());
 
+		$cache = $this->cache->get($user);
+		if ($cache) {
+			return Response::success($cache);
+		}
 
-        global $wpdb;
-        $votes = [];
+		$votes = [];
 
-        foreach (jvbGlobalKarma() as $type => $content_types) {
-            foreach ($content_types as $content_type) {
-                $table_name = $wpdb->prefix . BASE . 'karma_'. $content_type;
+		foreach (jvbGlobalKarma() as $type => $content_types) {
+			foreach ($content_types as $content_type) {
+				$table = CustomTable::for('karma_' . $content_type);
 
-                // Skip if table doesn't exist
-                if ($wpdb->get_var("SHOW TABLES LIKE '$table_name'") != $table_name) {
-                    continue;
-                }
+				// Skip if table doesn't exist
+				global $wpdb;
+				if ($wpdb->get_var("SHOW TABLES LIKE '{$table->getFullTableName()}'") != $table->getFullTableName()) {
+					continue;
+				}
 
-                $results = $wpdb->get_results(
-                    $wpdb->prepare(
-                        "SELECT item_id, vote, date
-                FROM {$table_name}
-                WHERE user_id = %d",
-                        $user
-                    )
-                );
+				$results = $table->where(['user_id' => $user])->getResults();
 
-                if ($results && !is_wp_error($results)) {
-                    foreach ($results as $vote) {
-                        $votes[$content_type][$vote->item_id] = $vote->vote;
-                    }
-                }
-            }
-        }
+				if (!empty($results)) {
+					foreach ($results as $vote) {
+						$votes[$content_type][$vote->item_id] = $vote->vote;
+					}
+				}
+			}
+		}
 
-        // Store in cache
-        $this->cache->set($user, $votes);
+		$this->cache->set($user, $votes);
 
-        return new WP_REST_Response($votes);
-    }
+		return Response::success($votes);
+	}
 }

--
Gitblit v1.10.0