ILIAS  release_5-4 Revision v5.4.26-12-gabc799a52e6
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 public static $providerObject = array();
28
32 protected static $deleted_ids_cache = array();
33
37 protected static $ref_ids_by_obj_id = array();
38
42 protected static $accessible_ref_ids_by_user = array();
43
47 protected $num_sent_messages = 0;
48
50 private $ilDB;
51
54
59 public function __construct(\ilDBInterface $database = null, \ilForumNotificationCache $notificationCache = null)
60 {
61 $this->settings = new ilSetting('frma');
62
63 if ($database === null) {
64 global $DIC;
65 $ilDB = $DIC->database();
66 }
67 $this->ilDB = $ilDB;
68
69 if ($notificationCache === null) {
70 $notificationCache = new \ilForumNotificationCache();
71 }
72 $this->notificationCache = $notificationCache;
73 }
74
75 public function getId()
76 {
77 return "frm_notification";
78 }
79
80 public function getTitle()
81 {
82 global $DIC;
83
84 return $DIC->language()->txt("cron_forum_notification");
85 }
86
87 public function getDescription()
88 {
89 global $DIC;
90
91 return $DIC->language()->txt("cron_forum_notification_crob_desc");
92 }
93
94 public function getDefaultScheduleType()
95 {
97 }
98
99 public function getDefaultScheduleValue()
100 {
101 return 1;
102 }
103
104 public function hasAutoActivation()
105 {
106 return false;
107 }
108
109 public function hasFlexibleSchedule()
110 {
111 return true;
112 }
113
117 public function hasCustomSettings()
118 {
119 return true;
120 }
121
125 public function keepAlive()
126 {
127 $this->logger->debug('Sending ping to cron manager ...');
128 \ilCronManager::ping($this->getId());
129 $this->logger->debug(sprintf('Current memory usage: %s', memory_get_usage(true)));
130 }
131
135 public function run()
136 {
137 global $DIC;
138
139 $ilSetting = $DIC->settings();
140 $lng = $DIC->language();
141
142 $this->logger = $DIC->logger()->frm();
143
145
146 $lng->loadLanguageModule('forum');
147
148 $this->logger->info('Started forum notification job ...');
149
150 if (!($last_run_datetime = $ilSetting->get('cron_forum_notification_last_date'))) {
151 $last_run_datetime = null;
152 }
153
154 $this->num_sent_messages = 0;
155 $cj_start_date = date('Y-m-d H:i:s');
156
157 if ($last_run_datetime != null &&
158 checkDate(date('m', strtotime($last_run_datetime)), date('d', strtotime($last_run_datetime)), date('Y', strtotime($last_run_datetime)))) {
159 $threshold = max(strtotime($last_run_datetime), strtotime('-' . (int) $this->settings->get('max_notification_age', 30) . ' days', time()));
160 } else {
161 $threshold = strtotime('-' . (int) $this->settings->get('max_notification_age', 30) . ' days', time());
162 }
163
164 $this->logger->info(sprintf('Threshold for forum event determination is: %s', date('Y-m-d H:i:s', $threshold)));
165
166 $threshold_date = date('Y-m-d H:i:s', $threshold);
167
168 $this->sendNotificationForNewPosts($threshold_date);
169
170 $this->sendNotificationForUpdatedPosts($threshold_date);
171
172 $this->sendNotificationForCensoredPosts($threshold_date);
173
174 $this->sendNotificationForUncensoredPosts($threshold_date);
175
177
179
180 $ilSetting->set('cron_forum_notification_last_date', $cj_start_date);
181
182 $mess = 'Sent ' . $this->num_sent_messages . ' messages.';
183
184 $this->logger->info($mess);
185 $this->logger->info('Finished forum notification job');
186
187 $result = new ilCronJobResult();
188 if ($this->num_sent_messages) {
190 $result->setMessage($mess);
191 };
192 $result->setStatus($status);
193 return $result;
194 }
195
200 protected function getRefIdsByObjId($a_obj_id)
201 {
202 if (!array_key_exists($a_obj_id, self::$ref_ids_by_obj_id)) {
203 self::$ref_ids_by_obj_id[$a_obj_id] = ilObject::_getAllReferences($a_obj_id);
204 }
205
206 return (array) self::$ref_ids_by_obj_id[$a_obj_id];
207 }
208
214 protected function getFirstAccessibleRefIdBUserAndObjId($a_user_id, $a_obj_id)
215 {
216 global $DIC;
217 $ilAccess = $DIC->access();
218
219 if (!array_key_exists($a_user_id, self::$accessible_ref_ids_by_user)) {
220 self::$accessible_ref_ids_by_user[$a_user_id] = array();
221 }
222
223 if (!array_key_exists($a_obj_id, self::$accessible_ref_ids_by_user[$a_user_id])) {
224 $accessible_ref_id = 0;
225 foreach ($this->getRefIdsByObjId($a_obj_id) as $ref_id) {
226 if ($ilAccess->checkAccessOfUser($a_user_id, 'read', '', $ref_id)) {
227 $accessible_ref_id = $ref_id;
228 break;
229 }
230 }
231 self::$accessible_ref_ids_by_user[$a_user_id][$a_obj_id] = $accessible_ref_id;
232 }
233
234 return (int) self::$accessible_ref_ids_by_user[$a_user_id][$a_obj_id];
235 }
236
241 public function sendCronForumNotification($res, $notification_type)
242 {
243 global $DIC;
244 $ilDB = $DIC->database();
245
246 while ($row = $ilDB->fetchAssoc($res)) {
247 if ($notification_type == ilForumMailNotification::TYPE_POST_DELETED
248 || $notification_type == ilForumMailNotification::TYPE_THREAD_DELETED) {
249 // important! save the deleted_id to cache before proceeding getFirstAccessibleRefIdBUserAndObjId !
250 self::$deleted_ids_cache[$row['deleted_id']] = $row['deleted_id'];
251 }
252
253 $ref_id = $this->getFirstAccessibleRefIdBUserAndObjId($row['user_id'], $row['obj_id']);
254 if ($ref_id < 1) {
255 $this->logger->debug(sprintf(
256 'The recipient with id %s has no "read" permission for object with id %s',
257 $row['user_id'],
258 $row['obj_id']
259 ));
260 continue;
261 }
262
263 $row['ref_id'] = $ref_id;
264
265 if ($this->existsProviderObject($row['pos_pk'])) {
266 self::$providerObject[$row['pos_pk']]->addRecipient($row['user_id']);
267 } else {
268 $this->addProviderObject($row);
269 }
270 }
271
272 $usrIdsToPreload = array();
273 foreach (self::$providerObject as $provider) {
274 if ($provider->getPosAuthorId()) {
275 $usrIdsToPreload[$provider->getPosAuthorId()] = $provider->getPosAuthorId();
276 }
277 if ($provider->getPosDisplayUserId()) {
278 $usrIdsToPreload[$provider->getPosDisplayUserId()] = $provider->getPosDisplayUserId();
279 }
280 if ($provider->getPostUpdateUserId()) {
281 $usrIdsToPreload[$provider->getPostUpdateUserId()] = $provider->getPostUpdateUserId();
282 }
283 }
284
285 ilForumAuthorInformationCache::preloadUserObjects(array_unique($usrIdsToPreload));
286
287 $i = 0;
288 foreach (self::$providerObject as $provider) {
289 if ($i > 0 && ($i % self::KEEP_ALIVE_CHUNK_SIZE) == 0) {
290 $this->keepAlive();
291 }
292
293 $recipients = array_unique($provider->getCronRecipients());
294
295 $this->logger->info(sprintf(
296 'Trying to send forum notifications for posting id "%s", type "%s" and recipients: %s',
297 $provider->getPostId(),
298 $notification_type,
299 implode(', ', $recipients)
300 ));
301
302 $mailNotification = new ilForumMailNotification($provider, $this->logger);
303 $mailNotification->setIsCronjob(true);
304 $mailNotification->setType($notification_type);
305 $mailNotification->setRecipients($recipients);
306
307 $mailNotification->send();
308
309 $this->num_sent_messages += count($provider->getCronRecipients());
310 $this->logger->info(sprintf("Sent notifications ... "));
311
312 ++$i;
313 }
314
315 $this->resetProviderCache();
316 }
317
322 public function existsProviderObject($post_id)
323 {
324 if (isset(self::$providerObject[$post_id])) {
325 return true;
326 }
327 return false;
328 }
329
333 private function addProviderObject($row)
334 {
335 $tmp_provider = new ilForumCronNotificationDataProvider($row, $this->notificationCache);
336
337 self::$providerObject[$row['pos_pk']] = $tmp_provider;
338 self::$providerObject[$row['pos_pk']]->addRecipient($row['user_id']);
339 }
340
344 private function resetProviderCache()
345 {
346 self::$providerObject = array();
347 }
348
354 public function addToExternalSettingsForm($a_form_id, array &$a_fields, $a_is_active)
355 {
356 global $DIC;
357 $lng = $DIC->language();
358
359 switch ($a_form_id) {
361 $a_fields['cron_forum_notification'] = $a_is_active ?
362 $lng->txt('enabled') :
363 $lng->txt('disabled');
364 break;
365 }
366 }
367
371 public function activationWasToggled($a_currently_active)
372 {
373 global $DIC;
374
375 $value = 1;
376 // propagate cron-job setting to object setting
377 if ((bool) $a_currently_active) {
378 $value = 2;
379 }
380 $DIC->settings()->set('forum_notification', $value);
381 }
382
387 {
388 global $DIC;
389 $lng = $DIC->language();
390
391 $lng->loadLanguageModule('forum');
392
393 $max_notification_age = new ilNumberInputGUI($lng->txt('frm_max_notification_age'), 'max_notification_age');
394 $max_notification_age->setSize(5);
395 $max_notification_age->setSuffix($lng->txt('frm_max_notification_age_unit'));
396 $max_notification_age->setRequired(true);
397 $max_notification_age->allowDecimals(false);
398 $max_notification_age->setMinValue(1);
399 $max_notification_age->setInfo($lng->txt('frm_max_notification_age_info'));
400 $max_notification_age->setValue($this->settings->get('max_notification_age', 30));
401
402 $a_form->addItem($max_notification_age);
403 }
404
409 public function saveCustomSettings(ilPropertyFormGUI $a_form)
410 {
411 $this->settings->set('max_notification_age', $a_form->getInput('max_notification_age'));
412 return true;
413 }
414
418 private function sendNotificationForNewPosts(string $threshold_date)
419 {
420 $condition = '
421 frm_posts.pos_status = %s AND (
422 (frm_posts.pos_date >= %s AND frm_posts.pos_date = frm_posts.pos_activation_date) OR
423 (frm_posts.pos_activation_date >= %s AND frm_posts.pos_date < frm_posts.pos_activation_date)
424 ) ';
425 $types = array('integer', 'timestamp', 'timestamp');
426 $values = array(1, $threshold_date, $threshold_date);
427
428 $res = $this->ilDB->queryf(
429 $this->createForumPostSql($condition),
430 $types,
431 $values
432 );
433
434 $this->sendNotification(
435 $res,
436 'new posting',
438 );
439 }
440
444 private function sendNotificationForUpdatedPosts(string $threshold_date)
445 {
446 $condition = '
447 frm_posts.pos_cens = %s AND frm_posts.pos_status = %s AND
448 (frm_posts.pos_update > frm_posts.pos_date AND frm_posts.pos_update >= %s) ';
449 $types = array('integer', 'integer', 'timestamp');
450 $values = array(0, 1, $threshold_date);
451
452 $res = $this->ilDB->queryf(
453 $this->createForumPostSql($condition),
454 $types,
455 $values
456 );
457
458 $this->sendNotification(
459 $res,
460 'updated posting',
462 );
463 }
464
468 private function sendNotificationForCensoredPosts(string $threshold_date)
469 {
470 $condition = '
471 frm_posts.pos_cens = %s AND frm_posts.pos_status = %s AND
472 (frm_posts.pos_cens_date >= %s AND frm_posts.pos_cens_date > frm_posts.pos_activation_date ) ';
473 $types = array('integer', 'integer', 'timestamp');
474 $values = array(1, 1, $threshold_date);
475
476 $res = $this->ilDB->queryf(
477 $this->createForumPostSql($condition),
478 $types,
479 $values
480 );
481
482 $this->sendNotification(
483 $res,
484 'censored posting',
486 );
487 }
488
492 private function sendNotificationForUncensoredPosts(string $threshold_date)
493 {
494 $condition = '
495 frm_posts.pos_cens = %s AND frm_posts.pos_status = %s AND
496 (frm_posts.pos_cens_date >= %s AND frm_posts.pos_cens_date > frm_posts.pos_activation_date ) ';
497 $types = array('integer', 'integer', 'timestamp');
498 $values = array(0, 1, $threshold_date);
499
500 $res = $this->ilDB->queryf(
501 $this->createForumPostSql($condition),
502 $types,
503 $values
504 );
505
506 $this->sendNotification(
507 $res,
508 'uncensored posting',
510 );
511 }
512
514 {
515 $res = $this->ilDB->queryF(
517 array('integer'),
518 array(1)
519 );
520
522 $res,
523 'frm_threads_deleted',
524 'deleted threads',
526 );
527 }
528
530 {
531 $res = $this->ilDB->queryF(
533 array('integer'),
534 array(0)
535 );
536
538 $res,
539 'frm_posts_deleted',
540 'deleted postings',
542 );
543 }
544
550 private function sendNotification(\ilPDOStatement $res, string $actionName, int $notificationType)
551 {
552 $numRows = $this->ilDB->numRows($res);
553 if ($numRows > 0) {
554 $this->logger->info(sprintf('Sending notifications for %s "%s" events ...', $numRows, $actionName));
555 $this->sendCronForumNotification($res, $notificationType);
556 $this->logger->info(sprintf('Sent notifications for %s ...', $actionName));
557 }
558
559 $this->keepAlive();
560 }
561
568 private function sendDeleteNotifcations(\ilPDOStatement $res, string $action, string $actionDescription, int $notificationType)
569 {
570 $numRows = $this->ilDB->numRows($res);
571 if ($numRows > 0) {
572 $this->logger->info(sprintf('Sending notifications for %s "%s" events ...', $numRows, $actionDescription));
573 $this->sendCronForumNotification($res, $notificationType);
574 if (count(self::$deleted_ids_cache) > 0) {
575 $this->ilDB->manipulate('DELETE FROM frm_posts_deleted WHERE ' . $this->ilDB->in('deleted_id', self::$deleted_ids_cache, false, 'integer'));
576 $this->logger->info('Deleted obsolete entries of table "' . $action . '" ...');
577 }
578 $this->logger->info(sprintf('Sent notifications for %s ...', $actionDescription));
579 }
580
581 $this->keepAlive();
582 }
583
588 private function createForumPostSql($condition) : string
589 {
590 return '
591 SELECT frm_threads.thr_subject thr_subject,
592 frm_data.top_name top_name,
593 frm_data.top_frm_fk obj_id,
594 frm_notification.user_id user_id,
595 frm_threads.thr_pk thread_id,
596 frm_posts.*
597 FROM frm_notification, frm_posts, frm_threads, frm_data, frm_posts_tree
598 WHERE frm_posts.pos_thr_fk = frm_threads.thr_pk AND ' . $condition . '
599 AND ((frm_threads.thr_top_fk = frm_data.top_pk AND frm_data.top_frm_fk = frm_notification.frm_id)
600 OR (frm_threads.thr_pk = frm_notification.thread_id
601 AND frm_data.top_pk = frm_threads.thr_top_fk) )
602 AND frm_posts.pos_display_user_id != frm_notification.user_id
603 AND frm_posts_tree.pos_fk = frm_posts.pos_pk AND frm_posts_tree.parent_pos != 0
604 ORDER BY frm_posts.pos_date ASC';
605 }
606
610 private function createSelectOfDeletionNotificationsSql() : string
611 {
612 return '
613 SELECT frm_posts_deleted.thread_title thr_subject,
614 frm_posts_deleted.forum_title top_name,
615 frm_posts_deleted.obj_id obj_id,
616 frm_notification.user_id user_id,
617 frm_posts_deleted.pos_display_user_id,
618 frm_posts_deleted.pos_usr_alias,
619 frm_posts_deleted.deleted_id,
620 frm_posts_deleted.post_date pos_date,
621 frm_posts_deleted.post_title pos_subject,
622 frm_posts_deleted.post_message pos_message,
623 frm_posts_deleted.deleted_by
624
625 FROM frm_notification, frm_posts_deleted
626
627 WHERE ( frm_posts_deleted.obj_id = frm_notification.frm_id
628 OR frm_posts_deleted.thread_id = frm_notification.thread_id)
629 AND frm_posts_deleted.pos_display_user_id != frm_notification.user_id
630 AND frm_posts_deleted.is_thread_deleted = %s
631 ORDER BY frm_posts_deleted.post_date ASC';
632 }
633}
$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.
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.
$action
$i
Definition: disco.tpl.php:19
Interface ilDBInterface.
$row
global $ilSetting
Definition: privfeed.php:17
global $DIC
Definition: saml.php:7
$lng
foreach($_POST as $key=> $value) $res
settings()
Definition: settings.php:2
$values