ILIAS  release_7 Revision v7.30-3-g800a261c036
class.ilForumCronNotification.php
Go to the documentation of this file.
1<?php
2/* Copyright (c) 1998-2009 ILIAS open source, Extended GPL, see docs/LICENSE */
3
11{
13
17 protected $settings;
18
22 protected $logger;
23
27 protected $tree;
28
32 public static $providerObject = array();
33
37 protected static $deleted_ids_cache = array();
38
42 protected static $ref_ids_by_obj_id = array();
43
47 protected static $accessible_ref_ids_by_user = array();
48
50 private static $container_by_frm_ref_id = [];
51
55 protected $num_sent_messages = 0;
56
58 private $ilDB;
59
62
67 public function __construct(\ilDBInterface $database = null, \ilForumNotificationCache $notificationCache = null)
68 {
69 $this->settings = new ilSetting('frma');
70
71 global $DIC;
72 if ($database === null) {
73 $ilDB = $DIC->database();
74 }
75 $this->ilDB = $ilDB;
76
77 if ($notificationCache === null) {
78 $notificationCache = new \ilForumNotificationCache();
79 }
80 $this->notificationCache = $notificationCache;
81 }
82
83 public function getId()
84 {
85 return "frm_notification";
86 }
87
88 public function getTitle()
89 {
90 global $DIC;
91
92 return $DIC->language()->txt("cron_forum_notification");
93 }
94
95 public function getDescription()
96 {
97 global $DIC;
98
99 return $DIC->language()->txt("cron_forum_notification_crob_desc");
100 }
101
102 public function getDefaultScheduleType()
103 {
105 }
106
107 public function getDefaultScheduleValue()
108 {
109 return 1;
110 }
111
112 public function hasAutoActivation()
113 {
114 return false;
115 }
116
117 public function hasFlexibleSchedule()
118 {
119 return true;
120 }
121
125 public function hasCustomSettings()
126 {
127 return true;
128 }
129
133 public function keepAlive()
134 {
135 $this->logger->debug('Sending ping to cron manager ...');
136 \ilCronManager::ping($this->getId());
137 $this->logger->debug(sprintf('Current memory usage: %s', memory_get_usage(true)));
138 }
139
143 public function run()
144 {
145 global $DIC;
146
147 $ilSetting = $DIC->settings();
148 $lng = $DIC->language();
149 $this->tree = $DIC->repositoryTree();
150
151 $this->logger = $DIC->logger()->frm();
152
154
155 $lng->loadLanguageModule('forum');
156
157 $this->logger->info('Started forum notification job ...');
158
159 if (!($last_run_datetime = $ilSetting->get('cron_forum_notification_last_date'))) {
160 $last_run_datetime = null;
161 }
162
163 $this->num_sent_messages = 0;
164 $cj_start_date = date('Y-m-d H:i:s');
165
166 if ($last_run_datetime != null &&
167 checkDate(date('m', strtotime($last_run_datetime)), date('d', strtotime($last_run_datetime)), date('Y', strtotime($last_run_datetime)))) {
168 $threshold = max(strtotime($last_run_datetime), strtotime('-' . (int) $this->settings->get('max_notification_age', 30) . ' days', time()));
169 } else {
170 $threshold = strtotime('-' . (int) $this->settings->get('max_notification_age', 30) . ' days', time());
171 }
172
173 $this->logger->info(sprintf('Threshold for forum event determination is: %s', date('Y-m-d H:i:s', $threshold)));
174
175 $threshold_date = date('Y-m-d H:i:s', $threshold);
176
177 $this->sendNotificationForNewPosts($threshold_date);
178
179 $this->sendNotificationForUpdatedPosts($threshold_date);
180
181 $this->sendNotificationForCensoredPosts($threshold_date);
182
183 $this->sendNotificationForUncensoredPosts($threshold_date);
184
186
188
189 $ilSetting->set('cron_forum_notification_last_date', $cj_start_date);
190
191 $mess = 'Sent ' . $this->num_sent_messages . ' messages.';
192
193 $this->logger->info($mess);
194 $this->logger->info('Finished forum notification job');
195
196 $result = new ilCronJobResult();
197 if ($this->num_sent_messages) {
199 $result->setMessage($mess);
200 };
201 $result->setStatus($status);
202 return $result;
203 }
204
209 protected function getRefIdsByObjId($a_obj_id)
210 {
211 if (!array_key_exists($a_obj_id, self::$ref_ids_by_obj_id)) {
212 self::$ref_ids_by_obj_id[$a_obj_id] = ilObject::_getAllReferences($a_obj_id);
213 }
214
215 return (array) self::$ref_ids_by_obj_id[$a_obj_id];
216 }
217
223 protected function getFirstAccessibleRefIdBUserAndObjId($a_user_id, $a_obj_id)
224 {
225 global $DIC;
226 $ilAccess = $DIC->access();
227
228 if (!array_key_exists($a_user_id, self::$accessible_ref_ids_by_user)) {
229 self::$accessible_ref_ids_by_user[$a_user_id] = array();
230 }
231
232 if (!array_key_exists($a_obj_id, self::$accessible_ref_ids_by_user[$a_user_id])) {
233 $accessible_ref_id = 0;
234 foreach ($this->getRefIdsByObjId($a_obj_id) as $ref_id) {
235 if ($ilAccess->checkAccessOfUser($a_user_id, 'read', '', $ref_id)) {
236 $accessible_ref_id = $ref_id;
237 break;
238 }
239 }
240 self::$accessible_ref_ids_by_user[$a_user_id][$a_obj_id] = $accessible_ref_id;
241 }
242
243 return (int) self::$accessible_ref_ids_by_user[$a_user_id][$a_obj_id];
244 }
245
250 public function sendCronForumNotification($res, $notification_type)
251 {
252 global $DIC;
253 $ilDB = $DIC->database();
254
255 while ($row = $ilDB->fetchAssoc($res)) {
256 if ($notification_type == ilForumMailNotification::TYPE_POST_DELETED
257 || $notification_type == ilForumMailNotification::TYPE_THREAD_DELETED) {
258 // important! save the deleted_id to cache before proceeding getFirstAccessibleRefIdBUserAndObjId !
259 self::$deleted_ids_cache[$row['deleted_id']] = $row['deleted_id'];
260 }
261
262 $ref_id = $this->getFirstAccessibleRefIdBUserAndObjId($row['user_id'], $row['obj_id']);
263 if ($ref_id < 1) {
264 $this->logger->debug(sprintf(
265 'The recipient with id %s has no "read" permission for object with id %s',
266 $row['user_id'],
267 $row['obj_id']
268 ));
269 continue;
270 }
271
272 $row['ref_id'] = $ref_id;
273
274 $container = $this->determineClosestContainer($ref_id);
275 if ($container instanceof ilObjCourse || $container instanceof ilObjGroup) {
276 $row['closest_container'] = $container;
277 }
278
279 if ($this->existsProviderObject($row['pos_pk'])) {
280 self::$providerObject[$row['pos_pk']]->addRecipient($row['user_id']);
281 } else {
282 $this->addProviderObject($row);
283 }
284 }
285
286 $usrIdsToPreload = array();
287 foreach (self::$providerObject as $provider) {
288 if ($provider->getPosAuthorId()) {
289 $usrIdsToPreload[$provider->getPosAuthorId()] = $provider->getPosAuthorId();
290 }
291 if ($provider->getPosDisplayUserId()) {
292 $usrIdsToPreload[$provider->getPosDisplayUserId()] = $provider->getPosDisplayUserId();
293 }
294 if ($provider->getPostUpdateUserId()) {
295 $usrIdsToPreload[$provider->getPostUpdateUserId()] = $provider->getPostUpdateUserId();
296 }
297 }
298
299 ilForumAuthorInformationCache::preloadUserObjects(array_unique($usrIdsToPreload));
300
301 $i = 0;
302 foreach (self::$providerObject as $provider) {
303 if ($i > 0 && ($i % self::KEEP_ALIVE_CHUNK_SIZE) == 0) {
304 $this->keepAlive();
305 }
306
307 $recipients = array_unique($provider->getCronRecipients());
308
309 $this->logger->info(sprintf(
310 'Trying to send forum notifications for posting id "%s", type "%s" and recipients: %s',
311 $provider->getPostId(),
312 $notification_type,
313 implode(', ', $recipients)
314 ));
315
316 $mailNotification = new ilForumMailNotification($provider, $this->logger);
317 $mailNotification->setIsCronjob(true);
318 $mailNotification->setType($notification_type);
319 $mailNotification->setRecipients($recipients);
320
321 $mailNotification->send();
322
323 $this->num_sent_messages += count($provider->getCronRecipients());
324 $this->logger->info(sprintf("Sent notifications ... "));
325
326 ++$i;
327 }
328
329 $this->resetProviderCache();
330 }
331
336 public function determineClosestContainer(int $frm_ref_id) : ?ilObject
337 {
338 if (isset(self::$container_by_frm_ref_id[$frm_ref_id])) {
339 return self::$container_by_frm_ref_id[$frm_ref_id];
340 }
341
342 $ref_id = $this->tree->checkForParentType($frm_ref_id, 'crs');
343 if (!($ref_id > 0)) {
344 $ref_id = $this->tree->checkForParentType($frm_ref_id, 'grp');
345 }
346
347 if ($ref_id > 0) {
350 self::$container_by_frm_ref_id[$frm_ref_id] = $container;
351 return $container;
352 }
353
354 return null;
355 }
356
361 public function existsProviderObject($post_id)
362 {
363 if (isset(self::$providerObject[$post_id])) {
364 return true;
365 }
366 return false;
367 }
368
372 private function addProviderObject($row)
373 {
374 $tmp_provider = new ilForumCronNotificationDataProvider($row, $this->notificationCache);
375
376 self::$providerObject[$row['pos_pk']] = $tmp_provider;
377 self::$providerObject[$row['pos_pk']]->addRecipient($row['user_id']);
378 }
379
383 private function resetProviderCache()
384 {
385 self::$providerObject = array();
386 }
387
393 public function addToExternalSettingsForm($a_form_id, array &$a_fields, $a_is_active)
394 {
395 global $DIC;
396 $lng = $DIC->language();
397
398 switch ($a_form_id) {
400 $a_fields['cron_forum_notification'] = $a_is_active ?
401 $lng->txt('enabled') :
402 $lng->txt('disabled');
403 break;
404 }
405 }
406
410 public function activationWasToggled($a_currently_active)
411 {
412 global $DIC;
413
414 $value = 1;
415 // propagate cron-job setting to object setting
416 if ((bool) $a_currently_active) {
417 $value = 2;
418 }
419 $DIC->settings()->set('forum_notification', $value);
420 }
421
426 {
427 global $DIC;
428 $lng = $DIC->language();
429
430 $lng->loadLanguageModule('forum');
431
432 $max_notification_age = new ilNumberInputGUI($lng->txt('frm_max_notification_age'), 'max_notification_age');
433 $max_notification_age->setSize(5);
434 $max_notification_age->setSuffix($lng->txt('frm_max_notification_age_unit'));
435 $max_notification_age->setRequired(true);
436 $max_notification_age->allowDecimals(false);
437 $max_notification_age->setMinValue(1);
438 $max_notification_age->setInfo($lng->txt('frm_max_notification_age_info'));
439 $max_notification_age->setValue($this->settings->get('max_notification_age', 30));
440
441 $a_form->addItem($max_notification_age);
442 }
443
448 public function saveCustomSettings(ilPropertyFormGUI $a_form)
449 {
450 $this->settings->set('max_notification_age', $a_form->getInput('max_notification_age'));
451 return true;
452 }
453
457 private function sendNotificationForNewPosts(string $threshold_date)
458 {
459 $condition = '
460 frm_posts.pos_status = %s AND (
461 (frm_posts.pos_date >= %s AND frm_posts.pos_date = frm_posts.pos_activation_date) OR
462 (frm_posts.pos_activation_date >= %s AND frm_posts.pos_date < frm_posts.pos_activation_date)
463 ) ';
464 $types = array('integer', 'timestamp', 'timestamp');
465 $values = array(1, $threshold_date, $threshold_date);
466
467 $res = $this->ilDB->queryf(
468 $this->createForumPostSql($condition),
469 $types,
470 $values
471 );
472
473 $this->sendNotification(
474 $res,
475 'new posting',
477 );
478 }
479
483 private function sendNotificationForUpdatedPosts(string $threshold_date)
484 {
485 $condition = '
486 frm_posts.pos_cens = %s AND frm_posts.pos_status = %s AND
487 (frm_posts.pos_update > frm_posts.pos_date AND frm_posts.pos_update >= %s) ';
488 $types = array('integer', 'integer', 'timestamp');
489 $values = array(0, 1, $threshold_date);
490
491 $res = $this->ilDB->queryf(
492 $this->createForumPostSql($condition),
493 $types,
494 $values
495 );
496
497 $this->sendNotification(
498 $res,
499 'updated posting',
501 );
502 }
503
507 private function sendNotificationForCensoredPosts(string $threshold_date)
508 {
509 $condition = '
510 frm_posts.pos_cens = %s AND frm_posts.pos_status = %s AND
511 (frm_posts.pos_cens_date >= %s AND frm_posts.pos_cens_date > frm_posts.pos_activation_date ) ';
512 $types = array('integer', 'integer', 'timestamp');
513 $values = array(1, 1, $threshold_date);
514
515 $res = $this->ilDB->queryf(
516 $this->createForumPostSql($condition),
517 $types,
518 $values
519 );
520
521 $this->sendNotification(
522 $res,
523 'censored posting',
525 );
526 }
527
531 private function sendNotificationForUncensoredPosts(string $threshold_date)
532 {
533 $condition = '
534 frm_posts.pos_cens = %s AND frm_posts.pos_status = %s AND
535 (frm_posts.pos_cens_date >= %s AND frm_posts.pos_cens_date > frm_posts.pos_activation_date ) ';
536 $types = array('integer', 'integer', 'timestamp');
537 $values = array(0, 1, $threshold_date);
538
539 $res = $this->ilDB->queryf(
540 $this->createForumPostSql($condition),
541 $types,
542 $values
543 );
544
545 $this->sendNotification(
546 $res,
547 'uncensored posting',
549 );
550 }
551
553 {
554 $res = $this->ilDB->queryF(
556 array('integer'),
557 array(1)
558 );
559
561 $res,
562 'frm_threads_deleted',
563 'deleted threads',
565 );
566 }
567
569 {
570 $res = $this->ilDB->queryF(
572 array('integer'),
573 array(0)
574 );
575
577 $res,
578 'frm_posts_deleted',
579 'deleted postings',
581 );
582 }
583
589 private function sendNotification(\ilPDOStatement $res, string $actionName, int $notificationType)
590 {
591 $numRows = $this->ilDB->numRows($res);
592 if ($numRows > 0) {
593 $this->logger->info(sprintf('Sending notifications for %s "%s" events ...', $numRows, $actionName));
594 $this->sendCronForumNotification($res, $notificationType);
595 $this->logger->info(sprintf('Sent notifications for %s ...', $actionName));
596 }
597
598 $this->keepAlive();
599 }
600
607 private function sendDeleteNotifcations(\ilPDOStatement $res, string $action, string $actionDescription, int $notificationType)
608 {
609 $numRows = $this->ilDB->numRows($res);
610 if ($numRows > 0) {
611 $this->logger->info(sprintf('Sending notifications for %s "%s" events ...', $numRows, $actionDescription));
612 $this->sendCronForumNotification($res, $notificationType);
613 if (count(self::$deleted_ids_cache) > 0) {
614 $this->ilDB->manipulate('DELETE FROM frm_posts_deleted WHERE ' . $this->ilDB->in('deleted_id', self::$deleted_ids_cache, false, 'integer'));
615 $this->logger->info('Deleted obsolete entries of table "' . $action . '" ...');
616 }
617 $this->logger->info(sprintf('Sent notifications for %s ...', $actionDescription));
618 }
619
620 $this->keepAlive();
621 }
622
627 private function createForumPostSql($condition) : string
628 {
629 return '
630 SELECT frm_threads.thr_subject thr_subject,
631 frm_data.top_name top_name,
632 frm_data.top_frm_fk obj_id,
633 frm_notification.user_id user_id,
634 frm_threads.thr_pk thread_id,
635 frm_posts.*
636 FROM frm_notification, frm_posts, frm_threads, frm_data, frm_posts_tree
637 WHERE frm_posts.pos_thr_fk = frm_threads.thr_pk AND ' . $condition . '
638 AND ((frm_threads.thr_top_fk = frm_data.top_pk AND frm_data.top_frm_fk = frm_notification.frm_id)
639 OR (frm_threads.thr_pk = frm_notification.thread_id
640 AND frm_data.top_pk = frm_threads.thr_top_fk) )
641 AND frm_posts.pos_display_user_id != frm_notification.user_id
642 AND frm_posts_tree.pos_fk = frm_posts.pos_pk AND frm_posts_tree.parent_pos != 0
643 ORDER BY frm_posts.pos_date ASC';
644 }
645
649 private function createSelectOfDeletionNotificationsSql() : string
650 {
651 return '
652 SELECT frm_posts_deleted.thread_title thr_subject,
653 frm_posts_deleted.forum_title top_name,
654 frm_posts_deleted.obj_id obj_id,
655 frm_notification.user_id user_id,
656 frm_posts_deleted.pos_display_user_id,
657 frm_posts_deleted.pos_usr_alias,
658 frm_posts_deleted.deleted_id,
659 frm_posts_deleted.post_date pos_date,
660 frm_posts_deleted.post_title pos_subject,
661 frm_posts_deleted.post_message pos_message,
662 frm_posts_deleted.deleted_by
663
664 FROM frm_notification, frm_posts_deleted
665
666 WHERE ( frm_posts_deleted.obj_id = frm_notification.frm_id
667 OR frm_posts_deleted.thread_id = frm_notification.thread_id)
668 AND frm_posts_deleted.pos_display_user_id != frm_notification.user_id
669 AND frm_posts_deleted.is_thread_deleted = %s
670 ORDER BY frm_posts_deleted.post_date ASC';
671 }
672}
$result
An exception for terminatinating execution or to throw for unit testing.
Cron job result data container.
Cron job application base class.
const SCHEDULE_TYPE_IN_HOURS
static ping($a_job_id)
Keep cron job alive.
saveCustomSettings(ilPropertyFormGUI $a_form)
addCustomSettingsToForm(ilPropertyFormGUI $a_form)
sendDeleteNotifcations(\ilPDOStatement $res, string $action, string $actionDescription, int $notificationType)
sendCronForumNotification($res, $notification_type)
sendNotificationForUncensoredPosts(string $threshold_date)
sendNotification(\ilPDOStatement $res, string $actionName, int $notificationType)
getDefaultScheduleValue()
Get schedule value.
sendNotificationForUpdatedPosts(string $threshold_date)
getFirstAccessibleRefIdBUserAndObjId($a_user_id, $a_obj_id)
hasAutoActivation()
Is to be activated on "installation".
sendNotificationForCensoredPosts(string $threshold_date)
hasFlexibleSchedule()
Can the schedule be configured?
sendNotificationForNewPosts(string $threshold_date)
addToExternalSettingsForm($a_form_id, array &$a_fields, $a_is_active)
__construct(\ilDBInterface $database=null, \ilForumNotificationCache $notificationCache=null)
Class ilForumNotificationCache.
This class represents a number property in a property form.
Class ilObjCourse.
Class ilObjGroup.
static getInstanceByRefId($a_ref_id, $stop_on_error=true)
get an instance of an Ilias object by reference id
Class ilObject Basic functions for all objects.
static _getAllReferences($a_id)
get all reference ids of object
Class ilPDOStatement is a Wrapper Class for PDOStatement.
This class represents a property form user interface.
addItem($a_item)
Add Item (Property, SectionHeader).
getInput($a_post_var, $ensureValidation=true)
Returns the value of a HTTP-POST variable, identified by the passed id.
ILIAS Setting Class.
global $DIC
Definition: goto.php:24
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
$i
Definition: metadata.php:24
global $ilSetting
Definition: privfeed.php:17
$lng
foreach($_POST as $key=> $value) $res
settings()
Definition: settings.php:2
$container
Definition: wac.php:13