PluginProbe
Edit Flow / 0.7.3
Edit Flow v0.7.3
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.7.3, at modules/editorial-comments/editorial-comments.php

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