PluginProbe
bbPress / 2.6.19
bbPress v2.6.19
2.6.19 2.6.18 2.6.17 trunk 2.0 2.0-beta-1 2.0-beta-2b 2.0-beta-3 2.0-beta-3b 2.0-rc-2 2.0-rc-3 2.0-rc-4 2.0-rc-5 2.0.1 2.0.2 2.0.3 2.1 2.1-beta-1 2.1-rc1 2.1-rc2 2.1-rc3 2.1-rc4 2.1.1 2.1.2 2.1.3 All 74 releases
bbpress / includes / admin / replies.php

replies.php in bbPress 2.6.19, at includes/admin/replies.php

1,220 lines 39.5 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 /**
4 * bbPress Replies Admin Class
5 *
6 * @package bbPress
7 * @subpackage Administration
8 */
9
10 // Exit if accessed directly
11 defined( 'ABSPATH' ) || exit;
12
13 if ( ! class_exists( 'BBP_Replies_Admin' ) ) :
14 /**
15 * Loads bbPress replies admin area
16 *
17 * @package bbPress
18 * @subpackage Administration
19 * @since 2.0.0 bbPress (r3095)
20 */
21 class BBP_Replies_Admin {
22
23 /** Variables *************************************************************/
24
25 /**
26 * @var The post type of this admin component
27 */
28 private $post_type = '';
29
30 private $accepted_topic_moves = array();
31
32 /** Functions *************************************************************/
33
34 /**
35 * The main bbPress admin loader
36 *
37 * @since 2.0.0 bbPress (r3095)
38 */
39 public function __construct() {
40 $this->setup_globals();
41 $this->setup_actions();
42 }
43
44 /**
45 * Setup the admin hooks, actions and filters
46 *
47 * @since 2.0.0 bbPress (r3376)
48 * @since 2.6.0 bbPress (r6101) Added bulk actions
49 *
50 * @access private
51 */
52 private function setup_actions() {
53
54 // Messages
55 add_filter( 'post_updated_messages', array( $this, 'updated_messages' ) );
56
57 // Reply bulk actions, added in WordPress 4.7, see #WP16031
58 if ( bbp_get_major_wp_version() >= 4.7 ) {
59 add_filter( 'bulk_actions-edit-reply', array( $this, 'bulk_actions' ) );
60 add_filter( 'handle_bulk_actions-edit-reply', array( $this, 'handle_bulk_actions' ), 10, 3 );
61 add_filter( 'bulk_post_updated_messages', array( $this, 'bulk_post_updated_messages' ), 10, 2 );
62 }
63
64 // Reply column headers.
65 add_filter( 'manage_' . $this->post_type . '_posts_columns', array( $this, 'column_headers' ) );
66
67 // Reply columns (in post row)
68 add_action( 'manage_' . $this->post_type . '_posts_custom_column', array( $this, 'column_data' ), 10, 2 );
69 add_filter( 'post_row_actions', array( $this, 'row_actions' ), 10, 2 );
70
71 // Reply meta-box actions
72 add_action( 'add_meta_boxes', array( $this, 'attributes_metabox' ) );
73 add_action( 'add_meta_boxes', array( $this, 'author_metabox' ) );
74 add_action( 'add_meta_boxes', array( $this, 'comments_metabox' ) );
75 add_action( 'save_post', array( $this, 'save_meta_boxes' ) );
76 add_filter( 'wp_insert_post_data', array( $this, 'filter_post_data' ), 20, 2 );
77
78 // Check if there are any bbp_toggle_reply_* requests on admin_init, also have a message displayed
79 add_action( 'load-edit.php', array( $this, 'toggle_reply' ) );
80 add_action( 'load-edit.php', array( $this, 'toggle_reply_notice' ) );
81
82 // Add ability to filter topics and replies per forum
83 add_filter( 'restrict_manage_posts', array( $this, 'filter_dropdown' ) );
84 add_filter( 'bbp_request', array( $this, 'filter_post_rows' ) );
85
86 // Empty spam
87 add_filter( 'manage_posts_extra_tablenav', array( $this, 'filter_empty_spam' ) );
88
89 // Contextual Help
90 add_action( 'load-edit.php', array( $this, 'edit_help' ) );
91 add_action( 'load-post.php', array( $this, 'new_help' ) );
92 add_action( 'load-post-new.php', array( $this, 'new_help' ) );
93 }
94
95 /**
96 * Admin globals
97 *
98 * @since 2.0.0 bbPress (r3376)
99 *
100 * @access private
101 */
102 private function setup_globals() {
103 $this->post_type = bbp_get_reply_post_type();
104 }
105
106 /** Contextual Help *******************************************************/
107
108 /**
109 * Contextual help for bbPress reply edit page
110 *
111 * @since 2.1.0 bbPress (r3686)
112 */
113 public function edit_help() {
114
115 // Overview
116 get_current_screen()->add_help_tab(
117 array(
118 'id' => 'overview',
119 'title' => __( 'Overview', 'bbpress' ),
120 'content' =>
121 '<p>' . __( 'This screen provides access to all of your replies. You can customize the display of this screen to suit your workflow.', 'bbpress' ) . '</p>'
122 )
123 );
124
125 // Screen Content
126 get_current_screen()->add_help_tab(
127 array(
128 'id' => 'screen-content',
129 'title' => __( 'Screen Content', 'bbpress' ),
130 'content' =>
131 '<p>' . __( 'You can customize the display of this screen&#8217;s contents in a number of ways:', 'bbpress' ) . '</p>' .
132 '<ul>' .
133 '<li>' . __( 'You can hide/display columns based on your needs and decide how many replies to list per screen using the Screen Options tab.', 'bbpress' ) . '</li>' .
134 '<li>' . __( 'You can filter the list of replies by reply status using the text links in the upper left to show All, Published, Draft, Pending, Trashed, or Spam replies. The default view is to show all replies.', 'bbpress' ) . '</li>' .
135 '<li>' . __( 'You can view replies in a simple title list or with an excerpt. Choose the view you prefer by clicking on the icons at the top of the list on the right.', 'bbpress' ) . '</li>' .
136 '<li>' . __( 'You can refine the list to show only replies in a specific forum or from a specific month by using the dropdown menus above the replies list. Click the Filter button after making your selection.', 'bbpress' ) . '</li>' .
137 '</ul>'
138 )
139 );
140
141 // Available Actions
142 get_current_screen()->add_help_tab(
143 array(
144 'id' => 'action-links',
145 'title' => __( 'Available Actions', 'bbpress' ),
146 'content' =>
147 '<p>' . __( 'Hovering over a row in the replies list will display action links that allow you to manage your reply. You can perform the following actions:', 'bbpress' ) . '</p>' .
148 '<ul>' .
149 '<li>' . __( '<strong>Edit</strong> takes you to the editing screen for that reply. You can also reach that screen by clicking on the reply title.', 'bbpress' ) . '</li>' .
150 //'<li>' . __( '<strong>Quick Edit</strong> provides inline access to the metadata of your reply, allowing you to update reply details without leaving this screen.', 'bbpress' ) . '</li>' .
151 '<li>' . __( '<strong>Trash</strong> removes your reply from this list and places it in the trash, from which you can permanently delete it.', 'bbpress' ) . '</li>' .
152 '<li>' . __( '<strong>Spam</strong> removes your reply from this list and places it in the spam queue, from which you can permanently delete it.', 'bbpress' ) . '</li>' .
153 '<li>' . __( '<strong>View</strong> will take you to your live site to view the reply.', 'bbpress' ) . '</li>' .
154 '<li>' . __( '<strong>Approve</strong> will change the status from pending to publish.', 'bbpress' ) . '</li>' .
155 '</ul>'
156 )
157 );
158
159 // Bulk Actions
160 get_current_screen()->add_help_tab(
161 array(
162 'id' => 'bulk-actions',
163 'title' => __( 'Bulk Actions', 'bbpress' ),
164 'content' =>
165 '<p>' . __( 'You can also edit, spam, or move multiple replies to the trash at once. Select the replies you want to act on using the checkboxes, then select the action you want to take from the Bulk Actions menu and click Apply.', 'bbpress' ) . '</p>' .
166 '<p>' . __( 'When using Bulk Edit, you can change the metadata (categories, author, etc.) for all selected replies at once. To remove a reply from the grouping, just click the x next to its name in the Bulk Edit area that appears.', 'bbpress' ) . '</p>'
167 )
168 );
169
170 // Help Sidebar
171 get_current_screen()->set_help_sidebar(
172 '<p><strong>' . __( 'For more information:', 'bbpress' ) . '</strong></p>' .
173 '<p>' . __( '<a href="https://codex.bbpress.org" target="_blank">bbPress Documentation</a>', 'bbpress' ) . '</p>' .
174 '<p>' . __( '<a href="https://bbpress.org/forums/" target="_blank">bbPress Support Forums</a>', 'bbpress' ) . '</p>'
175 );
176 }
177
178 /**
179 * Contextual help for bbPress reply edit page
180 *
181 * @since 2.1.0 bbPress (r3686)
182 */
183 public function new_help() {
184
185 $customize_display = '<p>' . __( 'The title field and the big reply editing Area are fixed in place, but you can reposition all the other boxes using drag and drop, and can minimize or expand them by clicking the title bar of each box. Use the Screen Options tab to unhide more boxes (Excerpt, Send Trackbacks, Custom Fields, Discussion, Slug, Author) or to choose a 1- or 2-column layout for this screen.', 'bbpress' ) . '</p>';
186
187 get_current_screen()->add_help_tab(
188 array(
189 'id' => 'customize-display',
190 'title' => __( 'Customizing This Display', 'bbpress' ),
191 'content' => $customize_display,
192 )
193 );
194
195 get_current_screen()->add_help_tab(
196 array(
197 'id' => 'title-reply-editor',
198 'title' => __( 'Title and Reply Editor', 'bbpress' ),
199 'content' =>
200 '<p>' . __( '<strong>Title</strong> - Enter a title for your reply. After you enter a title, you&#8217;ll see the permalink below, which you can edit.', 'bbpress' ) . '</p>' .
201 '<p>' . __( '<strong>Reply Editor</strong> - Enter the text for your reply. There are two modes of editing: Visual and HTML. Choose the mode by clicking on the appropriate tab. Visual mode gives you a WYSIWYG editor. Click the last icon in the row to get a second row of controls. The HTML mode allows you to enter raw HTML along with your reply text. You can insert media files by clicking the icons above the reply editor and following the directions. You can go to the distraction-free writing screen via the Fullscreen icon in Visual mode (second to last in the top row) or the Fullscreen button in HTML mode (last in the row). Once there, you can make buttons visible by hovering over the top area. Exit Fullscreen back to the regular reply editor.', 'bbpress' ) . '</p>'
202 )
203 );
204
205 $publish_box = '<p>' . __( '<strong>Publish</strong> - You can set the terms of publishing your reply in the Publish box. For Status, Visibility, and Publish (immediately), click on the Edit link to reveal more options. Visibility includes options for password-protecting a reply or making it stay at the top of your blog indefinitely (sticky). Publish (immediately) allows you to set a future or past date and time, so you can schedule a reply to be published in the future or backdate a reply.', 'bbpress' ) . '</p>';
206
207 if ( current_theme_supports( 'reply-thumbnails' ) && post_type_supports( bbp_get_reply_post_type(), 'thumbnail' ) ) {
208 $publish_box .= '<p>' . __( '<strong>Featured Image</strong> - This allows you to associate an image with your reply without inserting it. This is usually useful only if your theme makes use of the featured image as a reply thumbnail on the home page, a custom header, etc.', 'bbpress' ) . '</p>';
209 }
210
211 get_current_screen()->add_help_tab(
212 array(
213 'id' => 'reply-attributes',
214 'title' => __( 'Reply Attributes', 'bbpress' ),
215 'content' =>
216 '<p>' . __( 'Select the attributes that your reply should have:', 'bbpress' ) . '</p>' .
217 '<ul>' .
218 '<li>' . __( '<strong>Forum</strong> dropdown determines the parent forum that the reply belongs to. Select the forum, or leave the default (Use Forum of Topic) to post the reply in forum of the topic.', 'bbpress' ) . '</li>' .
219 '<li>' . __( '<strong>Topic</strong> determines the parent topic that the reply belongs to.', 'bbpress' ) . '</li>' .
220 '<li>' . __( '<strong>Reply To</strong> determines the threading of the reply.', 'bbpress' ) . '</li>' .
221 '</ul>'
222 )
223 );
224
225 get_current_screen()->add_help_tab(
226 array(
227 'id' => 'publish-box',
228 'title' => __( 'Publish Box', 'bbpress' ),
229 'content' => $publish_box,
230 )
231 );
232
233 get_current_screen()->set_help_sidebar(
234 '<p><strong>' . __( 'For more information:', 'bbpress' ) . '</strong></p>' .
235 '<p>' . __( '<a href="https://codex.bbpress.org" target="_blank">bbPress Documentation</a>', 'bbpress' ) . '</p>' .
236 '<p>' . __( '<a href="https://bbpress.org/forums/" target="_blank">bbPress Support Forums</a>', 'bbpress' ) . '</p>'
237 );
238 }
239
240 /**
241 * Add spam/unspam bulk actions to the bulk action dropdown.
242 *
243 * @since 2.6.0 bbPress (r6101)
244 *
245 * @param array $actions The list of bulk actions.
246 * @return array The filtered list of bulk actions.
247 */
248 public function bulk_actions( $actions ) {
249
250 if ( current_user_can( 'moderate' ) ) {
251 if ( bbp_get_spam_status_id() === get_query_var( 'post_status' ) ) {
252 $actions['unspam'] = esc_html__( 'Unspam', 'bbpress' );
253 } else {
254 $actions['spam'] = esc_html__( 'Spam', 'bbpress' );
255 }
256 }
257
258 return $actions;
259 }
260
261 /**
262 * Add custom bulk action updated messages for replies.
263 *
264 * @since 2.6.0 bbPress (r6101)
265 *
266 * @param array $bulk_messages Arrays of messages, each keyed by the corresponding post type.
267 * @param array $bulk_counts Array of item counts for each message, used to build internationalized strings.
268 */
269 public function bulk_post_updated_messages( $bulk_messages, $bulk_counts ) {
270 /* translators: %s: Number of replies */
271 $bulk_messages['reply']['updated'] = _n( '%s reply updated.', '%s replies updated.', $bulk_counts['updated'], 'bbpress');
272 $bulk_messages['reply']['locked'] = ( 1 === $bulk_counts['locked'] )
273 ? __( '1 reply not updated, somebody is editing it.', 'bbpress' )
274 /* translators: %s: Number of replies */
275 : _n( '%s reply not updated, somebody is editing it.', '%s replies not updated, somebody is editing them.', $bulk_counts['locked'], 'bbpress' );
276
277 return $bulk_messages;
278 }
279
280 /**
281 * Handle spam/unspam bulk actions.
282 *
283 * @since 2.6.0 bbPress (r6101)
284 *
285 * @param string $sendback The sendback URL.
286 * @param string $doaction The action to be taken.
287 * @param array $post_ids The post IDS to take the action on.
288 * @return string The sendback URL.
289 */
290 public function handle_bulk_actions( $sendback, $doaction, $post_ids ) {
291
292 $sendback = remove_query_arg( array( 'spam', 'unspam' ), $sendback );
293 $updated = $locked = 0;
294
295 if ( 'spam' === $doaction ) {
296
297 foreach ( (array) $post_ids as $post_id ) {
298 if ( ! current_user_can( 'moderate', $post_id ) ) {
299 wp_die( esc_html__( 'Sorry, you are not allowed to spam this item.', 'bbpress' ) );
300 }
301
302 if ( wp_check_post_lock( $post_id ) ) {
303 $locked++;
304 continue;
305 }
306
307 if ( ! bbp_spam_reply( $post_id ) ) {
308 wp_die( esc_html__( 'Error in spamming reply.', 'bbpress' ) );
309 }
310
311 ++$updated;
312 }
313
314 $sendback = add_query_arg(
315 array(
316 'updated' => $updated,
317 'ids' => implode( ',', $post_ids ),
318 'locked' => $locked
319 ),
320 $sendback
321 );
322
323 } elseif ( 'unspam' === $doaction ) {
324
325 foreach ( (array) $post_ids as $post_id ) {
326 if ( ! current_user_can( 'moderate', $post_id ) ) {
327 wp_die( esc_html__( 'Sorry, you are not allowed to unspam this reply.', 'bbpress' ) );
328 }
329
330 if ( wp_check_post_lock( $post_id ) ) {
331 $locked++;
332 continue;
333 }
334
335 if ( ! bbp_unspam_reply( $post_id ) ) {
336 wp_die( esc_html__( 'Error in unspamming reply.', 'bbpress' ) );
337 }
338
339 ++$updated;
340 }
341
342 $sendback = add_query_arg(
343 array(
344 'updated' => $updated,
345 'ids' => implode( ',', $post_ids ),
346 'locked' => $locked
347 ),
348 $sendback
349 );
350 }
351
352 return $sendback;
353 }
354
355 /**
356 * Add the reply attributes meta-box
357 *
358 * @since 2.4.0 bbPress (r4991)
359 */
360 public function attributes_metabox() {
361 add_meta_box(
362 'bbp_reply_attributes',
363 esc_html__( 'Reply Attributes', 'bbpress' ),
364 'bbp_reply_metabox',
365 $this->post_type,
366 'side',
367 'high'
368 );
369 }
370
371 /**
372 * Add the author info meta-box
373 *
374 * Allows editing of information about an author
375 *
376 * @since 2.0.0 bbPress (r3120)
377 */
378 public function author_metabox() {
379
380 // Bail if post_type is not a reply
381 if ( empty( $_GET['action'] ) || ( 'edit' !== $_GET['action'] ) ) {
382 return;
383 }
384
385 // Add the meta-box
386 add_meta_box(
387 'bbp_author_metabox',
388 esc_html__( 'Author Information', 'bbpress' ),
389 'bbp_author_metabox',
390 $this->post_type,
391 'side',
392 'high'
393 );
394 }
395
396 /**
397 * Remove comments & discussion meta-boxes if comments are not supported
398 *
399 * @since 2.6.0 bbPress (r6186)
400 */
401 public function comments_metabox() {
402 if ( ! post_type_supports( $this->post_type, 'comments' ) ) {
403 remove_meta_box( 'commentstatusdiv', $this->post_type, 'normal' );
404 remove_meta_box( 'commentsdiv', $this->post_type, 'normal' );
405 }
406 }
407
408 /**
409 * Pass the reply attributes for processing
410 *
411 * @since 2.6.0 bbPress (r6056)
412 *
413 * @param int $reply_id Reply id
414 * @return int Parent id
415 */
416 public function save_meta_boxes( $reply_id ) {
417 $accepted_move = isset( $this->accepted_topic_moves[ $reply_id ] )
418 ? $this->accepted_topic_moves[ $reply_id ]
419 : array();
420 unset( $this->accepted_topic_moves[ $reply_id ] );
421
422 // Revisions can also fire save_post with the metabox nonce.
423 if ( ! bbp_is_reply( $reply_id ) ) {
424 return $reply_id;
425 }
426
427 // Bail if doing an autosave
428 if ( bbp_doing_autosave() ) {
429 return $reply_id;
430 }
431
432 // Bail if not a post request
433 if ( ! bbp_is_post_request() ) {
434 return $reply_id;
435 }
436
437 // Check action exists
438 if ( empty( $_POST['action'] ) ) {
439 return $reply_id;
440 }
441
442 // Nonce check
443 if ( empty( $_POST['bbp_reply_metabox'] ) || ! wp_verify_nonce( $_POST['bbp_reply_metabox'], 'bbp_reply_metabox_save' ) ) {
444 return $reply_id;
445 }
446
447 // Bail if current user cannot edit this reply
448 if ( ! current_user_can( 'edit_reply', $reply_id ) && empty( $accepted_move ) ) {
449 return $reply_id;
450 }
451
452 // Use the accepted topic and its forum, rather than independent form values.
453 $topic_id = ! empty( $accepted_move )
454 ? $accepted_move['new']
455 : bbp_get_reply_topic_id( $reply_id );
456 $forum_id = bbp_get_topic_forum_id( $topic_id );
457 $reply_to = ! empty( $_POST['bbp_reply_to'] ) ? bbp_validate_reply_to( (int) $_POST['bbp_reply_to'], $reply_id ) : 0;
458 if ( ! empty( $reply_to ) && ( bbp_get_reply_topic_id( $reply_to ) !== $topic_id || ! current_user_can( 'read_reply', $reply_to ) ) ) {
459 $reply_to = 0;
460 }
461
462 // Get reply author data
463 $anonymous_data = bbp_filter_anonymous_post_data();
464 $author_id = bbp_get_reply_author_id( $reply_id );
465 $is_edit = ( isset( $_POST['hidden_post_status'] ) && ( 'draft' !== $_POST['hidden_post_status'] ) );
466
467 // Formally update the reply
468 bbp_update_reply( $reply_id, $topic_id, $forum_id, $anonymous_data, $author_id, $is_edit, $reply_to );
469 if ( ! empty( $accepted_move['old'] ) ) {
470 bbp_update_reply_position( $reply_id );
471 bbp_move_reply_count( $reply_id, $accepted_move['old'], $accepted_move['new'] );
472 }
473
474 // Allow other fun things to happen
475 do_action( 'bbp_reply_attributes_metabox_save', $reply_id, $topic_id, $forum_id, $reply_to );
476 do_action( 'bbp_author_metabox_save', $reply_id, $anonymous_data );
477
478 return $reply_id;
479 }
480
481 /**
482 * Keep admin reply moves within topics the current user may moderate.
483 *
484 * @since 2.6.19 bbPress (r7688)
485 *
486 * @param array $data Sanitized post data.
487 * @param array $postarr Unprocessed post data.
488 * @return array Filtered post data.
489 */
490 public function filter_post_data( $data, $postarr ) {
491
492 // Only filter administration saves of existing replies.
493 if ( ! is_admin() || empty( $postarr['ID'] ) || ( bbp_get_reply_post_type() !== $data['post_type'] ) ) {
494 return $data;
495 }
496
497 $reply = bbp_get_reply( $postarr['ID'] );
498 if ( empty( $reply ) || (int) $reply->post_parent === (int) $data['post_parent'] ) {
499 return $data;
500 }
501
502 $old_topic_id = (int) $reply->post_parent;
503 $new_topic_id = (int) $data['post_parent'];
504 $new_topic = bbp_get_topic( $new_topic_id );
505 $new_forum_id = bbp_get_topic_forum_id( $new_topic_id );
506 $is_new = ( 'auto-draft' === $reply->post_status );
507 $is_editor = ! empty( $_POST['action'] ) && ( 'editpost' === $_POST['action'] );
508
509 // Other admin save paths cannot complete a bbPress reply move.
510 if ( ! $is_editor && ! $is_new ) {
511 $data['post_parent'] = $reply->post_parent;
512 return $data;
513 }
514
515 // Match the reply move checks before WordPress changes post_parent.
516 if (
517 ! $is_editor
518 || empty( $_POST['bbp_reply_metabox'] )
519 || ! is_string( $_POST['bbp_reply_metabox'] )
520 || ! wp_verify_nonce( $_POST['bbp_reply_metabox'], 'bbp_reply_metabox_save' )
521 || ! current_user_can( 'edit_reply', $reply->ID )
522 || ( $is_new && ! current_user_can( 'publish_replies' ) )
523 || ( ! $is_new && ! empty( $old_topic_id ) && ( ! current_user_can( 'moderate', $old_topic_id ) || ! current_user_can( 'edit_topic', $old_topic_id ) ) )
524 || empty( $new_topic )
525 || ( $is_new && ( ! current_user_can( 'read_topic', $new_topic_id ) || ( bbp_is_topic_closed( $new_topic_id ) && ! current_user_can( 'edit_topic', $new_topic_id ) ) ) )
526 || ( $is_new && ( ! current_user_can( 'read_forum', $new_forum_id ) || ( bbp_is_forum_closed( $new_forum_id ) && ! current_user_can( 'edit_forum', $new_forum_id ) ) ) )
527 || ( ! $is_new && ( ! current_user_can( 'moderate', $new_topic_id ) || ! current_user_can( 'edit_topic', $new_topic_id ) ) )
528 ) {
529 if ( $is_new ) {
530 wp_die(
531 esc_html__( 'The selected topic is not available for this reply.', 'bbpress' ),
532 '',
533 array(
534 'response' => 403,
535 'back_link' => true,
536 )
537 );
538 }
539 $data['post_parent'] = $reply->post_parent;
540 } else {
541 $this->accepted_topic_moves[ $reply->ID ] = array(
542 'old' => $old_topic_id,
543 'new' => $new_topic_id,
544 );
545 }
546
547 return $data;
548 }
549
550 /**
551 * Toggle reply
552 *
553 * Handles the admin-side spamming/unspamming of replies
554 *
555 * @since 2.0.0 bbPress (r3095)
556 */
557 public function toggle_reply() {
558
559 // Bail if not a topic toggle action
560 if ( ! bbp_is_get_request() || empty( $_GET['action'] ) || empty( $_GET['reply_id'] ) ) {
561 return;
562 }
563
564 // Bail if not an allowed action
565 $action = sanitize_key( $_GET['action'] );
566 if ( empty( $action ) || ! in_array( $action, $this->get_allowed_action_toggles(), true ) ) {
567 return;
568 }
569
570 // Get reply and die if empty
571 $reply_id = bbp_get_reply_id( $_GET['reply_id'] );
572 if ( ! bbp_get_reply( $reply_id ) ) {
573 wp_die( esc_html__( 'The reply was not found.', 'bbpress' ) );
574 }
575
576 // What is the user doing here?
577 if ( ! current_user_can( 'moderate', $reply_id ) ) {
578 wp_die( esc_html__( 'You do not have permission to do that.', 'bbpress' ) );
579 }
580
581 // Defaults
582 $post_data = array( 'ID' => $reply_id );
583 $message = '';
584 $success = false;
585
586 switch ( $action ) {
587 case 'bbp_toggle_reply_approve' :
588 check_admin_referer( 'approve-reply_' . $reply_id );
589
590 $is_approve = bbp_is_reply_public( $reply_id );
591 $message = ( true === $is_approve )
592 ? 'unpproved'
593 : 'approved';
594 $success = ( true === $is_approve )
595 ? bbp_unapprove_reply( $reply_id )
596 : bbp_approve_reply( $reply_id );
597
598 break;
599
600 case 'bbp_toggle_reply_spam' :
601 check_admin_referer( 'spam-reply_' . $reply_id );
602
603 $is_spam = bbp_is_reply_spam( $reply_id );
604 $message = ( true === $is_spam )
605 ? 'unspammed'
606 : 'spammed';
607 $success = ( true === $is_spam )
608 ? bbp_unspam_reply( $reply_id )
609 : bbp_spam_reply( $reply_id );
610
611 break;
612 }
613
614 // Setup the message
615 $retval = array(
616 'bbp_reply_toggle_notice' => $message,
617 'reply_id' => $reply_id
618 );
619
620 // Prepare for failure
621 if ( ( false === $success ) || is_wp_error( $success ) ) {
622 $retval['failed'] = '1';
623 }
624
625 // Filter all message args
626 $retval = apply_filters( 'bbp_toggle_reply_action_admin', $retval, $reply_id, $action );
627
628 // Do additional reply toggle actions (admin side)
629 do_action( 'bbp_toggle_reply_admin', $success, $post_data, $action, $retval );
630
631 // Redirect back to the reply
632 $redirect = add_query_arg( $retval, remove_query_arg( array( 'action', 'reply_id' ) ) );
633 bbp_redirect( $redirect );
634 }
635
636 /**
637 * Toggle reply notices
638 *
639 * Display the success/error notices from
640 * {@link BBP_Admin::toggle_reply()}
641 *
642 * @since 2.0.0 bbPress (r3095)
643 */
644 public function toggle_reply_notice() {
645
646 // Bail if missing reply toggle action
647 if ( ! bbp_is_get_request() || empty( $_GET['reply_id'] ) || empty( $_GET['bbp_reply_toggle_notice'] ) ) {
648 return;
649 }
650
651 // Bail if not an allowed notice
652 $notice = sanitize_key( $_GET['bbp_reply_toggle_notice'] );
653 if ( empty( $notice ) || ! in_array( $notice, $this->get_allowed_notice_toggles(), true ) ) {
654 return;
655 }
656
657 // Bail if no reply_id or notice
658 $reply_id = bbp_get_reply_id( $_GET['reply_id'] );
659 if ( empty( $reply_id ) ) {
660 return;
661 }
662
663 // Bail if reply is missing
664 if ( ! bbp_get_reply( $reply_id ) ) {
665 return;
666 }
667
668 // Use the title in the responses
669 $reply_title = bbp_get_reply_title( $reply_id );
670 $is_failure = ! empty( $_GET['failed'] );
671 $message = '';
672
673 switch ( $notice ) {
674 case 'spammed' :
675 $message = ( true === $is_failure )
676 /* translators: %s: Reply title */
677 ? sprintf( esc_html__( 'There was a problem marking the reply "%1$s" as spam.', 'bbpress' ), $reply_title )
678 /* translators: %s: Reply title */
679 : sprintf( esc_html__( 'Reply "%1$s" successfully marked as spam.', 'bbpress' ), $reply_title );
680 break;
681
682 case 'unspammed' :
683 $message = ( true === $is_failure )
684 /* translators: %s: Reply title */
685 ? sprintf( esc_html__( 'There was a problem unmarking the reply "%1$s" as spam.', 'bbpress' ), $reply_title )
686 /* translators: %s: Reply title */
687 : sprintf( esc_html__( 'Reply "%1$s" successfully unmarked as spam.', 'bbpress' ), $reply_title );
688 break;
689
690 case 'approved' :
691 $message = ( true === $is_failure )
692 /* translators: %s: Reply title */
693 ? sprintf( esc_html__( 'There was a problem approving the reply "%1$s".', 'bbpress' ), $reply_title )
694 /* translators: %s: Reply title */
695 : sprintf( esc_html__( 'Reply "%1$s" successfully approved.', 'bbpress' ), $reply_title );
696 break;
697
698 case 'unapproved' :
699 $message = ( true === $is_failure )
700 /* translators: %s: Reply title */
701 ? sprintf( esc_html__( 'There was a problem unapproving the reply "%1$s".', 'bbpress' ), $reply_title )
702 /* translators: %s: Reply title */
703 : sprintf( esc_html__( 'Reply "%1$s" successfully unapproved.', 'bbpress' ), $reply_title );
704 break;
705 }
706
707 // Do additional reply toggle notice filters (admin side)
708 $message = apply_filters( 'bbp_toggle_reply_notice_admin', $message, $reply_id, $notice, $is_failure );
709 $class = ( true === $is_failure )
710 ? 'error'
711 : 'updated';
712
713 // Add the notice
714 bbp_admin()->add_notice( $message, $class, true );
715 }
716
717 /**
718 * Returns an array of keys used to sort row actions
719 *
720 * @since 2.6.0 bbPress (r6772)
721 *
722 * @return array
723 */
724 private function get_row_action_sort_order() {
725
726 /**
727 * Filters the row action sort order for replies.
728 *
729 * @since 2.6.0 bbPress (r6772)
730 *
731 * @param array $order The default sort order.
732 */
733 return (array) apply_filters(
734 'bbp_admin_reply_row_action_sort_order',
735 array(
736 'edit',
737 'approved',
738 'unapproved',
739 'spam',
740 'unspam',
741 'trash',
742 'untrash',
743 'delete',
744 'view'
745 )
746 );
747 }
748
749 /**
750 * Returns an array of notice toggles
751 *
752 * @since 2.6.0 bbPress (r6397)
753 *
754 * @return array
755 */
756 private function get_allowed_notice_toggles() {
757
758 // Filter & return
759 return apply_filters(
760 'bbp_admin_replies_allowed_notice_toggles',
761 array(
762 'spammed',
763 'unspammed',
764 'approved',
765 'unapproved'
766 )
767 );
768 }
769
770 /**
771 * Returns an array of notice toggles
772 *
773 * @since 2.6.0 bbPress (r6397)
774 *
775 * @return array
776 */
777 private function get_allowed_action_toggles() {
778
779 // Filter & return
780 return apply_filters(
781 'bbp_admin_replies_allowed_action_toggles',
782 array(
783 'bbp_toggle_reply_spam',
784 'bbp_toggle_reply_approve'
785 )
786 );
787 }
788
789 /**
790 * Manage the column headers for the replies page
791 *
792 * @since 2.4.0 bbPress (r4991)
793 *
794 * @param array $columns The columns
795 *
796 * @return array $columns bbPress reply columns
797 */
798 public function column_headers( $columns ) {
799 $columns = array(
800 'cb' => '<input type="checkbox" />',
801 'title' => esc_html__( 'Title', 'bbpress' ),
802 'bbp_reply_forum' => esc_html__( 'Forum', 'bbpress' ),
803 'bbp_reply_topic' => esc_html__( 'Topic', 'bbpress' ),
804 'bbp_reply_author' => esc_html__( 'Author', 'bbpress' ),
805 'bbp_reply_created' => esc_html__( 'Created', 'bbpress' ),
806 );
807
808 // Filter & return
809 return apply_filters( 'bbp_admin_replies_column_headers', $columns );
810 }
811
812 /**
813 * Print extra columns for the replies page
814 *
815 * @since 2.4.0 bbPress (r4991)
816 *
817 * @param string $column Column
818 * @param int $reply_id reply id
819 */
820 public function column_data( $column, $reply_id ) {
821
822 // Get topic ID
823 $topic_id = bbp_get_reply_topic_id( $reply_id );
824
825 // Populate Column Data
826 switch ( $column ) {
827
828 // Topic
829 case 'bbp_reply_topic' :
830
831 // Get title
832 $topic_title = ! empty( $topic_id )
833 ? bbp_get_topic_title( $topic_id )
834 : '';
835
836 // Output topic name
837 if ( ! empty( $topic_title ) ) {
838 echo $topic_title;
839
840 // Output dash
841 } else {
842 ?>
843 <span aria-hidden="true">&mdash;</span>
844 <span class="screen-reader-text"><?php esc_html_e( 'No topic', 'bbpress' ); ?></span>
845 <?php
846 }
847
848 break;
849
850 // Forum
851 case 'bbp_reply_forum' :
852
853 // Get Forum ID's
854 $reply_forum_id = bbp_get_reply_forum_id( $reply_id );
855 $topic_forum_id = bbp_get_topic_forum_id( $topic_id );
856
857 // Forum Title
858 $forum_title = ! empty( $reply_forum_id )
859 ? bbp_get_forum_title( $reply_forum_id )
860 : '';
861
862 // Alert capable users of reply forum mismatch
863 if ( $reply_forum_id !== $topic_forum_id ) {
864 if ( current_user_can( 'edit_others_replies' ) || current_user_can( 'moderate', $reply_id ) ) {
865 $forum_title .= '<div class="attention">' . esc_html__( '(Mismatch)', 'bbpress' ) . '</div>';
866 }
867 }
868
869 // Output forum name
870 if ( ! empty( $forum_title ) ) {
871 echo $forum_title;
872
873 // Reply has no forum
874 } else {
875 ?>
876 <span aria-hidden="true">&mdash;</span>
877 <span class="screen-reader-text"><?php esc_html_e( 'No forum', 'bbpress' ); ?></span>
878 <?php
879 }
880
881 break;
882
883 // Author
884 case 'bbp_reply_author' :
885 bbp_reply_author_display_name( $reply_id );
886 break;
887
888 // Freshness
889 case 'bbp_reply_created':
890
891 // Output last activity time and date
892 printf( '%1$s <br /> %2$s',
893 get_the_date(),
894 esc_attr( get_the_time() )
895 );
896
897 break;
898
899 // Do action for anything else
900 default :
901 do_action( 'bbp_admin_replies_column_data', $column, $reply_id );
902 break;
903 }
904 }
905
906 /**
907 * Reply Row actions
908 *
909 * Remove the quick-edit action link under the reply title and add the
910 * content and spam link
911 *
912 * @since 2.4.0 bbPress (r4991)
913 *
914 * @param array $actions Actions
915 * @param object $reply Reply object
916 *
917 * @return array $actions Actions
918 */
919 public function row_actions( $actions = array(), $reply = false ) {
920
921 // Disable quick edit (too much to do here)
922 unset( $actions['inline hide-if-no-js'] );
923
924 // View link
925 $view_link = bbp_get_reply_url( $reply->ID );
926
927 // Maybe add view=all
928 if ( ! in_array( $reply->post_status, array( bbp_get_closed_status_id(), bbp_get_public_status_id() ), true ) ) {
929 $view_link = bbp_add_view_all( $view_link, true );
930 }
931
932 // Reply view links to topic
933 $view_title = sprintf(
934 /* translators: %s: Reply title */
935 __( 'View &#8220;%s&#8221;', 'bbpress' ),
936 bbp_get_reply_title( $reply->ID )
937 );
938
939 $actions['view'] = sprintf(
940 '<a href="%1$s" title="%2$s" rel="permalink">%3$s</a>',
941 esc_url( $view_link ),
942 esc_attr( $view_title ),
943 esc_html__( 'View', 'bbpress' )
944 );
945
946 // User cannot view replies in trash
947 if ( ( bbp_get_trash_status_id() === $reply->post_status ) && ! current_user_can( 'view_trash' ) ) {
948 unset( $actions['view'] );
949 }
950
951 // Only show the actions if the user is capable of viewing them
952 if ( current_user_can( 'moderate', $reply->ID ) ) {
953
954 // Show the 'approve' link on non-published posts only and 'unapprove' on published posts only
955 $args = array(
956 'reply_id' => $reply->ID,
957 'action' => 'bbp_toggle_reply_approve'
958 );
959 $approve_uri = wp_nonce_url( add_query_arg( $args, remove_query_arg( array( 'bbp_reply_toggle_notice', 'reply_id', 'failed', 'super' ) ) ), 'approve-reply_' . $reply->ID );
960 if ( bbp_is_reply_public( $reply->ID ) ) {
961 $actions['unapproved'] = '<a href="' . esc_url( $approve_uri ) . '" title="' . esc_attr__( 'Unapprove this reply', 'bbpress' ) . '">' . _x( 'Unapprove', 'Unapprove reply', 'bbpress' ) . '</a>';
962 } else {
963 $actions['approved'] = '<a href="' . esc_url( $approve_uri ) . '" title="' . esc_attr__( 'Approve this reply', 'bbpress' ) . '">' . _x( 'Approve', 'Approve reply', 'bbpress' ) . '</a>';
964 }
965
966 // Show the 'spam' link on published and pending replies and 'not spam' on spammed replies
967 if ( in_array( $reply->post_status, array( bbp_get_public_status_id(), bbp_get_trash_status_id(), bbp_get_pending_status_id(), bbp_get_spam_status_id() ), true ) ) {
968 $args = array(
969 'reply_id' => $reply->ID,
970 'action' => 'bbp_toggle_reply_spam'
971 );
972 $spam_uri = wp_nonce_url( add_query_arg( $args, remove_query_arg( array( 'bbp_reply_toggle_notice', 'reply_id', 'failed', 'super' ) ) ), 'spam-reply_' . $reply->ID );
973 if ( ! bbp_is_reply_spam( $reply->ID ) ) {
974 $actions['spam'] = '<a href="' . esc_url( $spam_uri ) . '" title="' . esc_attr__( 'Mark this reply as spam', 'bbpress' ) . '">' . esc_html__( 'Spam', 'bbpress' ) . '</a>';
975 } else {
976 $actions['unspam'] = '<a href="' . esc_url( $spam_uri ) . '" title="' . esc_attr__( 'Mark the reply as not spam', 'bbpress' ) . '">' . esc_html__( 'Not Spam', 'bbpress' ) . '</a>';
977 }
978 }
979 }
980
981 // Trash
982 if ( current_user_can( 'delete_reply', $reply->ID ) ) {
983 $trash_days = bbp_get_trash_days( bbp_get_reply_post_type() );
984
985 if ( bbp_get_trash_status_id() === $reply->post_status ) {
986 $post_type_object = get_post_type_object( bbp_get_reply_post_type() );
987 $actions['untrash'] = "<a title='" . esc_attr__( 'Restore this item from the Trash', 'bbpress' ) . "' href='" . esc_url( wp_nonce_url( admin_url( sprintf( $post_type_object->_edit_link . '&amp;action=untrash', $reply->ID ) ), 'untrash-post_' . $reply->ID ) ) . "'>" . esc_html__( 'Restore', 'bbpress' ) . '</a>';
988 } elseif ( ! empty( $trash_days ) ) {
989 $actions['trash'] = "<a class='submitdelete' title='" . esc_attr__( 'Move this item to the Trash', 'bbpress' ) . "' href='" . esc_url( get_delete_post_link( $reply->ID ) ) . "'>" . esc_html__( 'Trash', 'bbpress' ) . '</a>';
990 }
991
992 if ( ( bbp_get_trash_status_id() === $reply->post_status ) || empty( $trash_days ) ) {
993 $actions['delete'] = "<a class='submitdelete' title='" . esc_attr__( 'Delete this item permanently', 'bbpress' ) . "' href='" . esc_url( get_delete_post_link( $reply->ID, '', true ) ) . "'>" . esc_html__( 'Delete Permanently', 'bbpress' ) . '</a>';
994 }
995 }
996
997 // Sort & return
998 return $this->sort_row_actions( $actions );
999 }
1000
1001 /**
1002 * Sort row actions by key
1003 *
1004 * @since 2.6.0 bbPress (r6772)
1005 *
1006 * @param array $actions
1007 *
1008 * @return array
1009 */
1010 private function sort_row_actions( $actions = array() ) {
1011
1012 // Return value
1013 $retval = array();
1014
1015 // Known row actions, in sort order
1016 $known_actions = $this->get_row_action_sort_order();
1017
1018 // Sort known actions, and keep any unknown ones
1019 foreach ( $known_actions as $key ) {
1020 if ( isset( $actions[ $key ] ) ) {
1021 $retval[ $key ] = $actions[ $key ];
1022 unset( $actions[ $key ] );
1023 }
1024 }
1025
1026 // Combine & return
1027 return $retval + $actions;
1028 }
1029
1030 /**
1031 * Add forum dropdown to topic and reply list table filters
1032 *
1033 * @since 2.0.0 bbPress (r3095)
1034 *
1035 * @return bool False. If post type is not topic or reply
1036 */
1037 public function filter_dropdown() {
1038
1039 // Get which forum is selected
1040 $selected = ! empty( $_GET['bbp_forum_id'] )
1041 ? (int) $_GET['bbp_forum_id']
1042 : 0;
1043
1044 // Show the forums dropdown
1045 bbp_dropdown(
1046 array(
1047 'selected' => $selected,
1048 'show_none' => esc_html__( 'In all forums', 'bbpress' )
1049 )
1050 );
1051 }
1052
1053 /**
1054 * Add "Empty Spam" button for moderators
1055 *
1056 * @since 2.6.0 bbPress (r6791)
1057 */
1058 public function filter_empty_spam() {
1059
1060 // Bail if not viewing spam
1061 if ( empty( $_GET['post_status'] ) || ( bbp_get_spam_status_id() !== $_GET['post_status'] ) && current_user_can( 'moderate' ) ) {
1062 return;
1063 }
1064
1065 ?>
1066
1067 <div class="alignleft actions"><?php
1068
1069 // Output the nonce & button
1070 wp_nonce_field( 'bulk-destroy', '_destroy_nonce' );
1071 submit_button(
1072 esc_attr__( 'Empty Spam', 'bbpress' ),
1073 'button-secondary apply',
1074 'delete_all',
1075 false
1076 );
1077
1078 ?></div><?php
1079 }
1080
1081 /**
1082 * Adjust the request query and include the forum id
1083 *
1084 * @since 2.0.0 bbPress (r3095)
1085 *
1086 * @param array $query_vars Query variables from {@link WP_Query}
1087 * @return array Processed Query Vars
1088 */
1089 public function filter_post_rows( $query_vars ) {
1090
1091 // Add post_parent query_var if one is present
1092 if ( ! empty( $_GET['bbp_forum_id'] ) ) {
1093 $query_vars['meta_key'] = '_bbp_forum_id';
1094 $query_vars['meta_type'] = 'NUMERIC';
1095 $query_vars['meta_value'] = (int) $_GET['bbp_forum_id'];
1096 }
1097
1098 // Return manipulated query_vars
1099 return $query_vars;
1100 }
1101
1102 /**
1103 * Custom user feedback messages for reply post type
1104 *
1105 * @since 2.0.0 bbPress (r3097)
1106 *
1107 * @global int $post_ID
1108 *
1109 * @param array $messages
1110 *
1111 * @return array
1112 */
1113 public function updated_messages( $messages ) {
1114 global $post_ID;
1115
1116 // URL for the current topic
1117 $topic_url = bbp_get_topic_permalink( bbp_get_reply_topic_id( $post_ID ) );
1118
1119 // Current reply's post_date
1120 $post_date = bbp_get_global_post_field( 'post_date', 'raw' );
1121
1122 // Messages array
1123 $messages[ $this->post_type ] = array(
1124 0 => '', // Left empty on purpose
1125
1126 // Updated
1127 1 => sprintf(
1128 '%1$s <a href="%2$s">%3$s</a>',
1129 esc_html__( 'Reply updated.', 'bbpress' ),
1130 $topic_url,
1131 esc_html__( 'View topic', 'bbpress' )
1132 ),
1133
1134 // Custom field updated
1135 2 => esc_html__( 'Custom field updated.', 'bbpress' ),
1136
1137 // Custom field deleted
1138 3 => esc_html__( 'Custom field deleted.', 'bbpress' ),
1139
1140 // Reply updated
1141 4 => esc_html__( 'Reply updated.', 'bbpress' ),
1142
1143 // Restored from revision
1144 /* translators: %s: date and time of the revision */
1145 5 => isset( $_GET['revision'] )
1146 /* translators: %s: Date and time of the revision */
1147 ? sprintf( esc_html__( 'Reply restored to revision from %s', 'bbpress' ), wp_post_revision_title( (int) $_GET['revision'], false ) )
1148 : false,
1149
1150 // Reply created
1151 6 => sprintf(
1152 '%1$s <a href="%2$s">%3$s</a>',
1153 esc_html__( 'Reply created.', 'bbpress' ),
1154 $topic_url,
1155 esc_html__( 'View topic', 'bbpress' )
1156 ),
1157
1158 // Reply saved
1159 7 => esc_html__( 'Reply saved.', 'bbpress' ),
1160
1161 // Reply submitted
1162 8 => sprintf(
1163 '%1$s <a href="%2$s" target="_blank">%3$s</a>',
1164 esc_html__( 'Reply submitted.', 'bbpress' ),
1165 esc_url( add_query_arg( 'preview', 'true', $topic_url ) ),
1166 esc_html__( 'Preview topic', 'bbpress' )
1167 ),
1168
1169 // Reply scheduled
1170 9 => sprintf(
1171 '%1$s <a target="_blank" href="%2$s">%3$s</a>',
1172 sprintf(
1173 /* translators: Publish box date format, see http://php.net/date */
1174 esc_html__( 'Reply scheduled for: %s.', 'bbpress' ),
1175 '<strong>' . date_i18n( __( 'M j, Y @ G:i', 'bbpress' ), strtotime( $post_date ) ) . '</strong>'
1176 ),
1177 $topic_url,
1178 esc_html__( 'Preview topic', 'bbpress' )
1179 ),
1180
1181 // Reply draft updated
1182 10 => sprintf(
1183 '%1$s <a href="%2$s" target="_blank">%3$s</a>',
1184 esc_html__( 'Reply draft updated.', 'bbpress' ),
1185 esc_url( add_query_arg( 'preview', 'true', $topic_url ) ),
1186 esc_html__( 'Preview topic', 'bbpress' )
1187 ),
1188 );
1189
1190 return $messages;
1191 }
1192 }
1193 endif; // class_exists check
1194
1195 /**
1196 * Setup bbPress Replies Admin
1197 *
1198 * This is currently here to make hooking and unhooking of the admin UI easy.
1199 * It could use dependency injection in the future, but for now this is easier.
1200 *
1201 * @since 2.0.0 bbPress (r3343)
1202 *
1203 * @param WP_Screen $current_screen Current screen object
1204 */
1205 function bbp_admin_replies( $current_screen ) {
1206
1207 // Bail if not in site admin
1208 if ( ! is_blog_admin() ) {
1209 return;
1210 }
1211
1212 // Bail if not a forum screen
1213 if ( empty( $current_screen->post_type ) || ( bbp_get_reply_post_type() !== $current_screen->post_type ) ) {
1214 return;
1215 }
1216
1217 // Init the replies admin
1218 bbp_admin()->replies = new BBP_Replies_Admin();
1219 }
1220