b882092f5d
These changes were applied DIRECTLY to the production cPanel server via Fileman/save_file_content API calls between ~10:30 PT and 14:44 PT today. None of them were committed or pushed at the time, violating the Software → Repo rule (CLAUDE.md, added 2026-05-18). Captured by Claude (VSCode-side) at Sami request so the changes can be reviewed/cherry-picked instead of being silently lost. Notable concerns: - class-beep.php shrank from 3387 → 1395 bytes (likely truncation, not edit) - beep.css shrank from 35431 → 22948 bytes (-12.5 KB; ~half the file gone) - class-replies.php shrank from 8793 → 6406 bytes - 3 new files (class-magic-links.php, class-follows.php, create_poll_tables.php) were created server-only with no repo presence Source: live cPanel snapshot at /tmp/beep-snapshot-2026-05-20/
208 lines
6.8 KiB
PHP
208 lines
6.8 KiB
PHP
<?php
|
|
/**
|
|
* Beep Follow System — class-follows.php
|
|
*
|
|
* REST endpoints for following/unfollowing users.
|
|
*/
|
|
|
|
if (!defined('ABSPATH')) exit;
|
|
|
|
class Beep_Follows {
|
|
|
|
const REST_NAMESPACE = 'beep/v1';
|
|
|
|
public function register_routes() {
|
|
// Follow a user: POST /beep/v1/follow/<user_id>
|
|
register_rest_route(self::REST_NAMESPACE, '/follow/(?P<user_id>\d+)', [
|
|
'methods' => 'POST',
|
|
'callback' => [$this, 'follow_user'],
|
|
'permission_callback' => [$this, 'require_login'],
|
|
]);
|
|
|
|
// Unfollow a user: DELETE /beep/v1/follow/<user_id>
|
|
register_rest_route(self::REST_NAMESPACE, '/follow/(?P<user_id>\d+)', [
|
|
'methods' => 'DELETE',
|
|
'callback' => [$this, 'unfollow_user'],
|
|
'permission_callback' => [$this, 'require_login'],
|
|
]);
|
|
|
|
// Check follow status: GET /beep/v1/follow/<user_id>
|
|
register_rest_route(self::REST_NAMESPACE, '/follow/(?P<user_id>\d+)', [
|
|
'methods' => 'GET',
|
|
'callback' => [$this, 'check_follow'],
|
|
'permission_callback' => '__return_true',
|
|
]);
|
|
|
|
// Get users I'm following: GET /beep/v1/following
|
|
register_rest_route(self::REST_NAMESPACE, '/following', [
|
|
'methods' => 'GET',
|
|
'callback' => [$this, 'get_following'],
|
|
'permission_callback' => [$this, 'require_login'],
|
|
]);
|
|
|
|
// Get my followers: GET /beep/v1/followers
|
|
register_rest_route(self::REST_NAMESPACE, '/followers', [
|
|
'methods' => 'GET',
|
|
'callback' => [$this, 'get_followers'],
|
|
'permission_callback' => [$this, 'require_login'],
|
|
]);
|
|
}
|
|
|
|
public function require_login() {
|
|
return is_user_logged_in() ? true : new WP_Error('rest_not_logged_in', 'You must be logged in', ['status' => 401]);
|
|
}
|
|
|
|
public function follow_user($request) {
|
|
$user_id = intval($request->get_param('user_id'));
|
|
$follower_id = get_current_user_id();
|
|
|
|
if ($user_id === $follower_id) {
|
|
return new WP_Error('rest_cannot_follow_self', 'You cannot follow yourself', ['status' => 400]);
|
|
}
|
|
|
|
$target = get_userdata($user_id);
|
|
if (!$target) {
|
|
return new WP_Error('rest_user_not_found', 'User not found', ['status' => 404]);
|
|
}
|
|
|
|
global $wpdb;
|
|
$table = $wpdb->prefix . 'beep_follows';
|
|
|
|
$existing = $wpdb->get_row($wpdb->prepare(
|
|
"SELECT ID FROM `$table` WHERE follower_id = %d AND following_id = %d",
|
|
$follower_id, $user_id
|
|
));
|
|
|
|
if ($existing) {
|
|
return ['status' => 'following', 'user_id' => $user_id, 'display_name' => $target->display_name];
|
|
}
|
|
|
|
$wpdb->insert($table, [
|
|
'follower_id' => $follower_id,
|
|
'following_id' => $user_id,
|
|
], ['%d', '%d']);
|
|
|
|
return ['status' => 'following', 'user_id' => $user_id, 'display_name' => $target->display_name];
|
|
}
|
|
|
|
public function unfollow_user($request) {
|
|
$user_id = intval($request->get_param('user_id'));
|
|
$follower_id = get_current_user_id();
|
|
|
|
global $wpdb;
|
|
$table = $wpdb->prefix . 'beep_follows';
|
|
|
|
$wpdb->delete($table, [
|
|
'follower_id' => $follower_id,
|
|
'following_id' => $user_id,
|
|
], ['%d', '%d']);
|
|
|
|
return ['status' => 'unfollowed', 'user_id' => $user_id];
|
|
}
|
|
|
|
public function check_follow($request) {
|
|
$user_id = intval($request->get_param('user_id'));
|
|
$current_user = get_current_user_id();
|
|
|
|
global $wpdb;
|
|
$table = $wpdb->prefix . 'beep_follows';
|
|
|
|
$is_following = false;
|
|
if ($current_user) {
|
|
$is_following = (bool) $wpdb->get_var($wpdb->prepare(
|
|
"SELECT ID FROM `$table` WHERE follower_id = %d AND following_id = %d",
|
|
$current_user, $user_id
|
|
));
|
|
}
|
|
|
|
$target = get_userdata($user_id);
|
|
if (!$target) {
|
|
return new WP_Error('rest_user_not_found', 'User not found', ['status' => 404]);
|
|
}
|
|
|
|
$follower_count = intval($wpdb->get_var($wpdb->prepare(
|
|
"SELECT COUNT(*) FROM `$table` WHERE following_id = %d", $user_id
|
|
)));
|
|
|
|
$following_count = intval($wpdb->get_var($wpdb->prepare(
|
|
"SELECT COUNT(*) FROM `$table` WHERE follower_id = %d", $user_id
|
|
)));
|
|
|
|
return [
|
|
'is_following' => $is_following,
|
|
'user_id' => $user_id,
|
|
'display_name' => $target->display_name,
|
|
'user_login' => $target->user_login,
|
|
'follower_count' => $follower_count,
|
|
'following_count' => $following_count,
|
|
];
|
|
}
|
|
|
|
public function get_following($request) {
|
|
$user_id = get_current_user_id();
|
|
|
|
global $wpdb;
|
|
$table = $wpdb->prefix . 'beep_follows';
|
|
|
|
$following = $wpdb->get_results($wpdb->prepare(
|
|
"SELECT u.ID, u.display_name, u.user_login, f.created_at
|
|
FROM `$table` f
|
|
JOIN {$wpdb->users} u ON u.ID = f.following_id
|
|
WHERE f.follower_id = %d
|
|
ORDER BY f.created_at DESC",
|
|
$user_id
|
|
), ARRAY_A);
|
|
|
|
return ['following' => $following, 'count' => count($following)];
|
|
}
|
|
|
|
public function get_followers($request) {
|
|
$user_id = get_current_user_id();
|
|
|
|
global $wpdb;
|
|
$table = $wpdb->prefix . 'beep_follows';
|
|
|
|
$followers = $wpdb->get_results($wpdb->prepare(
|
|
"SELECT u.ID, u.display_name, u.user_login, f.created_at
|
|
FROM `$table` f
|
|
JOIN {$wpdb->users} u ON u.ID = f.follower_id
|
|
WHERE f.following_id = %d
|
|
ORDER BY f.created_at DESC",
|
|
$user_id
|
|
), ARRAY_A);
|
|
|
|
return ['followers' => $followers, 'count' => count($followers)];
|
|
}
|
|
|
|
public static function is_following($follower_id, $following_id) {
|
|
global $wpdb;
|
|
$table = $wpdb->prefix . 'beep_follows';
|
|
return (bool) $wpdb->get_var($wpdb->prepare(
|
|
"SELECT ID FROM `$table` WHERE follower_id = %d AND following_id = %d",
|
|
$follower_id, $following_id
|
|
));
|
|
}
|
|
|
|
public static function get_follower_count($user_id) {
|
|
global $wpdb;
|
|
$table = $wpdb->prefix . 'beep_follows';
|
|
return intval($wpdb->get_var($wpdb->prepare(
|
|
"SELECT COUNT(*) FROM `$table` WHERE following_id = %d", $user_id
|
|
)));
|
|
}
|
|
|
|
public static function get_following_count($user_id) {
|
|
global $wpdb;
|
|
$table = $wpdb->prefix . 'beep_follows';
|
|
return intval($wpdb->get_var($wpdb->prepare(
|
|
"SELECT COUNT(*) FROM `$table` WHERE follower_id = %d", $user_id
|
|
)));
|
|
}
|
|
}
|
|
|
|
// Bootstrap — register routes
|
|
add_action('rest_api_init', function() {
|
|
$f = new Beep_Follows();
|
|
$f->register_routes();
|
|
});
|