ILIAS  trunk Revision v11.0_alpha-3011-gc6b235a2e85
class.ilObjForum.php
Go to the documentation of this file.
1<?php
2
19declare(strict_types=1);
20
26class ilObjForum extends ilObject
27{
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
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 {
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)
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
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
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
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
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}
$id
plugin.php for ilComponentBuildPluginInfoObjectiveTest::testAddPlugins
Definition: plugin.php:23
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 _getInstance(int $a_copy_id)
static _getInstanceByObjId(int $a_obj_id)
Class ilForumDraftHistory.
Class ilForumModerators.
Class ilForumNotification.
Class ilForumPostDraft.
static getInstance(int $a_obj_id=0)
Class Forum core functions for forum.
static _getModerators(int $a_ref_id)
static _getInstanceByObjId(int $a_obj_id)
Get singleton instance.
Component logger with individual log levels by component id.
static _getDefaultVisibilityForRefId(int $a_ref_id)
Get default visibility for reference id.
Class ilObjForum.
static _deleteReadEntries(int $a_post_id)
__construct(int $a_id=0, bool $a_call_by_reference=true)
static lookupForumIdByRefId(int $ref_id)
static lookupForumIdByObjId(int $obj_id)
cloneAutoGeneratedRoles(self $new_obj)
static array $ref_id_to_forum_id_cache
markAllThreadsRead(int $a_usr_id)
initDefaultRoles()
init default roles settings Purpose of this function is to create a local role folder and local roles...
readonly ILIAS DI RBACServices $rbac
markThreadRead(int $a_usr_id, int $a_thread_id)
static _lookupModeratorRole(int $a_ref_id)
readonly ilLogger $logger
static lookupStatisticsByRefId(int $ref_id)
static preloadForumIdsByRefIds(array $ref_ids)
static getUserIdsOfLastPostsByRefIdAndThreadIds(int $ref_id, array $thread_ids)
static array $forum_statistics_cache
updateModeratorRole(int $role_id)
deleteDraftsByForumId(int $forum_id)
setPermissions(int $parent_ref_id)
isRead($a_usr_id, $a_post_id)
readonly ilSetting $settings
isParentMembershipEnabledContainer()
static lookupLastPostByRefId(int $ref_id)
getCountUnread(int $a_usr_id, int $a_thread_id=0, bool $ignoreRoot=false)
static array $obj_id_to_forum_id_cache
static _lookupThreadSubject(int $a_thread_id)
static mergeForumUserRead(int $merge_source_thread_id, int $merge_target_thread_id)
static preloadForumIdsByObjIds(array $obj_ids)
updateModificationUserId(int $usr_id)
markPostRead(int $a_usr_id, int $a_thread_id, int $a_post_id)
static _deleteUser(int $a_usr_id)
static array $forum_last_post_cache
create()
note: title, description and type should be set when this function is called
markPostUnread(int $a_user_id, int $a_post_id)
static createDefaultRole(string $a_title, string $a_description, string $a_tpl_name, int $a_ref_id)
static getInstanceByRefId(int $ref_id, bool $stop_on_error=true)
get an instance of an Ilias object by reference id
Class ilObject Basic functions for all objects.
static _lookupObjectId(int $ref_id)
Properties $object_properties
setOfflineStatus(bool $status)
static _exists(string $a_parent_type, int $a_id, string $a_lang="", bool $a_no_cache=false)
Checks whether page exists.
static lookupTranslations(string $a_parent_type, int $a_id)
Lookup translations.
Base class for course and group participants.
ILIAS Setting Class.
static now()
Return current timestamp in Y-m-d H:i:s format.
$res
Definition: ltiservices.php:69
__construct(Container $dic, ilPlugin $plugin)
@inheritDoc
global $ilSetting
Definition: privfeed.php:31
global $DIC
Definition: shib_login.php:26