PluginProbe
UpStream: a Project Management Plugin for WordPress / 1.39.1
UpStream: a Project Management Plugin for WordPress v1.39.1
trunk 1.39.0 1.39.1 1.39.2 1.39.3 2.0.7 2.1.0
upstream / includes / class-up-comment.php

class-up-comment.php in UpStream: a Project Management Plugin for WordPress 1.39.1, at includes/class-up-comment.php

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