1<?php
2/* Copyright (c) 1998-2012 ILIAS open source, Extended GPL, see docs/LICENSE */
3
4/**
5* @author Michael Jansen <mjansen@databay.de>
6* @version $Id:$
7*
8* @ingroup ModulesForum
9*/
10class ilForumTopic
11{
12    private $id = 0;
13
14    private $forum_id = 0;
15
16    private $frm_obj_id = 0;
17
18    private $display_user_id = 0;
19
20    private $user_alias = '';
21
22    private $subject = '';
23
24    private $createdate = null;
25
26    private $changedate = null;
27
28    private $num_posts = 0;
29
30    private $last_post_string = '';
31
32    private $visits = 0;
33
34    private $import_name = '';
35
36    private $is_sticky = 0;
37
38    private $is_closed = 0;
39
40    private $orderField = '';
41
42    /** @var null|ilForumPost */
43    private $last_post = null;
44
45    private $db = null;
46
47    private $is_moderator = false;
48
49    private $thr_author_id = 0;
50
51    /**
52     * @var double
53     */
54    private $average_rating = 0;
55
56    private $orderDirection = 'DESC';
57
58    protected static $possibleOrderDirections = array('ASC', 'DESC');
59
60    /**
61    * Constructor
62    *
63    * Returns an object of a forum topic. The constructor calls the private method read()
64    * to load the topic data from database into the object.
65    *
66    * @param  	integer	$a_id			primary key of a forum topic (optional)
67    * @param  	bool	$a_is_moderator	moderator-status of the current user (optional)
68    * @param	bool	$preventImplicitRead	Prevents the implicit database query if an id was passed
69    *
70    * @access	public
71    */
72    public function __construct($a_id = 0, $a_is_moderator = false, $preventImplicitRead = false)
73    {
74        global $DIC;
75
76        $this->is_moderator = $a_is_moderator;
77        $this->db = $DIC->database();
78        $this->user = $DIC->user();
79        $this->id = $a_id;
80
81        if (!$preventImplicitRead) {
82            $this->read();
83        }
84    }
85
86    /**
87     * @param $data
88     */
89    public function assignData($data)
90    {
91        $this->setId((int) $data['thr_pk']);
92        $this->setForumId((int) $data['thr_top_fk']);
93        $this->setSubject($data['thr_subject']);
94        $this->setDisplayUserId((int) $data['thr_display_user_id']);
95        $this->setUserAlias($data['thr_usr_alias']);
96        $this->setLastPostString($data['last_post_string']);
97        $this->setCreateDate($data['thr_date']);
98        $this->setChangeDate($data['thr_update']);
99        $this->setVisits((int) $data['visits']);
100        $this->setImportName($data['import_name']);
101        $this->setSticky((int) $data['is_sticky']);
102        $this->setClosed((int) $data['is_closed']);
103        $this->setAverageRating($data['avg_rating']);
104        $this->setThrAuthorId($data['thr_author_id']);
105
106        // Aggregated values
107        $this->setNumPosts((int) $data['num_posts']);
108        $this->setNumUnreadPosts((int) $data['num_unread_posts']);
109        $this->setNumNewPosts((int) $data['num_new_posts']);
110        $this->setUserNotificationEnabled((bool) $data['usr_notification_is_enabled']);
111    }
112
113    /**
114    * Inserts the object data into database
115    *
116    * @return 	bool	true in case of success, false in case of failure
117    * @access 	public
118    */
119    public function insert()
120    {
121        if ($this->forum_id) {
122            $nextId = $this->db->nextId('frm_threads');
123
124            $this->db->insert(
125                'frm_threads',
126                array(
127                'thr_pk' => array('integer', $nextId),
128                'thr_top_fk' => array('integer', $this->forum_id),
129                'thr_subject' => array('text', $this->subject),
130                'thr_display_user_id' => array('integer', $this->display_user_id),
131                'thr_usr_alias' => array('text', $this->user_alias),
132                'thr_num_posts' => array('integer', $this->num_posts),
133                'thr_last_post' => array('text', $this->last_post_string),
134                'thr_date' => array('timestamp', $this->createdate),
135                'thr_update' => array('timestamp', null),
136                'import_name' => array('text', $this->import_name),
137                'is_sticky' => array('integer', $this->is_sticky),
138                'is_closed' => array('integer', $this->is_closed),
139                'avg_rating' => array('float', $this->average_rating),
140                'thr_author_id' => array('integer', $this->thr_author_id)
141            )
142            );
143
144            $this->id = $nextId;
145
146            return true;
147        }
148
149        return false;
150    }
151
152    /**
153    * Updates an existing topic
154    *
155    * @return 	bool	true in case of success, false in case of failure
156    * @access 	public
157    */
158    public function update()
159    {
160        if ($this->id) {
161            $this->db->manipulateF(
162                '
163				UPDATE frm_threads
164				SET thr_top_fk = %s,
165					thr_subject = %s,
166					thr_update = %s,
167					thr_num_posts = %s,
168					thr_last_post = %s,
169					avg_rating = %s
170				WHERE thr_pk = %s',
171                array('integer', 'text','timestamp', 'integer', 'text', 'float', 'integer'),
172                array(	$this->forum_id,
173                        $this->subject,
174            /*			$this->changedate, */
175                        date('Y-m-d H:i:s'),
176                        $this->num_posts,
177                        $this->last_post_string,
178                        $this->average_rating,
179                        $this->id
180            )
181            );
182
183            return true;
184        }
185
186        return false;
187    }
188
189    /**
190    * Reads the data of the current object id from database and loads it into the object.
191    *
192    * @return  	bool	true in case of success, false in case of failure
193    *
194    * @access 	private
195    */
196    private function read()
197    {
198        if ($this->id) {
199            $res = $this->db->queryf(
200                '
201				SELECT frm_threads.*, top_frm_fk frm_obj_id
202				FROM frm_threads
203				INNER JOIN frm_data ON top_pk = thr_top_fk
204				WHERE thr_pk = %s',
205                array('integer'),
206                array($this->id)
207            );
208
209            $row = $res->fetchRow(ilDBConstants::FETCHMODE_OBJECT);
210
211            if (is_object($row)) {
212                $this->thr_pk = $row->pos_pk;   // thr_pk = pos_pk ??!??!
213                $this->forum_id = $row->thr_top_fk;
214                $this->display_user_id = $row->thr_display_user_id;
215                $this->user_alias = $row->thr_usr_alias;
216                $this->subject = html_entity_decode($row->thr_subject);
217                $this->createdate = $row->thr_date;
218                $this->changedate = $row->thr_update;
219                $this->import_name = $row->import_name;
220                $this->num_posts = $row->thr_num_posts;
221                $this->last_post_string = $row->thr_last_post;
222                $this->visits = $row->visits;
223                $this->is_sticky = $row->is_sticky;
224                $this->is_closed = $row->is_closed;
225                $this->frm_obj_id = $row->frm_obj_id;
226                $this->average_rating = $row->avg_rating;
227                $this->thr_author_id = $row->thr_author_id;
228
229                return true;
230            }
231            $this->id = 0;
232            return false;
233        }
234
235        return false;
236    }
237
238    /**
239    * Calls the private method read() to load the topic data from database into the object.
240    *
241    * @return  	bool	true in case of success, false in case of failure
242    * @access 	public
243    */
244    public function reload()
245    {
246        return $this->read();
247    }
248
249    /**
250    * Fetches the primary key of the first post node of the current topic from database and returns it.
251    *
252    * @return  	integer		primary key of the first post node
253    * @access 	public
254    */
255    public function getFirstPostId()
256    {
257        $res = $this->db->queryf(
258            '
259			SELECT * FROM frm_posts_tree
260			WHERE thr_fk = %s
261			AND parent_pos = %s',
262            array('integer', 'integer'),
263            array($this->id, '1')
264        );
265
266        $row = $res->fetchRow(ilDBConstants::FETCHMODE_OBJECT);
267
268        return $row->pos_fk ? $row->pos_fk : 0;
269    }
270
271    /**
272    * Updates the visit counter of the current topic.
273    *
274    * @access	public
275    */
276    public function updateVisits()
277    {
278        $checkTime = time() - (60 * 60);
279
280        if ($_SESSION['frm_visit_frm_threads_' . $this->id] < $checkTime) {
281            $_SESSION['frm_visit_frm_threads_' . $this->id] = time();
282
283            $this->db->manipulateF(
284                '
285				UPDATE frm_threads
286				SET	visits = visits + 1
287				WHERE thr_pk = %s',
288                array('integer'),
289                array($this->id)
290            );
291        }
292
293        return true;
294    }
295
296    /**
297    * Fetches and returns the number of posts for the given user id.
298    *
299    * @param  	integer		$a_user_id		user id
300    * @return	integer		number of posts
301    * @access	public
302    */
303    public function countPosts($ignoreRoot = false)
304    {
305        $res = $this->db->queryf(
306            '
307			SELECT COUNT(*) cnt
308			FROM frm_posts
309			INNER JOIN frm_posts_tree ON frm_posts_tree.pos_fk = pos_pk
310			WHERE pos_thr_fk = %s' . ($ignoreRoot ? ' AND parent_pos != 0 ' : ''),
311            array('integer'),
312            array($this->id)
313        );
314
315        $rec = $res->fetchRow(ilDBConstants::FETCHMODE_ASSOC);
316
317        return $rec['cnt'];
318    }
319
320    /**
321     * Fetches and returns the number of active posts for the given user id.
322     *
323     * @param bool $ignoreRoot
324     * @return    integer        number of active posts
325     * @access    public
326     */
327    public function countActivePosts($ignoreRoot = false)
328    {
329        $res = $this->db->queryf(
330            '
331			SELECT COUNT(*) cnt
332			FROM frm_posts
333			INNER JOIN frm_posts_tree ON frm_posts_tree.pos_fk = pos_pk
334			WHERE (pos_status = %s
335				 OR (pos_status = %s AND pos_display_user_id = %s))
336			AND pos_thr_fk = %s' . ($ignoreRoot ? ' AND parent_pos != 0 ' : ''),
337            array('integer', 'integer', 'integer', 'integer'),
338            array('1', '0', $this->user->getId(), $this->id)
339        );
340
341        $rec = $res->fetchRow(ilDBConstants::FETCHMODE_ASSOC);
342
343        return $rec['cnt'];
344    }
345
346    /**
347     * Fetches and returns an object of the first post in the current topic
348     * @param bool $isModerator
349     * @param bool $preventImplicitRead
350    * @return	ilForumPost		object of a post
351    */
352    public function getFirstPostNode($isModerator = false, $preventImplicitRead = false)
353    {
354        $res = $this->db->queryF(
355            '
356			SELECT *
357			FROM frm_posts
358			INNER JOIN frm_posts_tree ON pos_fk = pos_pk
359			WHERE parent_pos = %s
360			AND thr_fk = %s',
361            array('integer', 'integer'),
362            array(0, $this->id)
363        );
364
365        $row = $this->db->fetchAssoc($res);
366
367        $post = new ilForumPost($row['pos_pk'], $isModerator, $preventImplicitRead);
368        $post->assignData($row);
369
370        return $post;
371    }
372
373    /**
374    * Fetches and returns an object of the last post in the current topic.
375    *
376    * @return	ilForumPost		object of the last post
377    * @access	public
378    */
379    public function getLastPost()
380    {
381        if ($this->id) {
382            $this->db->setLimit(1);
383            $res = $this->db->queryf(
384                '
385				SELECT pos_pk
386				FROM frm_posts
387				WHERE pos_thr_fk = %s
388				ORDER BY pos_date DESC',
389                array('integer'),
390                array($this->id)
391            );
392
393            $row = $res->fetchRow(ilDBConstants::FETCHMODE_OBJECT);
394
395            return new ilForumPost($row->pos_pk);
396        }
397
398        return false;
399    }
400
401    /**
402    * Fetches and returns an object of the last active post in the current topic.
403    *
404    * @return	ilForumPost		object of the last active post
405    * @access	public
406    */
407    public function getLastActivePost()
408    {
409        if ($this->id) {
410            $this->db->setLimit(1);
411            $res = $this->db->queryf(
412                '
413				SELECT pos_pk
414				FROM frm_posts
415				WHERE pos_thr_fk = %s
416				AND (pos_status = %s OR
417					(pos_status = %s AND pos_display_user_id = %s))
418				ORDER BY pos_date DESC',
419                array('integer', 'integer', 'integer', 'integer'),
420                array($this->id, '1', '0', $this->user->getId())
421            );
422
423            $row = $res->fetchRow(ilDBConstants::FETCHMODE_OBJECT);
424
425            return new ilForumPost($row->pos_pk);
426        }
427
428        return false;
429    }
430
431    public function getAllPosts()
432    {
433        $posts = array();
434
435        if ($this->id) {
436            $res = $this->db->queryf(
437                '
438				SELECT pos_pk
439				FROM frm_posts
440				WHERE pos_thr_fk = %s',
441                array('integer'),
442                array($this->id)
443            );
444
445            while ($row = $res->fetchRow(ilDBConstants::FETCHMODE_OBJECT)) {
446                $posts[$row->pos_pk] = $row;
447            }
448        }
449
450        return is_array($posts) ? $posts : array();
451    }
452
453    /**
454    * Fetches and returns an array of posts from the post tree, starting with the node object passed by
455    * the first paramter.
456    *
457    * @param    ilForumPost	$a_post_node	node-object of a post
458    * @return	array		array of post objects
459    * @access	public
460    */
461    public function getPostTree(ilForumPost $a_post_node)
462    {
463        $posts = array();
464
465        $data = array();
466        $data_types = array();
467
468        $query = '
469			SELECT 			is_author_moderator, pos_author_id, pos_pk, fpt_date, rgt, pos_top_fk, pos_thr_fk,
470							pos_display_user_id, pos_usr_alias, pos_subject,
471							pos_status, pos_message, pos_date, pos_update,
472							update_user, pos_cens, pos_cens_com, notify,
473							import_name, fpt_pk, parent_pos, lft, depth,
474							(CASE
475							WHEN fur.post_id IS NULL ' .
476                                ($this->user->getId() == ANONYMOUS_USER_ID ? ' AND 1 = 2 ' : '') . '
477							THEN 0
478							ELSE 1
479							END) post_read,
480							firstname, lastname, title, login
481
482			FROM 			frm_posts_tree
483
484			INNER JOIN 		frm_posts
485				ON 			pos_fk = pos_pk
486
487			LEFT JOIN		usr_data
488				ON			pos_display_user_id  = usr_id
489
490			LEFT JOIN		frm_user_read fur
491				ON			fur.thread_id = pos_thr_fk
492				AND			fur.post_id = pos_pk
493				AND			fur.usr_id = %s
494
495			WHERE 			lft > %s AND lft < %s
496				AND 		thr_fk = %s';
497
498        array_push($data_types, 'integer', 'integer', 'integer', 'integer');
499        array_push($data, $this->user->getId(), $a_post_node->getLft(), $a_post_node->getRgt(), $a_post_node->getThreadId());
500
501        if ($this->orderField != "") {
502            $query .= " ORDER BY " . $this->orderField . " " . $this->getOrderDirection();
503        }
504
505        $res = $this->db->queryF($query, $data_types, $data);
506
507        $usr_ids = [];
508        while ($row = $this->db->fetchAssoc($res)) {
509            $post = new ilForumPost($row['pos_pk'], false, true);
510            $post->assignData($row);
511
512            if (!$this->is_moderator) {
513                if (!$post->isActivated() && $post->getPosAuthorId() != $this->user->getId()) {
514                    continue;
515                }
516            }
517
518            if ((int) $row['pos_display_user_id']) {
519                $usr_ids[(int) $row['pos_display_user_id']] = (int) $row['pos_display_user_id'];
520            }
521            if ((int) $row['update_user']) {
522                $usr_ids[(int) $row['update_user']] = (int) $row['update_user'];
523            }
524
525            $posts[] = $post;
526        }
527
528        ilForumAuthorInformationCache::preloadUserObjects(array_values($usr_ids));
529
530        return $posts;
531    }
532
533    /**
534    * Moves all posts within the current thread to a new forum
535    *
536    * @param    integer 	$old_obj_id object id of the current forum
537    * @param    integer 	$old_pk		primary key of old forum
538    * @param    integer 	$new_obj_id	object id of the new forum
539    * @param    integer 	$new_pk		primary key of new forum
540    * @return	integer 	number of afffected rows by updating posts
541    * @access	public
542    */
543    public function movePosts($old_obj_id, $old_pk, $new_obj_id, $new_pk)
544    {
545        if ($this->id) {
546            $nodes = $this->getAllPosts();
547            if (is_array($nodes)) {
548                $postsMoved = array();
549                // Move attachments
550                try {
551                    foreach ($nodes as $node) {
552                        $file_obj = new ilFileDataForum((int) $old_obj_id, (int) $node->pos_pk);
553                        $moved = $file_obj->moveFilesOfPost((int) $new_obj_id);
554
555                        if (true === $moved) {
556                            $postsMoved[] = array(
557                                'from' => $old_obj_id,
558                                'to' => $new_obj_id,
559                                'position_id' => (int) $node->pos_pk
560                            );
561                        }
562
563                        unset($file_obj);
564                    }
565                } catch (\ilFileUtilsException $exception) {
566                    foreach ($postsMoved as $postedInformation) {
567                        $file_obj = new ilFileDataForum($postedInformation['to'], $postedInformation['position_id']);
568                        $file_obj->moveFilesOfPost($postedInformation['from']);
569                    }
570
571                    throw $exception;
572                }
573            }
574
575            $current_id = $this->id;
576
577            $ilAtomQuery = $this->db->buildAtomQuery();
578            $ilAtomQuery->addTableLock('frm_user_read');
579            $ilAtomQuery->addTableLock('frm_thread_access');
580
581            $ilAtomQuery->addQueryCallable(function (ilDBInterface $ilDB) use ($new_obj_id, $current_id) {
582                $ilDB->manipulateF(
583                    '
584				DELETE FROM frm_user_read
585				WHERE obj_id = %s AND thread_id =%s',
586                    array('integer', 'integer'),
587                    array($new_obj_id, $current_id)
588                );
589
590                $ilDB->manipulateF(
591                    '
592				UPDATE frm_user_read
593				SET obj_id = %s
594				WHERE thread_id = %s',
595                    array('integer', 'integer'),
596                    array($new_obj_id, $current_id)
597                );
598
599                $ilDB->manipulateF(
600                    '
601				DELETE FROM frm_thread_access
602				WHERE obj_id = %s AND thread_id =%s',
603                    array('integer', 'integer'),
604                    array($new_obj_id, $current_id)
605                );
606
607                $ilDB->manipulateF(
608                    '
609				UPDATE frm_thread_access
610				SET obj_id = %s
611				WHERE thread_id =%s',
612                    array('integer', 'integer'),
613                    array($new_obj_id, $current_id)
614                );
615            });
616
617            $ilAtomQuery->run();
618
619            $this->db->manipulateF(
620                '
621				UPDATE frm_posts
622				SET pos_top_fk = %s
623				WHERE pos_thr_fk = %s',
624                array('integer', 'integer'),
625                array($new_pk, $this->id)
626            );
627
628            // update all related news
629            $posts = $this->db->queryf(
630                '
631				SELECT * FROM frm_posts WHERE pos_thr_fk = %s',
632                array('integer'),
633                array($this->id)
634            );
635
636            $old_obj_id = ilForum::_lookupObjIdForForumId($old_pk);
637
638            $new_obj_id = ilForum::_lookupObjIdForForumId($new_pk);
639
640            while ($post = $posts->fetchRow(ilDBConstants::FETCHMODE_ASSOC)) {
641                $news_id = ilNewsItem::getFirstNewsIdForContext(
642                    $old_obj_id,
643                    "frm",
644                    $post["pos_pk"],
645                    "pos"
646                );
647                $news_item = new ilNewsItem($news_id);
648                $news_item->setContextObjId($new_obj_id);
649                $news_item->update();
650                //echo "<br>-".$post["pos_pk"]."-".$old_obj_id."-".$new_obj_id."-";
651            }
652
653            return count($nodes);
654        }
655
656        return 0;
657    }
658
659    public function getNestedSetPostChildren($pos_id = null, $levels = null)
660    {
661        $data = null;
662        $objProperties = ilForumProperties::getInstance($this->getFrmObjId());
663        $is_post_activation_enabled = $objProperties->isPostActivationEnabled();
664
665        if ($pos_id !== null) {
666            $res = $this->db->queryF(
667                "
668				SELECT		lft, rgt, depth
669				FROM		frm_posts_tree
670				WHERE		pos_fk = %s
671				AND			thr_fk = %s",
672                array('integer', 'integer'),
673                array($pos_id, $this->id)
674            );
675
676            $data = $this->db->fetchAssoc($res);
677        }
678
679        $query = '
680			SELECT			fpt.depth,
681							fpt.rgt,
682							fpt.parent_pos,
683							fp.pos_pk,
684							fp.pos_subject,
685							fp.pos_usr_alias,
686							fp.pos_date,
687							fp.pos_update,
688							fp.pos_status,
689							fp.pos_display_user_id,
690							fp.pos_usr_alias,
691							fp.import_name,
692							fp.pos_author_id,
693							fp.is_author_moderator,
694							fur.post_id,
695							(CASE
696							WHEN fur.post_id IS NULL ' .
697                                ($this->user->getId() == ANONYMOUS_USER_ID ? ' AND 1 = 2 ' : '') . '
698							THEN 0
699							ELSE 1
700							END) post_read,
701							COUNT(fpt2.pos_fk) children
702
703			FROM			frm_posts_tree fpt
704
705			INNER JOIN		frm_posts fp
706				ON			fp.pos_pk = fpt.pos_fk
707
708			LEFT JOIN		frm_posts_tree fpt2
709				 ON         fpt2.lft BETWEEN fpt.lft AND fpt.rgt
710				 AND		fpt.thr_fk = fpt2.thr_fk
711				 AND		fpt.pos_fk != fpt2.pos_fk ';
712
713
714        $query .= '
715			LEFT JOIN		frm_user_read fur
716				ON			fur.thread_id = fp.pos_thr_fk
717				AND			fur.post_id = fp.pos_pk
718				AND			fur.usr_id = ' . $this->db->quote($this->user->getId(), 'integer') . '
719
720			LEFT JOIN		usr_data ud
721				ON			ud.usr_id = fp.pos_display_user_id
722
723			WHERE			fpt.thr_fk = ' . $this->db->quote($this->id, 'integer');
724
725        if ($data) {
726            $query .= '		AND fpt.lft > ' . $this->db->quote($data['lft'], 'integer') .
727                    '		AND fpt.lft < ' . $this->db->quote($data['rgt'], 'integer') . ' ';
728        }
729        if ($is_post_activation_enabled && !$this->is_moderator) {
730            $query .= ' AND (fp.pos_status = 1 OR fp.pos_status = 0 AND fp.pos_display_user_id = ' . $this->db->quote($this->user->getId(), 'integer') . ') ';
731        }
732
733        if ($data && is_numeric($levels)) {
734            $query .= ' AND fpt.depth <= ' . $this->db->quote($data['depth'] + $levels, 'integer') . ' ';
735        }
736
737        $query .= ' GROUP BY fpt.depth,
738							fpt.rgt,
739							fpt.parent_pos,
740							fp.pos_pk,
741							fp.pos_subject,
742							fp.pos_usr_alias,
743							fp.pos_date,
744							fp.pos_update,
745							fp.pos_status,
746							fp.pos_display_user_id,
747							fp.pos_usr_alias,
748							fp.import_name,
749							fp.pos_author_id,
750							fp.is_author_moderator,
751							fur.post_id
752					ORDER BY fpt.rgt DESC
753		';
754
755        $queryCounter = '
756			SELECT			pos_fk
757			FROM			frm_posts_tree fpt
758			INNER JOIN		frm_posts fp
759				ON			fp.pos_pk = fpt.pos_fk
760			WHERE			fpt.thr_fk = ' . $this->db->quote($this->id, 'integer');
761
762        if ($is_post_activation_enabled && !$this->is_moderator) {
763            $queryCounter .= ' AND (fp.pos_status = 1 OR fp.pos_status = 0 AND fp.pos_display_user_id = ' . $this->db->quote($this->user->getId(), 'integer') . ') ';
764        }
765        $queryCounter .= ' ORDER BY fpt.rgt DESC';
766
767        $resCounter = $this->db->query($queryCounter);
768        $counter = array();
769        $i = 0;
770        while ($row = $this->db->fetchAssoc($resCounter)) {
771            $counter[$row['pos_fk']] = $i++;
772        }
773
774        $res = $this->db->query($query);
775        $children = array();
776        $usr_ids = array();
777        while ($row = $this->db->fetchAssoc($res)) {
778            if ((int) $row['pos_display_user_id']) {
779                $usr_ids[] = (int) $row['pos_display_user_id'];
780            }
781
782            $row['counter'] = $counter[$row['pos_pk']];
783            $children[] = $row;
784        }
785
786        ilForumAuthorInformationCache::preloadUserObjects(array_unique($usr_ids));
787
788        return $children;
789    }
790
791    /**
792    * Check whether a user's notification about new posts in a thread is enabled (result > 0) or not (result == 0).
793    * @param    integer		$a_user_id		id of an user
794    * @return	bool		true in case of success, false in case of failure
795    * @access	public
796    */
797    public function isNotificationEnabled($a_user_id)
798    {
799        if ($this->id && $a_user_id) {
800            $result = $this->db->queryf(
801                '
802				SELECT COUNT(notification_id) cnt FROM frm_notification
803				WHERE user_id = %s AND thread_id = %s',
804                array('integer', 'integer'),
805                array($a_user_id, $this->id)
806            );
807
808            while ($record = $this->db->fetchAssoc($result)) {
809                return (bool) $record['cnt'];
810            }
811
812            return false;
813        }
814
815        return false;
816    }
817
818    /**
819    * Enable a user's notification about new posts in a thread.
820    * @param    integer	$a_user_id		id of an user
821    * @return	bool	true in case of success, false in case of failure
822    * @access	public
823    */
824    public function enableNotification($a_user_id)
825    {
826        if ($this->id && $a_user_id) {
827            if (!$this->isNotificationEnabled($a_user_id)) {
828                $nextId = $this->db->nextId('frm_notification');
829                $this->db->manipulateF(
830                    '
831					INSERT INTO frm_notification
832					(	notification_id,
833						user_id,
834						thread_id
835					)
836					VALUES(%s, %s, %s)',
837                    array('integer', 'integer', 'integer'),
838                    array($nextId, $a_user_id, $this->id)
839                );
840
841                return true;
842            }
843            return false;
844        }
845
846        return false;
847    }
848
849    /**
850    * Disable a user's notification about new posts in a thread.
851    * @param    integer	$a_user_id		id of an user
852    * @return	bool	true in case of success, false in case of failure
853    * @access	public
854    */
855    public function disableNotification($a_user_id)
856    {
857        if ($this->id && $a_user_id) {
858            $this->db->manipulateF(
859                '
860				DELETE FROM frm_notification
861				WHERE user_id = %s
862				AND thread_id = %s',
863                array('integer', 'integer'),
864                array($a_user_id, $this->id)
865            );
866
867            return false;
868        }
869
870        return false;
871    }
872
873    /**
874    * Sets the current topic sticky.
875    *
876    * @return  	bool	true in case of success, false in case of failure
877    * @access 	public
878    */
879    public function makeSticky()
880    {
881        if ($this->id && !$this->is_sticky) {
882            $this->db->manipulateF(
883                '
884				UPDATE frm_threads
885				SET is_sticky = %s
886				WHERE thr_pk = %s',
887                array('integer', 'integer'),
888                array('1', $this->id)
889            );
890
891            $this->is_sticky = 1;
892
893            return true;
894        }
895
896        return false;
897    }
898
899    /**
900    * Sets the current topic non-sticky.
901    *
902    * @return  	bool	true in case of success, false in case of failure
903    * @access 	public
904    */
905    public function unmakeSticky()
906    {
907        if ($this->id && $this->is_sticky) {
908            $this->db->manipulateF(
909                '
910				UPDATE frm_threads
911				SET is_sticky = %s
912				WHERE thr_pk = %s',
913                array('integer', 'integer'),
914                array('0', $this->id)
915            );
916
917            $this->is_sticky = 0;
918
919            return true;
920        }
921
922        return false;
923    }
924
925    /**
926    * Closes the current topic.
927    *
928    * @return  	bool	true in case of success, false in case of failure
929    * @access 	public
930    */
931    public function close()
932    {
933        if ($this->id && !$this->is_closed) {
934            $this->db->manipulateF(
935                '
936				UPDATE frm_threads
937				SET is_closed = %s
938				WHERE thr_pk = %s',
939                array('integer', 'integer'),
940                array('1', $this->id)
941            );
942
943            $this->is_closed = 1;
944
945            return true;
946        }
947
948        return false;
949    }
950
951    /**
952    * Reopens the current topic.
953    *
954    * @return  	bool	true in case of success, false in case of failure
955    * @access 	public
956    */
957    public function reopen()
958    {
959        if ($this->id && $this->is_closed) {
960            $this->db->manipulateF(
961                '
962				UPDATE frm_threads
963				SET is_closed = %s
964				WHERE thr_pk = %s',
965                array('integer', 'integer'),
966                array('0', $this->id)
967            );
968
969            $this->is_closed = 0;
970
971            return true;
972        }
973
974        return false;
975    }
976
977    /**
978     * @return int
979     */
980    public function getAverageRating()
981    {
982        return $this->average_rating;
983    }
984
985    /**
986     * @param int $average_rating
987     */
988    public function setAverageRating($average_rating)
989    {
990        $this->average_rating = $average_rating;
991    }
992
993    public function setId($a_id)
994    {
995        $this->id = $a_id;
996    }
997    public function getId()
998    {
999        return $this->id;
1000    }
1001    public function setForumId($a_forum_id)
1002    {
1003        $this->forum_id = $a_forum_id;
1004    }
1005    public function getForumId()
1006    {
1007        return $this->forum_id;
1008    }
1009    public function setDisplayUserId($a_user_id)
1010    {
1011        $this->display_user_id = $a_user_id;
1012    }
1013    public function getDisplayUserId()
1014    {
1015        return $this->display_user_id;
1016    }
1017    public function setUserAlias($a_user_alias)
1018    {
1019        $this->user_alias = $a_user_alias;
1020    }
1021    public function getUserAlias()
1022    {
1023        return $this->user_alias;
1024    }
1025    public function setSubject($a_subject)
1026    {
1027        $this->subject = $a_subject;
1028    }
1029    public function getSubject()
1030    {
1031        return $this->subject;
1032    }
1033    public function setCreateDate($a_createdate)
1034    {
1035        $this->createdate = $a_createdate;
1036    }
1037    public function getCreateDate()
1038    {
1039        return $this->createdate;
1040    }
1041    public function setChangeDate($a_changedate)
1042    {
1043        if ($a_changedate == '0000-00-00 00:00:00') {
1044            $this->changedate = null;
1045        } else {
1046            $this->changedate = $a_changedate;
1047        }
1048    }
1049    public function getChangeDate()
1050    {
1051        return $this->changedate;
1052    }
1053    public function setImportName($a_import_name)
1054    {
1055        $this->import_name = $a_import_name;
1056    }
1057    public function getImportName()
1058    {
1059        return $this->import_name;
1060    }
1061    public function setLastPostString($a_last_post)
1062    {
1063        if ($a_last_post == '') {
1064            $a_last_post = null;
1065        }
1066
1067        $this->last_post_string = $a_last_post;
1068    }
1069    public function getLastPostString()
1070    {
1071        return $this->last_post_string;
1072    }
1073    public function setVisits($a_visits)
1074    {
1075        $this->visits = $a_visits;
1076    }
1077    public function getVisits()
1078    {
1079        return $this->visits;
1080    }
1081    public function setSticky($a_sticky)
1082    {
1083        $this->is_sticky = $a_sticky;
1084    }
1085    public function isSticky()
1086    {
1087        return $this->is_sticky == 1 ? true : false;
1088    }
1089    public function setClosed($a_closed)
1090    {
1091        $this->is_closed = $a_closed;
1092    }
1093    public function isClosed()
1094    {
1095        return $this->is_closed == 1 ? true : false;
1096    }
1097    public function setOrderField($a_order_field)
1098    {
1099        $this->orderField = $a_order_field;
1100    }
1101    public function getOrderField()
1102    {
1103        return $this->orderField;
1104    }
1105    public function setModeratorRight($bool)
1106    {
1107        $this->is_moderator = $bool;
1108    }
1109    public function getModeratorRight()
1110    {
1111        return $this->is_moderator;
1112    }
1113    public function getFrmObjId()
1114    {
1115        return $this->frm_obj_id;
1116    }
1117
1118    /**
1119     * @param int $thr_author_id
1120     */
1121    public function setThrAuthorId($thr_author_id)
1122    {
1123        $this->thr_author_id = $thr_author_id;
1124    }
1125
1126    /**
1127     * @return int
1128     */
1129    public function getThrAuthorId()
1130    {
1131        return $this->thr_author_id;
1132    }
1133
1134    /**
1135    * Looks up the title/subject of a topic/thread
1136    *
1137    * @param	integer id of the topic/thread
1138    * @return  	string	title/subject of the topic/thread
1139    * @access 	public
1140    * @static
1141    */
1142    public static function _lookupTitle($a_topic_id)
1143    {
1144        global $DIC;
1145        $ilDB = $DIC->database();
1146
1147        $res = $ilDB->queryf(
1148            '
1149			SELECT thr_subject
1150			FROM frm_threads
1151			WHERE thr_pk = %s',
1152            array('integer'),
1153            array($a_topic_id)
1154        );
1155        $row = $ilDB->fetchObject($res);
1156
1157        if (is_object($row)) {
1158            return $row->thr_subject;
1159        }
1160
1161        return '';
1162    }
1163
1164    public function updateThreadTitle()
1165    {
1166        $this->db->update(
1167            'frm_threads',
1168            array('thr_subject' => array('text',$this->getSubject())),
1169            array('thr_pk' => array('integer', $this->getId()))
1170        );
1171
1172        $first_node = $this->getFirstPostNode();
1173        $first_node->setSubject($this->getSubject());
1174        $first_node->update();
1175    }
1176
1177    /**
1178     * @param $a_num_posts
1179     * @return ilForumTopic
1180     */
1181    public function setNumPosts($a_num_posts)
1182    {
1183        $this->num_posts = $a_num_posts;
1184        return $this;
1185    }
1186
1187    /**
1188     * @return int
1189     */
1190    public function getNumPosts()
1191    {
1192        return $this->num_posts;
1193    }
1194
1195    /**
1196     * @param int $num_new_posts
1197     * @return ilForumTopic
1198     */
1199    public function setNumNewPosts($num_new_posts)
1200    {
1201        $this->num_new_posts = $num_new_posts;
1202        return $this;
1203    }
1204
1205    /**
1206     * @return int
1207     */
1208    public function getNumNewPosts()
1209    {
1210        return $this->num_new_posts;
1211    }
1212
1213    /**
1214     * @param int $num_unread_posts
1215     * @return ilForumTopic
1216     */
1217    public function setNumUnreadPosts($num_unread_posts)
1218    {
1219        $this->num_unread_posts = $num_unread_posts;
1220        return $this;
1221    }
1222
1223    /**
1224     * @return int
1225     */
1226    public function getNumUnreadPosts()
1227    {
1228        return $this->num_unread_posts;
1229    }
1230
1231    /**
1232     * @param boolean $user_notification_enabled
1233     * @return ilForumTopic
1234     */
1235    public function setUserNotificationEnabled($user_notification_enabled)
1236    {
1237        $this->user_notification_enabled = $user_notification_enabled;
1238        return $this;
1239    }
1240
1241    /**
1242     * @return boolean
1243     */
1244    public function getUserNotificationEnabled()
1245    {
1246        return $this->user_notification_enabled;
1247    }
1248
1249    public function setOrderDirection($direction)
1250    {
1251        if (!in_array(strtoupper($direction), self::$possibleOrderDirections)) {
1252            $direction = current(self::$possibleOrderDirections);
1253        }
1254
1255        $this->orderDirection = $direction;
1256        return $this;
1257    }
1258
1259    public function getOrderDirection()
1260    {
1261        return $this->orderDirection;
1262    }
1263
1264    public static function lookupForumIdByTopicId($a_topic_id)
1265    {
1266        global $DIC;
1267        $ilDB = $DIC->database();
1268
1269        $res = $ilDB->queryF(
1270            'SELECT thr_top_fk FROM frm_threads WHERE thr_pk = %s',
1271            array('integer'),
1272            array($a_topic_id)
1273        );
1274
1275        $row = $ilDB->fetchAssoc($res);
1276
1277        return $row['thr_top_fk'];
1278    }
1279
1280    public function getSorting()
1281    {
1282        return $this->thread_sorting;
1283    }
1284    public function updateMergedThread()
1285    {
1286        $this->db->update(
1287            'frm_threads',
1288            array(
1289                'thr_num_posts' => array('integer', $this->getNumPosts()),
1290                'visits' => array('integer', $this->getVisits()),
1291                'thr_last_post' => array('text', $this->getLastPostString()),
1292                'thr_subject' => array('text', $this->getSubject())
1293            ),
1294            array('thr_pk' => array('integer', $this->getId()))
1295        );
1296    }
1297
1298    /**
1299     * @param integer $thread_id
1300     * @return string datetime
1301     */
1302    public static function _lookupDate($thread_id)
1303    {
1304        global $DIC;
1305        $ilDB = $DIC->database();
1306
1307        $res = $ilDB->queryF(
1308            'SELECT thr_date FROM frm_threads WHERE thr_pk = %s',
1309            array('integer'),
1310            array((int) $thread_id)
1311        );
1312
1313        $row = $ilDB->fetchAssoc($res);
1314
1315        return $row['thr_date'] ? $row['thr_date'] : '0000-00-00 00:00:00';
1316    }
1317
1318    /**
1319     * @return ilForumPost|null
1320     */
1321    public function getLastPostForThreadOverview()
1322    {
1323        return $this->last_post;
1324    }
1325
1326    /**
1327     * @param ilForumPost $post
1328     */
1329    public function setLastPostForThreadOverview(ilForumPost $post)
1330    {
1331        $this->last_post = $post;
1332    }
1333}
1334