diff --git a/includes/class-replies.php b/includes/class-replies.php index cd3d809..d1b9107 100755 --- a/includes/class-replies.php +++ b/includes/class-replies.php @@ -1,7 +1,5 @@ 'POST', 'callback' => [__CLASS__, 'submit_beep'], - 'permission_callback' => [Beep_Likes::class, 'can_interact'], + 'permission_callback' => ['Beep_Likes', 'can_interact'], ]); - register_rest_route('beep/v1', '/reply/(?P\d+)', [ 'methods' => 'POST', 'callback' => [__CLASS__, 'submit_reply'], - 'permission_callback' => [Beep_Likes::class, 'can_interact'], + 'permission_callback' => ['Beep_Likes', 'can_interact'], ]); - - register_rest_route('beep/v1', '/beep/(?P\d+)', [ + register_rest_route('beep/v1', '/thread/(?P\d+)', [ + 'methods' => 'GET', + 'callback' => [__CLASS__, 'get_thread_api'], + 'permission_callback' => '__return_true', + ]); + register_rest_route('beep/v1', '/delete/(?P\d+)', [ 'methods' => 'DELETE', 'callback' => [__CLASS__, 'delete_beep'], - 'permission_callback' => function ($req) { - return current_user_can('delete_post', (int) $req['id']); - }, + 'permission_callback' => ['Beep_Likes', 'can_interact'], ]); - register_rest_route('beep/v1', '/reply/(?P\d+)', [ 'methods' => 'DELETE', 'callback' => [__CLASS__, 'delete_reply'], - 'permission_callback' => function () { - return current_user_can('moderate_comments'); - }, + 'permission_callback' => ['Beep_Likes', 'can_interact'], ]); } public static function submit_beep($request) { - $content = trim((string) $request->get_param('content')); - $length = function_exists('mb_strlen') ? mb_strlen($content) : strlen($content); - - if ($content === '' || $length > BEEP_CHAR_LIMIT) { - return new WP_Error('beep_invalid_content', - 'Content must be 1-' . BEEP_CHAR_LIMIT . ' characters.', - ['status' => 400]); + $content = sanitize_textarea_field($request->get_param('content')); + if (empty($content)) { + return new WP_Error('empty_content', 'Beep cannot be empty.', ['status' => 400]); } - $post_id = wp_insert_post([ - 'post_type' => Beep_CPT::POST_TYPE, + 'post_type' => 'beep', + 'post_content' => $content, 'post_status' => 'publish', 'post_author' => get_current_user_id(), - 'post_content' => wp_kses($content, []), - 'post_title' => wp_trim_words($content, 8, '...'), - ], true); - - if (is_wp_error($post_id)) { - return $post_id; - } - - if (is_wp_error($post_id)) { - return $post_id; - } - - $body = $request->get_json_params(); - $quote_id = isset($body['quote_id']) ? (int) $body['quote_id'] : 0; - if ($quote_id) { - Beep_Quotes::create_quote($post_id, $quote_id); - } - - $gif_url = isset($body['gif_url']) ? esc_url_raw($body['gif_url']) : ''; - if ($gif_url) { - update_post_meta($post_id, 'beep_gif_url', $gif_url); - } - - return rest_ensure_response([ - 'id' => $post_id, - 'html' => beep_render_post(get_post($post_id)), ]); + if (is_wp_error($post_id)) { return $post_id; } + return rest_ensure_response(['id' => $post_id, 'url' => get_permalink($post_id)]); } public static function submit_reply($request) { - $post_id = (int) $request['id']; - $content = trim((string) $request->get_param('content')); - $parent_id = (int) $request->get_param('parent'); - $length = function_exists('mb_strlen') ? mb_strlen($content) : strlen($content); - - if (get_post_type($post_id) !== Beep_CPT::POST_TYPE) { - return new WP_Error('beep_not_a_beep', 'Not a beep.', ['status' => 400]); + $post_id = (int) $request['id']; + $parent = get_post($post_id); + if (!$parent || $parent->post_type !== 'beep') { + return new WP_Error('invalid_post', 'Invalid beep post.', ['status' => 400]); } - if ($content === '' || $length > BEEP_CHAR_LIMIT) { - return new WP_Error('beep_invalid_content', - 'Reply must be 1-' . BEEP_CHAR_LIMIT . ' characters.', - ['status' => 400]); + $content = sanitize_textarea_field($request->get_param('content')); + if (empty($content)) { + return new WP_Error('empty_content', 'Reply cannot be empty.', ['status' => 400]); } - // Validate the parent comment belongs to this post if provided. - if ($parent_id > 0) { - $parent = get_comment($parent_id); - if (!$parent || (int) $parent->comment_post_ID !== $post_id) { - return new WP_Error('beep_bad_parent', 'Invalid parent.', ['status' => 400]); - } - } - - $user = wp_get_current_user(); - - $approved = get_option('comment_moderation') ? 0 : 1; - if (get_option('comment_previously_approved')) { - $prev = get_comments([ - 'user_id' => $user->ID, - 'status' => 'approve', - 'count' => true, - ]); - if (!$prev) { - $approved = 0; - } - } - $comment_id = wp_insert_comment([ - 'comment_post_ID' => $post_id, - 'comment_author' => $user->display_name, - 'comment_author_email' => $user->user_email, - 'comment_author_url' => '', - 'comment_content' => wp_kses($content, []), - 'comment_type' => 'comment', - 'comment_parent' => $parent_id, - 'user_id' => $user->ID, - 'comment_approved' => $approved, + 'comment_post_ID' => $post_id, + 'comment_content' => $content, + 'comment_author' => get_current_user_id() ? null : 'Anonymous', + 'comment_type' => 'comment', + 'user_id' => get_current_user_id(), ]); - if (!$comment_id) { - return new WP_Error('beep_insert_failed', 'Reply could not be saved.', ['status' => 500]); + return new WP_Error('insert_failed', 'Could not save reply.', ['status' => 500]); } - - $comment = get_comment($comment_id); - $pending = ($comment->comment_approved == 0); - - // Depth of this reply for client-side indenting. - $depth = 0; - $cur = $comment; - while ($cur && (int) $cur->comment_parent > 0 && $depth < 10) { - $cur = get_comment((int) $cur->comment_parent); - $depth++; - } - $can_interact = Beep_Likes::can_interact(); - - return rest_ensure_response([ - 'id' => $comment_id, - 'parent' => (int) $comment->comment_parent, - 'pending' => $pending, - 'html' => $pending ? '' : beep_render_reply($comment, [], $depth, $post_id, $can_interact), - ]); + return rest_ensure_response(['id' => $comment_id]); } public static function delete_beep($request) { $post_id = (int) $request['id']; - if (get_post_type($post_id) !== Beep_CPT::POST_TYPE) { - return new WP_Error('beep_not_a_beep', 'Not a beep.', ['status' => 400]); + $post = get_post($post_id); + if (!$post || $post->post_type !== 'beep') { + return new WP_Error('invalid_post', 'Invalid beep.', ['status' => 400]); } $deleted = wp_delete_post($post_id, true); if (!$deleted) { - return new WP_Error('beep_delete_failed', 'Could not delete.', ['status' => 500]); + return new WP_Error('delete_failed', 'Could not delete.', ['status' => 500]); } return rest_ensure_response(['deleted' => true]); } @@ -170,7 +90,7 @@ class Beep_Replies { $comment_id = (int) $request['id']; $deleted = wp_delete_comment($comment_id, true); if (!$deleted) { - return new WP_Error('beep_delete_failed', 'Could not delete.', ['status' => 500]); + return new WP_Error('delete_failed', 'Could not delete.', ['status' => 500]); } return rest_ensure_response(['deleted' => true]); } @@ -187,16 +107,11 @@ class Beep_Replies { public static function reply_count($post_id) { return (int) get_comments_number($post_id); } -} - /** - * Get the full conversation/thread for a beep. - * Returns the root beep and all nested replies in tree order. - */ public static function get_thread($post_id, $depth = 0, $max_depth = 10) { if ($depth > $max_depth) return []; $post = get_post($post_id); - if (!$post || $post->post_type !== Beep_CPT::POST_TYPE) return []; + if (!$post || $post->post_type !== 'beep') return []; $thread = [self::format_beep_reply($post, $depth)]; $replies = get_comments([ 'post_id' => $post_id, 'status' => 'approve', 'order' => 'ASC', @@ -225,6 +140,12 @@ class Beep_Replies { return $result; } + public static function get_thread_api($request) { + $post_id = (int) $request['post_id']; + $thread = self::get_thread($post_id); + return rest_ensure_response(['thread' => $thread]); + } + public static function format_beep_reply($post, $depth = 0) { $author_id = $post->post_author; $author = get_userdata($author_id);