PluginProbe
Edit Flow / 0.9
Edit Flow v0.9
0.11.1 0.11.0 0.7.2 0.7.3 0.7.4 0.7.5 0.7.6 0.8 0.8.1 0.8.2 0.9 0.9.1 0.9.2 0.9.3 0.9.4 0.9.5 0.9.6 0.9.7 0.9.8 0.9.9 trunk 0.1.5 0.10.0 0.10.1 0.10.2 All 44 releases
edit-flow / modules / editorial-comments / editorial-comments.php

editorial-comments.php in Edit Flow 0.9, at modules/editorial-comments/editorial-comments.php

609 lines 21.6 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * class EF_Editorial_Comments
4 * Threaded commenting in the admin for discussion between writers and editors
5 *
6 * @author batmoo
7 */
8
9 if ( !class_exists( 'EF_Editorial_Comments' ) ) {
10
11 class EF_Editorial_Comments extends EF_Module
12 {
13 // This is comment type used to differentiate editorial comments
14 const comment_type = 'editorial-comment';
15
16 function __construct() {
17
18 $this->module_url = $this->get_module_url( __FILE__ );
19 // Register the module with Edit Flow
20 $args = array(
21 'title' => __( 'Editorial Comments', 'edit-flow' ),
22 'short_description' => __( 'Share internal notes with your team.', 'edit-flow' ),
23 'extended_description' => __( 'Use editorial comments to hold a private discussion about a post. Communicate directly with your writers or editors about what works and what needs to be improved for each piece.', 'edit-flow' ),
24 'module_url' => $this->module_url,
25 'img_url' => $this->module_url . 'lib/editorial_comments_s128.png',
26 'slug' => 'editorial-comments',
27 'default_options' => array(
28 'enabled' => 'on',
29 'post_types' => array(
30 'post' => 'on',
31 'page' => 'on',
32 ),
33 ),
34 'configure_page_cb' => 'print_configure_view',
35 'configure_link_text' => __( 'Choose Post Types', 'edit-flow' ),
36 'autoload' => false,
37 'settings_help_tab' => array(
38 'id' => 'ef-editorial-comments-overview',
39 'title' => __('Overview', 'edit-flow'),
40 'content' => __('<p>Editorial comments help you cut down on email overload and keep the conversation close to where it matters: your content. Threaded commenting in the admin, similar to what you find at the end of a blog post, allows writers and editors to privately leave feedback and discuss what needs to be changed before publication.</p><p>Anyone with access to view the story in progress will also have the ability to comment on it. If you have notifications enabled, those following the post will receive an email every time a comment is left.</p>', 'edit-flow'),
41 ),
42 'settings_help_sidebar' => __( '<p><strong>For more information:</strong></p><p><a href="http://editflow.org/features/editorial-comments/">Editorial Comments Documentation</a></p><p><a href="http://wordpress.org/tags/edit-flow?forum_id=10">Edit Flow Forum</a></p><p><a href="https://github.com/danielbachhuber/Edit-Flow">Edit Flow on Github</a></p>', 'edit-flow' ),
43 );
44 $this->module = EditFlow()->register_module( 'editorial_comments', $args );
45 }
46
47 /**
48 * Initialize the rest of the stuff in the class if the module is active
49 */
50 function init() {
51
52 add_action( 'add_meta_boxes', array ( $this, 'add_post_meta_box' ) );
53 add_action( 'admin_init', array( $this, 'register_settings' ) );
54 add_action( 'admin_enqueue_scripts', array( $this, 'add_admin_scripts' ) );
55 add_action( 'wp_ajax_editflow_ajax_insert_comment', array( $this, 'ajax_insert_comment' ) );
56
57 // Add Editorial Comments to the calendar if the calendar is activated
58 if ( $this->module_enabled( 'calendar' ) ) {
59 // Still in progress. See: https://www.pivotaltracker.com/story/show/5930884 and https://www.pivotaltracker.com/story/show/5930895
60 //add_filter( 'ef_calendar_item_information_fields', array( $this, 'filter_calendar_item_fields' ), null, 2 );
61 }
62 }
63
64 /**
65 * Upgrade our data in case we need to
66 *
67 * @since 0.7
68 */
69 function upgrade( $previous_version ) {
70 global $edit_flow;
71
72 // Upgrade path to v0.7
73 if ( version_compare( $previous_version, '0.7' , '<' ) ) {
74 // Technically we've run this code before so we don't want to auto-install new data
75 $edit_flow->update_module_option( $this->module->name, 'loaded_once', true );
76 }
77
78 }
79
80 /**
81 * Load any of the admin scripts we need but only on the pages we need them
82 */
83 function add_admin_scripts( ) {
84 global $pagenow;
85
86 $post_type = $this->get_current_post_type();
87 $supported_post_types = $this->get_post_types_for_module( $this->module );
88 if ( !in_array( $post_type, $supported_post_types ) )
89 return;
90
91 if ( !in_array( $pagenow, array( 'post.php', 'page.php', 'post-new.php', 'page-new.php' ) ) )
92 return;
93
94 wp_enqueue_script( 'edit_flow-post_comment', $this->module_url . 'lib/editorial-comments.js', array( 'jquery','post' ), EDIT_FLOW_VERSION, true );
95 wp_localize_script( 'edit_flow-post_comment', '__ef_localize_post_comment', array(
96 'and' => esc_html__( 'and', 'edit-flow' ),
97 'none_notified' => esc_html__( 'No one will be notified.', 'edit-flow' ),
98 ) );
99
100 wp_enqueue_style( 'edit-flow-editorial-comments-css', $this->module_url . 'lib/editorial-comments.css', false, EDIT_FLOW_VERSION, 'all' );
101
102 $thread_comments = (int) get_option('thread_comments');
103 ?>
104 <script type="text/javascript">
105 var ef_thread_comments = <?php echo ($thread_comments) ? $thread_comments : 0; ?>;
106 </script>
107 <?php
108
109 }
110
111 /**
112 * Add the editorial comments metabox to enabled post types
113 *
114 * @uses add_meta_box()
115 */
116 function add_post_meta_box() {
117
118 $supported_post_types = $this->get_post_types_for_module( $this->module );
119 foreach ( $supported_post_types as $post_type )
120 add_meta_box('edit-flow-editorial-comments', __('Editorial Comments', 'edit-flow'), array($this, 'editorial_comments_meta_box'), $post_type, 'normal' );
121
122 }
123
124 /**
125 * Get the total number of editorial comments for a post
126 *
127 * @param int $id Unique post ID
128 * @return int $comment_count Number of editorial comments for a post
129 */
130 function get_editorial_comment_count( $id ) {
131 global $wpdb;
132 $comment_count = $wpdb->get_var($wpdb->prepare("SELECT COUNT(*) FROM $wpdb->comments WHERE comment_post_ID = %d AND comment_type = %s", $id, self::comment_type));
133 if ( !$comment_count )
134 $comment_count = 0;
135 return $comment_count;
136 }
137
138 function editorial_comments_meta_box( ) {
139 global $post, $post_ID;
140 ?>
141 <div id="ef-comments_wrapper">
142 <a name="editorialcomments"></a>
143
144 <?php
145 // Show comments only if not a new post
146 if( ! in_array( $post->post_status, array( 'new', 'auto-draft' ) ) ) :
147
148 // Unused since switched to wp_list_comments
149 $editorial_comments = ef_get_comments_plus (
150 array(
151 'post_id' => $post->ID,
152 'comment_type' => self::comment_type,
153 'orderby' => 'comment_date',
154 'order' => 'ASC',
155 'status' => self::comment_type
156 )
157 );
158 ?>
159
160 <ul id="ef-comments">
161 <?php
162 // We use this so we can take advantage of threading and such
163
164 wp_list_comments(
165 array(
166 'type' => self::comment_type,
167 'callback' => array($this, 'the_comment'),
168 'end-callback' => '__return_false'
169 ),
170 $editorial_comments
171 );
172 ?>
173 </ul>
174
175 <?php $this->the_comment_form(); ?>
176
177 <?php
178 else :
179 ?>
180 <p><?php _e( 'You can add editorial comments to a post once you\'ve saved it for the first time.', 'edit-flow' ); ?></p>
181 <?php
182 endif;
183 ?>
184 <div class="clear"></div>
185 </div>
186 <div class="clear"></div>
187 <?php
188 }
189
190 /**
191 * Displays the main commenting form
192 */
193 function the_comment_form( ) {
194 global $post;
195
196 ?>
197 <a href="#" id="ef-comment_respond" onclick="editorialCommentReply.open();return false;" class="button-primary alignright hide-if-no-js" title=" <?php _e( 'Respond to this post', 'edit-flow' ); ?>"><span><?php _e( 'Respond to this post', 'edit-flow' ); ?></span></a>
198
199 <!-- Reply form, hidden until reply clicked by user -->
200 <div id="ef-replyrow" style="display: none;">
201 <div id="ef-replycontainer">
202 <textarea id="ef-replycontent" name="replycontent" cols="40" rows="5"></textarea>
203 </div>
204
205 <?php if ( $this->module_enabled( 'notifications' ) ) : ?>
206 <label for="ef-reply-notifier"><?php esc_html_e( 'The following will be notified:', 'edit-flow' ); ?>
207 <input id="ef-reply-notifier" class="ef-reply-notifier-message" readonly>
208 </label>
209 <?php endif; ?>
210
211 <p id="ef-replysubmit">
212 <a class="ef-replysave button-primary alignright" href="#comments-form">
213 <span id="ef-replybtn"><?php _e('Submit Response', 'edit-flow') ?></span>
214 </a>
215 <a class="ef-replycancel button-secondary alignright" href="#comments-form"><?php _e( 'Cancel', 'edit-flow' ); ?></a>
216 <img alt="Sending comment..." src="<?php echo admin_url('/images/wpspin_light.gif') ?>" class="alignright" style="display: none;" id="ef-comment_loading" />
217 <br class="clear" style="margin-bottom:35px;" />
218 <span style="display: none;" class="error"></span>
219 </p>
220
221 <input type="hidden" value="" id="ef-comment_parent" name="ef-comment_parent" />
222 <input type="hidden" name="ef-post_id" id="ef-post_id" value="<?php echo esc_attr( $post->ID ); ?>" />
223
224 <?php wp_nonce_field('comment', 'ef_comment_nonce', false); ?>
225
226 <br class="clear" />
227 </div>
228
229 <?php
230 }
231
232 /**
233 * Maybe display who was notified underneath an editorial comment.
234 *
235 * @param int $comment_id
236 * @return void
237 */
238 function maybe_output_comment_meta( $comment_id ) {
239 if ( ! $this->module_enabled( 'notifications' ) || ! apply_filters( 'ef_editorial_comments_show_notified_users', true ) ) {
240 return;
241 }
242
243 $notification = get_comment_meta( $comment_id, 'notification_list', true );
244
245 if ( empty( $notification ) ) {
246 $message = esc_html__( 'No users or groups were notified.', 'edit-flow' );
247 } else {
248 $message = '<strong>'. esc_html__( 'Notified', 'edit-flow' ) . ':</strong> ' . esc_html( $notification );
249 }
250
251 echo '<p class="ef-notification-meta">' . $message . '</p>';
252 }
253
254 /**
255 * Displays a single comment
256 */
257 function the_comment($comment, $args, $depth) {
258 global $current_user, $userdata;
259
260 // Get current user
261 wp_get_current_user() ;
262
263 $GLOBALS['comment'] = $comment;
264
265 // Deleting editorial comments is not enabled for now for the sake of transparency. However, we could consider
266 // EF comment edits (with history, if possible). P2 already allows for edits without history, so even that might work.
267 // Pivotal ticket: https://www.pivotaltracker.com/story/show/18483757
268 //$delete_url = esc_url( wp_nonce_url( "comment.php?action=deletecomment&p=$comment->comment_post_ID&c=$comment->comment_ID", "delete-comment_$comment->comment_ID" ) );
269
270 $actions = array();
271
272 $actions_string = '';
273 // Comments can only be added by users that can edit the post
274 if ( current_user_can('edit_post', $comment->comment_post_ID) ) {
275 $actions['reply'] = '<a onclick="editorialCommentReply.open(\''.$comment->comment_ID.'\',\''.$comment->comment_post_ID.'\');return false;" class="vim-r hide-if-no-js" title="'.__( 'Reply to this comment', 'edit-flow' ).'" href="#">' . __( 'Reply', 'edit-flow' ) . '</a>';
276
277 $sep = ' ';
278 $i = 0;
279 foreach ( $actions as $action => $link ) {
280 ++$i;
281 // Reply and quickedit need a hide-if-no-js span
282 if ( 'reply' == $action || 'quickedit' == $action )
283 $action .= ' hide-if-no-js';
284
285 $actions_string .= "<span class='$action'>$sep$link</span>";
286 }
287 }
288
289 ?>
290
291 <li id="comment-<?php echo esc_attr( $comment->comment_ID ); ?>" <?php comment_class( array( 'comment-item', wp_get_comment_status($comment->comment_ID) ) ); ?>>
292
293 <?php echo get_avatar( $comment->comment_author_email, 50 ); ?>
294
295 <div class="post-comment-wrap">
296 <h5 class="comment-meta">
297 <?php printf( __('<span class="comment-author">%1$s</span><span class="meta"> said on %2$s at %3$s</span>', 'edit-flow'),
298 comment_author_email_link( $comment->comment_author ),
299 get_comment_date( get_option( 'date_format' ) ),
300 get_comment_time() ); ?>
301 </h5>
302
303 <div class="comment-content"><?php comment_text(); ?></div>
304 <?php $this->maybe_output_comment_meta( $comment->comment_ID ); ?>
305 <p class="row-actions"><?php echo $actions_string; ?></p>
306
307 </div>
308 </li>
309 <?php
310 }
311
312 /**
313 * Handles AJAX insert comment
314 */
315 function ajax_insert_comment( ) {
316 global $current_user, $user_ID, $wpdb;
317
318 // Verify nonce
319 if ( !wp_verify_nonce( $_POST['_nonce'], 'comment') )
320 die( __( "Nonce check failed. Please ensure you're supposed to be adding editorial comments.", 'edit-flow' ) );
321
322 // Get user info
323 wp_get_current_user();
324
325 // Set up comment data
326 $post_id = absint( $_POST['post_id'] );
327 $parent = absint( $_POST['parent'] );
328
329 // Only allow the comment if user can edit post
330 // @TODO: allow contributers to add comments as well (?)
331 if ( ! current_user_can( 'edit_post', $post_id ) )
332 die( __('Sorry, you don\'t have the privileges to add editorial comments. Please talk to your Administrator.', 'edit-flow' ) );
333
334 // Verify that comment was actually entered
335 $comment_content = trim($_POST['content']);
336 if( !$comment_content )
337 die( __( "Please enter a comment.", 'edit-flow' ) );
338
339 // Check that we have a post_id and user logged in
340 if( $post_id && $current_user ) {
341
342 // set current time
343 $time = current_time('mysql', $gmt = 0);
344
345 // Set comment data
346 $data = array(
347 'comment_post_ID' => (int) $post_id,
348 'comment_author' => esc_sql($current_user->display_name),
349 'comment_author_email' => esc_sql($current_user->user_email),
350 'comment_author_url' => esc_sql($current_user->user_url),
351 'comment_content' => wp_kses($comment_content, array('a' => array('href' => array(),'title' => array()),'b' => array(),'i' => array(),'strong' => array(),'em' => array(),'u' => array(),'del' => array(), 'blockquote' => array(), 'sub' => array(), 'sup' => array() )),
352 'comment_type' => self::comment_type,
353 'comment_parent' => (int) $parent,
354 'user_id' => (int) $user_ID,
355 'comment_author_IP' => esc_sql($_SERVER['REMOTE_ADDR']),
356 'comment_agent' => esc_sql($_SERVER['HTTP_USER_AGENT']),
357 'comment_date' => $time,
358 'comment_date_gmt' => $time,
359 // Set to -1?
360 'comment_approved' => self::comment_type,
361 );
362
363 $data = apply_filters( 'ef_pre_insert_editorial_comment', $data );
364
365 // Insert Comment
366 $comment_id = wp_insert_comment($data);
367 $comment = get_comment($comment_id);
368
369 // Save the list of notified users/usergroups.
370 if ( $this->module_enabled( 'notifications' ) && apply_filters( 'ef_editorial_comments_show_notified_users', true ) ) {
371 $notification = isset( $_POST['notification'] ) ? sanitize_text_field( $_POST['notification'] ) : '';
372
373 if ( ! empty( $notification ) && __( 'No one will be notified.', 'edit-flow' ) !== $notification ) {
374 add_comment_meta( $comment_id, 'notification_list', $notification );
375 }
376 }
377
378 // Register actions -- will be used to set up notifications and other modules can hook into this
379 if ( $comment_id )
380 do_action( 'ef_post_insert_editorial_comment', $comment );
381
382 // Prepare response
383 $response = new WP_Ajax_Response();
384
385 ob_start();
386 $this->the_comment( $comment, '', '' );
387 $comment_list_item = ob_get_contents();
388 ob_end_clean();
389
390 $response->add( array(
391 'what' => 'comment',
392 'id' => $comment_id,
393 'data' => $comment_list_item,
394 'action' => ($parent) ? 'reply' : 'new'
395 ));
396
397 $response->send();
398
399 } else {
400 die( __('There was a problem of some sort. Try again or contact your administrator.', 'edit-flow') );
401 }
402 }
403
404 /**
405 * Register settings for editorial comments so we can partially use the Settings API
406 * (We use the Settings API for form generation, but not saving)
407 *
408 * @since 0.7
409 */
410 function register_settings() {
411 add_settings_section( $this->module->options_group_name . '_general', false, '__return_false', $this->module->options_group_name );
412 add_settings_field( 'post_types', __( 'Enable for these post types:', 'edit-flow' ), array( $this, 'settings_post_types_option' ), $this->module->options_group_name, $this->module->options_group_name . '_general' );
413 }
414
415 /**
416 * Chose the post types for editorial comments
417 *
418 * @since 0.7
419 */
420 function settings_post_types_option() {
421 global $edit_flow;
422 $edit_flow->settings->helper_option_custom_post_type( $this->module );
423 }
424
425 /**
426 * Validate our user input as the settings are being saved
427 *
428 * @since 0.7
429 */
430 function settings_validate( $new_options ) {
431
432 // Whitelist validation for the post type options
433 if ( !isset( $new_options['post_types'] ) )
434 $new_options['post_types'] = array();
435 $new_options['post_types'] = $this->clean_post_type_options( $new_options['post_types'], $this->module->post_type_support );
436
437 return $new_options;
438
439 }
440
441 /**
442 * Settings page for editorial comments
443 *
444 * @since 0.7
445 */
446 function print_configure_view() {
447 ?>
448
449 <form class="basic-settings" action="<?php echo add_query_arg( 'page', $this->module->settings_slug, get_admin_url( null, 'admin.php' ) ); ?>" method="post">
450 <?php settings_fields( $this->module->options_group_name ); ?>
451 <?php do_settings_sections( $this->module->options_group_name ); ?>
452 <?php
453 echo '<input id="edit_flow_module_name" name="edit_flow_module_name" type="hidden" value="' . esc_attr( $this->module->name ) . '" />';
454 ?>
455 <p class="submit"><?php submit_button( null, 'primary', 'submit', false ); ?><a class="cancel-settings-link" href="<?php echo EDIT_FLOW_SETTINGS_PAGE; ?>"><?php _e( 'Back to Edit Flow', 'edit-flow' ); ?></a></p>
456
457 </form>
458 <?php
459 }
460
461 /**
462 * If the Edit Flow Calendar is enabled, add the editorial comment count to the post overlay.
463 *
464 * @since 0.7
465 * @uses apply_filters( 'ef_calendar_item_information_fields' )
466 *
467 * @param array $calendar_fields Additional data fields to include on the calendar
468 * @param int $post_id Unique ID for the post data we're building
469 * @return array $calendar_fields Calendar fields with our viewable Editorial Metadata added
470 */
471 function filter_calendar_item_fields( $calendar_fields, $post_id ) {
472 // Make sure we respect which post type we're on
473 if ( !in_array( get_post_type( $post_id ), $this->get_post_types_for_module( $this->module ) ) )
474 return $calendar_fields;
475
476 // Name/value for the field to add
477 $comment_count_data = array(
478 'label' => $this->module->title,
479 'value' => $this->get_editorial_comment_count( $post_id ),
480 );
481
482 $calendar_fields[$this->module->slug] = $comment_count_data;
483
484 return $calendar_fields;
485 }
486
487 }
488
489 }
490
491 /**
492 * Retrieve a list of comments -- overloaded from get_comments and with mods by filosofo (SVN Ticket #10668)
493 *
494 * @param mixed $args Optional. Array or string of options to override defaults.
495 * @return array List of comments.
496 */
497 function ef_get_comments_plus( $args = '' ) {
498 global $wpdb;
499
500 $defaults = array(
501 'author_email' => '',
502 'ID' => '',
503 'karma' => '',
504 'number' => '',
505 'offset' => '',
506 'orderby' => '',
507 'order' => 'DESC',
508 'parent' => '',
509 'post_ID' => '',
510 'post_id' => 0,
511 'status' => '',
512 'type' => '',
513 'user_id' => '',
514 );
515
516 $args = wp_parse_args( $args, $defaults );
517 extract( $args, EXTR_SKIP );
518
519 // $args can be whatever, only use the args defined in defaults to compute the key
520 $key = md5( serialize( compact(array_keys($defaults)) ) );
521 $last_changed = wp_cache_get('last_changed', 'comment');
522 if ( !$last_changed ) {
523 $last_changed = time();
524 wp_cache_set('last_changed', $last_changed, 'comment');
525 }
526 $cache_key = "get_comments:$key:$last_changed";
527
528 if ( $cache = wp_cache_get( $cache_key, 'comment' ) ) {
529 return $cache;
530 }
531
532 $post_id = absint($post_id);
533
534 if ( 'hold' == $status )
535 $approved = "comment_approved = '0'";
536 elseif ( 'approve' == $status )
537 $approved = "comment_approved = '1'";
538 elseif ( 'spam' == $status )
539 $approved = "comment_approved = 'spam'";
540 elseif( ! empty( $status ) )
541 $approved = $wpdb->prepare( "comment_approved = %s", $status );
542 else
543 $approved = "( comment_approved = '0' OR comment_approved = '1' )";
544
545 $order = ( 'ASC' == $order ) ? 'ASC' : 'DESC';
546
547 if ( ! empty( $orderby ) ) {
548 $ordersby = is_array($orderby) ? $orderby : preg_split('/[,\s]/', $orderby);
549 $ordersby = array_intersect(
550 $ordersby,
551 array(
552 'comment_agent',
553 'comment_approved',
554 'comment_author',
555 'comment_author_email',
556 'comment_author_IP',
557 'comment_author_url',
558 'comment_content',
559 'comment_date',
560 'comment_date_gmt',
561 'comment_ID',
562 'comment_karma',
563 'comment_parent',
564 'comment_post_ID',
565 'comment_type',
566 'user_id',
567 )
568 );
569 $orderby = empty( $ordersby ) ? 'comment_date_gmt' : implode(', ', $ordersby);
570 } else {
571 $orderby = 'comment_date_gmt';
572 }
573
574 $number = absint($number);
575 $offset = absint($offset);
576
577 if ( !empty($number) ) {
578 if ( $offset )
579 $number = 'LIMIT ' . $offset . ',' . $number;
580 else
581 $number = 'LIMIT ' . $number;
582
583 } else {
584 $number = '';
585 }
586
587 $post_where = '';
588
589 if ( ! empty($post_id) )
590 $post_where .= $wpdb->prepare( 'comment_post_ID = %d AND ', $post_id );
591 if ( '' !== $author_email )
592 $post_where .= $wpdb->prepare( 'comment_author_email = %s AND ', $author_email );
593 if ( '' !== $karma )
594 $post_where .= $wpdb->prepare( 'comment_karma = %d AND ', $karma );
595 if ( 'comment' == $type )
596 $post_where .= "comment_type = '' AND ";
597 elseif ( ! empty( $type ) )
598 $post_where .= $wpdb->prepare( 'comment_type = %s AND ', $type );
599 if ( '' !== $parent )
600 $post_where .= $wpdb->prepare( 'comment_parent = %d AND ', $parent );
601 if ( '' !== $user_id )
602 $post_where .= $wpdb->prepare( 'user_id = %d AND ', $user_id );
603
604 $comments = $wpdb->get_results( "SELECT * FROM $wpdb->comments WHERE $post_where $approved ORDER BY $orderby $order $number" );
605 wp_cache_add( $cache_key, $comments, 'comment' );
606
607 return $comments;
608 }
609