ILIAS  trunk Revision v11.0_alpha-2638-g80c1d007f79
class.ilObjForum.php
Go to the documentation of this file.
1 <?php
2 
19 declare(strict_types=1);
20 
26 class ilObjForum extends ilObject
27 {
28  public ilForum $Forum;
30  private static array $obj_id_to_forum_id_cache = [];
32  private static array $ref_id_to_forum_id_cache = [];
34  private static array $forum_statistics_cache = [];
36  private static array $forum_last_post_cache = [];
37  private readonly \ILIAS\DI\RBACServices $rbac;
38  private readonly ilLogger $logger;
39  private readonly ilSetting $settings;
40 
41  public function __construct(int $a_id = 0, bool $a_call_by_reference = true)
42  {
43  global $DIC;
44 
45  $this->type = 'frm';
46  parent::__construct($a_id, $a_call_by_reference);
47 
48  $this->rbac = $DIC->rbac();
49  $this->logger = $DIC->logger()->root();
50 
51  $this->settings = $DIC->settings();
52  $this->Forum = new ilForum();
53  }
54 
55  public function create(): int
56  {
57  $id = parent::create();
58 
59  $properties = ilForumProperties::getInstance($this->getId());
60  $properties->setDefaultView(
61  (int) $this->settings->get('forum_default_view', (string) ilForumProperties::VIEW_DATE_ASC)
62  );
63  $properties->setAnonymisation(false);
64  $properties->setStatisticsStatus(false);
65  $properties->setPostActivation(false);
66  $properties->insert();
67 
68  $this->createSettings();
69 
70  $this->setOfflineStatus(true);
71  $this->update();
72  $this->saveData();
73 
74  return $id;
75  }
76 
77  public function setPermissions(int $parent_ref_id): void
78  {
79  parent::setPermissions($parent_ref_id);
80 
81  $roles = [self::_lookupModeratorRole($this->getRefId())];
82  $this->rbac->admin()->assignUser($roles[0], $this->getOwner());
83  $this->updateModeratorRole($roles[0]);
84  }
85 
86  public function updateModeratorRole(int $role_id): void
87  {
88  $this->db->manipulate(
89  'UPDATE frm_data SET top_mods = ' . $this->db->quote(
90  $role_id,
91  'integer'
92  ) . ' WHERE top_frm_fk = ' . $this->db->quote($this->getId(), 'integer')
93  );
94  }
95 
96  public static function _lookupThreadSubject(int $a_thread_id): string
97  {
98  global $DIC;
99 
100  $ilDB = $DIC->database();
101 
102  $res = $ilDB->queryF('SELECT thr_subject FROM frm_threads WHERE thr_pk = %s', ['integer'], [$a_thread_id]);
103  while ($row = $ilDB->fetchObject($res)) {
104  return $row->thr_subject ?? '';
105  }
106 
107  return '';
108  }
109 
110  public function getCountUnread(int $a_usr_id, int $a_thread_id = 0, bool $ignoreRoot = false): int
111  {
112  $a_frm_id = $this->getId();
113  $topic_id = 0;
114  $num_posts = 0;
115  $count_read = 0;
116 
117  if ($a_thread_id === 0) {
118  $res = $this->db->queryF('SELECT top_pk FROM frm_data WHERE top_frm_fk = %s', ['integer'], [$a_frm_id]);
119  while ($row = $this->db->fetchObject($res)) {
120  $topic_id = (int) $row->top_pk;
121  }
122 
123  $res = $this->db->queryF(
124  '
125  SELECT COUNT(pos_pk) num_posts
126  FROM frm_posts
127  LEFT JOIN frm_posts_tree ON frm_posts_tree.pos_fk = pos_pk
128  WHERE pos_top_fk = %s' . ($ignoreRoot ? ' AND parent_pos != 0 ' : ''),
129  ['integer'],
130  [$topic_id]
131  );
132 
133  while ($row = $this->db->fetchObject($res)) {
134  $num_posts = (int) $row->num_posts;
135  }
136 
137  $res = $this->db->queryF(
138  'SELECT COUNT(post_id) count_read FROM frm_user_read WHERE obj_id = %s AND usr_id = %s',
139  ['integer', 'integer'],
140  [$a_frm_id, $a_usr_id]
141  );
142 
143  while ($row = $this->db->fetchObject($res)) {
144  $count_read = (int) $row->count_read;
145  }
146  } else {
147  $res = $this->db->queryF(
148  '
149  SELECT COUNT(pos_pk) num_posts FROM frm_posts
150  LEFT JOIN frm_posts_tree ON frm_posts_tree.pos_fk = pos_pk
151  WHERE pos_thr_fk = %s' . ($ignoreRoot ? ' AND parent_pos != 0 ' : ''),
152  ['integer'],
153  [$a_thread_id]
154  );
155 
156  $row = $this->db->fetchObject($res);
157  $num_posts = (int) $row->num_posts;
158 
159  $res = $this->db->queryF(
160  '
161  SELECT COUNT(post_id) count_read FROM frm_user_read
162  WHERE obj_id = %s
163  AND usr_id = %s
164  AND thread_id = %s',
165  ['integer', 'integer', 'integer'],
166  [$a_frm_id, $a_frm_id, $a_thread_id]
167  );
168 
169  $row = $this->db->fetchObject($res);
170  $count_read = (int) $row->count_read;
171  }
172  $unread = $num_posts - $count_read;
173 
174  return max($unread, 0);
175  }
176 
177  public function markThreadRead(int $a_usr_id, int $a_thread_id): bool
178  {
179  $res = $this->db->queryF('SELECT pos_pk FROM frm_posts WHERE pos_thr_fk = %s', ['integer'], [$a_thread_id]);
180  while ($row = $this->db->fetchObject($res)) {
181  $this->markPostRead($a_usr_id, $a_thread_id, (int) $row->pos_pk);
182  }
183 
184  return true;
185  }
186 
187  public function markAllThreadsRead(int $a_usr_id): void
188  {
189  $res = $this->db->queryF(
190  'SELECT thr_pk FROM frm_data, frm_threads WHERE top_frm_fk = %s AND top_pk = thr_top_fk',
191  ['integer'],
192  [$this->getId()]
193  );
194 
195  while ($row = $this->db->fetchObject($res)) {
196  $this->markThreadRead($a_usr_id, (int) $row->thr_pk);
197  }
198  }
199 
200  public function markPostRead(int $a_usr_id, int $a_thread_id, int $a_post_id): void
201  {
202  $res = $this->db->queryF(
203  '
204  SELECT thread_id FROM frm_user_read
205  WHERE usr_id = %s
206  AND obj_id = %s
207  AND thread_id = %s
208  AND post_id = %s',
209  ['integer', 'integer', 'integer', 'integer'],
210  [$a_usr_id, $this->getId(), $a_thread_id, $a_post_id]
211  );
212 
213  if ($this->db->numRows($res) === 0) {
214  $this->db->manipulateF(
215  '
216  INSERT INTO frm_user_read
217  ( usr_id,
218  obj_id,
219  thread_id,
220  post_id
221  )
222  VALUES (%s,%s,%s,%s)',
223  ['integer', 'integer', 'integer', 'integer'],
224  [$a_usr_id, $this->getId(), $a_thread_id, $a_post_id]
225  );
226  }
227  }
228 
229  public function markPostUnread(int $a_user_id, int $a_post_id): void
230  {
231  $this->db->manipulateF(
232  'DELETE FROM frm_user_read WHERE usr_id = %s AND post_id = %s',
233  ['integer', 'integer'],
234  [$a_user_id, $a_post_id]
235  );
236  }
237 
238  public function isRead($a_usr_id, $a_post_id): bool
239  {
240  $res = $this->db->queryF(
241  'SELECT * FROM frm_user_read WHERE usr_id = %s AND post_id = %s',
242  ['integer', 'integer'],
243  [$a_usr_id, $a_post_id]
244  );
245 
246  return (bool) $this->db->numRows($res);
247  }
248 
249  public static function _deleteUser(int $a_usr_id): void
250  {
251  global $DIC;
252 
253  $data = [$a_usr_id];
254 
255  $DIC->database()->manipulateF('DELETE FROM frm_user_read WHERE usr_id = %s', ['integer'], $data);
256  $DIC->database()->manipulateF('DELETE FROM frm_notification WHERE user_id = %s', ['integer'], $data);
257  }
258 
259  public static function _deleteReadEntries(int $a_post_id): void
260  {
261  global $DIC;
262 
263  $DIC->database()->manipulateF('DELETE FROM frm_user_read WHERE post_id = %s', ['integer'], [$a_post_id]);
264  }
265 
266  public function updateModificationUserId(int $usr_id): void
267  {
268  $this->db->manipulateF(
269  'UPDATE frm_data SET update_user = %s WHERE top_frm_fk = %s',
270  ['integer', 'integer'],
271  [$usr_id, $this->getId()],
272  );
273  }
274 
275  public function update(): bool
276  {
277  if (parent::update()) {
278  $this->db->manipulateF(
279  'UPDATE frm_data SET top_name = %s, top_description = %s, top_update = %s, update_user = %s WHERE top_frm_fk = %s',
280  ['text', 'text', 'timestamp', 'integer', 'integer'],
281  [
282  $this->getTitle(),
283  $this->getDescription(),
284  date("Y-m-d H:i:s"),
285  $this->user->getId(),
286  $this->getId()
287  ]
288  );
289 
290  return true;
291  }
292 
293  return false;
294  }
295 
296  public function cloneObject(int $target_id, int $copy_id = 0, bool $omit_tree = false): ?ilObject
297  {
299  $new_obj = parent::cloneObject($target_id, $copy_id, $omit_tree);
300  $this->cloneAutoGeneratedRoles($new_obj);
301 
302  ilForumProperties::getInstance($this->getId())->copy($new_obj->getId());
303  $this->Forum->setMDB2WhereCondition('top_frm_fk = %s ', ['integer'], [$this->getId()]);
304  $topData = $this->Forum->getOneTopic();
305 
306  $this->db->update('frm_data', [
307  'top_name' => ['text', $topData->getTopName()],
308  'top_description' => ['text', $topData->getTopDescription()],
309  'top_num_posts' => ['integer', $topData->getTopNumPosts()],
310  'top_num_threads' => ['integer', $topData->getTopNumThreads()],
311  'top_last_post' => ['text', $topData->getTopLastPost()],
312  'top_date' => ['timestamp', $topData->getTopDate()],
313  'visits' => ['integer', $topData->getVisits()],
314  'top_update' => ['timestamp', $topData->getTopUpdate()],
315  'update_user' => ['integer', $topData->getUpdateUser()],
316  'top_usr_id' => ['integer', $topData->getTopUsrId()]
317  ], [
318  'top_frm_fk' => ['integer', $new_obj->getId()]
319  ]);
320 
321  $cwo = ilCopyWizardOptions::_getInstance($copy_id);
322  $options = $cwo->getOptions($this->getRefId());
323 
324  $options['threads'] = $this->Forum::getSortedThreadSubjects($this->getId());
325 
326  $new_frm = $new_obj->Forum;
327  $new_frm->setMDB2WhereCondition('top_frm_fk = %s ', ['integer'], [$new_obj->getId()]);
328 
329  $new_frm->setForumId($new_obj->getId());
330  $new_frm->setForumRefId($new_obj->getRefId());
331 
332  $new_topic = $new_frm->getOneTopic();
333  foreach (array_keys($options['threads']) as $thread_id) {
334  $this->Forum->setMDB2WhereCondition('thr_pk = %s ', ['integer'], [$thread_id]);
335 
336  $old_thread = $this->Forum->getOneThread();
337 
338  $old_post_id = $this->Forum->getRootPostIdByThread($old_thread->getId());
339 
340  $newThread = new ilForumTopic(0, true, true);
341  $newThread->setSticky($old_thread->isSticky());
342  $newThread->setForumId($new_topic->getTopPk());
343  $newThread->setThrAuthorId($old_thread->getThrAuthorId());
344  $newThread->setDisplayUserId($old_thread->getDisplayUserId());
345  $newThread->setSubject($old_thread->getSubject());
346  $newThread->setUserAlias($old_thread->getUserAlias());
347  $newThread->setCreateDate($old_thread->getCreateDate());
348 
349  try {
350  $top_pos = $old_thread->getFirstVisiblePostNode();
351  } catch (OutOfBoundsException) {
352  $top_pos = new ilForumPost($old_post_id);
353  }
354 
355  $newPostId = $new_frm->generateThread(
356  $newThread,
357  $top_pos->getMessage(),
358  $top_pos->isNotificationEnabled(),
359  false,
360  true,
361  (bool) ($old_thread->getNumPosts() - 1)
362  );
363 
364  $old_forum_files = new ilFileDataForum($this->getId(), $top_pos->getId());
365  $old_forum_files->ilClone($new_obj->getId(), $newPostId);
366  }
367 
368  $source_ref_id = $this->getRefId();
369  $target_ref_id = $new_obj->getRefId();
370  $target_notifications = new ilForumNotification($target_ref_id);
371 
372  if ($source_ref_id > 0 && $target_ref_id > 0 &&
373  $this->tree->getParentId($source_ref_id) === $this->tree->getParentId($target_ref_id)) {
374  if ($new_obj->isParentMembershipEnabledContainer()) {
375  $target_notifications->cloneFromSource($source_ref_id);
376  }
377  } else {
379  $target_notifications->updateUserNotifications($new_obj->getAllForumParticipants(), $object_properties);
380  }
381 
382  if (ilForumPage::_exists($this->getType(), $this->getId())) {
383  $translations = ilContentPagePage::lookupTranslations($this->getType(), $this->getId());
384  foreach ($translations as $language) {
385  $originalPageObject = new ilForumPage($this->getId(), 0, $language);
386  $copiedXML = $originalPageObject->copyXmlContent();
387 
388  $duplicatePageObject = new ilForumPage();
389  $duplicatePageObject->setId($new_obj->getId());
390  $duplicatePageObject->setParentId($new_obj->getId());
391  $duplicatePageObject->setLanguage($language);
392  $duplicatePageObject->setXMLContent($copiedXML);
393  $duplicatePageObject->createFromXML();
394  }
395  }
396 
397  $cwo = ilCopyWizardOptions::_getInstance($copy_id);
398  //copy online status if object is not the root copy object
399  if (!$cwo->isRootNode($this->getRefId())) {
400  $new_obj->setOfflineStatus($this->getOfflineStatus());
401  } else {
402  $new_obj->setOfflineStatus(true);
403  }
404  $new_obj->update();
405 
406  return $new_obj;
407  }
408 
409  public function isParentMembershipEnabledContainer(): bool
410  {
411  $maybe_grp_ref_id = $this->tree->checkForParentType($this->getRefId(), 'grp');
412  if ($maybe_grp_ref_id > 0) {
413  return true;
414  }
415 
416  return $this->tree->checkForParentType($this->getRefId(), 'crs') > 0;
417  }
418 
422  public function getAllForumParticipants(): array
423  {
424  $participant_usr_ids = ilForum::_getModerators($this->getRefId());
425 
426  try {
427  $participants = $this->parentParticipants();
428  $participant_usr_ids = array_merge(
429  $participant_usr_ids,
430  $participants->getAdmins(),
431  $participants->getMembers(),
432  $participants->getTutors()
433  );
434  } catch (DomainException) {
435  }
436 
437  return array_unique($participant_usr_ids);
438  }
439 
444  {
445  if (!$this->isParentMembershipEnabledContainer()) {
446  throw new DomainException('Parent is not a membership enabled container.');
447  }
448 
449  $maybe_grp_ref_id = $this->tree->checkForParentType($this->getRefId(), 'grp');
450  if ($maybe_grp_ref_id > 0) {
451  $parent_obj = ilObjectFactory::getInstanceByRefId($maybe_grp_ref_id);
452 
453  return ilGroupParticipants::_getInstanceByObjId($parent_obj->getId());
454  }
455 
456  $crs_ref_id = $this->tree->checkForParentType($this->getRefId(), 'crs');
457  $parent_obj = ilObjectFactory::getInstanceByRefId($crs_ref_id);
458 
459  return ilCourseParticipants::_getInstanceByObjId($parent_obj->getId());
460  }
461 
462  public function cloneAutoGeneratedRoles(self $new_obj): void
463  {
464  $src_moderator_role_id = self::_lookupModeratorRole($this->getRefId());
465  $new_moderator_role_id = self::_lookupModeratorRole($new_obj->getRefId());
466 
467  if (
468  0 === $src_moderator_role_id ||
469  0 === $new_moderator_role_id ||
470  0 === $this->getRefId() ||
471  0 === $new_obj->getRefId()
472  ) {
473  $this->logger->write(__METHOD__ . ' : Error cloning auto generated role: il_frm_moderator');
474  }
475 
476  $this->rbac->admin()->copyRolePermissions(
477  $src_moderator_role_id,
478  $this->getRefId(),
479  $new_obj->getRefId(),
480  $new_moderator_role_id,
481  true
482  );
483 
484  $this->logger->write(__METHOD__ . ' : Finished copying of role il_frm_moderator.');
485 
486  $moderators = new ilForumModerators($this->getRefId());
487  $src_moderator_usr_ids = $moderators->getCurrentModerators();
488  foreach ($src_moderator_usr_ids as $usr_id) {
489  // The object owner is already member of the moderator role when this method is called
490  // Since the new static caches are introduced with ILIAS 5.0, a database error occurs if we try to assign the user here.
491  if ($this->getOwner() !== $usr_id) {
492  $this->rbac->admin()->assignUser($new_moderator_role_id, $usr_id);
493  }
494  }
495  }
496 
497  public function delete(): bool
498  {
499  $this->Forum->setForumId($this->getId());
500 
501  if (!parent::delete()) {
502  return false;
503  }
504 
505  if (ilForumPage::_exists($this->getType(), $this->getId())) {
506  $originalPageObject = new ilForumPage($this->getId());
507  $originalPageObject->delete();
508  }
509 
510  $this->Forum->setMDB2WhereCondition('top_frm_fk = %s ', [ilDBConstants::T_INTEGER], [$this->getId()]);
511 
512  $topData = $this->Forum->getOneTopic();
513 
514  $threads = $this->Forum->getAllThreads($topData->getTopPk(), [
515  'is_moderator' => true,
516  ]);
517  $thread_ids_to_delete = [];
518  foreach ($threads['items'] as $thread) {
519  $thread_ids_to_delete[$thread->getId()] = $thread->getId();
520  }
521 
522  // Get All posting IDs
523  $posting_ids = [];
524  $res = $this->db->query(
525  'SELECT pos_pk FROM frm_posts WHERE '
526  . $this->db->in('pos_thr_fk', $thread_ids_to_delete, false, ilDBConstants::T_INTEGER)
527  );
528 
529  while ($row = $res->fetchObject()) {
530  $posting_ids[] = (int) $row->pos_pk;
531  }
532 
533  $tmp_file_obj = new ilFileDataForum($this->getId());
534  $tmp_file_obj->delete($posting_ids);
535 
536  // Get All draft IDs
537  $draft_ids = [];
538  $res = $this->db->query(
539  'SELECT draft_id FROM frm_posts_drafts WHERE '
540  . $this->db->in('thread_id', $thread_ids_to_delete, false, ilDBConstants::T_INTEGER)
541  );
542 
543  while ($row = $res->fetchObject()) {
544  $draft_ids[] = (int) $row->draft_id;
545  }
546 
547  $tmp_file_obj = new ilFileDataForumDrafts($this->getId());
548  $tmp_file_obj->delete($draft_ids);
549 
550  $this->db->manipulate(
551  'DELETE FROM frm_posts_tree WHERE ' . $this->db->in(
552  'thr_fk',
553  $thread_ids_to_delete,
554  false,
556  )
557  );
558  $this->db->manipulate(
559  'DELETE FROM frm_posts WHERE ' . $this->db->in(
560  'pos_thr_fk',
561  $thread_ids_to_delete,
562  false,
564  )
565  );
566  $this->db->manipulate(
567  'DELETE FROM frm_threads WHERE ' . $this->db->in(
568  'thr_pk',
569  $thread_ids_to_delete,
570  false,
572  )
573  );
574 
575  $obj_id = [$this->getId()];
576 
577  $this->db->manipulateF('DELETE FROM frm_data WHERE top_frm_fk = %s', [ilDBConstants::T_INTEGER], $obj_id);
578  $this->db->manipulateF('DELETE FROM frm_settings WHERE obj_id = %s', [ilDBConstants::T_INTEGER], $obj_id);
579  $this->db->manipulateF('DELETE FROM frm_user_read WHERE obj_id = %s', [ilDBConstants::T_INTEGER], $obj_id);
580  $this->db->manipulate(
581  'DELETE FROM frm_notification WHERE ' . $this->db->in(
582  'thread_id',
583  $thread_ids_to_delete,
584  false,
585  'integer'
586  )
587  );
588  $this->db->manipulateF('DELETE FROM frm_notification WHERE frm_id = %s', [ilDBConstants::T_INTEGER], $obj_id);
589  $this->db->manipulateF('DELETE FROM frm_posts_deleted WHERE obj_id = %s', [ilDBConstants::T_INTEGER], $obj_id);
590  $this->deleteDraftsByForumId($topData->getTopPk());
591 
592  return true;
593  }
594 
595  private function deleteDraftsByForumId(int $forum_id): void
596  {
597  $res = $this->db->queryF(
598  'SELECT draft_id FROM frm_posts_drafts WHERE forum_id = %s',
599  ['integer'],
600  [$forum_id]
601  );
602 
603  $draft_ids = [];
604  while ($row = $this->db->fetchAssoc($res)) {
605  $draft_ids[] = (int) $row['draft_id'];
606  }
607 
608  if ($draft_ids !== []) {
609  $historyObj = new ilForumDraftsHistory();
610  $historyObj->deleteHistoryByDraftIds($draft_ids);
611 
612  $draftObj = new ilForumPostDraft();
613  $draftObj->deleteDraftsByDraftIds($draft_ids);
614  }
615  }
616 
617  public function initDefaultRoles(): void
618  {
620  'il_frm_moderator_' . $this->getRefId(),
621  "Moderator of forum obj_no." . $this->getId(),
622  'il_frm_moderator',
623  $this->getRefId()
624  );
625  }
626 
627  public static function _lookupModeratorRole(int $a_ref_id): int
628  {
629  global $DIC;
630 
631  $ilDB = $DIC->database();
632 
633  $mod_title = 'il_frm_moderator_' . $a_ref_id;
634 
635  $res = $ilDB->queryF('SELECT obj_id FROM object_data WHERE title = %s', ['text'], [$mod_title]);
636  while ($row = $ilDB->fetchObject($res)) {
637  return (int) $row->obj_id;
638  }
639 
640  return 0;
641  }
642 
643  public function createSettings(): void
644  {
645  global $DIC;
646 
647  $ref_id = 0;
648  if ($DIC->http()->wrapper()->query()->has('ref_id')) {
649  $ref_id = $DIC->http()->wrapper()->query()->retrieve(
650  'ref_id',
651  $DIC->refinery()->kindlyTo()->int()
652  );
653  }
654 
655  // news settings (public notifications yes/no)
656  $default_visibility = ilNewsItem::_getDefaultVisibilityForRefId($ref_id);
657  if ($default_visibility === 'public') {
658  ilBlockSetting::_write('news', 'public_notifications', '1', 0, $this->getId());
659  }
660  }
661 
662  public function saveData(): void
663  {
664  $nextId = $this->db->nextId('frm_data');
665 
666  $top_data = [
667  'top_frm_fk' => $this->getId(),
668  'top_name' => $this->getTitle(),
669  'top_description' => $this->getDescription(),
670  'top_num_posts' => 0,
671  'top_num_threads' => 0,
672  'top_last_post' => null,
673  'top_mods' => 0,
674  'top_usr_id' => $this->user->getId(),
675  'top_date' => ilUtil::now()
676  ];
677 
678  $this->db->manipulateF(
679  '
680  INSERT INTO frm_data
681  (
682  top_pk,
683  top_frm_fk,
684  top_name,
685  top_description,
686  top_num_posts,
687  top_num_threads,
688  top_last_post,
689  top_mods,
690  top_date,
691  top_usr_id
692  )
693  VALUES(%s, %s, %s, %s, %s, %s, %s, %s, %s, %s)',
694  [
695  'integer',
696  'integer',
697  'text',
698  'text',
699  'integer',
700  'integer',
701  'text',
702  'integer',
703  'timestamp',
704  'integer'
705  ],
706  [
707  $nextId,
708  $top_data['top_frm_fk'],
709  $top_data['top_name'],
710  $top_data['top_description'],
711  $top_data['top_num_posts'],
712  $top_data['top_num_threads'],
713  $top_data['top_last_post'],
714  $top_data['top_mods'],
715  $top_data['top_date'],
716  $top_data['top_usr_id']
717  ]
718  );
719  }
720 
721  public static function lookupForumIdByObjId(int $obj_id): int
722  {
723  if (array_key_exists($obj_id, self::$obj_id_to_forum_id_cache)) {
724  return self::$obj_id_to_forum_id_cache[$obj_id];
725  }
726 
727  self::preloadForumIdsByObjIds([$obj_id]);
728 
729  return self::$obj_id_to_forum_id_cache[$obj_id];
730  }
731 
732  public static function lookupForumIdByRefId(int $ref_id): int
733  {
734  if (array_key_exists($ref_id, self::$ref_id_to_forum_id_cache)) {
735  return self::$ref_id_to_forum_id_cache[$ref_id];
736  }
737 
738  self::preloadForumIdsByRefIds([$ref_id]);
739 
740  return self::$ref_id_to_forum_id_cache[$ref_id];
741  }
742 
746  public static function preloadForumIdsByObjIds(array $obj_ids): void
747  {
748  global $DIC;
749 
750  $ilDB = $DIC->database();
751 
752  if (count($obj_ids) === 1) {
753  $in = ' objr.obj_id = ' . $ilDB->quote(current($obj_ids), 'integer') . ' ';
754  } else {
755  $in = $ilDB->in('objr.obj_id', $obj_ids, false, 'integer');
756  }
757  $query = "
758  SELECT frmd.top_pk, objr.ref_id, objr.obj_id
759  FROM object_reference objr
760  INNER JOIN frm_data frmd ON frmd.top_frm_fk = objr.obj_id
761  WHERE $in
762  ";
763  $res = $ilDB->query($query);
764 
765  // Prepare cache array
766  foreach ($obj_ids as $obj_id) {
767  self::$obj_id_to_forum_id_cache[$obj_id] = 0;
768  }
769 
770  while ($row = $ilDB->fetchAssoc($res)) {
771  self::$obj_id_to_forum_id_cache[(int) $row['obj_id']] = (int) $row['top_pk'];
772  self::$ref_id_to_forum_id_cache[(int) $row['ref_id']] = (int) $row['top_pk'];
773  }
774  }
775 
779  public static function preloadForumIdsByRefIds(array $ref_ids): void
780  {
781  global $DIC;
782 
783  $ilDB = $DIC->database();
784 
785  if (count($ref_ids) === 1) {
786  $in = " objr.ref_id = " . $ilDB->quote(current($ref_ids), 'integer') . " ";
787  } else {
788  $in = $ilDB->in('objr.ref_id', $ref_ids, false, 'integer');
789  }
790  $query = "
791  SELECT frmd.top_pk, objr.ref_id, objr.obj_id
792  FROM object_reference objr
793  INNER JOIN frm_data frmd ON frmd.top_frm_fk = objr.obj_id
794  WHERE $in
795  ";
796  $res = $ilDB->query($query);
797 
798  // Prepare cache array
799  foreach ($ref_ids as $ref_id) {
800  if (!array_key_exists($ref_id, self::$ref_id_to_forum_id_cache)) {
801  self::$ref_id_to_forum_id_cache[$ref_id] = 0;
802  }
803  }
804 
805  while ($row = $ilDB->fetchAssoc($res)) {
806  self::$obj_id_to_forum_id_cache[(int) $row['obj_id']] = (int) $row['top_pk'];
807  self::$ref_id_to_forum_id_cache[(int) $row['ref_id']] = (int) $row['top_pk'];
808  }
809  }
810 
814  public static function lookupStatisticsByRefId(int $ref_id): array
815  {
816  global $DIC;
817 
818  $ilAccess = $DIC->access();
819  $ilUser = $DIC->user();
820  $ilDB = $DIC->database();
821  $ilSetting = $DIC->settings();
822 
823  if (isset(self::$forum_statistics_cache[$ref_id])) {
824  return self::$forum_statistics_cache[$ref_id];
825  }
826 
827  $statistics = [
828  'num_posts' => 0,
829  'num_unread_posts' => 0,
830  ];
831 
832  $forumId = self::lookupForumIdByRefId($ref_id);
833  if ($forumId === 0) {
834  self::$forum_statistics_cache[$ref_id] = $statistics;
835  return self::$forum_statistics_cache[$ref_id];
836  }
837 
839  $is_post_activation_enabled = $objProperties->isPostActivationEnabled();
840 
841  $act_clause = '';
842 
843  if ($is_post_activation_enabled && !$ilAccess->checkAccess('moderate_frm', '', $ref_id)) {
844  $act_clause .= ' AND (frm_posts.pos_status = ' . $ilDB->quote(
845  1,
846  'integer'
847  ) . ' OR frm_posts.pos_author_id = ' . $ilDB->quote($ilUser->getId(), 'integer') . ') ';
848  }
849 
850  if (!$ilUser->isAnonymous()) {
851  $query = "
852  (SELECT COUNT(frm_posts.pos_pk) cnt
853  FROM frm_posts
854  INNER JOIN frm_posts_tree tree1
855  ON tree1.pos_fk = frm_posts.pos_pk
856  AND tree1.parent_pos != 0
857  INNER JOIN frm_threads ON frm_posts.pos_thr_fk = frm_threads.thr_pk
858  WHERE frm_threads.thr_top_fk = %s $act_clause)
859 
860  UNION ALL
861 
862  (SELECT COUNT(DISTINCT(frm_user_read.post_id)) cnt
863  FROM frm_user_read
864  INNER JOIN frm_posts ON frm_user_read.post_id = frm_posts.pos_pk
865  INNER JOIN frm_posts_tree tree1
866  ON tree1.pos_fk = frm_posts.pos_pk
867  AND tree1.parent_pos != 0
868  INNER JOIN frm_threads ON frm_threads.thr_pk = frm_posts.pos_thr_fk
869  WHERE frm_user_read.usr_id = %s AND frm_posts.pos_top_fk = %s $act_clause)
870  ";
871 
872  $types = ['integer', 'integer', 'integer'];
873  $values = [$forumId, $ilUser->getId(), $forumId];
874 
875  $mapping = array_keys($statistics);
876  $res = $ilDB->queryF(
877  $query,
878  $types,
879  $values
880  );
881  for ($i = 0; $i < 2; $i++) {
882  $row = $ilDB->fetchAssoc($res);
883 
884  $statistics[$mapping[$i]] = (int) ((is_array($row) ? $row['cnt'] : 0));
885 
886  if ($i === 1) {
887  // unread = all - read
888  $statistics[$mapping[$i]] = $statistics[$mapping[$i - 1]] - $statistics[$mapping[$i]];
889  }
890  }
891  } else {
892  $query = "
893  SELECT COUNT(frm_posts.pos_pk) cnt
894  FROM frm_posts
895  INNER JOIN frm_posts_tree tree1
896  ON tree1.pos_fk = frm_posts.pos_pk
897  AND tree1.parent_pos != 0
898  INNER JOIN frm_threads ON frm_posts.pos_thr_fk = frm_threads.thr_pk
899  WHERE frm_threads.thr_top_fk = %s $act_clause
900  ";
901  $types = ['integer'];
902  $values = [$forumId];
903  $res = $ilDB->queryF(
904  $query,
905  $types,
906  $values
907  );
908  $row = $ilDB->fetchAssoc($res);
909 
910  $statistics = [
911  'num_posts' => (int) $row['cnt'],
912  'num_unread_posts' => (int) $row['cnt'],
913  ];
914  }
915 
916  self::$forum_statistics_cache[$ref_id] = $statistics;
917 
918  return self::$forum_statistics_cache[$ref_id];
919  }
920 
921  public static function lookupLastPostByRefId(int $ref_id): ?array
922  {
923  global $DIC;
924 
925  $ilAccess = $DIC->access();
926  $ilUser = $DIC->user();
927  $ilDB = $DIC->database();
928 
929  if (array_key_exists($ref_id, self::$forum_last_post_cache)) {
930  return self::$forum_last_post_cache[$ref_id];
931  }
932 
933  $forumId = self::lookupForumIdByRefId($ref_id);
934  if ($forumId === 0) {
935  self::$forum_last_post_cache[$ref_id] = null;
936  return self::$forum_last_post_cache[$ref_id];
937  }
938 
939  $act_clause = '';
940  if (!$ilAccess->checkAccess('moderate_frm', '', $ref_id)) {
941  $act_clause .= ' AND (frm_posts.pos_status = ' . $ilDB->quote(
942  1,
943  'integer'
944  ) . ' OR frm_posts.pos_author_id = ' . $ilDB->quote($ilUser->getId(), 'integer') . ') ';
945  }
946 
947  $ilDB->setLimit(1, 0);
948  $query = "
949  SELECT *
950  FROM frm_posts
951  INNER JOIN frm_posts_tree tree1
952  ON tree1.pos_fk = frm_posts.pos_pk
953  AND tree1.parent_pos != 0
954  WHERE pos_top_fk = %s $act_clause
955  ORDER BY pos_date DESC
956  ";
957  $res = $ilDB->queryF(
958  $query,
959  ['integer'],
960  [$forumId]
961  );
962 
963  $data = $ilDB->fetchAssoc($res);
964  if (!is_array($data) || empty($data)) {
965  self::$forum_last_post_cache[$ref_id] = null;
966  return self::$forum_last_post_cache[$ref_id];
967  }
968 
969  $casted_data = [];
970  $casted_data['pos_pk'] = (int) $data['pos_pk'];
971  $casted_data['pos_top_fk'] = (int) $data['pos_top_fk'];
972  $casted_data['pos_thr_fk'] = (int) $data['pos_thr_fk'];
973  $casted_data['pos_usr_alias'] = (string) $data['pos_usr_alias'];
974  $casted_data['pos_subject'] = (string) $data['pos_subject'];
975  $casted_data['pos_date'] = (string) $data['pos_date'];
976  $casted_data['pos_update'] = (string) $data['pos_update'];
977  $casted_data['update_user'] = (int) $data['update_user'];
978  $casted_data['pos_cens'] = (int) $data['pos_cens'];
979  $casted_data['pos_cens_com'] = (string) $data['pos_cens_com'];
980  $casted_data['notify'] = (int) $data['notify'];
981  $casted_data['import_name'] = (string) $data['import_name'];
982  $casted_data['pos_status'] = (int) $data['pos_status'];
983  $casted_data['pos_message'] = (string) $data['pos_message'];
984  $casted_data['pos_author_id'] = (int) $data['pos_author_id'];
985  $casted_data['pos_display_user_id'] = (int) $data['pos_display_user_id'];
986  $casted_data['is_author_moderator'] = (int) $data['is_author_moderator'];
987  $casted_data['pos_cens_date'] = (string) $data['pos_cens_date'];
988  $casted_data['pos_activation_date'] = (string) $data['pos_activation_date'];
989 
990  self::$forum_last_post_cache[$ref_id] = $casted_data;
991 
992  return self::$forum_last_post_cache[$ref_id];
993  }
994 
999  public static function getUserIdsOfLastPostsByRefIdAndThreadIds(int $ref_id, array $thread_ids): array
1000  {
1001  global $DIC;
1002 
1003  $ilAccess = $DIC->access();
1004  $ilUser = $DIC->user();
1005  $ilDB = $DIC->database();
1006 
1007  $act_clause = '';
1008  $act_inner_clause = '';
1009  if (!$ilAccess->checkAccess('moderate_frm', '', $ref_id)) {
1010  $act_clause .= " AND (t1.pos_status = " . $ilDB->quote(
1011  1,
1012  "integer"
1013  ) . " OR t1.pos_author_id = " . $ilDB->quote($ilUser->getId(), "integer") . ") ";
1014  $act_inner_clause .= " AND (t3.pos_status = " . $ilDB->quote(
1015  1,
1016  "integer"
1017  ) . " OR t3.pos_author_id = " . $ilDB->quote($ilUser->getId(), "integer") . ") ";
1018  }
1019 
1020  $in = $ilDB->in("t1.pos_thr_fk", $thread_ids, false, 'integer');
1021  $inner_in = $ilDB->in("t3.pos_thr_fk", $thread_ids, false, 'integer');
1022 
1023  $query = "
1024  SELECT t1.pos_display_user_id, t1.update_user
1025  FROM frm_posts t1
1026  INNER JOIN frm_posts_tree tree1 ON tree1.pos_fk = t1.pos_pk AND tree1.parent_pos != 0
1027  INNER JOIN (
1028  SELECT t3.pos_thr_fk, MAX(t3.pos_date) pos_date
1029  FROM frm_posts t3
1030  INNER JOIN frm_posts_tree tree2 ON tree2.pos_fk = t3.pos_pk AND tree2.parent_pos != 0
1031  WHERE $inner_in $act_inner_clause
1032  GROUP BY t3.pos_thr_fk
1033  ) t2 ON t2.pos_thr_fk = t1.pos_thr_fk AND t2.pos_date = t1.pos_date
1034  WHERE $in $act_clause
1035  GROUP BY t1.pos_thr_fk, t1.pos_display_user_id, t1.update_user
1036  ";
1037 
1038  $usr_ids = [];
1039 
1040  $res = $ilDB->query($query);
1041  while ($row = $ilDB->fetchAssoc($res)) {
1042  if ((int) $row['pos_display_user_id'] !== 0) {
1043  $usr_ids[] = (int) $row['pos_display_user_id'];
1044  }
1045  if ((int) $row['update_user'] !== 0) {
1046  $usr_ids[] = (int) $row['update_user'];
1047  }
1048  }
1049 
1050  return array_unique($usr_ids);
1051  }
1052 
1053  public static function mergeForumUserRead(int $merge_source_thread_id, int $merge_target_thread_id): void
1054  {
1055  global $DIC;
1056 
1057  $DIC->database()->update(
1058  'frm_user_read',
1059  ['thread_id' => ['integer', $merge_target_thread_id]],
1060  ['thread_id' => ['integer', $merge_source_thread_id]]
1061  );
1062  }
1063 
1064  public function getNumStickyThreads(): int
1065  {
1066  $res = $this->db->query(
1067  'SELECT COUNT(is_sticky) num_sticky FROM frm_threads
1068  INNER JOIN frm_data ON top_pk = thr_top_fk
1069  WHERE frm_data.top_frm_fk = ' . $this->db->quote($this->getId(), 'integer') . '
1070  AND is_sticky = ' . $this->db->quote(1, 'integer')
1071  );
1072  if ($row = $this->db->fetchAssoc($res)) {
1073  return (int) $row['num_sticky'];
1074  }
1075 
1076  return 0;
1077  }
1078 
1082  public function getPageObjIds(): array
1083  {
1084  $pageObjIds = [];
1085 
1086  $sql = 'SELECT DISTINCT page_id FROM page_object WHERE parent_id = %s AND parent_type = %s';
1087  $res = $this->db->queryF(
1088  $sql,
1089  ['integer', 'text'],
1090  [$this->getId(), $this->getType()]
1091  );
1092 
1093  while ($row = $this->db->fetchAssoc($res)) {
1094  $pageObjIds[] = (int) $row['page_id'];
1095  }
1096 
1097  return $pageObjIds;
1098  }
1099 }
ilClone(int $new_obj_id, int $new_posting_id)
__construct(int $a_id=0, bool $a_call_by_reference=true)
$res
Definition: ltiservices.php:66
static mergeForumUserRead(int $merge_source_thread_id, int $merge_target_thread_id)
static _lookupThreadSubject(int $a_thread_id)
static array $forum_statistics_cache
updateModificationUserId(int $usr_id)
static _getDefaultVisibilityForRefId(int $a_ref_id)
Get default visibility for reference id.
static lookupForumIdByObjId(int $obj_id)
static _lookupModeratorRole(int $a_ref_id)
updateModeratorRole(int $role_id)
static getUserIdsOfLastPostsByRefIdAndThreadIds(int $ref_id, array $thread_ids)
deleteDraftsByForumId(int $forum_id)
readonly ilLogger $logger
static createDefaultRole(string $a_title, string $a_description, string $a_tpl_name, int $a_ref_id)
static preloadForumIdsByObjIds(array $obj_ids)
isParentMembershipEnabledContainer()
static lookupTranslations(string $a_parent_type, int $a_id)
Lookup translations.
static lookupForumIdByRefId(int $ref_id)
static now()
Return current timestamp in Y-m-d H:i:s format.
Properties $object_properties
markAllThreadsRead(int $a_usr_id)
static _deleteReadEntries(int $a_post_id)
Class ilForumDraftHistory.
static getInstance(int $a_obj_id=0)
while($session_entry=$r->fetchRow(ilDBConstants::FETCHMODE_ASSOC)) return null
static _deleteUser(int $a_usr_id)
static _getInstanceByObjId(int $a_obj_id)
static lookupStatisticsByRefId(int $ref_id)
static _exists(string $a_parent_type, int $a_id, string $a_lang="", bool $a_no_cache=false)
Checks whether page exists.
static getInstanceByRefId(int $ref_id, bool $stop_on_error=true)
get an instance of an Ilias object by reference id
global $DIC
Definition: shib_login.php:26
static _write(string $a_type, string $a_setting, string $a_value, int $a_user=0, int $a_block_id=0)
Write setting to database.
static _lookupObjectId(int $ref_id)
readonly ilSetting $settings
static array $obj_id_to_forum_id_cache
setOfflineStatus(bool $status)
static array $forum_last_post_cache
getCountUnread(int $a_usr_id, int $a_thread_id=0, bool $ignoreRoot=false)
static array $ref_id_to_forum_id_cache
static lookupLastPostByRefId(int $ref_id)
static _getInstanceByObjId(int $a_obj_id)
Get singleton instance.
isRead($a_usr_id, $a_post_id)
global $ilSetting
Definition: privfeed.php:31
$id
plugin.php for ilComponentBuildPluginInfoObjectiveTest::testAddPlugins
Definition: plugin.php:23
__construct(Container $dic, ilPlugin $plugin)
readonly ILIAS DI RBACServices $rbac
static _getModerators(int $a_ref_id)
static _getInstance(int $a_copy_id)
markPostUnread(int $a_user_id, int $a_post_id)
setPermissions(int $parent_ref_id)
markThreadRead(int $a_usr_id, int $a_thread_id)
static preloadForumIdsByRefIds(array $ref_ids)
markPostRead(int $a_usr_id, int $a_thread_id, int $a_post_id)
cloneAutoGeneratedRoles(self $new_obj)