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)