source: branches/1.3/libs/Comment.php @ 1742

Revision 1742, 20.2 KB checked in by nick_ramsay, 3 years ago (diff)

[Branch 1.3] Reduced the SQL result set for child comments by three columns. Checking the comment tree for each parent comment is quite time consuming so we have to shave off any unnecessary baggage.

Line 
1<?php
2/**
3 * The Comment class contains some useful methods for using comments
4 *
5 * PHP version 5
6 *
7 * LICENSE: Hotaru CMS is free software: you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License as
9 * published by the Free Software Foundation, either version 3 of
10 * the License, or (at your option) any later version.
11 *
12 * Hotaru CMS is distributed in the hope that it will be useful, but WITHOUT
13 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
14 * FITNESS FOR A PARTICULAR PURPOSE.
15 *
16 * You should have received a copy of the GNU General Public License along
17 * with Hotaru CMS. If not, see http://www.gnu.org/licenses/.
18 *
19 * @category  Content Management System
20 * @package   HotaruCMS
21 * @author    Nick Ramsay <admin@hotarucms.org>
22 * @copyright Copyright (c) 2010, Hotaru CMS
23 * @license   http://www.gnu.org/copyleft/gpl.html GNU General Public License
24 * @link      http://www.hotarucms.org/
25 */
26   
27class Comment
28{
29        protected $id           = 0;
30        protected $parent       = 0;
31        protected $postId       = 0;
32        protected $author       = 0;
33        protected $date         = '';
34        protected $status       = 'approved';
35        protected $votes_up     = 0;
36        protected $votes_down   = 0;
37        protected $content      = '';
38        protected $type         = 'newcomment';   // or "editcomment"
39        protected $subscribe    = 0;
40        protected $levels       = 0;         // max nesting levels
41        protected $depth        = 0;         // this nesting level
42        protected $email        = '';
43        protected $allowableTags = '';
44        protected $itemsPerPage = 20;
45        protected $pagination   = '';
46        protected $thisForm     = '';
47        protected $allForms     = 'checked';
48        protected $avatars      = '';
49        protected $avatarSize   = 16;
50        protected $voting       = '';
51        protected $order        = 'asc';   // oldest comments first
52       
53       
54        /**
55         * Access modifier to set protected properties
56         */
57        public function __set($var, $val)
58        {
59                $this->$var = $val; 
60        }
61       
62       
63        /**
64         * Access modifier to get protected properties
65         */
66        public function __get($var)
67        {
68                return $this->$var;
69        }
70       
71       
72        /**
73         * Count comments
74         *
75         * @param bool $digits_only - return just the count (if false, returns "3 comments", etc.)
76         * @param string $no_comments_text - e.g. "Leave a comment" or "No comments"
77         * @return string - text to show, e.g. "3 comments"
78         */
79        function countComments($h, $digits_only = true, $no_comments_text = '')
80        {
81                $sql = "SELECT COUNT(comment_id) FROM " . TABLE_COMMENTS . " WHERE comment_post_id = %d AND comment_status = %s";
82                $query = $h->db->prepare($sql, $h->post->id, 'approved');
83               
84                $h->smartCache('on', 'comments', 60, $query); // start using cache
85                $num_comments = $h->db->get_var($query);
86                $h->smartCache('off'); // stop using cache
87               
88                if ($digits_only) { return $num_comments; } // just return the number
89               
90                if ($num_comments == 1) {
91                        return "1 " . $h->lang['comments_singular_link'];
92                } elseif ($num_comments > 1) {
93                        return $num_comments . " " . $h->lang['comments_plural_link'];
94                }
95               
96                return $no_comments_text;  // shows "Leave a comment" above comment form when no comments
97        }
98       
99       
100        /**
101         * Count all user comments
102         *
103         * @param int $user_id
104         * @return int
105         */
106        function countUserComments($h, $user_id = 0)
107        {
108                if (!$user_id) { $user_id = $h->currentUser->id; }
109               
110                $sql = "SELECT COUNT(comment_id) FROM " . TABLE_COMMENTS . " WHERE comment_user_id = %d AND comment_status = %s";
111                $query = $h->db->prepare($sql, $user_id , 'approved');
112               
113                $h->smartCache('on', 'comments', 60, $query); // start using cache
114                $num_comments = $h->db->get_var($query);
115                $h->smartCache('off'); // stop using cache
116               
117                return $num_comments;
118        }
119       
120       
121        /**
122         * Read all comment parents
123         *
124         * @param int $post_id - the id of the post this comment is on
125         * @param array|false
126         */
127        function readAllParents($h, $post_id, $order = "ASC")
128        {
129                $sql = "SELECT * FROM " . TABLE_COMMENTS . " WHERE comment_post_id = %d AND comment_parent = %d AND comment_status = %s ORDER BY comment_date " . $order;
130                $query = $h->db->prepare($sql, $post_id, 0, 'approved');
131               
132                $h->smartCache('on', 'comments', 60, $query); // start using cache
133                $parents = $h->db->get_results($query);
134                $h->smartCache('off'); // stop using cache
135               
136                if($parents) { return $parents; } else { return false; }
137        }
138       
139       
140        /**
141         * Read all comment children
142         *
143         * @param int $parent - the id of the parent comment
144         * @param array|false
145         */
146        function readAllChildren($h, $parent)
147        {
148                $fields = "comment_id, comment_post_id, comment_user_id, comment_parent, comment_date, comment_status, comment_content, comment_votes_up, comment_votes_down, comment_subscribe";
149               
150                $sql = "SELECT " . $fields . " FROM " . TABLE_COMMENTS . " WHERE comment_parent = %d AND comment_status = %s ORDER BY comment_date";
151                $query = $h->db->prepare($sql, $parent, 'approved');
152               
153                $h->smartCache('on', 'comments', 60, $query); // start using cache
154                $children = $h->db->get_results($query);
155                $h->smartCache('off'); // stop using cache
156               
157                if($children) { return $children; } else { return false; }
158        }
159       
160       
161        /**
162         * Get comment from database
163         *
164         * @param int $comment_id
165         * @return array|false
166         */
167        function getComment($h, $comment_id = 0)
168        {
169                $sql = "SELECT * FROM " . TABLE_COMMENTS . " WHERE comment_id = %d";
170                $query = $h->db->prepare($sql, $comment_id);
171               
172                $h->smartCache('on', 'comments', 60, $query); // start using cache
173                $comment = $h->db->get_row($query);
174                $h->smartCache('off'); // stop using cache
175               
176                if($comment) { return $comment; } else { return false; }
177        }
178       
179       
180        /**
181         * Get all comments from database
182         *
183         * @param int $post_id - you can limit comments to a single post
184         * @return array|false
185         */
186        function getAllComments($h, $post_id = 0, $order = "ASC", $limit = 0, $userid = 0)
187        {
188                // limiting is used in the rssFeed function. Other than that, pagination does limiting for us.
189                if(!$limit) { $limit = ''; } else { $limit = " LIMIT "  .$limit; }
190               
191                if ($post_id) {
192                        // get all comments from specified post
193                        $sql = "SELECT * FROM " . TABLE_COMMENTS . " WHERE comment_post_id = %d AND comment_status = %s ORDER BY comment_date " . $order;
194                        $query = $h->db->prepare($sql, $post_id, 'approved');
195                        $h->smartCache('on', 'comments', 60, $query); // start using cache
196                        $comments = $h->db->get_results($query);
197                } else {
198                        // get all comments
199                        if ($userid) {
200                                $sql = "SELECT * FROM " . TABLE_COMMENTS . " WHERE comment_archived = %s AND comment_status = %s AND comment_user_id = %d ORDER BY comment_date " . $order . $limit;
201                                $query = $h->db->prepare($sql, 'N', 'approved', $userid);
202                                $h->smartCache('on', 'comments', 60, $query); // start using cache
203                                $comments = $h->db->get_results($query);
204                        } else {
205                                $sql = "SELECT * FROM " . TABLE_COMMENTS . " WHERE comment_archived = %s AND comment_status = %s ORDER BY comment_date " . $order . $limit;
206                                $query = $h->db->prepare($sql, 'N', 'approved');
207                                $h->smartCache('on', 'comments', 60, $query ); // start using cache
208                                $comments = $h->db->get_results($query );
209                        }
210                }
211                $h->smartCache('off'); // stop using cache
212               
213                if($comments) { return $comments; } else { return false; }
214        }
215       
216       
217        /**
218         * Get all comments from database
219         *
220         * @param int $post_id - you can limit comments to a single post
221         * @return array|false
222         */
223        function getAllCommentsCount($h, $order = "ASC", $userid = 0)
224        {
225                // get all comments
226                if ($userid) {
227                        $sql = "SELECT count(*) AS number FROM " . TABLE_COMMENTS . " WHERE comment_archived = %s AND comment_status = %s AND comment_user_id = %d ORDER BY comment_date " . $order;
228                        $query = $h->db->prepare($sql, 'N', 'approved', $userid);
229                        $h->smartCache('on', 'comments', 60, $query); // start using cache
230                        $comment_count = $h->db->get_var($query);
231                } else {
232                        $sql = "SELECT count(*) AS number FROM " . TABLE_COMMENTS . " WHERE comment_archived = %s AND comment_status = %s ORDER BY comment_date " . $order;
233                        $query = $h->db->prepare($sql, 'N', 'approved');
234                        $h->smartCache('on', 'comments', 60, $query); // start using cache
235                        $comment_count = $h->db->get_var($query);
236                }
237                $h->smartCache('off'); // stop using cache
238               
239                if($comment_count) { return $comment_count; } else { return false; }
240        }
241       
242       
243        /**
244         * Get all comments from database
245         *
246         * @param int $post_id - you can limit comments to a single post
247         * @return array|false
248         */
249        function getAllCommentsQuery($h, $order = "ASC", $userid = 0)
250        {
251                // get all comments
252                if ($userid) {
253                        $sql = "SELECT * FROM " . TABLE_COMMENTS . " WHERE comment_status = %s AND comment_user_id = %d ORDER BY comment_date " . $order;
254                        $query = $h->db->prepare($sql, 'approved', $userid);
255                } else {
256                        $sql = "SELECT * FROM " . TABLE_COMMENTS . " WHERE comment_status = %s ORDER BY comment_date " . $order;
257                        $query = $h->db->prepare($sql, 'approved');
258                }
259               
260                if($query) { return $query; } else { return false; }
261        }
262       
263       
264        /**
265         * Read comment
266         *
267         * @param array $comment
268         */
269        function readComment($h, $comment = array())
270        {
271                $this->id = $comment->comment_id;
272                $this->parent = $comment->comment_parent;
273                $this->postId = $comment->comment_post_id;
274                $this->author = $comment->comment_user_id;
275                $this->date = $comment->comment_date;
276                $this->status = $comment->comment_status;
277                $this->votes_up = $comment->comment_votes_up;
278                $this->votes_down = $comment->comment_votes_down;
279                $this->content = urldecode($comment->comment_content);
280                $this->subscribe = $comment->comment_subscribe;
281               
282                $h->pluginHook('comment_read_comment');
283               
284                return $this;
285        }
286       
287       
288        /**
289         * Add comment
290         *
291         * @return true
292         */
293        function addComment($h)
294        {
295                $result = array(); // this will be sent back containing various info
296               
297                // setup
298                $result['set_pending'] = '';
299                $result['comments_approved'] = 0;
300                $result['comments_needed'] = 0;
301                $result['exceeded_daily_limit'] = false;
302                $result['exceeded_url_limit'] = false;
303                $result['under_moderation'] = false;
304                $result['not_enough_comments'] = false;
305               
306                $h->pluginHook('comment_pre_add_comment');  // Akismet uses this to change the status
307               
308                $can_comment = $h->currentUser->getPermission('can_comment'); // This was already checked, but Akismet sometimes reverts the status, so we do it again.
309               
310                if ($can_comment == 'mod') { // forces all to 'pending' if user's comments are moderated
311                        $this->status = 'pending';
312                        $result['under_moderation'] = true;
313                }
314               
315                // Get settings from database...
316                $comments_settings = $h->getSerializedSettings('comments');
317               
318                $set_pending = $comments_settings['comment_set_pending'];
319                $daily_limit = $comments_settings['comment_daily_limit'];
320                $url_limit = $comments_settings['comment_url_limit'];
321               
322                $result['set_pending'] = $set_pending;
323               
324                if ($h->currentUser->role == 'member')
325                {
326                        if ($daily_limit && ($daily_limit < $this->countDailyComments($h)))
327                        {
328                                 // exceeded daily limit, set to pending
329                                $this->status = 'pending';
330                                $result['exceeded_daily_limit'] = true;
331                        }
332                       
333                        if ($url_limit && ($url_limit < $this->countUrls()))
334                        {
335                                // exceeded url limit, set to pending
336                                $this->status = 'pending';
337                                $result['exceeded_url_limit'] = true;
338                        }
339                       
340                }
341
342                if ($set_pending == 'some_pending') {
343                        $comments_approved = $this->commentsApproved($h, $h->currentUser->id);
344                        $x_comments_needed = $comments_settings['comment_x_comments'];
345                        if ($comments_approved < $x_comments_needed) {
346                                $result['not_enough_comments'] = true;
347                                $this->status = 'pending';
348                        }
349                }
350               
351                if ($set_pending == 'all_pending') {
352                        $this->status = 'pending';
353                }
354               
355                $sql = "INSERT INTO " . TABLE_COMMENTS . " SET comment_post_id = %d, comment_user_id = %d, comment_parent = %d, comment_date = CURRENT_TIMESTAMP, comment_status = %s, comment_content = %s, comment_subscribe = %d, comment_updateby = %d";
356               
357                $h->db->query($h->db->prepare($sql, $this->postId, $this->author, $this->parent, $this->status, urlencode(trim(stripslashes($this->content))), $this->subscribe, $h->currentUser->id));
358               
359                $last_insert_id = $h->db->get_var($h->db->prepare("SELECT LAST_INSERT_ID()"));
360               
361                $this->id = $last_insert_id;
362                $h->vars['last_insert_id'] = $last_insert_id;    // make it available outside this class
363               
364                $h->pluginHook('comment_post_add_comment');
365               
366                return $result;
367        }
368
369               
370        /**
371         * Edit comment
372         *
373         * @return true
374         */
375        function editComment($h)
376        {
377                $sql = "UPDATE " . TABLE_COMMENTS . " SET comment_status = %s, comment_content = %s, comment_subscribe = %d, comment_updateby = %d WHERE comment_id = %d";
378                $h->db->query($h->db->prepare($sql, $this->status, urlencode(trim(stripslashes($this->content))), $this->subscribe, $h->currentUser->id, $this->id));
379               
380                $h->comment->id = $this->id; // a small hack to get the id for use in plugins.
381                $h->pluginHook('comment_update_comment');
382               
383                return true;
384        }
385       
386       
387        /**
388         * Physically delete a comment from the database
389         *
390         */   
391        public function deleteComment($h, $comment_id = 0)
392        {
393                if (!$comment_id) { $comment_id = $this->id; }
394                if (!$comment_id) { return false; }
395               
396                $sql = "DELETE FROM " . TABLE_COMMENTS . " WHERE comment_id = %d";
397                $h->db->query($h->db->prepare($sql, $comment_id));
398               
399                // delete any votes for this comment
400                $sql = "DELETE FROM " . TABLE_COMMENTVOTES . " WHERE cvote_comment_id = %d";
401                $h->db->query($h->db->prepare($sql, $this->id));
402               
403                $h->comment->id = $comment_id; // a small hack to get the id for use in plugins.
404                $h->pluginHook('comment_delete_comment');
405               
406                // Need to clear both these caches to be sure related items are updated in widgets, etc.:
407                $h->clearCache('html_cache', false);
408                $h->clearCache('db_cache', false);
409        }
410       
411       
412        /**
413         * Physically delete all comments by a specified user (and responses)
414         *
415         * @param array $user_id
416         * @return bool
417         */
418        public function deleteComments($h, $user_id = 0)
419        {
420                if (!$user_id) { return false; }
421               
422                $sql = "SELECT comment_id FROM " . DB_PREFIX . "comments WHERE comment_user_id = %d";
423                $results = $h->db->get_results($h->db->prepare($sql, $user_id));
424               
425                if ($results) {
426                        foreach ($results as $r) {
427                                $h->comment->id = $r->comment_id;   // used by other plugins in "comment_delete_comment" function/hook
428                                $this->deleteComment($h, $h->comment->id);    // delete parent comment
429                                $this->deleteCommentTree($h, $h->comment->id);  // delete all children of that comment regardless of user
430                        }
431                }
432               
433                return true;
434        }
435       
436       
437        /**
438         * Recurse through comment tree, deleting all
439         *
440         * @param int $comment_id - id of current comment
441         * @return bool
442         */
443        public function deleteCommentTree($h, $comment_id)
444        {
445                while ($children = $this->readAllChildren($h, $comment_id)) {
446                        foreach ($children as $child) {
447                                $this->readComment($h, $child);
448                                $this->deleteComment($h, $this->id);
449                                if ($this->deletecommentTree($h, $this->id)) {
450                                        return true;
451                                }
452                        }
453       
454                        return false;
455                }
456        }
457       
458       
459        /**
460         * Recurse through comment tree, setting all to 'pending'
461         *
462         * @param int $comment_id - id of current comment
463         * @return bool
464         */
465        public function setPendingCommentTree($h, $comment_id)
466        {
467                while ($children = $this->readAllChildren($h, $comment_id)) {
468                        foreach ($children as $child) {
469                                $this->readComment($h, $child);
470                                $this->status = 'pending';
471                                $this->editComment($h);
472                                if ($this->setPendingCommentTree($h, $this->id)) {
473                                        return true;
474                                }
475                        }
476                       
477                        return false;
478                }
479        }
480       
481       
482        /**
483         * Determine if the comment form is open or closed
484         *
485         * @param int $post_id
486         * @return string 'open' or 'closed'
487         */
488        function formStatus($h, $type)
489        {
490                if ($type == 'select') {
491                        $sql = "SELECT post_comments FROM " . TABLE_POSTS . " WHERE post_id = %d";
492                        $form_status = $h->db->get_var($h->db->prepare($sql, $h->post->id));
493                       
494                        if ($form_status) { return $form_status; } else { return 'open'; } // default 'open'
495                }
496               
497                if ($type == 'open' || $type == 'closed') {
498                        $h->comment->form = $type;
499                        $sql = "UPDATE " . TABLE_POSTS . " SET post_comments = %s WHERE post_id = %d";
500                        $h->db->query($h->db->prepare($sql, $type, $h->post->id));
501                }
502        }
503       
504       
505        /**
506         * Unsubscribe from a thread
507         *
508         * @param int $post_id
509         * @return true
510         */
511        function unsubscribe($h, $post_id)
512        {
513                $h->readPost($post_id);
514       
515                $sql = "UPDATE " . TABLE_COMMENTS . " SET comment_subscribe = %d WHERE comment_post_id = %d AND comment_user_id = %d";
516                $h->db->query($h->db->prepare($sql, 0, $h->post->id, $h->currentUser->id));
517
518                // Check if the currentUser is the post author
519                if ($h->post->author == $h->currentUser->id) {
520                // Check if the user subscribed to comments as a submitter
521                        if ($h->post->subscribe == 1) {
522                                $sql = "UPDATE " . TABLE_POSTS . " SET post_subscribe = %d WHERE post_id = %d AND post_author = %d";
523                                $h->db->query($h->db->prepare($sql, 0, $h->post->id, $h->currentUser->id));
524                        }
525                }
526                return true;
527        }
528       
529       
530        /**
531         * Update thread subscription
532         *
533         * @param int $post_id
534         * @return true
535         */
536        function updateSubscribe($h, $post_id)
537        {
538                if ($this->subscribe == 1)
539                {
540                        $sql = "UPDATE " . TABLE_COMMENTS . " SET comment_subscribe = %d WHERE comment_post_id = %d AND comment_user_id = %d";
541                        $h->db->query($h->db->prepare($sql, 1, $h->post->id, $h->currentUser->id));
542                }
543                else
544                {
545                        $this->unsubscribe($h, $post_id);
546                }
547        }
548       
549       
550        /**
551         * Count how many approved comments a user has had
552         *
553         * @param int $userid
554         * @return int
555         */
556        public function commentsApproved($h, $userid)
557        {
558                $sql = "SELECT COUNT(*) FROM " . TABLE_COMMENTS . " WHERE comment_status = %s AND comment_user_id = %d";
559                $query = $h->db->prepare($sql, 'approved', $userid);
560               
561                $h->smartCache('on', 'comments', 60, $query); // start using cache
562                $count = $h->db->get_var($query);
563                $h->smartCache('off'); // stop using cache
564               
565                return $count;
566        }
567       
568       
569        /**
570         * Count daily comments for this commenter
571         *
572         * @return int
573         */
574        public function countDailyComments($h)
575        {
576                $start = date('YmdHis', time_block());
577                $end = date('YmdHis', strtotime("-1 day"));
578                $sql = "SELECT COUNT(comment_id) FROM " . TABLE_COMMENTS . " WHERE comment_archived = %s AND comment_user_id = %d AND (comment_date >= %s AND comment_date <= %s)";
579                $query = $h->db->prepare($sql, 'N', $this->author, $end, $start);
580               
581                $h->smartCache('on', 'comments', 60, $query); // start using cache
582                $count = $h->db->get_var($query);
583                $h->smartCache('off'); // stop using cache
584               
585                return $count;
586        }
587       
588       
589        /**
590         * Count urls in comment
591         *
592         * @return int
593         * @link http://www.liamdelahunty.com/tips/php_url_count_check_for_comment_spam.php
594         */
595        public function countUrls()
596        {
597                $text = $this->content;
598               
599                //$http = substr_count($text, "http");
600                $href = substr_count($text, "href");
601                $url = substr_count($text, "[url");
602               
603                return $href + $url;
604        }
605       
606       
607        /**
608         * Stats for Admin homepage
609         *
610         * @param string $stat_type
611         * @return int
612         */
613        public function stats($h, $stat_type = '')
614        {
615                switch ($stat_type) {
616                        case 'total_comments':
617                                $query = "SELECT count(comment_id) FROM " . TABLE_COMMENTS;
618                                $h->smartCache('on', 'comments', 60, $query); // start using cache
619                                $comments = $h->db->get_var($query);
620                                break;
621                        case 'approved_comments':
622                                $sql = "SELECT count(comment_id) FROM " . TABLE_COMMENTS . " WHERE comment_status = %s";
623                                $query = $h->db->prepare($sql, 'approved');
624                                $h->smartCache('on', 'comments', 60, $query); // start using cache
625                                $comments = $h->db->get_var($query);
626                                break;
627                        case 'pending_comments':
628                                $sql = "SELECT count(comment_id) FROM " . TABLE_COMMENTS . " WHERE comment_status = %s";
629                                $query = $h->db->prepare($sql, 'pending');
630                                $h->smartCache('on', 'comments', 60, $query); // start using cache
631                                $comments = $h->db->get_var($query);
632                                break;
633                        case 'archived_comments':
634                                $sql = "SELECT count(comment_id) FROM " . TABLE_COMMENTS . " WHERE comment_archived = %s";
635                                $query = $h->db->prepare($sql, 'Y');
636                                $h->smartCache('on', 'comments', 60, $query); // start using cache
637                                $comments = $h->db->get_var($query);
638                                break;
639                        default:
640                                $comments = '';
641                }
642                $h->smartCache('off'); // stop using cache
643               
644                return $comments;
645        }
646}
647?>
Note: See TracBrowser for help on using the repository browser.