PluginProbe
UpStream: a Project Management Plugin for WordPress / trunk
UpStream: a Project Management Plugin for WordPress vtrunk
trunk 1.39.0 1.39.1 1.39.2 1.39.3 2.0.7 2.1.0
← All changes | includes/class-comment.php +578 -578 2.0.7trunk View file →
@@ -1,578 +1,578 @@
1 -<?php
2 -/**
3 - * Struct that represents an UpStream Comment.
4 - *
5 - * @package UpStream
6 - */
7 -
8 -namespace UpStream;
9 -
10 -// Prevent direct access.
11 -if ( ! defined( 'ABSPATH' ) ) {
12 - exit;
13 -}
14 -
15 -/**
16 - * Struct that represents an UpStream Comment.
17 - *
18 - * @since 1.13.0
19 - */
20 -class Comment extends Struct {
21 - /**
22 - * Comment ID.
23 - *
24 - * @since 1.13.0
25 - *
26 - * @var int $id
27 - */
28 - public $id;
29 -
30 - /**
31 - * Project ID where the comments belongs to.
32 - *
33 - * @since 1.13.0
34 - *
35 - * @var int $project_id
36 - */
37 - public $project_id;
38 -
39 - /**
40 - * Comment parent ID.
41 - *
42 - * @since 1.13.0
43 - *
44 - * @var int $parent_id
45 - */
46 - public $parent_id;
47 -
48 - /**
49 - * Comment content.
50 - *
51 - * @since 1.13.0
52 - *
53 - * @var string $content
54 - */
55 - public $content;
56 -
57 - /**
58 - * Comment current status.
59 - * Valid values are: -2, -1, 0, 1 ~ representing spam, trash, unapproved, approved.
60 - *
61 - * @since 1.13.0
62 - *
63 - * @var int $state
64 - */
65 - public $state;
66 -
67 - /**
68 - * Comment author.
69 - *
70 - * @since 1.13.0
71 - *
72 - * @var object $created_by
73 - */
74 - public $created_by;
75 -
76 - /**
77 - * Date info where the comment was added.
78 - *
79 - * @since 1.13.0
80 - *
81 - * @var object $created_at
82 - */
83 - public $created_at;
84 -
85 - /**
86 - * Current cached user capabilities related to comments.
87 - *
88 - * @since 1.13.0
89 - *
90 - * @var object $current_user_cap
91 - */
92 - public $current_user_cap;
93 -
94 - /**
95 - * Cached author info.
96 - *
97 - * @since 1.13.0
98 - * @access protected
99 - *
100 - * @var object $author
101 - */
102 - protected $author;
103 -
104 - /**
105 - * Class constructor.
106 - *
107 - * @since 1.13.0
108 - *
109 - * @param string $content Comment content.
110 - * @param int $project_id Project ID.
111 - * @param int $user_id Author ID.
112 - */
113 - public function __construct( $content = '', $project_id = 0, $user_id = 0 ) {
114 - if ( ! empty( $content ) ) {
115 - // Make sure the comment content is always filtered.
116 - $allowed_tags = apply_filters( 'upstream_allowed_tags_in_comments', array() );
117 - $this->content = wp_kses( $content, wp_kses_allowed_html( $allowed_tags ) );
118 - }
119 -
120 - if ( (int) $project_id <= 0 ) {
121 - $this->project_id = upstream_post_id();
122 - } else {
123 - $this->project_id = (int) $project_id;
124 - }
125 -
126 - if ( (int) $user_id > 0 ) {
127 - $author = get_user_by( 'id', $user_id );
128 - }
129 -
130 - $user = wp_get_current_user();
131 -
132 - $user_has_admin_capabilities = upstream_is_user_either_manager_or_admin( $user );
133 - $user_can_moderate_comments = ! $user_has_admin_capabilities ? user_can( $user, 'moderate_comments' ) : true;
134 - $this->current_user_cap = (object) array(
135 - 'can_reply' => ! $user_has_admin_capabilities ? user_can( $user, 'publish_project_discussion' ) : true,
136 - 'can_moderate' => $user_can_moderate_comments,
137 - 'can_delete' => ! $user_has_admin_capabilities ? $user_can_moderate_comments || user_can(
138 - $user,
139 - 'delete_project_discussion'
140 - ) : true,
141 - );
142 -
143 - $this->author = isset( $author ) ? $author : $user;
144 -
145 - $this->created_by = (object) array(
146 - 'id' => $author->ID,
147 - 'name' => $author->display_name,
148 - 'avatar' => upstream_get_user_avatar_url( $author->ID ),
149 - 'email' => $author->user_email,
150 - );
151 -
152 - $this->created_at = (object) array(
153 - 'timestamp' => 0,
154 - 'utc' => '',
155 - 'localized' => '',
156 - 'humanized' => '',
157 - );
158 -
159 - $this->parent_id = 0;
160 - $this->state = 1;
161 - }
162 -
163 - /**
164 - * Fill missing comment info from a given comment array.
165 - *
166 - * @since 1.13.0
167 - * @static
168 - *
169 - * @param array $custom_data Associative array with comment info.
170 - *
171 - * @return array
172 - */
173 - public static function arrayToWPPatterns( $custom_data ) {
174 - $default_data = array(
175 - 'comment_post_ID' => 0,
176 - 'comment_author' => '',
177 - 'comment_author_email' => '',
178 - 'comment_author_IP' => '',
179 - 'comment_date' => '',
180 - 'comment_date_gmt' => '',
181 - 'comment_content' => null,
182 - 'comment_agent' => '',
183 - 'user_id' => 0,
184 - 'comment_approved' => 1,
185 - 'comment_type' => '',
186 - );
187 -
188 - $data = array_merge( $default_data, (array) $custom_data );
189 -
190 - return $data;
191 - }
192 -
193 - /**
194 - * Load a given comment data into its own instance based on ID.
195 - *
196 - * @since 1.13.0
197 - * @static
198 - *
199 - * @param int $comment_id Comment ID to be loaded.
200 - *
201 - * @return Comment
202 - */
203 - public static function load( $comment_id ) {
204 - $data = get_comment( $comment_id );
205 -
206 - if ( empty( $data ) ) {
207 - return null;
208 - }
209 -
210 - $comment = new Comment( $data->comment_content, $data->comment_post_ID, $data->user_id );
211 - $comment->id = (int) $data->comment_ID;
212 - $comment->created_at->timestamp = strtotime( $data->comment_date_gmt );
213 - $comment->created_at->utc = $data->comment_date_gmt;
214 - $comment->created_at->localized = $data->comment_date;
215 - $comment->updateHumanizedDate();
216 - $comment->state = self::convertStateToInt( $data->comment_approved );
217 - $comment->parent_id = (int) $data->comment_parent;
218 -
219 - return $comment;
220 - }
221 -
222 - /**
223 - * Convert a given comment state into its equivalent int value.
224 - *
225 - * @since 1.13.0
226 - * @static
227 - *
228 - * @param string $state State to be converted.
229 - *
230 - * @return int
231 - */
232 - public static function convertStateToInt( $state ) {
233 - if ( is_numeric( $state ) ) {
234 - $state = (int) $state;
235 - } elseif ( 'approve' === $state ) {
236 - $state = 1;
237 - } elseif ( 'hold' === $state ) {
238 - $state = 0;
239 - } elseif ( 'trash' === $state ) {
240 - $state = -1;
241 - } elseif ( 'spam' === $state ) {
242 - $state = -2;
243 - }
244 -
245 - return $state;
246 - }
247 -
248 - /**
249 - * Either insert/update the comment into DB.
250 - *
251 - * @since 1.13.0
252 - *
253 - * @return mixed int if inserted or bool if updated
254 - * @throws \Exception Unable to save the data into database.
255 - */
256 - public function save() {
257 - if ( $this->isNew() ) {
258 - $this->doFilters();
259 -
260 - $data = $this->toWpPatterns();
261 -
262 - // Commented to avoid comment rejection by WordPress on simmilar comments made across different items on the same project.
263 - // if (is_wp_error($integrity_check)) {
264 - // throw new \Exception($integrity_check->get_error_message());
265 - // }.
266 -
267 - $this->created_at->timestamp = time();
268 - $this->created_at->utc = gmdate( 'Y-m-d H:i:s', $this->created_at->timestamp );
269 - $data['comment_date_gmt'] = $this->created_at->utc;
270 - $integrity_check = wp_allow_comment( $data, true );
271 - $this->state = 'spam' !== $integrity_check ? (int) $integrity_check : $integrity_check;
272 - $date_format = get_option( 'date_format' );
273 - $time_format = get_option( 'time_format' );
274 - $the_date_time_format = $date_format . ' ' . $time_format;
275 - $date = \DateTime::createFromFormat( 'Y-m-d H:i:s', $this->created_at->utc );
276 - $this->created_at->localized = $date->format( $the_date_time_format );
277 - $data['comment_date'] = $date->format( 'Y-m-d H:i:s' );
278 - $this->created_at->humanized = _x( 'just now', 'Comment was very recently added.', 'upstream' );
279 - $allowed_tags = apply_filters( 'upstream_allowed_tags_in_comments', array() );
280 - $data['comment_content'] = wp_kses( $data['comment_content'], wp_kses_allowed_html( $allowed_tags ) );
281 - $a = $this->html2text( $data['comment_content'] );
282 - $comment_id = wp_insert_comment( $data );
283 -
284 - if ( ! $comment_id ) {
285 - throw new \Exception( __( 'Unable to save the data into database.', 'upstream' ) );
286 - }
287 -
288 - $this->id = $comment_id;
289 -
290 - return $this->id;
291 - } else {
292 - $data = $this->toWpPatterns();
293 - $success = (bool) wp_update_comment( $data );
294 -
295 - if ( ! $success ) {
296 - throw new \Exception( __( 'Unable to save the data into database.', 'upstream' ) );
297 - }
298 -
299 - return true;
300 - }
301 - }
302 -
303 - /**
304 - * HTML to text
305 - *
306 - * @since 1.13.0
307 - *
308 - * @param string $document The document to convert.
309 - */
310 - public function html2text( $document ) {
311 - $rules = array(
312 - '@<script[^>]*?>.*?</script>@si',
313 - '@<[\/\!]*?[^<>]*?>@si',
314 - '@([\r\n])[\s]+@',
315 - '@&(quot|#34);@i',
316 - '@&(amp|#38);@i',
317 - '@&(lt|#60);@i',
318 - '@&(gt|#62);@i',
319 - '@&(nbsp|#160);@i',
320 - '@&(iexcl|#161);@i',
321 - '@&(cent|#162);@i',
322 - '@&(pound|#163);@i',
323 - '@&(copy|#169);@i',
324 - '@&(reg|#174);@i',
325 - '@&#(d+);@i',
326 - );
327 -
328 - $replace = array(
329 - '',
330 - '',
331 - '',
332 - '',
333 - '&',
334 - '<',
335 - '>',
336 - ' ',
337 - chr( 161 ),
338 - chr( 162 ),
339 - chr( 163 ),
340 - chr( 169 ),
341 - chr( 174 ),
342 - 'chr()',
343 - );
344 -
345 - return preg_replace( $rules, $replace, $document );
346 - }
347 -
348 - /**
349 - * Check if instance represents a new or an existent comment.
350 - *
351 - * @since 1.13.0
352 - *
353 - * @return bool
354 - */
355 - public function isNew() {
356 - return (int) $this->id <= 0;
357 - }
358 -
359 - /**
360 - * Apply WordPress comment filters to the instance data.
361 - *
362 - * @since 1.13.0
363 - *
364 - * @uses wp_filter_comment
365 - */
366 - public function doFilters() {
367 - $data = $this->toWpPatterns();
368 -
369 - // TODO: why did it filter here and then later elsewhere.
370 - $safe_data = $data; // code: wp_filter_comment( $data ).
371 -
372 - $this->created_by->id = (int) $safe_data['user_id'];
373 - $this->created_by->agent = $safe_data['comment_agent'];
374 - $this->created_by->name = $safe_data['comment_author'];
375 - $this->created_by->email = $safe_data['comment_author_email'];
376 - $this->created_by->ip = $safe_data['comment_author_IP'];
377 - $this->content = $safe_data['comment_content'];
378 - }
379 -
380 - /**
381 - * Retrieve an associative array following WordPress Comments design pattern with the instance's data.
382 - *
383 - * @since 1.13.0
384 - *
385 - * @return array
386 - */
387 - public function toWpPatterns() {
388 - $data = array(
389 - 'comment_id' => (int) $this->id,
390 - 'comment_post_ID' => (int) $this->project_id,
391 - 'comment_author_url' => '',
392 - 'user_id' => (int) $this->created_by->id,
393 - 'comment_author' => $this->created_by->name,
394 - 'comment_author_email' => $this->created_by->email,
395 - 'comment_content' => $this->content,
396 - 'comment_approved' => self::convertStateToWpPatterns( $this->state ),
397 - 'comment_author_IP' => isset( $this->created_by->ip ) ? $this->created_by->ip : '',
398 - 'comment_agent' => isset( $this->created_by->agent ) ? $this->created_by->agent : '',
399 - 'comment_parent' => (int) $this->parent_id > 0 ? $this->parent_id : 0,
400 - 'comment_type' => '',
401 - );
402 -
403 - return $data;
404 - }
405 -
406 - /**
407 - * Convert a given comment state into its equivalent WordPress value.
408 - *
409 - * @since 1.13.0
410 - * @static
411 - *
412 - * @param mixed $state The state to be translated.
413 - *
414 - * @return mixed
415 - */
416 - public static function convertStateToWpPatterns( $state ) {
417 - if ( is_numeric( $state ) ) {
418 - $state = (int) $state;
419 -
420 - if ( -1 === $state ) {
421 - $state = 'trash';
422 - }
423 - } elseif ( 'approve' === $state ) {
424 - $state = 1;
425 - } elseif ( 'hold' === $state ) {
426 - $state = 0;
427 - }
428 -
429 - return $state;
430 - }
431 -
432 - /**
433 - * Unapprove the comment.
434 - *
435 - * @since 1.13.0
436 - *
437 - * @return bool
438 - */
439 - public function unapprove() {
440 - if ( ! $this->isNew() ) {
441 - $success = self::updateApprovalState( $this->id, 0 );
442 -
443 - if ( $success ) {
444 - $this->state = 0;
445 - }
446 -
447 - return $success;
448 - }
449 -
450 - return false;
451 - }
452 -
453 - /**
454 - * Update comment state statically.
455 - *
456 - * @since 1.13.0
457 - * @access protected
458 - * @static
459 - *
460 - * @param int $comment_id Comment ID to be updated.
461 - * @param mixed $new_state Comment's new state.
462 - *
463 - * @return bool
464 - */
465 - protected static function updateApprovalState( $comment_id, $new_state ) {
466 - if ( ! in_array( strtolower( (string) $new_state ), array( '1', '0', 'spam', 'trash' ), true ) ) {
467 - return false;
468 - }
469 -
470 - $data = array(
471 - 'comment_ID' => (int) $comment_id,
472 - 'comment_approved' => $new_state,
473 - );
474 -
475 - $success = (bool) wp_update_comment( $data );
476 -
477 - return $success;
478 - }
479 -
480 - /**
481 - * Approve the comment.
482 - *
483 - * @since 1.13.0
484 - *
485 - * @return bool
486 - */
487 - public function approve() {
488 - if ( ! $this->isNew() ) {
489 - $success = self::updateApprovalState( $this->id, 1 );
490 -
491 - if ( $success ) {
492 - $this->state = 1;
493 - }
494 -
495 - return $success;
496 - }
497 -
498 - return false;
499 - }
500 -
501 - /**
502 - * Render the comment as HTML.
503 - *
504 - * @since 1.13.0
505 - *
506 - * @param bool $return Either return the HTML or render it instead.
507 - * @param bool $use_admin_layout Either use admin/frontend layout.
508 - * @param array $comments_cache Array of comments passed to render functions.
509 - *
510 - * @return string Will only return something if $return is true.
511 - */
512 - public function render( $return = false, $use_admin_layout = true, $comments_cache = array() ) {
513 - if ( empty( $this->current_user_cap ) ) {
514 - $user = wp_get_current_user();
515 - $user_has_admin_capabilities = upstream_is_user_either_manager_or_admin();
516 - $this->current_user_cap->can_reply = ! $user_has_admin_capabilities ? user_can(
517 - $user,
518 - 'publish_project_discussion'
519 - ) : true;
520 -
521 - $user_can_moderate = ! $user_has_admin_capabilities ? user_can(
522 - $user,
523 - 'moderate_comments'
524 - ) : true;
525 - $this->current_user_cap->can_moderate = $user_can_moderate;
526 - $this->current_user_cap->can_delete = ! $user_has_admin_capabilities ? ( $user_can_moderate || user_can(
527 - $user,
528 - 'delete_project_discussion'
529 - ) || $user->ID === (int) $created_by->id ) : true;
530 - }
531 -
532 - $this->updateHumanizedDate();
533 -
534 - if ( true === (bool) $return ) {
535 - ob_start();
536 -
537 - if ( true === (bool) $use_admin_layout ) {
538 - upstream_admin_display_message_item( $this, $comments_cache );
539 - } else {
540 - upstream_display_message_item( $this, $comments_cache );
541 - }
542 -
543 - $html = ob_get_contents();
544 -
545 - ob_end_clean();
546 -
547 - return $html;
548 - } else {
549 - if ( true === (bool) $use_admin_layout ) {
550 - upstream_admin_display_message_item( $this, $comments_cache );
551 - } else {
552 - upstream_display_message_item( $this, $comments_cache );
553 - }
554 - }
555 - }
556 -
557 - /**
558 - * Update comment's dates to human format.
559 - *
560 - * @since 1.13.0
561 - */
562 - public function updateHumanizedDate() {
563 - $date_format = get_option( 'date_format' );
564 - $time_format = get_option( 'time_format' );
565 - $the_date_time_format = $date_format . ' ' . $time_format;
566 - $current_timestamp = time();
567 -
568 - $date = \DateTime::createFromFormat( 'Y-m-d H:i:s', $this->created_at->utc );
569 - $date_timestamp = $date->getTimestamp();
570 -
571 - $this->created_at->localized = $date->format( $the_date_time_format );
572 - $this->created_at->humanized = sprintf(
573 - // translators: %s = human-readable time difference.
574 - _x( '%s ago', '%s = human-readable time difference', 'upstream' ),
575 - human_time_diff( $date_timestamp, $current_timestamp )
576 - );
577 - }
578 -}
1 +<?php
2 +/**
3 + * Struct that represents an UpStream Comment.
4 + *
5 + * @package UpStream
6 + */
7 +
8 +namespace UpStream;
9 +
10 +// Prevent direct access.
11 +if ( ! defined( 'ABSPATH' ) ) {
12 + exit;
13 +}
14 +
15 +/**
16 + * Struct that represents an UpStream Comment.
17 + *
18 + * @since 1.13.0
19 + */
20 +class Comment extends Struct {
21 + /**
22 + * Comment ID.
23 + *
24 + * @since 1.13.0
25 + *
26 + * @var int $id
27 + */
28 + public $id;
29 +
30 + /**
31 + * Project ID where the comments belongs to.
32 + *
33 + * @since 1.13.0
34 + *
35 + * @var int $project_id
36 + */
37 + public $project_id;
38 +
39 + /**
40 + * Comment parent ID.
41 + *
42 + * @since 1.13.0
43 + *
44 + * @var int $parent_id
45 + */
46 + public $parent_id;
47 +
48 + /**
49 + * Comment content.
50 + *
51 + * @since 1.13.0
52 + *
53 + * @var string $content
54 + */
55 + public $content;
56 +
57 + /**
58 + * Comment current status.
59 + * Valid values are: -2, -1, 0, 1 ~ representing spam, trash, unapproved, approved.
60 + *
61 + * @since 1.13.0
62 + *
63 + * @var int $state
64 + */
65 + public $state;
66 +
67 + /**
68 + * Comment author.
69 + *
70 + * @since 1.13.0
71 + *
72 + * @var object $created_by
73 + */
74 + public $created_by;
75 +
76 + /**
77 + * Date info where the comment was added.
78 + *
79 + * @since 1.13.0
80 + *
81 + * @var object $created_at
82 + */
83 + public $created_at;
84 +
85 + /**
86 + * Current cached user capabilities related to comments.
87 + *
88 + * @since 1.13.0
89 + *
90 + * @var object $current_user_cap
91 + */
92 + public $current_user_cap;
93 +
94 + /**
95 + * Cached author info.
96 + *
97 + * @since 1.13.0
98 + * @access protected
99 + *
100 + * @var object $author
101 + */
102 + protected $author;
103 +
104 + /**
105 + * Class constructor.
106 + *
107 + * @since 1.13.0
108 + *
109 + * @param string $content Comment content.
110 + * @param int $project_id Project ID.
111 + * @param int $user_id Author ID.
112 + */
113 + public function __construct( $content = '', $project_id = 0, $user_id = 0 ) {
114 + if ( ! empty( $content ) ) {
115 + // Make sure the comment content is always filtered.
116 + $allowed_tags = apply_filters( 'upstream_allowed_tags_in_comments', array() );
117 + $this->content = wp_kses( $content, wp_kses_allowed_html( $allowed_tags ) );
118 + }
119 +
120 + if ( (int) $project_id <= 0 ) {
121 + $this->project_id = upstream_post_id();
122 + } else {
123 + $this->project_id = (int) $project_id;
124 + }
125 +
126 + if ( (int) $user_id > 0 ) {
127 + $author = get_user_by( 'id', $user_id );
128 + }
129 +
130 + $user = wp_get_current_user();
131 +
132 + $user_has_admin_capabilities = upstream_is_user_either_manager_or_admin( $user );
133 + $user_can_moderate_comments = ! $user_has_admin_capabilities ? user_can( $user, 'moderate_comments' ) : true;
134 + $this->current_user_cap = (object) array(
135 + 'can_reply' => ! $user_has_admin_capabilities ? user_can( $user, 'publish_project_discussion' ) : true,
136 + 'can_moderate' => $user_can_moderate_comments,
137 + 'can_delete' => ! $user_has_admin_capabilities ? $user_can_moderate_comments || user_can(
138 + $user,
139 + 'delete_project_discussion'
140 + ) : true,
141 + );
142 +
143 + $this->author = isset( $author ) ? $author : $user;
144 +
145 + $this->created_by = (object) array(
146 + 'id' => $author->ID,
147 + 'name' => $author->display_name,
148 + 'avatar' => upstream_get_user_avatar_url( $author->ID ),
149 + 'email' => $author->user_email,
150 + );
151 +
152 + $this->created_at = (object) array(
153 + 'timestamp' => 0,
154 + 'utc' => '',
155 + 'localized' => '',
156 + 'humanized' => '',
157 + );
158 +
159 + $this->parent_id = 0;
160 + $this->state = 1;
161 + }
162 +
163 + /**
164 + * Fill missing comment info from a given comment array.
165 + *
166 + * @since 1.13.0
167 + * @static
168 + *
169 + * @param array $custom_data Associative array with comment info.
170 + *
171 + * @return array
172 + */
173 + public static function arrayToWPPatterns( $custom_data ) {
174 + $default_data = array(
175 + 'comment_post_ID' => 0,
176 + 'comment_author' => '',
177 + 'comment_author_email' => '',
178 + 'comment_author_IP' => '',
179 + 'comment_date' => '',
180 + 'comment_date_gmt' => '',
181 + 'comment_content' => null,
182 + 'comment_agent' => '',
183 + 'user_id' => 0,
184 + 'comment_approved' => 1,
185 + 'comment_type' => '',
186 + );
187 +
188 + $data = array_merge( $default_data, (array) $custom_data );
189 +
190 + return $data;
191 + }
192 +
193 + /**
194 + * Load a given comment data into its own instance based on ID.
195 + *
196 + * @since 1.13.0
197 + * @static
198 + *
199 + * @param int $comment_id Comment ID to be loaded.
200 + *
201 + * @return Comment
202 + */
203 + public static function load( $comment_id ) {
204 + $data = get_comment( $comment_id );
205 +
206 + if ( empty( $data ) ) {
207 + return null;
208 + }
209 +
210 + $comment = new Comment( $data->comment_content, $data->comment_post_ID, $data->user_id );
211 + $comment->id = (int) $data->comment_ID;
212 + $comment->created_at->timestamp = strtotime( $data->comment_date_gmt );
213 + $comment->created_at->utc = $data->comment_date_gmt;
214 + $comment->created_at->localized = $data->comment_date;
215 + $comment->updateHumanizedDate();
216 + $comment->state = self::convertStateToInt( $data->comment_approved );
217 + $comment->parent_id = (int) $data->comment_parent;
218 +
219 + return $comment;
220 + }
221 +
222 + /**
223 + * Convert a given comment state into its equivalent int value.
224 + *
225 + * @since 1.13.0
226 + * @static
227 + *
228 + * @param string $state State to be converted.
229 + *
230 + * @return int
231 + */
232 + public static function convertStateToInt( $state ) {
233 + if ( is_numeric( $state ) ) {
234 + $state = (int) $state;
235 + } elseif ( 'approve' === $state ) {
236 + $state = 1;
237 + } elseif ( 'hold' === $state ) {
238 + $state = 0;
239 + } elseif ( 'trash' === $state ) {
240 + $state = -1;
241 + } elseif ( 'spam' === $state ) {
242 + $state = -2;
243 + }
244 +
245 + return $state;
246 + }
247 +
248 + /**
249 + * Either insert/update the comment into DB.
250 + *
251 + * @since 1.13.0
252 + *
253 + * @return mixed int if inserted or bool if updated
254 + * @throws \Exception Unable to save the data into database.
255 + */
256 + public function save() {
257 + if ( $this->isNew() ) {
258 + $this->doFilters();
259 +
260 + $data = $this->toWpPatterns();
261 +
262 + // Commented to avoid comment rejection by WordPress on simmilar comments made across different items on the same project.
263 + // if (is_wp_error($integrity_check)) {
264 + // throw new \Exception($integrity_check->get_error_message());
265 + // }.
266 +
267 + $this->created_at->timestamp = time();
268 + $this->created_at->utc = gmdate( 'Y-m-d H:i:s', $this->created_at->timestamp );
269 + $data['comment_date_gmt'] = $this->created_at->utc;
270 + $integrity_check = wp_allow_comment( $data, true );
271 + $this->state = 'spam' !== $integrity_check ? (int) $integrity_check : $integrity_check;
272 + $date_format = get_option( 'date_format' );
273 + $time_format = get_option( 'time_format' );
274 + $the_date_time_format = $date_format . ' ' . $time_format;
275 + $date = \DateTime::createFromFormat( 'Y-m-d H:i:s', $this->created_at->utc );
276 + $this->created_at->localized = $date->format( $the_date_time_format );
277 + $data['comment_date'] = $date->format( 'Y-m-d H:i:s' );
278 + $this->created_at->humanized = _x( 'just now', 'Comment was very recently added.', 'upstream' );
279 + $allowed_tags = apply_filters( 'upstream_allowed_tags_in_comments', array() );
280 + $data['comment_content'] = wp_kses( $data['comment_content'], wp_kses_allowed_html( $allowed_tags ) );
281 + $a = $this->html2text( $data['comment_content'] );
282 + $comment_id = wp_insert_comment( $data );
283 +
284 + if ( ! $comment_id ) {
285 + throw new \Exception( __( 'Unable to save the data into database.', 'upstream' ) );
286 + }
287 +
288 + $this->id = $comment_id;
289 +
290 + return $this->id;
291 + } else {
292 + $data = $this->toWpPatterns();
293 + $success = (bool) wp_update_comment( $data );
294 +
295 + if ( ! $success ) {
296 + throw new \Exception( __( 'Unable to save the data into database.', 'upstream' ) );
297 + }
298 +
299 + return true;
300 + }
301 + }
302 +
303 + /**
304 + * HTML to text
305 + *
306 + * @since 1.13.0
307 + *
308 + * @param string $document The document to convert.
309 + */
310 + public function html2text( $document ) {
311 + $rules = array(
312 + '@<script[^>]*?>.*?</script>@si',
313 + '@<[\/\!]*?[^<>]*?>@si',
314 + '@([\r\n])[\s]+@',
315 + '@&(quot|#34);@i',
316 + '@&(amp|#38);@i',
317 + '@&(lt|#60);@i',
318 + '@&(gt|#62);@i',
319 + '@&(nbsp|#160);@i',
320 + '@&(iexcl|#161);@i',
321 + '@&(cent|#162);@i',
322 + '@&(pound|#163);@i',
323 + '@&(copy|#169);@i',
324 + '@&(reg|#174);@i',
325 + '@&#(d+);@i',
326 + );
327 +
328 + $replace = array(
329 + '',
330 + '',
331 + '',
332 + '',
333 + '&',
334 + '<',
335 + '>',
336 + ' ',
337 + chr( 161 ),
338 + chr( 162 ),
339 + chr( 163 ),
340 + chr( 169 ),
341 + chr( 174 ),
342 + 'chr()',
343 + );
344 +
345 + return preg_replace( $rules, $replace, $document );
346 + }
347 +
348 + /**
349 + * Check if instance represents a new or an existent comment.
350 + *
351 + * @since 1.13.0
352 + *
353 + * @return bool
354 + */
355 + public function isNew() {
356 + return (int) $this->id <= 0;
357 + }
358 +
359 + /**
360 + * Apply WordPress comment filters to the instance data.
361 + *
362 + * @since 1.13.0
363 + *
364 + * @uses wp_filter_comment
365 + */
366 + public function doFilters() {
367 + $data = $this->toWpPatterns();
368 +
369 + // TODO: why did it filter here and then later elsewhere.
370 + $safe_data = $data; // code: wp_filter_comment( $data ).
371 +
372 + $this->created_by->id = (int) $safe_data['user_id'];
373 + $this->created_by->agent = $safe_data['comment_agent'];
374 + $this->created_by->name = $safe_data['comment_author'];
375 + $this->created_by->email = $safe_data['comment_author_email'];
376 + $this->created_by->ip = $safe_data['comment_author_IP'];
377 + $this->content = $safe_data['comment_content'];
378 + }
379 +
380 + /**
381 + * Retrieve an associative array following WordPress Comments design pattern with the instance's data.
382 + *
383 + * @since 1.13.0
384 + *
385 + * @return array
386 + */
387 + public function toWpPatterns() {
388 + $data = array(
389 + 'comment_id' => (int) $this->id,
390 + 'comment_post_ID' => (int) $this->project_id,
391 + 'comment_author_url' => '',
392 + 'user_id' => (int) $this->created_by->id,
393 + 'comment_author' => $this->created_by->name,
394 + 'comment_author_email' => $this->created_by->email,
395 + 'comment_content' => $this->content,
396 + 'comment_approved' => self::convertStateToWpPatterns( $this->state ),
397 + 'comment_author_IP' => isset( $this->created_by->ip ) ? $this->created_by->ip : '',
398 + 'comment_agent' => isset( $this->created_by->agent ) ? $this->created_by->agent : '',
399 + 'comment_parent' => (int) $this->parent_id > 0 ? $this->parent_id : 0,
400 + 'comment_type' => '',
401 + );
402 +
403 + return $data;
404 + }
405 +
406 + /**
407 + * Convert a given comment state into its equivalent WordPress value.
408 + *
409 + * @since 1.13.0
410 + * @static
411 + *
412 + * @param mixed $state The state to be translated.
413 + *
414 + * @return mixed
415 + */
416 + public static function convertStateToWpPatterns( $state ) {
417 + if ( is_numeric( $state ) ) {
418 + $state = (int) $state;
419 +
420 + if ( -1 === $state ) {
421 + $state = 'trash';
422 + }
423 + } elseif ( 'approve' === $state ) {
424 + $state = 1;
425 + } elseif ( 'hold' === $state ) {
426 + $state = 0;
427 + }
428 +
429 + return $state;
430 + }
431 +
432 + /**
433 + * Unapprove the comment.
434 + *
435 + * @since 1.13.0
436 + *
437 + * @return bool
438 + */
439 + public function unapprove() {
440 + if ( ! $this->isNew() ) {
441 + $success = self::updateApprovalState( $this->id, 0 );
442 +
443 + if ( $success ) {
444 + $this->state = 0;
445 + }
446 +
447 + return $success;
448 + }
449 +
450 + return false;
451 + }
452 +
453 + /**
454 + * Update comment state statically.
455 + *
456 + * @since 1.13.0
457 + * @access protected
458 + * @static
459 + *
460 + * @param int $comment_id Comment ID to be updated.
461 + * @param mixed $new_state Comment's new state.
462 + *
463 + * @return bool
464 + */
465 + protected static function updateApprovalState( $comment_id, $new_state ) {
466 + if ( ! in_array( strtolower( (string) $new_state ), array( '1', '0', 'spam', 'trash' ), true ) ) {
467 + return false;
468 + }
469 +
470 + $data = array(
471 + 'comment_ID' => (int) $comment_id,
472 + 'comment_approved' => $new_state,
473 + );
474 +
475 + $success = (bool) wp_update_comment( $data );
476 +
477 + return $success;
478 + }
479 +
480 + /**
481 + * Approve the comment.
482 + *
483 + * @since 1.13.0
484 + *
485 + * @return bool
486 + */
487 + public function approve() {
488 + if ( ! $this->isNew() ) {
489 + $success = self::updateApprovalState( $this->id, 1 );
490 +
491 + if ( $success ) {
492 + $this->state = 1;
493 + }
494 +
495 + return $success;
496 + }
497 +
498 + return false;
499 + }
500 +
501 + /**
502 + * Render the comment as HTML.
503 + *
504 + * @since 1.13.0
505 + *
506 + * @param bool $return Either return the HTML or render it instead.
507 + * @param bool $use_admin_layout Either use admin/frontend layout.
508 + * @param array $comments_cache Array of comments passed to render functions.
509 + *
510 + * @return string Will only return something if $return is true.
511 + */
512 + public function render( $return = false, $use_admin_layout = true, $comments_cache = array() ) {
513 + if ( empty( $this->current_user_cap ) ) {
514 + $user = wp_get_current_user();
515 + $user_has_admin_capabilities = upstream_is_user_either_manager_or_admin();
516 + $this->current_user_cap->can_reply = ! $user_has_admin_capabilities ? user_can(
517 + $user,
518 + 'publish_project_discussion'
519 + ) : true;
520 +
521 + $user_can_moderate = ! $user_has_admin_capabilities ? user_can(
522 + $user,
523 + 'moderate_comments'
524 + ) : true;
525 + $this->current_user_cap->can_moderate = $user_can_moderate;
526 + $this->current_user_cap->can_delete = ! $user_has_admin_capabilities ? ( $user_can_moderate || user_can(
527 + $user,
528 + 'delete_project_discussion'
529 + ) || $user->ID === (int) $created_by->id ) : true;
530 + }
531 +
532 + $this->updateHumanizedDate();
533 +
534 + if ( true === (bool) $return ) {
535 + ob_start();
536 +
537 + if ( true === (bool) $use_admin_layout ) {
538 + upstream_admin_display_message_item( $this, $comments_cache );
539 + } else {
540 + upstream_display_message_item( $this, $comments_cache );
541 + }
542 +
543 + $html = ob_get_contents();
544 +
545 + ob_end_clean();
546 +
547 + return $html;
548 + } else {
549 + if ( true === (bool) $use_admin_layout ) {
550 + upstream_admin_display_message_item( $this, $comments_cache );
551 + } else {
552 + upstream_display_message_item( $this, $comments_cache );
553 + }
554 + }
555 + }
556 +
557 + /**
558 + * Update comment's dates to human format.
559 + *
560 + * @since 1.13.0
561 + */
562 + public function updateHumanizedDate() {
563 + $date_format = get_option( 'date_format' );
564 + $time_format = get_option( 'time_format' );
565 + $the_date_time_format = $date_format . ' ' . $time_format;
566 + $current_timestamp = time();
567 +
568 + $date = \DateTime::createFromFormat( 'Y-m-d H:i:s', $this->created_at->utc );
569 + $date_timestamp = $date->getTimestamp();
570 +
571 + $this->created_at->localized = $date->format( $the_date_time_format );
572 + $this->created_at->humanized = sprintf(
573 + // translators: %s = human-readable time difference.
574 + _x( '%s ago', '%s = human-readable time difference', 'upstream' ),
575 + human_time_diff( $date_timestamp, $current_timestamp )
576 + );
577 + }
578 +}