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