ILIAS  release_9 Revision v9.13-25-g2c18ec4c24f
class.ilForumTopic.php
Go to the documentation of this file.
1 <?php
2 
19 declare(strict_types=1);
20 
26 {
27  private int $forum_id = 0;
28  private int $frm_obj_id = 0;
29  private int $display_user_id = 0;
30  private ?string $user_alias = null;
31  private string $subject = '';
32  private ?string $createdate = null;
33  private ?string $changedate = null;
34  private int $num_posts = 0;
35  private ?string $last_post_string = null;
36  private int $visits = 0;
37  private ?string $import_name = null;
38  private bool $is_sticky = false;
39  private bool $is_closed = false;
40  private string $orderField = '';
41  private ?ilForumPost $last_post = null;
42  private ilDBInterface $db;
43  private int $thr_author_id = 0;
44  private float $average_rating = 0.0;
45  private string $orderDirection = 'DESC';
46  protected static array $possibleOrderDirections = ['ASC', 'DESC'];
47  private ilObjUser $user;
48  private int $num_unread_posts = 0;
49  private bool $user_notification_enabled = false;
50 
58  public function __construct(
59  private int $id = 0,
60  private bool $is_moderator = false,
61  bool $preventImplicitRead = false
62  ) {
63  global $DIC;
64  $this->db = $DIC->database();
65  $this->user = $DIC->user();
66 
67  if (!$preventImplicitRead) {
68  $this->read();
69  }
70  }
71 
72  public function assignData(array $data): void
73  {
74  $this->setId((int) $data['thr_pk']);
75  $this->setForumId((int) $data['thr_top_fk']);
76  $this->setSubject($data['thr_subject']);
77  $this->setDisplayUserId((int) $data['thr_display_user_id']);
78  $this->setUserAlias($data['thr_usr_alias']);
79  $this->setLastPostString($data['thr_last_post']);
80  $this->setCreateDate($data['thr_date']);
81  $this->setChangeDate($data['thr_update']);
82  $this->setVisits((int) $data['visits']);
83  $this->setImportName($data['import_name']);
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);
87  $this->setThrAuthorId((int) $data['thr_author_id']);
88 
89  // Aggregated values
90  if (isset($data['num_posts'])) {
91  $this->setNumPosts((int) $data['num_posts']);
92  }
93  if (isset($data['num_unread_posts'])) {
94  $this->setNumUnreadPosts((int) $data['num_unread_posts']);
95  }
96  if (isset($data['usr_notification_is_enabled'])) {
97  $this->setUserNotificationEnabled((bool) $data['usr_notification_is_enabled']);
98  }
99  }
100 
101  public function insert(): bool
102  {
103  if ($this->forum_id !== 0) {
104  $nextId = $this->db->nextId('frm_threads');
105 
106  $this->db->insert(
107  'frm_threads',
108  [
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]
123  ]
124  );
125 
126  $this->id = $nextId;
127 
128  return true;
129  }
130 
131  return false;
132  }
133 
134  public function update(): bool
135  {
136  if ($this->id !== 0) {
137  $this->db->manipulateF(
138  '
139  UPDATE frm_threads
140  SET thr_top_fk = %s,
141  thr_subject = %s,
142  thr_update = %s,
143  thr_num_posts = %s,
144  thr_last_post = %s,
145  avg_rating = %s
146  WHERE thr_pk = %s',
147  ['integer', 'text', 'timestamp', 'integer', 'text', 'text', 'integer'],
148  [
149  $this->forum_id,
150  $this->subject,
151  date('Y-m-d H:i:s'),
152  $this->num_posts,
153  $this->last_post_string,
154  (string) $this->average_rating,
155  $this->id
156  ]
157  );
158 
159  return true;
160  }
161 
162  return false;
163  }
164 
165  private function read(): bool
166  {
167  if ($this->id !== 0) {
168  $res = $this->db->queryF(
169  '
170  SELECT frm_threads.*, top_frm_fk frm_obj_id
171  FROM frm_threads
172  INNER JOIN frm_data ON top_pk = thr_top_fk
173  WHERE thr_pk = %s',
174  ['integer'],
175  [$this->id]
176  );
177 
178  $row = $res->fetchRow(ilDBConstants::FETCHMODE_OBJECT);
179 
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;
196 
197  return true;
198  }
199  $this->id = 0;
200  return false;
201  }
202 
203  return false;
204  }
205 
206  public function reload(): bool
207  {
208  return $this->read();
209  }
210 
211  public function getPostRootId(): int
212  {
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'],
217  [$this->id, 0, 1]
218  );
219 
220  if (($row = $this->db->fetchObject($res)) !== null) {
221  return (int) $row->pos_fk ?: 0;
222  }
223  return 0;
224  }
225 
226  public function getFirstVisiblePostId(): int
227  {
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'],
232  [$this->id, 0, 2]
233  );
234 
235  if (($row = $this->db->fetchObject($res)) !== null) {
236  return (int) $row->pos_fk ?: 0;
237  }
238  return 0;
239  }
240 
241  public function updateVisits(): void
242  {
243  $checkTime = time() - (60 * 60);
244 
245  if (ilSession::get('frm_visit_frm_threads_' . $this->id) < $checkTime) {
246  ilSession::set('frm_visit_frm_threads_' . $this->id, time());
247 
248  $this->db->manipulateF(
249  'UPDATE frm_threads SET visits = visits + 1 WHERE thr_pk = %s',
250  ['integer'],
251  [$this->id]
252  );
253  }
254  }
255 
256  public function countPosts(bool $ignoreRoot = false): int
257  {
258  $res = $this->db->queryF(
259  '
260  SELECT COUNT(*) cnt
261  FROM frm_posts
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 ' : ''),
264  ['integer'],
265  [$this->id]
266  );
267 
268  $row = $this->db->fetchAssoc($res);
269  if (is_array($row)) {
270  return (int) $row['cnt'];
271  }
272 
273  return 0;
274  }
275 
276  public function countActivePosts(bool $ignoreRoot = false): int
277  {
278  $res = $this->db->queryF(
279  '
280  SELECT COUNT(*) cnt
281  FROM frm_posts
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'],
287  ['1', '0', $this->user->getId(), $this->id]
288  );
289 
290  $row = $this->db->fetchAssoc($res);
291  if (is_array($row)) {
292  return (int) $row['cnt'];
293  }
294 
295  return 0;
296  }
297 
298  public function getPostRootNode(bool $isModerator = false, bool $preventImplicitRead = false): ilForumPost
299  {
300  $this->db->setLimit(1);
301  $res = $this->db->queryF(
302  '
303  SELECT *
304  FROM frm_posts
305  INNER JOIN frm_posts_tree ON pos_fk = pos_pk
306  WHERE parent_pos = %s
307  AND thr_fk = %s
308  ORDER BY rgt DESC',
309  ['integer', 'integer'],
310  [0, $this->id]
311  );
312 
313  if ($row = $this->db->fetchAssoc($res)) {
314  $post = new ilForumPost((int) $row['pos_pk'], $isModerator, $preventImplicitRead);
315  $post->assignData($row);
316  return $post;
317  }
318 
319  throw new OutOfBoundsException(sprintf('Could not find first posting by id: %s', $this->id));
320  }
321 
322  public function getFirstVisiblePostNode(bool $isModerator = false, bool $preventImplicitRead = false): ilForumPost
323  {
324  $this->db->setLimit(1);
325  $res = $this->db->queryF(
326  '
327  SELECT *
328  FROM frm_posts
329  INNER JOIN frm_posts_tree ON pos_fk = pos_pk
330  WHERE parent_pos != %s
331  AND thr_fk = %s
332  AND depth = %s
333  ORDER BY rgt DESC',
334  ['integer', 'integer', 'integer'],
335  [0, $this->id, 2]
336  );
337 
338  if ($row = $this->db->fetchAssoc($res)) {
339  $post = new ilForumPost((int) $row['pos_pk'], $isModerator, $preventImplicitRead);
340  $post->assignData($row);
341  return $post;
342  }
343 
344  throw new OutOfBoundsException(sprintf('Could not find first posting by id: %s', $this->id));
345  }
346 
347  public function getLastPost(): ilForumPost
348  {
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',
353  ['integer'],
354  [$this->id]
355  );
356 
357  if (($row = $this->db->fetchObject($res)) !== null) {
358  return new ilForumPost((int) $row->pos_pk);
359  }
360  }
361 
362  throw new OutOfBoundsException(sprintf('Could not find last posting by id: %s', $this->id));
363  }
364 
365  public function getLastActivePost(): ilForumPost
366  {
367  if ($this->id !== 0) {
368  $this->db->setLimit(1);
369  $res = $this->db->queryF(
370  '
371  SELECT pos_pk
372  FROM frm_posts
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()]
378  );
379 
380  if (($row = $this->db->fetchObject($res)) !== null) {
381  return new ilForumPost((int) $row->pos_pk);
382  }
383  }
384 
385  throw new OutOfBoundsException(sprintf('Could not find last active posting by id: %s', $this->id));
386  }
387 
391  public function getAllPostIds(): array
392  {
393  $posts = [];
394 
395  if ($this->id !== 0) {
396  $res = $this->db->queryF('SELECT pos_pk FROM frm_posts WHERE pos_thr_fk = %s', ['integer'], [$this->id]);
397 
398  while ($row = $res->fetchRow(ilDBConstants::FETCHMODE_OBJECT)) {
399  $posts[(int) $row->pos_pk] = (int) $row->pos_pk;
400  }
401  }
402 
403  return $posts;
404  }
405 
412  public function getPostTree(ilForumPost $a_post_node): array
413  {
414  $posts = [];
415  $data = [];
416  $data_types = [];
417 
418  if ($a_post_node->getLft() > 1) {
419  $dummy_root_condition = 'lft >= %s AND lft < %s';
420  } else {
421  $dummy_root_condition = 'lft > %s AND lft < %s';
422  }
423 
424  $query = '
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,
430  (CASE
431  WHEN fur.post_id IS NULL ' .
432  ($this->user->getId() === ANONYMOUS_USER_ID ? ' AND 1 = 2 ' : '') . '
433  THEN 0
434  ELSE 1
435  END) post_read,
436  firstname, lastname, title, login
437 
438  FROM frm_posts_tree
439 
440  INNER JOIN frm_posts
441  ON pos_fk = pos_pk
442 
443  LEFT JOIN usr_data
444  ON pos_display_user_id = usr_id
445 
446  LEFT JOIN frm_user_read fur
447  ON fur.thread_id = pos_thr_fk
448  AND fur.post_id = pos_pk
449  AND fur.usr_id = %s
450 
451  WHERE ' . $dummy_root_condition . '
452  AND thr_fk = %s';
453  $data_types[] = 'integer';
454  $data_types[] = 'integer';
455  $data_types[] = 'integer';
456  $data_types[] = 'integer';
457  $data[] = $this->user->getId();
458  $data[] = $a_post_node->getLft();
459  $data[] = $a_post_node->getRgt();
460  $data[] = $a_post_node->getThreadId();
461 
462  if ($this->orderField !== '') {
463  $query .= " ORDER BY " . $this->orderField . " " . $this->getOrderDirection();
464  }
465 
466  $res = $this->db->queryF($query, $data_types, $data);
467 
468  $usr_ids = [];
469  while ($row = $this->db->fetchAssoc($res)) {
470  $post = new ilForumPost((int) $row['pos_pk'], false, true);
471  $post->assignData($row);
472 
473  if (!$this->is_moderator && !$post->isActivated() && $post->getPosAuthorId() !== $this->user->getId()) {
474  continue;
475  }
476 
477  if ((int) $row['pos_display_user_id'] !== 0) {
478  $usr_ids[(int) $row['pos_display_user_id']] = (int) $row['pos_display_user_id'];
479  }
480  if ((int) $row['update_user'] !== 0) {
481  $usr_ids[(int) $row['update_user']] = (int) $row['update_user'];
482  }
483 
484  $posts[] = $post;
485  }
486 
488 
489  return $posts;
490  }
491 
501  public function movePosts(int $old_obj_id, int $old_pk, int $new_obj_id, int $new_pk): int
502  {
503  if ($this->id === 0) {
504  return 0;
505  }
506 
507  $post_ids = $this->getAllPostIds();
508  $postsMoved = [];
509  try {
510  foreach ($post_ids as $post_id) {
511  $file_obj = new ilFileDataForum($old_obj_id, $post_id);
512  $moved = $file_obj->moveFilesOfPost($new_obj_id);
513 
514  if ($moved) {
515  $postsMoved[] = [
516  'from' => $old_obj_id,
517  'to' => $new_obj_id,
518  'position_id' => $post_id
519  ];
520  }
521 
522  unset($file_obj);
523  }
524  } catch (ilFileUtilsException $exception) {
525  foreach ($postsMoved as $postedInformation) {
526  $file_obj = new ilFileDataForum($postedInformation['to'], $postedInformation['position_id']);
527  $file_obj->moveFilesOfPost($postedInformation['from']);
528  }
529 
530  throw $exception;
531  }
532 
533  $current_id = $this->id;
534 
535  $ilAtomQuery = $this->db->buildAtomQuery();
536  $ilAtomQuery->addTableLock('frm_user_read');
537 
538  $ilAtomQuery->addQueryCallable(static function (ilDBInterface $ilDB) use ($new_obj_id, $current_id): void {
539  $ilDB->manipulateF(
540  'DELETE FROM frm_user_read WHERE obj_id = %s AND thread_id =%s',
541  ['integer', 'integer'],
542  [$new_obj_id, $current_id]
543  );
544 
545  $ilDB->manipulateF(
546  'UPDATE frm_user_read SET obj_id = %s WHERE thread_id = %s',
547  ['integer', 'integer'],
548  [$new_obj_id, $current_id]
549  );
550  });
551 
552  $ilAtomQuery->run();
553 
554  $this->db->manipulateF(
555  'UPDATE frm_posts SET pos_top_fk = %s WHERE pos_thr_fk = %s',
556  ['integer', 'integer'],
557  [$new_pk, $this->id]
558  );
559 
560  $res = $this->db->queryF(
561  'SELECT * FROM frm_posts WHERE pos_thr_fk = %s',
562  ['integer'],
563  [$this->id]
564  );
565 
566  $old_obj_id = ilForum::_lookupObjIdForForumId($old_pk);
567  $new_obj_id = ilForum::_lookupObjIdForForumId($new_pk);
568 
569  while ($post = $this->db->fetchAssoc($res)) {
571  $old_obj_id,
572  'frm',
573  (int) $post['pos_pk'],
574  'pos'
575  );
576  $news_item = new ilNewsItem($news_id);
577  $news_item->setContextObjId($new_obj_id);
578  $news_item->update();
579  }
580 
581  return count($post_ids);
582  }
583 
584  public function getNestedSetPostChildren(?int $pos_id = null, ?int $num_levels = null): array
585  {
586  $data = null;
587  $objProperties = ilForumProperties::getInstance($this->getFrmObjId());
588  $is_post_activation_enabled = $objProperties->isPostActivationEnabled();
589 
590  if ($pos_id !== null) {
591  $res = $this->db->queryF(
592  "
593  SELECT lft, rgt, depth
594  FROM frm_posts_tree
595  WHERE pos_fk = %s
596  AND thr_fk = %s",
597  ['integer', 'integer'],
598  [$pos_id, $this->id]
599  );
600 
601  $data = $this->db->fetchAssoc($res);
602  }
603 
604  $query = '
605  SELECT fpt.depth,
606  fpt.rgt,
607  fpt.parent_pos,
608  fp.pos_pk,
609  fp.pos_subject,
610  fp.pos_usr_alias,
611  fp.pos_date,
612  fp.pos_update,
613  fp.pos_status,
614  fp.pos_display_user_id,
615  fp.pos_usr_alias,
616  fp.import_name,
617  fp.pos_author_id,
618  fp.is_author_moderator,
619  fur.post_id,
620  (CASE
621  WHEN fur.post_id IS NULL ' .
622  ($this->user->getId() === ANONYMOUS_USER_ID ? ' AND 1 = 2 ' : '') . '
623  THEN 0
624  ELSE 1
625  END) post_read,
626  COUNT(fpt2.pos_fk) children
627 
628  FROM frm_posts_tree fpt
629 
630  INNER JOIN frm_posts fp
631  ON fp.pos_pk = fpt.pos_fk
632 
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 ';
637 
638  $query .= '
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') . '
643 
644  LEFT JOIN usr_data ud
645  ON ud.usr_id = fp.pos_display_user_id
646 
647  WHERE fpt.thr_fk = ' . $this->db->quote($this->id, 'integer');
648 
649  if ($data) {
650  $query .= ' AND fpt.lft > ' . $this->db->quote($data['lft'], 'integer') .
651  ' AND fpt.lft < ' . $this->db->quote($data['rgt'], 'integer') . ' ';
652  }
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(),
656  'integer'
657  ) . ') ';
658  }
659 
660  if ($data && is_numeric($num_levels)) {
661  $query .= ' AND fpt.depth <= ' . $this->db->quote((int) $data['depth'] + $num_levels, 'integer') . ' ';
662  }
663 
664  $query .= ' GROUP BY fpt.depth,
665  fpt.rgt,
666  fpt.parent_pos,
667  fp.pos_pk,
668  fp.pos_subject,
669  fp.pos_usr_alias,
670  fp.pos_date,
671  fp.pos_update,
672  fp.pos_status,
673  fp.pos_display_user_id,
674  fp.pos_usr_alias,
675  fp.import_name,
676  fp.pos_author_id,
677  fp.is_author_moderator,
678  fur.post_id
679  ORDER BY fpt.rgt DESC
680  ';
681 
682  $queryCounter = '
683  SELECT pos_fk
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');
688 
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(),
692  'integer'
693  ) . ') ';
694  }
695  $queryCounter .= ' ORDER BY fpt.rgt DESC';
696 
697  $resCounter = $this->db->query($queryCounter);
698  $counter = [];
699  $i = 0;
700  while ($row = $this->db->fetchAssoc($resCounter)) {
701  $counter[(int) $row['pos_fk']] = $i++;
702  }
703 
704  $res = $this->db->query($query);
705  $children = [];
706  $usr_ids = [];
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'];
710  }
711 
712  $row['counter'] = $counter[$row['pos_pk']];
713  $casted_row = [];
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'];
730 
731  $children[] = $casted_row;
732  }
733 
735 
736  return $children;
737  }
738 
739  public function isNotificationEnabled(int $a_user_id): bool
740  {
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]
746  );
747 
748  if ($row = $this->db->fetchAssoc($result)) {
749  return (int) $row['cnt'] > 0;
750  }
751 
752  return false;
753  }
754 
755  return false;
756  }
757 
758  public function enableNotification(int $a_user_id): void
759  {
760  if ($this->id && $a_user_id && !$this->isNotificationEnabled($a_user_id)) {
761  $nextId = $this->db->nextId('frm_notification');
762  $this->db->manipulateF(
763  '
764  INSERT INTO frm_notification
765  ( notification_id,
766  user_id,
767  thread_id
768  )
769  VALUES(%s, %s, %s)',
770  ['integer', 'integer', 'integer'],
771  [$nextId, $a_user_id, $this->id]
772  );
773  }
774  }
775 
776  public function disableNotification(int $a_user_id): void
777  {
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]
783  );
784  }
785  }
786 
787  public function makeSticky(): bool
788  {
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'],
793  [1, $this->id]
794  );
795 
796  $this->is_sticky = true;
797  return true;
798  }
799 
800  return false;
801  }
802 
803  public function unmakeSticky(): bool
804  {
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'],
809  [0, $this->id]
810  );
811 
812  $this->is_sticky = false;
813  return true;
814  }
815 
816  return false;
817  }
818 
819  public function close(): void
820  {
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'],
825  [1, $this->id]
826  );
827  $this->is_closed = true;
828  }
829  }
830 
831  public function reopen(): void
832  {
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'],
837  [0, $this->id]
838  );
839 
840  $this->is_closed = false;
841  }
842  }
843 
844  public function getAverageRating(): float
845  {
846  return $this->average_rating;
847  }
848 
849  public function setAverageRating(float $average_rating): void
850  {
851  $this->average_rating = $average_rating;
852  }
853 
854  public function setId(int $a_id): void
855  {
856  $this->id = $a_id;
857  }
858 
859  public function getId(): int
860  {
861  return $this->id;
862  }
863 
864  public function setForumId(int $a_forum_id): void
865  {
866  $this->forum_id = $a_forum_id;
867  }
868 
869  public function getForumId(): int
870  {
871  return $this->forum_id;
872  }
873 
874  public function setDisplayUserId(int $a_user_id): void
875  {
876  $this->display_user_id = $a_user_id;
877  }
878 
879  public function getDisplayUserId(): int
880  {
881  return $this->display_user_id;
882  }
883 
884  public function setUserAlias(?string $a_user_alias): void
885  {
886  $this->user_alias = $a_user_alias;
887  }
888 
889  public function getUserAlias(): ?string
890  {
891  return $this->user_alias;
892  }
893 
894  public function setSubject(string $a_subject): void
895  {
896  $this->subject = $a_subject;
897  }
898 
899  public function getSubject(): string
900  {
901  return $this->subject;
902  }
903 
904  public function setCreateDate(?string $a_createdate): void
905  {
906  $this->createdate = $a_createdate;
907  }
908 
909  public function getCreateDate(): ?string
910  {
911  return $this->createdate;
912  }
913 
914  public function setChangeDate(?string $a_changedate): void
915  {
916  $this->changedate = $a_changedate;
917  }
918 
919  public function getChangeDate(): ?string
920  {
921  return $this->changedate;
922  }
923 
924  public function setImportName(?string $a_import_name): void
925  {
926  $this->import_name = $a_import_name;
927  }
928 
929  public function getImportName(): ?string
930  {
931  return $this->import_name;
932  }
933 
934  public function setLastPostString(?string $a_last_post): void
935  {
936  $this->last_post_string = $a_last_post;
937  }
938 
939  public function getLastPostString(): ?string
940  {
942  }
943 
944  public function setVisits(int $a_visits): void
945  {
946  $this->visits = $a_visits;
947  }
948 
949  public function getVisits(): int
950  {
951  return $this->visits;
952  }
953 
954  public function setSticky(bool $a_sticky): void
955  {
956  $this->is_sticky = $a_sticky;
957  }
958 
959  public function isSticky(): bool
960  {
961  return $this->is_sticky;
962  }
963 
964  public function setClosed(bool $a_closed): void
965  {
966  $this->is_closed = $a_closed;
967  }
968 
969  public function isClosed(): bool
970  {
971  return $this->is_closed;
972  }
973 
974  public function setOrderField(string $a_order_field): void
975  {
976  $this->orderField = $a_order_field;
977  }
978 
979  public function getOrderField(): string
980  {
981  return $this->orderField;
982  }
983 
984  public function getFrmObjId(): int
985  {
986  return $this->frm_obj_id;
987  }
988 
989  public function setThrAuthorId(int $thr_author_id): void
990  {
991  $this->thr_author_id = $thr_author_id;
992  }
993 
994  public function getThrAuthorId(): int
995  {
996  return $this->thr_author_id;
997  }
998 
999  public static function lookupTitle(int $a_topic_id): string
1000  {
1001  global $DIC;
1002  $ilDB = $DIC->database();
1003 
1004  $res = $ilDB->queryF(
1005  'SELECT thr_subject FROM frm_threads WHERE thr_pk = %s',
1006  ['integer'],
1007  [$a_topic_id]
1008  );
1009 
1010  if ($row = $ilDB->fetchObject($res)) {
1011  return (string) $row->thr_subject;
1012  }
1013 
1014  return '';
1015  }
1016 
1017  public function updateThreadTitle(): void
1018  {
1019  $this->db->update(
1020  'frm_threads',
1021  ['thr_subject' => ['text', $this->getSubject()]],
1022  ['thr_pk' => ['integer', $this->getId()]]
1023  );
1024 
1025  try {
1026  $first_node = $this->getFirstVisiblePostNode();
1027  $first_node->setSubject($this->getSubject());
1028  $first_node->update();
1029  } catch (OutOfBoundsException) {
1030  }
1031  }
1032 
1033  public function setNumPosts(int $a_num_posts): ilForumTopic
1034  {
1035  $this->num_posts = $a_num_posts;
1036  return $this;
1037  }
1038 
1039  public function getNumPosts(): int
1040  {
1041  return $this->num_posts;
1042  }
1043 
1044  public function setNumUnreadPosts(int $num_unread_posts): ilForumTopic
1045  {
1046  $this->num_unread_posts = $num_unread_posts;
1047  return $this;
1048  }
1049 
1050  public function getNumUnreadPosts(): int
1051  {
1052  return $this->num_unread_posts;
1053  }
1054 
1055  public function setUserNotificationEnabled(bool $status): ilForumTopic
1056  {
1057  $this->user_notification_enabled = $status;
1058  return $this;
1059  }
1060 
1061  public function isUserNotificationEnabled(): bool
1062  {
1064  }
1065 
1066  public function setOrderDirection(string $direction): ilForumTopic
1067  {
1068  if (!in_array(strtoupper($direction), self::$possibleOrderDirections, true)) {
1069  $direction = current(self::$possibleOrderDirections);
1070  }
1071 
1072  $this->orderDirection = $direction;
1073  return $this;
1074  }
1075 
1076  public function getOrderDirection(): string
1077  {
1078  return $this->orderDirection;
1079  }
1080 
1081  public static function lookupForumIdByTopicId(int $a_topic_id): int
1082  {
1083  global $DIC;
1084  $ilDB = $DIC->database();
1085 
1086  $res = $ilDB->queryF(
1087  'SELECT thr_top_fk FROM frm_threads WHERE thr_pk = %s',
1088  ['integer'],
1089  [$a_topic_id]
1090  );
1091 
1092  $row = $ilDB->fetchAssoc($res);
1093 
1094  return (int) $row['thr_top_fk'];
1095  }
1096 
1097  public function updateMergedThread(): void
1098  {
1099  $this->db->update(
1100  'frm_threads',
1101  [
1102  'thr_num_posts' => ['integer', $this->getNumPosts()],
1103  'visits' => ['integer', $this->getVisits()],
1104  'thr_last_post' => ['text', $this->getLastPostString()],
1105  'thr_subject' => ['text', $this->getSubject()]
1106  ],
1107  ['thr_pk' => ['integer', $this->getId()]]
1108  );
1109  }
1110 
1111  public static function lookupCreationDate(int $thread_id): ?string
1112  {
1113  global $DIC;
1114  $ilDB = $DIC->database();
1115 
1116  $res = $ilDB->queryF(
1117  'SELECT thr_date FROM frm_threads WHERE thr_pk = %s',
1118  ['integer'],
1119  [$thread_id]
1120  );
1121 
1122  $date = null;
1123  $row = $ilDB->fetchAssoc($res);
1124  if (is_array($row)) {
1125  $date = $row['thr_date'];
1126  }
1127 
1128  return $date;
1129  }
1130 
1132  {
1133  return $this->last_post;
1134  }
1135 
1137  {
1138  $this->last_post = $post;
1139  }
1140 }
static get(string $a_var)
$res
Definition: ltiservices.php:69
manipulateF(string $query, array $types, array $values)
const ANONYMOUS_USER_ID
Definition: constants.php:27
assignData(array $data)
getNestedSetPostChildren(?int $pos_id=null, ?int $num_levels=null)
setCreateDate(?string $a_createdate)
static array $possibleOrderDirections
ilForumPost $last_post
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)
global $DIC
Definition: feed.php:28
countActivePosts(bool $ignoreRoot=false)
setForumId(int $a_forum_id)
setNumUnreadPosts(int $num_unread_posts)
setAverageRating(float $average_rating)
setClosed(bool $a_closed)
static _lookupObjIdForForumId(int $a_for_id)
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
Definition: plugin.php:23
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)
$post
Definition: ltitoken.php:49
getFirstVisiblePostNode(bool $isModerator=false, bool $preventImplicitRead=false)
countPosts(bool $ignoreRoot=false)
static set(string $a_var, $a_val)
Set a value.
setVisits(int $a_visits)
ilDBInterface $db
setOrderDirection(string $direction)
bool $user_notification_enabled
setThrAuthorId(int $thr_author_id)