'GET', 'callback' => [__CLASS__, 'fetch_feed'], 'permission_callback' => '__return_true', 'args' => [ 'sort' => ['default' => 'latest'], 'limit' => ['default' => 50, 'validate_callback' => function ($v) { return is_numeric($v); }], ], ]); } /** * REST endpoint: re-render the feed list with the given sort. */ public static function fetch_feed($request) { $sort = $request->get_param('sort') === 'top' ? 'top' : 'latest'; $limit = max(1, (int) $request->get_param('limit')); $posts = ($sort === 'top') ? self::get_top_beeps($limit) : self::get_latest_beeps($limit); $html = ''; foreach ($posts as $p) { $html .= beep_render_post($p); } if ($html === '') { $html = '
No beeps yet.
'; } return rest_ensure_response(['html' => $html, 'sort' => $sort]); } public static function get_latest_beeps($limit) { global $wpdb; return $wpdb->get_results($wpdb->prepare( "SELECT * FROM {$wpdb->posts} WHERE post_type = %s AND post_status = 'publish' ORDER BY post_date DESC LIMIT %d", Beep_CPT::POST_TYPE, $limit )); } public static function get_top_beeps($limit) { global $wpdb; $likes = Beep_Likes::table_name(); return $wpdb->get_results($wpdb->prepare( "SELECT p.*, COALESCE(l.cnt, 0) AS like_count FROM {$wpdb->posts} p LEFT JOIN ( SELECT post_id, COUNT(*) AS cnt FROM $likes WHERE object_type = 'post' GROUP BY post_id ) l ON l.post_id = p.ID WHERE p.post_type = %s AND p.post_status = 'publish' ORDER BY COALESCE(l.cnt, 0) DESC, p.post_date DESC LIMIT %d", Beep_CPT::POST_TYPE, $limit )); } public static function render_feed($atts) { $atts = shortcode_atts([ 'limit' => 50, 'header' => 'Beep!', 'show_wordmark' => true, 'sort' => 'latest', ], $atts, 'beep_feed'); $limit = max(1, (int) $atts['limit']); $sort = ($atts['sort'] === 'top') ? 'top' : 'latest'; $posts = ($sort === 'top') ? self::get_top_beeps($limit) : self::get_latest_beeps($limit); $has_posts = !empty($posts); // Show magic login form if not logged in if (!is_user_logged_in()) { return Beep_Magic_Links::render_login_form() . beep_render_public_feed($posts, $atts); } ob_start(); ?>
Your account has been suspended from beeping.

Join in. Sign in to like and reply.

Log in Sign up
No beeps yet.
'; } ?>
var BeepConfig=%s;', json_encode([ 'restUrl' => esc_url_raw(rest_url('beep/v1/')), 'nonce' => wp_create_nonce('wp_rest'), 'charLimit' => BEEP_CHAR_LIMIT, 'loginUrl' => esc_url_raw(wp_login_url(is_singular() ? get_permalink() : home_url('/'))), 'isLoggedIn' => is_user_logged_in(), ]) ); $beep_script = ''; return ob_get_clean() . $beep_config . $beep_script . beep_render_thread_modal() . beep_render_gif_modal(); } /** * Register additional REST routes for media upload. */ public static function register_media_routes() { register_rest_route('beep/v1', '/media/(?P\d+)', [ 'methods' => 'POST', 'callback' => [__CLASS__, 'upload_media'], 'permission_callback' => function () { return is_user_logged_in() && !beep_user_is_banned(get_current_user_id()); }, ]); register_rest_route('beep/v1', '/media/(?P\d+)/(?P\d+)', [ 'methods' => 'DELETE', 'callback' => [__CLASS__, 'delete_media'], 'permission_callback' => function () { return is_user_logged_in() && !beep_user_is_banned(get_current_user_id()); }, ]); register_rest_route('beep/v1', '/voice/(?P\d+)', [ 'methods' => 'POST', 'callback' => [__CLASS__, 'upload_voice'], 'permission_callback' => function () { return is_user_logged_in() && !beep_user_is_banned(get_current_user_id()); }, ]); } public static function upload_media($request) { $post_id = (int) $request->get_param('post_id'); $post = get_post($post_id); if (!$post || $post->post_type !== Beep_CPT::POST_TYPE) { return new WP_Error('not_found', 'Beep not found.', 404); } if (!current_user_can('edit_post', $post_id)) { return new WP_Error('forbidden', 'Cannot edit this beep.', 403); } if (empty($_FILES['file'])) { return new WP_Error('no_file', 'No file uploaded.'); } $result = Beep_Media::handle_upload($post_id, $_FILES['file']); if (is_wp_error($result)) { return $result; } return rest_ensure_response($result); } public static function delete_media($request) { $post_id = (int) $request->get_param('post_id'); $attachment_id = (int) $request->get_param('attachment_id'); $post = get_post($post_id); if (!$post || $post->post_type !== Beep_CPT::POST_TYPE) { return new WP_Error('not_found', 'Beep not found.', 404); } if (!current_user_can('edit_post', $post_id)) { return new WP_Error('forbidden', 'Cannot edit this beep.', 403); } Beep_Media::remove_image($post_id, $attachment_id); return rest_ensure_response(['ok' => true]); } public static function upload_voice($request) { $post_id = (int) $request->get_param('post_id'); $post = get_post($post_id); if (!$post || $post->post_type !== Beep_CPT::POST_TYPE) { return new WP_Error('not_found', 'Beep not found.', 404); } if (!current_user_can('edit_post', $post_id)) { return new WP_Error('forbidden', 'Cannot edit this beep.', 403); } if (empty($_FILES['file'])) { return new WP_Error('no_file', 'No file uploaded.'); } $result = Beep_Media::handle_voice_upload($post_id, $_FILES['file']); if (is_wp_error($result)) { return $result; } return rest_ensure_response($result); } public static function register_thread_routes() { register_rest_route('beep/v1', '/thread/(?P\d+)', [ 'methods' => 'GET', 'callback' => [__CLASS__, 'get_thread'], 'permission_callback' => '__return_true', ]); } public static function get_thread($request) { $post_id = (int) $request->get_param('post_id'); if (get_post_type($post_id) !== Beep_CPT::POST_TYPE) { return new WP_Error('not_found', 'Beep not found.', 404); } $depth = (int) $request->get_param('depth') ?: 10; $thread = Beep_Replies::get_thread($post_id, 0, $depth); return rest_ensure_response($thread); } public static function register_quote_routes() { register_rest_route('beep/v1', '/quote/(?P\d+)', [ 'methods' => 'GET', 'callback' => [__CLASS__, 'get_quoted_post'], 'permission_callback' => '__return_true', ]); register_rest_route('beep/v1', '/lookup', [ 'methods' => 'GET', 'callback' => [__CLASS__, 'lookup_beep'], 'permission_callback' => '__return_true', ]); } public static function get_quoted_post($request) { $post_id = (int) $request->get_param('post_id'); $quoted = Beep_Quotes::get_quoted_post($post_id); if (!$quoted) { return new WP_Error('not_found', 'No quoted post found.', 404); } return rest_ensure_response($quoted); } public static function lookup_beep($request) { $query = sanitize_text_field($request->get_param('q')); if (!$query) { return new WP_Error('missing_query', 'Query parameter q is required.', 400); } if (is_numeric($query)) { $post = get_post((int) $query); if ($post && $post->post_type === Beep_CPT::POST_TYPE) { return rest_ensure_response(Beep_Quotes::get_quoted_post($post->ID)); } } if (preg_match('/\[beep:(\d+)\]/', $query, $m)) { $post = get_post((int) $m[1]); if ($post && $post->post_type === Beep_CPT::POST_TYPE) { return rest_ensure_response(Beep_Quotes::get_quoted_post($post->ID)); } } return new WP_Error('not_found', 'Beep not found.', 404); } }