PluginProbe
OpenStation: Desktop Windows, Dock & Virtual Desktops for WP Admin / 1.1.5
OpenStation: Desktop Windows, Dock & Virtual Desktops for WP Admin v1.1.5
1.1.10 1.1.9 1.1.8 1.1.7 1.1.6 1.1.5 1.1.4 1.1.3 1.1.2 1.1.1 1.1.0 1.0.1 1.0.0 0.9.8 0.9.7 0.9.6 0.9.4 0.9.5 0.9.3 0.9.2 0.9.1 0.9.0 0.8.9 0.8.8 0.8.7 All 34 releases
desktop-mode / includes / my-wordpress / comment-stats.php

comment-stats.php in OpenStation: Desktop Windows, Dock & Virtual Desktops for WP Admin 1.1.5, at includes/my-wordpress/comment-stats.php

260 lines 8.2 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * OpenStation — My WordPress: per-comment dossier endpoint.
4 *
5 * `GET /desktop-mode/v1/comment-stats/<id>` returns the rendered
6 * comment + author + parent post + thread context + reply tree +
7 * a count of how active the author has been across the site. Powers
8 * the right preview pane in the My WordPress folder when a comment
9 * is selected.
10 *
11 * Permissions:
12 * - The comment must be readable by the current user (approved,
13 * OR the user can `moderate_comments`, OR they're the comment
14 * author).
15 * - Author email / IP / user-agent only ship to viewers with
16 * `moderate_comments`.
17 *
18 * @package OpenStation
19 */
20
21 defined( 'ABSPATH' ) || exit;
22
23 /**
24 * Register the route.
25 */
26 function openstation_my_wordpress_register_comment_stats_route() {
27 register_rest_route(
28 'desktop-mode/v1',
29 '/comment-stats/(?P<id>\d+)',
30 array(
31 'methods' => WP_REST_Server::READABLE,
32 'callback' => 'openstation_my_wordpress_comment_stats_callback',
33 'permission_callback' => static function () {
34 return is_user_logged_in();
35 },
36 'args' => array(
37 'id' => array(
38 'required' => true,
39 'type' => 'integer',
40 'sanitize_callback' => 'absint',
41 ),
42 ),
43 )
44 );
45 }
46 add_action( 'rest_api_init', 'openstation_my_wordpress_register_comment_stats_route' );
47
48 /**
49 * Aggregator callback.
50 *
51 * @param WP_REST_Request $request REST request.
52 * @return array|WP_Error
53 */
54 function openstation_my_wordpress_comment_stats_callback( $request ) {
55 global $wpdb;
56 $comment_id = (int) $request->get_param( 'id' );
57 $comment = get_comment( $comment_id );
58 if ( ! $comment ) {
59 return new WP_Error(
60 'openstation_comment_not_found',
61 __( 'Comment not found.', 'desktop-mode' ),
62 array( 'status' => 404 )
63 );
64 }
65
66 $can_moderate = current_user_can( 'moderate_comments' );
67 $is_approved = '1' === (string) $comment->comment_approved;
68 $is_self = is_user_logged_in()
69 && (int) get_current_user_id() === (int) $comment->user_id
70 && (int) $comment->user_id > 0;
71
72 if ( ! $is_approved && ! $can_moderate && ! $is_self ) {
73 return new WP_Error(
74 'openstation_comment_forbidden',
75 __( 'You do not have permission to view this comment.', 'desktop-mode' ),
76 array( 'status' => 403 )
77 );
78 }
79
80 // ----- Comment body ------------------------------------------------
81 /** This filter is documented in wp-includes/comment-template.php */
82 $content_filtered = apply_filters( 'comment_text', $comment->comment_content, $comment, array() ); // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound -- Core's filter, applied so comment bodies render as they do everywhere else.
83 $body = array(
84 'id' => (int) $comment->comment_ID,
85 'parent' => (int) $comment->comment_parent,
86 'date' => mysql2date( 'c', $comment->comment_date_gmt, false ),
87 'status' => $is_approved
88 ? 'approved'
89 : ( '0' === (string) $comment->comment_approved
90 ? 'pending'
91 : (string) $comment->comment_approved ),
92 'rendered' => (string) $content_filtered,
93 'rendered_raw' => (string) $comment->comment_content,
94 'editLink' => $can_moderate
95 ? esc_url_raw(
96 admin_url(
97 'comment.php?action=editcomment&c=' . $comment->comment_ID
98 )
99 )
100 : '',
101 );
102 if ( $can_moderate ) {
103 $body['type'] = (string) $comment->comment_type;
104 $body['ip'] = (string) $comment->comment_author_IP;
105 $body['userAgent'] = (string) $comment->comment_agent;
106 $body['karma'] = (int) $comment->comment_karma;
107 }
108
109 // ----- Author ------------------------------------------------------
110 $author = array(
111 'name' => (string) $comment->comment_author,
112 'url' => esc_url_raw( (string) $comment->comment_author_url ),
113 'avatarUrl' => (string) get_avatar_url(
114 $comment,
115 array( 'size' => 96 )
116 ),
117 'userId' => (int) $comment->user_id,
118 );
119 if ( $can_moderate ) {
120 $author['email'] = (string) $comment->comment_author_email;
121 }
122 if ( $author['userId'] > 0 ) {
123 $user = get_userdata( $author['userId'] );
124 if ( $user ) {
125 $author['displayName'] = $user->display_name;
126 $author['profileLink'] = get_author_posts_url( $user->ID );
127 }
128 }
129
130 // ----- Parent post -------------------------------------------------
131 $post = get_post( (int) $comment->comment_post_ID );
132 $post_payload = null;
133 if ( $post ) {
134 $post_author = $post->post_author > 0
135 ? get_userdata( (int) $post->post_author )
136 : null;
137 $post_payload = array(
138 'id' => (int) $post->ID,
139 'title' => get_the_title( $post ),
140 'link' => (string) get_permalink( $post ),
141 'editLink' => current_user_can( 'edit_post', $post->ID )
142 ? (string) get_edit_post_link( $post->ID, 'raw' )
143 : '',
144 'status' => (string) $post->post_status,
145 'type' => (string) $post->post_type,
146 'date' => mysql2date( 'c', $post->post_date_gmt, false ),
147 'author' => $post_author
148 ? array(
149 'id' => (int) $post_author->ID,
150 'name' => $post_author->display_name,
151 'avatarUrl' => (string) get_avatar_url(
152 $post_author->ID,
153 array( 'size' => 48 )
154 ),
155 )
156 : null,
157 );
158 }
159
160 // ----- Parent comment (if this is a reply) -------------------------
161 $parent_payload = null;
162 if ( (int) $comment->comment_parent > 0 ) {
163 $parent_comment = get_comment( (int) $comment->comment_parent );
164 if ( $parent_comment ) {
165 $parent_payload = array(
166 'id' => (int) $parent_comment->comment_ID,
167 'authorName' => (string) $parent_comment->comment_author,
168 'date' => mysql2date( 'c', $parent_comment->comment_date_gmt, false ),
169 'excerpt' => wp_trim_words(
170 wp_strip_all_tags( $parent_comment->comment_content ),
171 40
172 ),
173 );
174 }
175 }
176
177 // ----- Replies (direct children) -----------------------------------
178 // Static SQL literal — must not go through a %s placeholder, which
179 // would quote it into an adjacent string literal and break the clause.
180 $reply_status_sql = $can_moderate
181 ? "comment_approved IN ( '0', '1' )"
182 : "comment_approved = '1'";
183 $reply_rows = $wpdb->get_results(
184 // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- $reply_status_sql is a fixed literal chosen above; no user input.
185 $wpdb->prepare(
186 "SELECT comment_ID, comment_author, comment_author_email,
187 comment_date_gmt, comment_content, comment_approved, user_id
188 FROM {$wpdb->comments}
189 WHERE comment_parent = %d
190 AND {$reply_status_sql}
191 ORDER BY comment_date_gmt ASC
192 LIMIT 20",
193 $comment->comment_ID
194 ),
195 ARRAY_A
196 );
197 $replies = array();
198 foreach ( (array) $reply_rows as $row ) {
199 $replies[] = array(
200 'id' => (int) $row['comment_ID'],
201 'authorName' => (string) $row['comment_author'],
202 'avatarUrl' => (string) get_avatar_url(
203 $row['comment_author_email'],
204 array( 'size' => 32 )
205 ),
206 'date' => mysql2date( 'c', (string) $row['comment_date_gmt'], false ),
207 'excerpt' => wp_trim_words(
208 wp_strip_all_tags( (string) $row['comment_content'] ),
209 40
210 ),
211 'status' => '1' === (string) $row['comment_approved']
212 ? 'approved'
213 : (string) $row['comment_approved'],
214 );
215 }
216
217 // ----- Author activity --------------------------------------------
218 // "How busy is this commenter site-wide?" — total approved
219 // comments by this email (or user_id when logged in).
220 $author_total = 0;
221 if ( $author['userId'] > 0 ) {
222 $author_total = (int) $wpdb->get_var(
223 $wpdb->prepare(
224 "SELECT COUNT(*) FROM {$wpdb->comments}
225 WHERE user_id = %d AND comment_approved = '1'",
226 $author['userId']
227 )
228 );
229 } elseif ( ! empty( $comment->comment_author_email ) ) {
230 $author_total = (int) $wpdb->get_var(
231 $wpdb->prepare(
232 "SELECT COUNT(*) FROM {$wpdb->comments}
233 WHERE comment_author_email = %s AND comment_approved = '1'",
234 (string) $comment->comment_author_email
235 )
236 );
237 }
238 $author['totalApprovedComments'] = $author_total;
239
240 $payload = array(
241 'comment' => $body,
242 'author' => $author,
243 'post' => $post_payload,
244 'parent' => $parent_payload,
245 'replies' => $replies,
246 );
247
248 /**
249 * Filter the per-comment dossier payload.
250 *
251 * @param array $payload Stats payload.
252 * @param int $comment_id Comment id.
253 */
254 return apply_filters(
255 'openstation_my_wordpress_comment_stats',
256 $payload,
257 $comment_id
258 );
259 }
260