PluginProbe
Comment Edit Core – Simple Comment Editing / 3.3.0
Comment Edit Core – Simple Comment Editing v3.3.0
3.3.0 1.9.0 1.9.1 1.9.3 1.9.4 2.0.0 2.1.0 2.1.1 2.1.11 2.1.3 2.1.5 2.1.7 2.1.9 2.2.0 2.2.1 2.3.0 2.3.1 2.3.11 2.3.12 2.3.14 2.3.2 2.3.3 2.3.4 2.3.5 2.3.6 All 101 releases
simple-comment-editing / simple-comment-editing.php

simple-comment-editing.php in Comment Edit Core – Simple Comment Editing 3.3.0, at simple-comment-editing.php

924 lines 27.8 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Main class for Comment Edit Lite.
4 *
5 * @package DLXPlugins\CommentEditLite
6 */
7
8 namespace DLXPlugins\CommentEditLite;
9
10 if ( ! defined( 'ABSPATH' ) ) {
11 die( 'No direct access.' );
12 }
13
14 use DLXPlugins\CommentEditLite\Admin\Admin_Settings;
15
16 /**
17 * Main class for Comment Edit Lite.
18 */
19 class Simple_Comment_Editing {
20
21 /**
22 * Class instance.
23 *
24 * @var Simple_Comment_Editing
25 */
26 private static $instance = null;
27
28 /**
29 * The loading image when editing a comment.
30 *
31 * @var string The loading image when editing a comment.
32 */
33 private static $loading_img = '';
34
35 /**
36 * Whether or not users can delete their comments.
37 *
38 * @var bool Whether or not users can delete their comments.
39 */
40 public static $allow_delete = true;
41
42 /**
43 * Error object for WP_Error.
44 *
45 * @var WP_Error Error object for WP_Error.
46 */
47 public static $errors;
48
49 /**
50 * The scheme (http/https) for admin-ajax.php.
51 *
52 * @var string The scheme (http/https) for admin-ajax.php.
53 */
54 private static $scheme;
55
56 /**
57 * Mailchimp API variable with <sp> (server prefix) for search/replace.
58 *
59 * @var string Mailchimp API variable.
60 */
61 private $mailchimp_api = 'https://<sp>.api.mailchimp.com/3.0/';
62
63 /**
64 * Retrieve an instance of the class.
65 *
66 * @return Simple_Comment_Editing
67 */
68 public static function get_instance() {
69 if ( null === self::$instance ) {
70 self::$instance = new self();
71 }
72 return self::$instance;
73 }
74
75 /**
76 * Main hook initializer. Should be called right after plugins have finished loading.
77 */
78 public function plugins_loaded() {
79 add_action( 'init', array( $this, 'init' ), 9 );
80
81 // Determine http/https admin-ajax issue.
82 self::$scheme = is_ssl() ? 'https' : 'http';
83
84 /**
85 * Filter: sce_loading_img
86 *
87 * Replace the loading image with a custom version.
88 *
89 * @since 1.0.0
90 *
91 * @param string $image_url URL path to the loading image.
92 */
93 self::$loading_img = esc_url( apply_filters( 'sce_loading_img', Functions::get_plugin_url( '/images/loading.gif' ) ) );
94
95 /**
96 * Filter: sce_allow_delete
97 *
98 * Determine if users can delete their comments
99 *
100 * @since 1.1.0
101 *
102 * @param bool $allow_delete True allows deletion, false does not
103 */
104 self::$allow_delete = (bool) apply_filters( 'sce_allow_delete', self::$allow_delete );
105 }
106
107 /**
108 * Initialize the plugin.
109 *
110 * @see init action.
111 */
112 public function init() {
113
114 // Skip out and do nothing if we're in the admin and not doing AJAX.
115 if ( is_admin() && ! defined( 'DOING_AJAX' ) ) {
116 return false;
117 }
118
119 // Load text domain.
120 load_plugin_textdomain(
121 'simple-comment-editing',
122 false,
123 dirname( plugin_basename( __FILE__ ) ) . '/languages/'
124 );
125
126 // Initialize errors.
127 self::$errors = new \WP_Error();
128 self::$errors->add( 'nonce_fail', __( 'You do not have permission to edit this comment.', 'simple-comment-editing' ) );
129 self::$errors->add( 'edit_fail', __( 'You can no longer edit this comment.', 'simple-comment-editing' ) );
130 self::$errors->add( 'timer_fail', __( 'Timer could not be stopped.', 'simple-comment-editing' ) );
131 self::$errors->add( 'comment_empty', __( 'Your comment cannot be empty. Delete instead?', 'simple-comment-editing' ) );
132 self::$errors->add( 'comment_marked_spam', __( 'This comment was marked as spam.', 'simple-comment-editing' ) );
133
134 // When a comment is posted.
135 add_action( 'comment_post', array( $this, 'comment_posted' ), 100, 1 );
136
137 // Loading scripts.
138 add_filter( 'sce_load_scripts', array( $this, 'maybe_load_scripts' ), 5, 1 );
139 add_action( 'wp_enqueue_scripts', array( $this, 'add_scripts' ) );
140
141 // Init ajax.
142 Ajax::run();
143
144 // Init mailchimp.
145 Mailchimp::run();
146
147 // Init WooCommerce.
148 WooCommerce::run();
149
150 /* Begin Filters */
151 if ( ! is_feed() && ! defined( 'DOING_SCE' ) ) {
152 add_filter( 'comment_excerpt', array( $this, 'add_edit_interface' ), 1000, 2 );
153 add_filter( 'comment_text', array( $this, 'add_edit_interface' ), 1000, 2 );
154 add_filter( 'thesis_comment_text', array( $this, 'add_edit_interface' ), 1000, 2 );
155 }
156
157 // Add button themes.
158 add_filter( 'sce_button_extra_save', array( $this, 'maybe_add_save_icon' ) );
159 add_filter( 'sce_button_extra_cancel', array( $this, 'maybe_add_cancel_icon' ) );
160 add_filter( 'sce_button_extra_delete', array( $this, 'maybe_add_delete_icon' ) );
161 add_filter( 'sce_wrapper_class', array( $this, 'output_theme_class' ) );
162 } //end init
163
164 /**
165 * Adds the SCE interface if a user can edit their comment
166 *
167 * Called via the comment_text or comment_excerpt filter to add the SCE editing interface to a comment.
168 *
169 * @param string $comment_content The comment content.
170 * @param array $passed_comment The comment object.
171 *
172 * @since 1.0
173 */
174 public function add_edit_interface( $comment_content, $passed_comment = false ) {
175 global $comment; // For Thesis.
176 if ( ( ! $comment && ! $passed_comment ) || empty( $comment_content ) ) {
177 return $comment_content;
178 }
179 if ( $passed_comment ) {
180 $comment = (object) $passed_comment; // phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited
181 }
182
183 $comment_id = absint( $comment->comment_ID );
184 $post_id = absint( $comment->comment_post_ID );
185
186 // Check to see if a user can edit their comment.
187 if ( ! Functions::can_edit( $comment_id, $post_id ) ) {
188 return $comment_content;
189 }
190
191 // Variables for later.
192 $original_content = $comment_content;
193 $raw_content = $comment->comment_content; // For later usage in the textarea.
194
195 // Yay, user can edit - Add the initial wrapper.
196 $comment_wrapper = sprintf( '<div id="sce-comment%d" class="sce-comment">%s</div>', $comment_id, $comment_content );
197
198 $classes = array( 'sce-edit-comment' );
199 /**
200 * Filter: sce_wrapper_class
201 *
202 * Filter allow editing of wrapper class
203 *
204 * @since 2.3.0
205 *
206 * @param array Array of classes for the initial wrapper
207 */
208 $classes = apply_filters( 'sce_wrapper_class', $classes );
209
210 // Create Overall wrapper for JS interface.
211 $sce_content = sprintf( '<div id="sce-edit-comment%d" class="%s">', $comment_id, esc_attr( implode( ' ', $classes ) ) );
212
213 // Edit Button.
214 $sce_content .= '<div class="sce-edit-button sce-hide">';
215 $ajax_edit_url = add_query_arg(
216 array(
217 'action' => 'sce_get_time_left',
218 'editComment' => 1,
219 'cid' => $comment_id,
220 'pid' => $post_id,
221 'nonce' => wp_create_nonce( 'sce-edit-comment' . $comment_id ),
222 ),
223 admin_url( 'admin-ajax.php', self::$scheme )
224 );
225
226 /**
227 * Filter: sce_text_edit
228 *
229 * Filter allow editing of edit text
230 *
231 * @since 2.0.0
232 *
233 * @param string Translated click to edit text
234 */
235 $click_to_edit_text = apply_filters( 'sce_text_edit', __( 'Click to Edit', 'simple-comment-editing' ) );
236
237 /**
238 * Filter: sce_text_edit_delete
239 *
240 * Filter allow editing of the delete text
241 *
242 * @since 2.6.0
243 *
244 * @param string Translated delete text
245 */
246 $delete_edit_text = apply_filters( 'sce_text_edit_delete', __( 'Delete Comment', 'simple-comment-editing' ) );
247
248 $allow_edit_delete = apply_filters( 'sce_allow_delete_button', false );
249 $allow_edit = apply_filters( 'sce_allow_edit_button', true );
250
251 if ( $allow_edit && ! $allow_edit_delete ) {
252 $sce_content .= sprintf( '<a class="sce-edit-button-main" href="%s">%s</a>', esc_url( $ajax_edit_url ), esc_html( $click_to_edit_text ) );
253 } elseif ( $allow_edit && $allow_edit_delete ) {
254 $sce_content .= sprintf( '<a class="sce-edit-button-main" href="%s">%s</a>', esc_url( $ajax_edit_url ), esc_html( $click_to_edit_text ) );
255 $sce_content .= '<span class="sce-seperator">&nbsp;&ndash;&nbsp;</span>';
256 $sce_content .= sprintf( '<a class="sce-delete-button-main" href="%s">%s</a>', esc_url( $ajax_edit_url ), esc_html( $delete_edit_text ) );
257 } elseif ( ! $allow_edit && $allow_edit_delete ) {
258 $sce_content .= sprintf( '<a class="sce-delete-button-main" href="%s">%s</a>', esc_url( $ajax_edit_url ), esc_html( $delete_edit_text ) );
259 } else {
260 $sce_content .= sprintf( '<a class="sce-edit-button-main" href="%s">%s</a>', esc_url( $ajax_edit_url ), esc_html( $click_to_edit_text ) );
261 }
262
263 /**
264 * Filter: sce_show_timer
265 *
266 * Filter allow you to hide the timer
267 *
268 * @since 2.3.0
269 *
270 * @param bool Whether to show the timer or not
271 */
272 if ( apply_filters( 'sce_show_timer', true ) && false === apply_filters( 'sce_unlimited_editing', false, $comment ) ) {
273 $sce_content .= '<span class="sce-seperator">&nbsp;&ndash;&nbsp;</span>';
274 $sce_content .= '<span class="sce-timer"></span>';
275 }
276 $sce_content .= '</div><!-- .sce-edit-button -->';
277
278 // Loading button.
279 $sce_content .= '<div class="sce-loading" style="display: none;">';
280 $sce_content .= sprintf( '<img src="%1$s" title="%2$s" alt="%2$s" />', esc_url( self::$loading_img ), esc_attr__( 'Loading', 'simple-comment-editing' ) );
281 $sce_content .= '</div><!-- sce-loading -->';
282
283 // Textarea.
284 $textarea_content = '<div class="sce-textarea" style="display: none;">';
285
286 /**
287 * Filter: sce_extra_fields_pre
288 *
289 * Filter to add additional form fields before the textarea.
290 *
291 * @since 3.0.0
292 *
293 * @param string Empty string
294 * @param int post_id POST ID
295 * @param int comment_id Comment ID
296 * @param WP_Comment comment Comment object.
297 */
298 $textarea_content .= apply_filters( 'sce_extra_fields_pre', '', $post_id, $comment_id, $comment );
299 $textarea_content .= '<div class="sce-comment-textarea">';
300 $textarea_content .= '<textarea class="sce-comment-text" %s>%s</textarea>';
301 $textarea_content .= '</div><!-- .sce-comment-textarea -->';
302
303 /**
304 * Filter: sce_extra_fields
305 *
306 * Filter to add additional form fields
307 *
308 * @since 1.5.0
309 *
310 * @param string Empty string
311 * @param int post_id POST ID
312 * @param int comment_id Comment ID
313 */
314 $textarea_content .= apply_filters( 'sce_extra_fields', '', $post_id, $comment_id );
315
316 $textarea_content .= '%s</div><!-- .sce-textarea -->';
317 $textarea_button_content = '<div class="sce-comment-edit-buttons">';
318
319 /**
320 * Filter: sce_text_save
321 *
322 * Filter allow editing of save text
323 *
324 * @since 2.0.0
325 *
326 * @param string Translated save text
327 */
328 $save_text = apply_filters( 'sce_text_save', __( 'Save', 'simple-comment-editing' ) );
329
330 /**
331 * Filter: sce_text_cancel
332 *
333 * Filter allow editing of cancel text
334 *
335 * @since 2.0.0
336 *
337 * @param string Translated cancel text
338 */
339 $cancel_text = apply_filters( 'sce_text_cancel', __( 'Cancel', 'simple-comment-editing' ) );
340
341 /**
342 * Filter: sce_text_delete
343 *
344 * Filter allow editing of delete text
345 *
346 * @since 2.0.0
347 *
348 * @param string Translated delete text
349 */
350 $delete_text = apply_filters( 'sce_text_delete', __( 'Delete', 'simple-comment-editing' ) );
351
352 $textarea_buttons = '<div class="sce-comment-edit-buttons-group">';
353
354 /**
355 * Filter: sce_button_extra_save
356 *
357 * Add an extra item before the save button text. This is useful for adding icons.
358 *
359 * @param string Empty string
360 */
361 $textarea_buttons .= sprintf( '<button class="sce-comment-save">%s%s</button>', apply_filters( 'sce_button_extra_save', '' ), esc_html( $save_text ) );
362
363 /**
364 * Filter: sce_button_extra_cancel
365 *
366 * Add an extra item before the cancel button text. This is useful for adding icons.
367 *
368 * @param string Empty string
369 */
370 $textarea_buttons .= sprintf( '<button class="sce-comment-cancel">%s%s</button>', apply_filters( 'sce_button_extra_cancel', '' ), esc_html( $cancel_text ) );
371
372 /**
373 * Filter: sce_button_extra_delete
374 *
375 * Add an extra item before the delete button text. This is useful for adding icons.
376 *
377 * @param string Empty string
378 */
379 $textarea_buttons .= self::$allow_delete ? sprintf( '<button class="sce-comment-delete">%s%s</button>', apply_filters( 'sce_button_extra_delete', '' ), esc_html( $delete_text ) ) : '';
380 $textarea_buttons .= '</div><!-- .sce-comment-edit-buttons-group -->';
381
382 /**
383 * Filter: sce_show_timer
384 *
385 * Filter allow you to hide the timer
386 *
387 * @param bool Whether to show the timer or not
388 */
389 if ( apply_filters( 'sce_show_timer', true ) ) {
390 $textarea_buttons .= '<div class="sce-timer"></div>';
391 }
392 /**
393 * Filter: sce_buttons
394 *
395 * Filter to add button content
396 *
397 * @since 1.3.0
398 *
399 * @param string $textarea_buttons Button HTML
400 * @param int $comment_id Comment ID
401 */
402 $textarea_buttons = apply_filters( 'sce_buttons', $textarea_buttons, $comment_id );
403 $textarea_button_content .= $textarea_buttons . '</div><!-- .sce-comment-edit-buttons -->';
404 $textarea_content = sprintf(
405 $textarea_content,
406 'style="max-width: 100%; min-height: 150px;"',
407 esc_textarea( $raw_content ),
408 $textarea_button_content
409 );
410
411 // End.
412 $sce_content .= $textarea_content . '</div><!-- .sce-edit-comment -->';
413
414 // Status Area.
415 $sce_content .= sprintf( '<div id="sce-edit-comment-status%d" class="sce-status" style="display: none;"></div><!-- .sce-status -->', $comment_id );
416
417 /**
418 * Filter: sce_content
419 *
420 * Filter to overral sce output
421 *
422 * @since 1.3.0
423 *
424 * @param string $sce_content SCE content
425 * @param int $comment_id Comment ID of the comment
426 */
427 $sce_content = apply_filters( 'sce_content', $sce_content, $comment_id );
428
429 // Return content.
430 $comment_content = $comment_wrapper . $sce_content;
431 return $comment_content;
432 }
433
434 /**
435 * Add a delete icon.
436 *
437 * Add a delete icon.
438 *
439 * @since 3.0.0
440 * @access public
441 *
442 * @param string $text Button text.
443 *
444 * @return string Button text
445 */
446 public function maybe_add_delete_icon( $text ) {
447 if ( true === Options::get_options( false, 'show_icons' ) ) {
448 return '<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 20 20"><path d="M6 19c0 1.1.9 2 2 2h8c1.1 0 2-.9 2-2V7H6v12zM19 4h-3.5l-1-1h-5l-1 1H5v2h14V4z"/><path d="M0 0h24v24H0z" fill="none"/></svg>';
449 }
450 return $text;
451 }
452
453 /**
454 * Add a cancel icon.
455 *
456 * Add a cancel icon.
457 *
458 * @since 3.0.0
459 * @access public
460 *
461 * @param string $text Button text.
462 *
463 * @return string Button text
464 */
465 public function maybe_add_cancel_icon( $text ) {
466 if ( true === Options::get_options( false, 'show_icons' ) ) {
467 return '<svg xmlns="http://www.w3.org/2000/svg" width="20" height="24" viewBox="0 0 24 20"><path d="M12 2C6.47 2 2 6.47 2 12s4.47 10 10 10 10-4.47 10-10S17.53 2 12 2zm5 13.59L15.59 17 12 13.41 8.41 17 7 15.59 10.59 12 7 8.41 8.41 7 12 10.59 15.59 7 17 8.41 13.41 12 17 15.59z"/><path d="M0 0h24v24H0z" fill="none"/></svg>';
468 }
469 return $text;
470 }
471
472 /**
473 * Add a save icon.
474 *
475 * Add a save icon.
476 *
477 * @since 3.0.0
478 * @access public
479 *
480 * @param string $text Button text.
481 *
482 * @return string Button text
483 */
484 public function maybe_add_save_icon( $text ) {
485 if ( true === Options::get_options( false, 'show_icons' ) ) {
486 return '<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 20 20"><path d="M0 0h24v24H0z" fill="none"/><path d="M17 3H5c-1.11 0-2 .9-2 2v14c0 1.1.89 2 2 2h14c1.1 0 2-.9 2-2V7l-4-4zm-5 16c-1.66 0-3-1.34-3-3s1.34-3 3-3 3 1.34 3 3-1.34 3-3 3zm3-10H5V5h10v4z"/></svg>';
487 }
488 return $text;
489 }
490
491 /**
492 * Returns a theme class.
493 *
494 * Returns a theme class.
495 *
496 * @since 1.0.0
497 * @access public
498 *
499 * @param array $classes SCE Wrapper class.
500 * @return array $classes New SCE theme classes
501 */
502 public function output_theme_class( $classes = array() ) {
503 $theme = Options::get_options( false, 'button_theme' );
504 if ( false === $theme ) {
505 return $classes;
506 }
507 $classes[] = $theme;
508 return $classes;
509 }
510
511 /**
512 * Adds the necessary JavaScript for the plugin (only loads on posts/pages)
513 *
514 * Called via the wp_enqueue_scripts
515 *
516 * @since 1.0
517 */
518 public function add_scripts() {
519 if ( ! is_single() && ! is_singular() && ! is_page() ) {
520 return;
521 }
522
523 // Check if there are any cookies present, otherwise don't load the scripts - WPAC_PLUGIN_NAME is for wp-ajaxify-comments (if the plugin is installed, load the JavaScript file).
524
525 /**
526 * Filter: sce_load_scripts
527 *
528 * Boolean to decide whether to load SCE scripts or not
529 *
530 * @since 1.5.0
531 *
532 * @param bool true to load scripts, false not
533 */
534 $load_scripts = apply_filters( 'sce_load_scripts', false );
535 if ( ! $load_scripts ) {
536 return;
537 }
538
539 $main_script_uri = Functions::get_plugin_url( '/dist/sce-editing.js' );
540 wp_enqueue_script(
541 'simple-comment-editing',
542 $main_script_uri,
543 array( 'wp-i18n', 'wp-hooks' ),
544 SCE_VERSION,
545 true
546 );
547 wp_enqueue_style(
548 'simple-comment-editing',
549 Functions::get_plugin_url( 'dist/sce-frontend.css' ),
550 array(),
551 SCE_VERSION,
552 'all'
553 );
554
555 /**
556 * Action: sce_scripts_loaded
557 *
558 * Allows other plugins to load scripts after SCE has loaded
559 *
560 * @since 2.3.4
561 */
562 do_action( 'sce_scripts_loaded' );
563
564 /* For translations in JS */
565 wp_set_script_translations( 'simple-comment-editing', 'simple-comment-editing' );
566
567 /**
568 * Filter: sce_allow_delete_confirmation
569 *
570 * Boolean to decide whether to show a delete confirmation
571 *
572 * @since 2.1.7
573 *
574 * @param bool true to show a confirmation, false if not
575 */
576 $allow_delete_confirmation = (bool) apply_filters( 'sce_allow_delete_confirmation', true );
577
578 wp_localize_script(
579 'simple-comment-editing',
580 'simple_comment_editing',
581 array(
582 'and' => __( 'and', 'simple-comment-editing' ),
583 'confirm_delete' => apply_filters( 'sce_confirm_delete', __( 'Do you want to delete this comment?', 'simple-comment-editing' ) ),
584 'comment_deleted' => apply_filters( 'sce_comment_deleted', __( 'Your comment has been removed.', 'simple-comment-editing' ) ),
585 'comment_deleted_error' => apply_filters( 'sce_comment_deleted_error', __( 'Your comment could not be deleted', 'simple-comment-editing' ) ),
586 'empty_comment' => apply_filters( 'sce_empty_comment', self::$errors->get_error_message( 'comment_empty' ) ),
587 'allow_delete' => self::$allow_delete,
588 'allow_delete_confirmation' => $allow_delete_confirmation,
589 'ajax_url' => admin_url( 'admin-ajax.php', self::$scheme ),
590 'nonce' => wp_create_nonce( 'sce-general-ajax-nonce' ),
591 'timer_appearance' => sanitize_text_field( Options::get_options( false, 'timer_appearance' ) ),
592 )
593 );
594
595 /**
596 * Action: sce_load_assets
597 *
598 * Allow other plugins to load scripts/styyles for SCE
599 *
600 * @since 2.3.0
601 */
602 do_action( 'sce_load_assets' );
603 }
604
605 /**
606 * When a comment has been posted.
607 *
608 * Called when a comment has been posted - Stores a cookie for later editing
609 *
610 * @since 1.0
611 *
612 * @param int $comment_id The Comment ID.
613 */
614 public function comment_posted( $comment_id ) {
615 $comment = get_comment( $comment_id, OBJECT );
616 $post_id = $comment->comment_post_ID;
617 $post = get_post( $post_id, OBJECT );
618 $comment_status = $comment->comment_approved;
619
620 // Do some initial checks to weed out those who shouldn't be able to have editable comments.
621 if ( 'spam' === $comment_status ) {
622 return; // Marked as spam - no editing allowed.
623 }
624
625 // Remove expired comments.
626 $this->remove_security_keys();
627
628 $user_id = Functions::get_user_id();
629
630 // Don't set a cookie if a comment is posted via Ajax.
631 /**
632 * Filter: sce_can_edit_cookie_bypass
633 * Bypass the cookie based user verification.
634 *
635 * @param boolean Whether to bypass cookie authentication
636 * @param object $comment Comment object
637 * @param int $comment_id The comment ID
638 * @param int $post_id The post ID of the comment
639 * @param int $user_id The logged in user ID
640 *
641 * @return boolean
642 */
643 $cookie_bypass = apply_filters( 'sce_can_edit_cookie_bypass', false, $comment, $comment_id, $post_id, $user_id );
644
645 // if we are logged in and are the comment author, bypass cookie check.
646 if ( 0 !== $user_id && ( $post->post_author === $user_id || $comment->user_id === $user_id ) ) {
647 $cookie_bypass = true;
648 update_comment_meta( $comment_id, '_sce', 'post_author' );
649 }
650 if ( ! defined( 'DOING_AJAX' ) && ! defined( 'EPOCH_API' ) ) {
651 if ( false === $cookie_bypass ) {
652 $this->generate_cookie_data( $post_id, $comment_id, 'setcookie' );
653 }
654 }
655 } //end comment_posted
656
657 /**
658 * Return a cookie's value
659 *
660 * Return a cookie's value
661 *
662 * @access private
663 * @since 1.5.0
664 *
665 * @param string $name Cookie name.
666 * @return string $value Cookie value.
667 */
668 private function get_cookie_value( $name ) {
669 if ( isset( $_COOKIE[ $name ] ) ) {
670 return sanitize_text_field( wp_unslash( $_COOKIE[ $name ] ) );
671 } else {
672 return false;
673 }
674 }
675
676 /**
677 * Return a comment object
678 *
679 * Return a comment object
680 *
681 * @access private
682 * @since 1.5.0
683 *
684 * @param int $comment_id Comment ID.
685 * @return obj Comment Object
686 */
687 public static function get_comment( $comment_id ) {
688 if ( isset( $GLOBALS['comment'] ) ) {
689 unset( $GLOBALS['comment'] ); // caching.
690 }
691 $comment_to_return = get_comment( $comment_id );
692 $GLOBALS['comment'] = $comment_to_return; // phpcs:ignore.
693 return $comment_to_return;
694 }
695
696 /**
697 * Generate or remove a comment cookie
698 *
699 * Generate or remove a comment cookie - Stored as post meta
700 *
701 * @access public
702 * @since 1.5.0
703 *
704 * @param int $post_id Post ID.
705 * @param int $comment_id Comment ID.
706 * @param string $return_action 'ajax', 'setcookie, 'removecookie'.
707 * @return JSON Array of cookie data only returned during Ajax requests
708 */
709 public function generate_cookie_data( $post_id = 0, $comment_id = 0, $return_action = 'ajax' ) {
710 if ( 'ajax' === $return_action ) {
711 check_ajax_referer( 'sce-general-ajax-nonce' );
712 }
713
714 if ( 0 === $post_id ) {
715 $post_id = isset( $_POST['post_id'] ) ? absint( $_POST['post_id'] ) : 0;
716 }
717
718 // Get comment ID.
719 if ( 0 === $comment_id ) {
720 $comment_id = isset( $_POST['comment_id'] ) ? absint( $_POST['comment_id'] ) : 0;
721 }
722
723 // Get comment for IP and user-agent data.
724 $comment = \get_comment( $comment_id, ARRAY_A );
725
726 // Get hash and random security key - Stored in the style of Ajax Edit Comments.
727 $comment_date_gmt = '';
728 $comment_author_ip = sanitize_text_field( $comment['comment_author_IP'] );
729 /**
730 * Filter: sce_pre_comment_user_ip
731 *
732 * Whether to use the IP filter (true by default)
733 *
734 * @since 2.7.1
735 *
736 * @param bool true to use the comment IP filter.
737 */
738 if ( apply_filters( 'sce_pre_comment_user_ip', true ) ) {
739 // Props: https://github.com/timreeves.
740 $comment_author_ip = apply_filters( 'pre_comment_user_ip', $comment_author_ip );
741 }
742 $comment_date_gmt = current_time( 'Y-m-d', 1 );
743 $user_agent = isset( $comment['comment_agent'] ) ? sanitize_text_field( $comment['comment_agent'] ) : '';
744 $hash = md5( $comment_author_ip . $comment_date_gmt . Functions::get_user_id() . $user_agent );
745
746 $rand = '_wpAjax' . $hash . md5( wp_generate_password( 30, true, true ) ) . '-' . time();
747 $maybe_save_meta = get_comment_meta( $comment_id, '_sce', true );
748 $cookie_name = 'SimpleCommentEditing' . $comment_id . $hash;
749 $cookie_value = $rand;
750 $cookie_expire = time() + ( 60 * Functions::get_comment_time() );
751
752 if ( ! $maybe_save_meta ) {
753 // Make sure we don't set post meta again for security reasons and subsequent calls to this method will generate a new key, so no calling it twice unless you want to remove a cookie.
754 update_comment_meta( $comment_id, '_sce', $rand );
755 } else {
756 // Kinda evil, but if you try to call this method twice, removes the cookie.
757 setcookie( $cookie_name, $cookie_value, time() - 60, COOKIEPATH, COOKIE_DOMAIN );
758 wp_send_json_success();
759 die( '' );
760 }
761
762 // Now store a cookie.
763 if ( 'setcookie' === $return_action ) {
764 setcookie( $cookie_name, $cookie_value, $cookie_expire, COOKIEPATH, COOKIE_DOMAIN );
765 } elseif ( 'removecookie' === $return_action ) {
766 setcookie( $cookie_name, $cookie_value, time() - 60, COOKIEPATH, COOKIE_DOMAIN );
767 }
768
769 $return = array(
770 'name' => $cookie_name,
771 'value' => $cookie_value,
772 'expires' => ( time() + ( 60 * Functions::get_comment_time() ) ) * 1000,
773 'post_id' => $post_id,
774 'comment_id' => $comment_id,
775 'path' => COOKIEPATH,
776 );
777 if ( 'ajax' === $return_action ) {
778 wp_send_json_success( $return );
779 exit;
780 } else {
781 return;
782 }
783 // Should never reach this point, but just in case I suppose.
784 wp_send_json_error();
785 die( '' );
786 }
787
788 /**
789 * Whether to load scripts or not. Will load scripts if logged in, has Ajax comments, or has a cookie.
790 *
791 * Called via the sce_load_scripts filter
792 *
793 * @since 1.5.0
794 *
795 * @param bool $yes True or False.
796 *
797 * @return bool True to load scripts, false if not
798 */
799 public function maybe_load_scripts( $yes ) {
800 if ( defined( 'WPAC_PLUGIN_NAME' ) || is_user_logged_in() ) {
801 return true;
802 }
803
804 /* Return True if user is logged in */
805 if ( is_user_logged_in() ) {
806 return true;
807 }
808
809 if ( ! isset( $_COOKIE ) || empty( $_COOKIE ) ) {
810 return;
811 }
812 $has_cookie = false;
813 foreach ( $_COOKIE as $cookie_name => $cookie_value ) {
814 if ( substr( $cookie_name, 0, 20 ) === 'SimpleCommentEditing' ) {
815 $has_cookie = true;
816 break;
817 }
818 }
819 return $has_cookie;
820 }
821
822 /**
823 * Removes a comment cookie
824 *
825 * Removes a comment cookie based on the passed comment
826 *
827 * @since 1.0
828 *
829 * @param associative array $comment The results from get_comment( $id, ARRAY_A ).
830 */
831 public static function remove_comment_cookie( $comment ) {
832 if ( ! is_array( $comment ) ) {
833 return;
834 }
835
836 $this->generate_cookie_data( $comment['comment_post_ID'], $comment['comment_ID'], 'removecookie' );
837 }
838
839 /**
840 * Remove security keys
841 *
842 * When a comment is posted, remove security keys
843 *
844 * @access private
845 * @since 2.0.2
846 */
847 private function remove_security_keys() {
848
849 $sce_security = get_transient( 'sce_security_keys' );
850 if ( ! $sce_security ) {
851
852 // Remove old SCE keys.
853 $security_key_count = get_option( 'ajax-edit-comments_security_key_count' );
854 if ( $security_key_count ) {
855 global $wpdb;
856 delete_option( 'ajax-edit-comments_security_key_count' );
857 $wpdb->query( "delete from {$wpdb->postmeta} where left(meta_value, 7) = '_wpAjax' ORDER BY {$wpdb->postmeta}.meta_id ASC" ); // phpcs:ignore.
858 }
859 // Delete expired meta.
860 global $wpdb;
861 $query = $wpdb->prepare( "delete from {$wpdb->commentmeta} where meta_key = '_sce' AND CAST( SUBSTRING(meta_value, LOCATE('-',meta_value ) +1 ) AS UNSIGNED) < %d", time() - ( Functions::get_comment_time() * MINUTE_IN_SECONDS ) );
862 $wpdb->query( $query ); // phpcs:ignore.
863 set_transient( 'sce_security_keys', true, HOUR_IN_SECONDS );
864 }
865 }
866 } //end class Simple_Comment_Editing
867
868 add_action( 'plugins_loaded', __NAMESPACE__ . '\sce_instantiate' );
869 /**
870 * Instantiate SCE.
871 */
872 function sce_instantiate() {
873 $sce = Simple_Comment_Editing::get_instance();
874 $sce->plugins_loaded();
875 if ( is_admin() && apply_filters( 'sce_show_admin', true ) ) {
876 new Admin_Settings();
877 $sce_enqueue = new Enqueue();
878 $sce_enqueue->run();
879 }
880 } //end sce_instantiate
881
882
883 register_activation_hook( Functions::get_plugin_file(), __NAMESPACE__ . '\sce_plugin_activate' );
884 add_action( 'admin_init', __NAMESPACE__ . '\sce_plugin_activate_redirect' );
885
886 /**
887 * Add an option upon activation to read in later when redirecting.
888 */
889 function sce_plugin_activate() {
890 if ( ! Functions::is_multisite() ) {
891 add_option( 'comment-edit-lite-activate', sanitize_text_field( Functions::get_plugin_file() ) );
892 }
893 }
894
895 /**
896 * Redirect to Comment Edit Lite settings page upon activation.
897 */
898 function sce_plugin_activate_redirect() {
899
900 // If on multisite, bail.
901 if ( Functions::is_multisite() ) {
902 return;
903 }
904
905 // Make sure we're in the admin and that the option is available.
906 if ( is_admin() && Functions::get_plugin_file() === get_option( 'comment-edit-lite-activate' ) ) {
907 delete_option( 'comment-edit-lite-activate' );
908 // GEt bulk activation variable if it exists.
909 $maybe_multi = filter_input( INPUT_GET, 'activate-multi', FILTER_VALIDATE_BOOLEAN );
910
911 // Return early if it's a bulk activation.
912 if ( $maybe_multi ) {
913 return;
914 }
915
916 $settings_url = admin_url( 'options-general.php?page=comment-edit-core' );
917 if ( class_exists( '\CommentEditPro\Comment_Edit_Pro' ) ) {
918 $settings_url = admin_url( 'options-general.php?page=comment-edit-pro' );
919 }
920 wp_safe_redirect( esc_url( $settings_url ) );
921 exit;
922 }
923 }
924