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  {
96  return self::SCHEDULE_TYPE_IN_HOURS;
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) {
189  $status = ilCronJobResult::STATUS_OK;
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 
521  $this->sendDeleteNotifcations(
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 
537  $this->sendDeleteNotifcations(
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 }
getFirstAccessibleRefIdBUserAndObjId($a_user_id, $a_obj_id)
settings()
Definition: settings.php:2
$result
This class represents a property form user interface.
$action
global $DIC
Definition: saml.php:7
sendCronForumNotification($res, $notification_type)
Class ilPDOStatement is a Wrapper Class for PDOStatement.
Cron job application base class.
Class ilForumCronNotificationDataProvider.
sendNotification(\ilPDOStatement $res, string $actionName, int $notificationType)
sendNotificationForUncensoredPosts(string $threshold_date)
addItem($a_item)
Add Item (Property, SectionHeader).
static _getAllReferences($a_id)
get all reference ids of object
addCustomSettingsToForm(ilPropertyFormGUI $a_form)
foreach($_POST as $key=> $value) $res
sendNotificationForCensoredPosts(string $threshold_date)
$lng
This class represents a number property in a property form.
$values
Class ilForumNotificationCache.
sendNotificationForNewPosts(string $threshold_date)
saveCustomSettings(ilPropertyFormGUI $a_form)
getInput($a_post_var, $ensureValidation=true)
Returns the value of a HTTP-POST variable, identified by the passed id.
addToExternalSettingsForm($a_form_id, array &$a_fields, $a_is_active)
$row
setSize($a_size)
Set Size.
static ping($a_job_id)
Keep cron job alive.
global $ilSetting
Definition: privfeed.php:17
$i
Definition: disco.tpl.php:19
sendNotificationForUpdatedPosts(string $threshold_date)
sendDeleteNotifcations(\ilPDOStatement $res, string $action, string $actionDescription, int $notificationType)
Cron job result data container.
__construct(\ilDBInterface $database=null, \ilForumNotificationCache $notificationCache=null)