19 declare(strict_types=1);
60 private bool $is_moderator =
false,
61 bool $preventImplicitRead =
false 64 $this->db = $DIC->database();
65 $this->
user = $DIC->user();
67 if (!$preventImplicitRead) {
74 $this->
setId((
int) $data[
'thr_pk']);
84 $this->
setSticky((
bool) $data[
'is_sticky']);
85 $this->
setClosed((
bool) $data[
'is_closed']);
86 $this->
setAverageRating(isset($data[
'avg_rating']) ? (
float) $data[
'avg_rating'] : 0);
90 if (isset($data[
'num_posts'])) {
93 if (isset($data[
'num_unread_posts'])) {
96 if (isset($data[
'usr_notification_is_enabled'])) {
103 if ($this->forum_id !== 0) {
104 $nextId = $this->db->nextId(
'frm_threads');
109 'thr_pk' => [
'integer', $nextId],
110 'thr_top_fk' => [
'integer', $this->forum_id],
111 'thr_subject' => [
'text', $this->subject],
112 'thr_display_user_id' => [
'integer', $this->display_user_id],
113 'thr_usr_alias' => [
'text', $this->user_alias],
114 'thr_num_posts' => [
'integer', $this->num_posts],
115 'thr_last_post' => [
'text', $this->last_post_string],
116 'thr_date' => [
'timestamp', $this->createdate],
117 'thr_update' => [
'timestamp', null],
118 'import_name' => [
'text', $this->import_name],
119 'is_sticky' => [
'integer', (
int) $this->is_sticky],
120 'is_closed' => [
'integer', (
int) $this->is_closed],
121 'avg_rating' => [
'text', (
string) $this->average_rating],
122 'thr_author_id' => [
'integer', $this->thr_author_id]
136 if ($this->
id !== 0) {
137 $this->db->manipulateF(
147 [
'integer',
'text',
'timestamp',
'integer',
'text',
'text',
'integer'],
153 $this->last_post_string,
154 (
string) $this->average_rating,
167 if ($this->
id !== 0) {
168 $res = $this->db->queryF(
170 SELECT frm_threads.*, top_frm_fk frm_obj_id 172 INNER JOIN frm_data ON top_pk = thr_top_fk 180 if (is_object($row)) {
181 $this->forum_id = (
int) $row->thr_top_fk;
182 $this->display_user_id = (
int) $row->thr_display_user_id;
183 $this->user_alias = $row->thr_usr_alias;
184 $this->subject = html_entity_decode((
string) $row->thr_subject);
185 $this->createdate = $row->thr_date;
186 $this->changedate = $row->thr_update;
187 $this->import_name = $row->import_name;
188 $this->num_posts = (
int) $row->thr_num_posts;
189 $this->last_post_string = $row->thr_last_post;
190 $this->visits = (
int) $row->visits;
191 $this->is_sticky = (bool) $row->is_sticky;
192 $this->is_closed = (
bool) $row->is_closed;
193 $this->frm_obj_id = (
int) $row->frm_obj_id;
194 $this->average_rating = (
float) $row->avg_rating;
195 $this->thr_author_id = (
int) $row->thr_author_id;
208 return $this->
read();
213 $this->db->setLimit(1);
214 $res = $this->db->queryF(
215 'SELECT pos_fk FROM frm_posts_tree WHERE thr_fk = %s AND parent_pos = %s AND depth = %s ORDER BY rgt DESC',
216 [
'integer',
'integer',
'integer'],
220 if (($row = $this->db->fetchObject(
$res)) !== null) {
221 return (
int) $row->pos_fk ?: 0;
228 $this->db->setLimit(1);
229 $res = $this->db->queryF(
230 'SELECT pos_fk FROM frm_posts_tree WHERE thr_fk = %s AND parent_pos != %s AND depth = %s ORDER BY rgt DESC',
231 [
'integer',
'integer',
'integer'],
235 if (($row = $this->db->fetchObject(
$res)) !== null) {
236 return (
int) $row->pos_fk ?: 0;
243 $checkTime = time() - (60 * 60);
245 if (
ilSession::get(
'frm_visit_frm_threads_' . $this->
id) < $checkTime) {
248 $this->db->manipulateF(
249 'UPDATE frm_threads SET visits = visits + 1 WHERE thr_pk = %s',
258 $res = $this->db->queryF(
262 INNER JOIN frm_posts_tree ON frm_posts_tree.pos_fk = pos_pk 263 WHERE pos_thr_fk = %s' . ($ignoreRoot ?
' AND parent_pos != 0 ' :
''),
268 $row = $this->db->fetchAssoc(
$res);
269 if (is_array($row)) {
270 return (
int) $row[
'cnt'];
278 $res = $this->db->queryF(
282 INNER JOIN frm_posts_tree ON frm_posts_tree.pos_fk = pos_pk 283 WHERE (pos_status = %s 284 OR (pos_status = %s AND pos_display_user_id = %s)) 285 AND pos_thr_fk = %s' . ($ignoreRoot ?
' AND parent_pos != 0 ' :
''),
286 [
'integer',
'integer',
'integer',
'integer'],
290 $row = $this->db->fetchAssoc(
$res);
291 if (is_array($row)) {
292 return (
int) $row[
'cnt'];
300 $this->db->setLimit(1);
301 $res = $this->db->queryF(
305 INNER JOIN frm_posts_tree ON pos_fk = pos_pk 306 WHERE parent_pos = %s 309 [
'integer',
'integer'],
313 if ($row = $this->db->fetchAssoc(
$res)) {
314 $post =
new ilForumPost((
int) $row[
'pos_pk'], $isModerator, $preventImplicitRead);
315 $post->assignData($row);
324 $this->db->setLimit(1);
325 $res = $this->db->queryF(
329 INNER JOIN frm_posts_tree ON pos_fk = pos_pk 330 WHERE parent_pos != %s 334 [
'integer',
'integer',
'integer'],
338 if ($row = $this->db->fetchAssoc(
$res)) {
339 $post =
new ilForumPost((
int) $row[
'pos_pk'], $isModerator, $preventImplicitRead);
340 $post->assignData($row);
349 if ($this->
id !== 0) {
350 $this->db->setLimit(1);
351 $res = $this->db->queryF(
352 'SELECT pos_pk FROM frm_posts WHERE pos_thr_fk = %s ORDER BY pos_date DESC',
357 if (($row = $this->db->fetchObject(
$res)) !== null) {
367 if ($this->
id !== 0) {
368 $this->db->setLimit(1);
369 $res = $this->db->queryF(
373 WHERE pos_thr_fk = %s 374 AND (pos_status = %s OR (pos_status = %s AND pos_display_user_id = %s)) 375 ORDER BY pos_date DESC',
376 [
'integer',
'integer',
'integer',
'integer'],
377 [$this->
id,
'1',
'0', $this->
user->getId()]
380 if (($row = $this->db->fetchObject(
$res)) !== null) {
385 throw new OutOfBoundsException(sprintf(
'Could not find last active posting by id: %s', $this->
id));
395 if ($this->
id !== 0) {
396 $res = $this->db->queryF(
'SELECT pos_pk FROM frm_posts WHERE pos_thr_fk = %s', [
'integer'], [$this->
id]);
399 $posts[(
int) $row->pos_pk] = (
int) $row->pos_pk;
418 if ($a_post_node->
getLft() > 1) {
419 $dummy_root_condition =
'lft >= %s AND lft < %s';
421 $dummy_root_condition =
'lft > %s AND lft < %s';
425 SELECT is_author_moderator, pos_author_id, pos_pk, fpt_date, rgt, pos_top_fk, pos_thr_fk, 426 pos_display_user_id, pos_usr_alias, pos_subject, 427 pos_status, pos_message, pos_date, pos_update, rcid, 428 update_user, pos_cens, pos_cens_com, notify, 429 import_name, fpt_pk, parent_pos, lft, depth, 431 WHEN fur.post_id IS NULL ' .
436 firstname, lastname, title, login 444 ON pos_display_user_id = usr_id 446 LEFT JOIN frm_user_read fur 447 ON fur.thread_id = pos_thr_fk 448 AND fur.post_id = pos_pk 451 WHERE ' . $dummy_root_condition .
' 453 $data_types[] =
'integer';
454 $data_types[] =
'integer';
455 $data_types[] =
'integer';
456 $data_types[] =
'integer';
462 if ($this->orderField !==
'') {
466 $res = $this->db->queryF($query, $data_types,
$data);
469 while ($row = $this->db->fetchAssoc(
$res)) {
471 $post->assignData($row);
473 if (!$this->is_moderator && !
$post->isActivated() &&
$post->getPosAuthorId() !== $this->
user->getId()) {
477 if ((
int) $row[
'pos_display_user_id'] !== 0) {
478 $usr_ids[(
int) $row[
'pos_display_user_id']] = (
int) $row[
'pos_display_user_id'];
480 if ((
int) $row[
'update_user'] !== 0) {
481 $usr_ids[(
int) $row[
'update_user']] = (
int) $row[
'update_user'];
501 public function movePosts(
int $old_obj_id,
int $old_pk,
int $new_obj_id,
int $new_pk):
int 503 if ($this->
id === 0) {
510 foreach ($post_ids as $post_id) {
512 $moved = $file_obj->moveFilesOfPost($new_obj_id);
516 'from' => $old_obj_id,
518 'position_id' => $post_id
525 foreach ($postsMoved as $postedInformation) {
526 $file_obj =
new ilFileDataForum($postedInformation[
'to'], $postedInformation[
'position_id']);
527 $file_obj->moveFilesOfPost($postedInformation[
'from']);
535 $ilAtomQuery = $this->db->buildAtomQuery();
536 $ilAtomQuery->addTableLock(
'frm_user_read');
538 $ilAtomQuery->addQueryCallable(
static function (
ilDBInterface $ilDB) use ($new_obj_id, $current_id):
void {
540 'DELETE FROM frm_user_read WHERE obj_id = %s AND thread_id =%s',
541 [
'integer',
'integer'],
542 [$new_obj_id, $current_id]
546 'UPDATE frm_user_read SET obj_id = %s WHERE thread_id = %s',
547 [
'integer',
'integer'],
548 [$new_obj_id, $current_id]
554 $this->db->manipulateF(
555 'UPDATE frm_posts SET pos_top_fk = %s WHERE pos_thr_fk = %s',
556 [
'integer',
'integer'],
560 $res = $this->db->queryF(
561 'SELECT * FROM frm_posts WHERE pos_thr_fk = %s',
569 while (
$post = $this->db->fetchAssoc(
$res)) {
573 (
int)
$post[
'pos_pk'],
577 $news_item->setContextObjId($new_obj_id);
578 $news_item->update();
581 return count($post_ids);
588 $is_post_activation_enabled = $objProperties->isPostActivationEnabled();
590 if ($pos_id !== null) {
591 $res = $this->db->queryF(
593 SELECT lft, rgt, depth 597 [
'integer',
'integer'],
614 fp.pos_display_user_id, 618 fp.is_author_moderator, 621 WHEN fur.post_id IS NULL ' .
626 COUNT(fpt2.pos_fk) children 628 FROM frm_posts_tree fpt 630 INNER JOIN frm_posts fp 631 ON fp.pos_pk = fpt.pos_fk 633 LEFT JOIN frm_posts_tree fpt2 634 ON fpt2.lft BETWEEN fpt.lft AND fpt.rgt 635 AND fpt.thr_fk = fpt2.thr_fk 636 AND fpt.pos_fk != fpt2.pos_fk ';
639 LEFT JOIN frm_user_read fur 640 ON fur.thread_id = fp.pos_thr_fk 641 AND fur.post_id = fp.pos_pk 642 AND fur.usr_id = ' . $this->db->quote($this->
user->getId(),
'integer') .
' 644 LEFT JOIN usr_data ud 645 ON ud.usr_id = fp.pos_display_user_id 647 WHERE fpt.thr_fk = ' . $this->db->quote($this->id,
'integer');
650 $query .=
' AND fpt.lft > ' . $this->db->quote(
$data[
'lft'],
'integer') .
651 ' AND fpt.lft < ' . $this->db->quote(
$data[
'rgt'],
'integer') .
' ';
653 if ($is_post_activation_enabled && !$this->is_moderator) {
654 $query .=
' AND (fp.pos_status = 1 OR fp.pos_status = 0 AND fp.pos_display_user_id = ' . $this->db->quote(
655 $this->
user->getId(),
660 if (
$data && is_numeric($num_levels)) {
661 $query .=
' AND fpt.depth <= ' . $this->db->quote((
int)
$data[
'depth'] + $num_levels,
'integer') .
' ';
664 $query .=
' GROUP BY fpt.depth, 673 fp.pos_display_user_id, 677 fp.is_author_moderator, 679 ORDER BY fpt.rgt DESC 684 FROM frm_posts_tree fpt 685 INNER JOIN frm_posts fp 686 ON fp.pos_pk = fpt.pos_fk 687 WHERE fpt.thr_fk = ' . $this->db->quote($this->
id,
'integer');
689 if ($is_post_activation_enabled && !$this->is_moderator) {
690 $queryCounter .=
' AND (fp.pos_status = 1 OR fp.pos_status = 0 AND fp.pos_display_user_id = ' . $this->db->quote(
691 $this->
user->getId(),
695 $queryCounter .=
' ORDER BY fpt.rgt DESC';
697 $resCounter = $this->db->query($queryCounter);
700 while ($row = $this->db->fetchAssoc($resCounter)) {
701 $counter[(
int) $row[
'pos_fk']] = $i++;
704 $res = $this->db->query($query);
707 while ($row = $this->db->fetchAssoc($res)) {
708 if ((
int) $row[
'pos_display_user_id'] !== 0) {
709 $usr_ids[] = (
int) $row[
'pos_display_user_id'];
712 $row[
'counter'] = $counter[$row[
'pos_pk']];
714 $casted_row[
'depth'] = (
int) $row[
'depth'];
715 $casted_row[
'rgt'] = (
int) $row[
'rgt'];
716 $casted_row[
'parent_pos'] = (
int) $row[
'parent_pos'];
717 $casted_row[
'pos_pk'] = (
int) $row[
'pos_pk'];
718 $casted_row[
'pos_subject'] = (string) $row[
'pos_subject'];
719 $casted_row[
'pos_usr_alias'] = (string) $row[
'pos_usr_alias'];
720 $casted_row[
'pos_date'] = (string) $row[
'pos_date'];
721 $casted_row[
'pos_update'] = (string) $row[
'pos_update'];
722 $casted_row[
'pos_status'] = (
int) $row[
'pos_status'];
723 $casted_row[
'pos_display_user_id'] = (
int) $row[
'pos_display_user_id'];
724 $casted_row[
'import_name'] = (string) $row[
'import_name'];
725 $casted_row[
'pos_author_id'] = (
int) $row[
'pos_author_id'];
726 $casted_row[
'is_author_moderator'] = (
int) $row[
'is_author_moderator'];
727 $casted_row[
'post_id'] = (
int) $row[
'post_id'];
728 $casted_row[
'post_read'] = (
int) $row[
'post_read'];
729 $casted_row[
'children'] = (
int) $row[
'children'];
731 $children[] = $casted_row;
741 if ($this->
id && $a_user_id) {
742 $result = $this->db->queryF(
743 'SELECT COUNT(notification_id) cnt FROM frm_notification WHERE user_id = %s AND thread_id = %s',
744 [
'integer',
'integer'],
745 [$a_user_id, $this->
id]
748 if ($row = $this->db->fetchAssoc($result)) {
749 return (
int) $row[
'cnt'] > 0;
761 $nextId = $this->db->nextId(
'frm_notification');
762 $this->db->manipulateF(
764 INSERT INTO frm_notification 770 [
'integer',
'integer',
'integer'],
771 [$nextId, $a_user_id, $this->
id]
778 if ($this->
id && $a_user_id) {
779 $this->db->manipulateF(
780 'DELETE FROM frm_notification WHERE user_id = %s AND thread_id = %s',
781 [
'integer',
'integer'],
782 [$a_user_id, $this->
id]
789 if ($this->
id && !$this->is_sticky) {
790 $this->db->manipulateF(
791 'UPDATE frm_threads SET is_sticky = %s WHERE thr_pk = %s',
792 [
'integer',
'integer'],
796 $this->is_sticky =
true;
805 if ($this->
id && $this->is_sticky) {
806 $this->db->manipulateF(
807 'UPDATE frm_threads SET is_sticky = %s WHERE thr_pk = %s',
808 [
'integer',
'integer'],
812 $this->is_sticky =
false;
821 if ($this->
id && !$this->is_closed) {
822 $this->db->manipulateF(
823 'UPDATE frm_threads SET is_closed = %s WHERE thr_pk = %s',
824 [
'integer',
'integer'],
827 $this->is_closed =
true;
833 if ($this->
id && $this->is_closed) {
834 $this->db->manipulateF(
835 'UPDATE frm_threads SET is_closed = %s WHERE thr_pk = %s',
836 [
'integer',
'integer'],
840 $this->is_closed =
false;
854 public function setId(
int $a_id): void
866 $this->forum_id = $a_forum_id;
876 $this->display_user_id = $a_user_id;
886 $this->user_alias = $a_user_alias;
896 $this->subject = $a_subject;
906 $this->createdate = $a_createdate;
916 $this->changedate = $a_changedate;
926 $this->import_name = $a_import_name;
936 $this->last_post_string = $a_last_post;
946 $this->visits = $a_visits;
956 $this->is_sticky = $a_sticky;
966 $this->is_closed = $a_closed;
976 $this->orderField = $a_order_field;
1002 $ilDB = $DIC->database();
1005 'SELECT thr_subject FROM frm_threads WHERE thr_pk = %s',
1011 return (
string) $row->thr_subject;
1021 [
'thr_subject' => [
'text', $this->
getSubject()]],
1022 [
'thr_pk' => [
'integer', $this->
getId()]]
1027 $first_node->setSubject($this->
getSubject());
1028 $first_node->update();
1035 $this->num_posts = $a_num_posts;
1057 $this->user_notification_enabled = $status;
1068 if (!in_array(strtoupper($direction), self::$possibleOrderDirections,
true)) {
1069 $direction = current(self::$possibleOrderDirections);
1072 $this->orderDirection = $direction;
1084 $ilDB = $DIC->database();
1087 'SELECT thr_top_fk FROM frm_threads WHERE thr_pk = %s',
1094 return (
int) $row[
'thr_top_fk'];
1102 'thr_num_posts' => [
'integer', $this->
getNumPosts()],
1103 'visits' => [
'integer', $this->
getVisits()],
1105 'thr_subject' => [
'text', $this->
getSubject()]
1107 [
'thr_pk' => [
'integer', $this->
getId()]]
1114 $ilDB = $DIC->database();
1117 'SELECT thr_date FROM frm_threads WHERE thr_pk = %s',
1124 if (is_array($row)) {
1125 $date = $row[
'thr_date'];
1138 $this->last_post =
$post;
static get(string $a_var)
manipulateF(string $query, array $types, array $values)
getNestedSetPostChildren(?int $pos_id=null, ?int $num_levels=null)
setCreateDate(?string $a_createdate)
static array $possibleOrderDirections
setUserAlias(?string $a_user_alias)
setDisplayUserId(int $a_user_id)
__construct(private int $id=0, private bool $is_moderator=false, bool $preventImplicitRead=false)
Returns an object of a forum topic.
static lookupTitle(int $a_topic_id)
movePosts(int $old_obj_id, int $old_pk, int $new_obj_id, int $new_pk)
Moves all posts within the current thread to a new forum.
enableNotification(int $a_user_id)
setLastPostString(?string $a_last_post)
setSticky(bool $a_sticky)
static lookupCreationDate(int $thread_id)
setNumPosts(int $a_num_posts)
getPostRootNode(bool $isModerator=false, bool $preventImplicitRead=false)
static getInstance(int $a_obj_id=0)
setImportName(?string $a_import_name)
countActivePosts(bool $ignoreRoot=false)
setForumId(int $a_forum_id)
getLastPostForThreadOverview()
setNumUnreadPosts(int $num_unread_posts)
setAverageRating(float $average_rating)
setClosed(bool $a_closed)
static _lookupObjIdForForumId(int $a_for_id)
isUserNotificationEnabled()
setChangeDate(?string $a_changedate)
A news item can be created by different sources.
setSubject(string $a_subject)
static getFirstNewsIdForContext(int $a_context_obj_id, string $a_context_obj_type, int $a_context_sub_obj_id=0, string $a_context_sub_obj_type="")
Get first new id of news set related to a certain context.
setLastPostForThreadOverview(ilForumPost $post)
setOrderField(string $a_order_field)
$id
plugin.php for ilComponentBuildPluginInfoObjectiveTest::testAddPlugins
setUserNotificationEnabled(bool $status)
static lookupForumIdByTopicId(int $a_topic_id)
getPostTree(ilForumPost $a_post_node)
Fetches and returns an array of posts from the post tree, starting with the node object passed by the...
isNotificationEnabled(int $a_user_id)
disableNotification(int $a_user_id)
getFirstVisiblePostNode(bool $isModerator=false, bool $preventImplicitRead=false)
countPosts(bool $ignoreRoot=false)
static set(string $a_var, $a_val)
Set a value.
setOrderDirection(string $direction)
bool $user_notification_enabled
setThrAuthorId(int $thr_author_id)