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