ILIAS  release_10 Revision v10.1-43-ga1241a92c2f
class.ilMembershipCronNotifications.php
Go to the documentation of this file.
1 <?php
2 
3 declare(strict_types=1);
4 
5 
6 
24 
30 {
31  protected ilLanguage $lng;
32  protected ilDBInterface $db;
33  protected ilLogger $logger;
34  protected ilTree $tree;
35 
36  public function __construct()
37  {
38  global $DIC;
39 
40  $this->lng = $DIC->language();
41  $this->db = $DIC->database();
42  $this->logger = $DIC->logger()->mmbr();
43  $this->tree = $DIC->repositoryTree();
44  }
45 
50 
51  public function getId(): string
52  {
53  return "mem_notification";
54  }
55 
56  public function getTitle(): string
57  {
58  return $this->lng->txt("enable_course_group_notifications");
59  }
60 
61  public function getDescription(): string
62  {
63  return $this->lng->txt("enable_course_group_notifications_desc");
64  }
65 
67  {
68  return CronJobScheduleType::SCHEDULE_TYPE_DAILY;
69  }
70 
71  public function getDefaultScheduleValue(): ?int
72  {
73  return null;
74  }
75 
76  public function hasAutoActivation(): bool
77  {
78  return false;
79  }
80 
81  public function hasFlexibleSchedule(): bool
82  {
83  return true;
84  }
85 
86  public function run(): ilCronJobResult
87  {
88  global $DIC;
89 
90  $this->logger->debug("===Member Notifications=== start");
91 
93  $status_details = null;
94 
95  $setting = new ilSetting("cron");
96  $last_run = $setting->get(get_class($this));
97 
98  // no last run?
99  if (!$last_run) {
100  $last_run = date("Y-m-d H:i:s", strtotime("yesterday"));
101 
102  $status_details = "No previous run found - starting from yesterday.";
103  } // migration: used to be date-only value
104  elseif (strlen($last_run) === 10) {
105  $last_run .= " 00:00:00";
106 
107  $status_details = "Switched from daily runs to open schedule.";
108  }
109 
110  // out-comment for debugging purposes
111  //$last_run = date("Y-m-d H:i:s", strtotime("yesterday"));
112 
113  $last_run_unix = strtotime($last_run);
114 
115  $this->logger->debug("Last run: " . $last_run);
116 
117  $this->data = new ilMembershipCronNotificationsData($last_run_unix, $this->getId());
118 
119  $this->logger->debug("prepare sending mails");
120 
121  // send mails (1 max for each user)
122 
123  $old_lng = $this->lng;
126 
127  $user_news_aggr = $this->data->getAggregatedNews();
128  if (count($user_news_aggr)) {
129  foreach ($user_news_aggr as $user_id => $user_news) {
130  $this->logger->debug("sending mails to user " . $user_id . ", nr news: " . count($user_news));
131  $this->sendMail($user_id, $user_news, $last_run);
132  $DIC->cron()->manager()->ping($this->getId());
133  }
134  // mails were sent - set cron job status accordingly
135  $status = ilCronJobResult::STATUS_OK;
136  }
137 
139 
140  $this->logger->debug("save run");
141 
142  // save last run
143  $setting->set(get_class($this), date("Y-m-d H:i:s"));
144  $result = new ilCronJobResult();
145  $result->setStatus($status);
146  if ($status_details) {
147  $result->setMessage($status_details);
148  }
149  $this->logger->debug("===Member Notifications=== done");
150  return $result;
151  }
152 
160  protected function parseNewsItem(
161  int $a_parent_ref_id,
162  array &$a_filter_map,
163  array $a_item,
164  $a_is_sub = false,
165  int $a_user_id = 0
166  ): string {
167  global $DIC;
168 
169  $obj_definiton = $DIC["objDefinition"];
170  $lng = ilLanguageFactory::_getLanguageOfUser($a_user_id);
171 
172  $lng->loadLanguageModule("news");
173  $wrong_parent = (array_key_exists($a_item["id"], $a_filter_map) &&
174  $a_parent_ref_id != $a_filter_map[$a_item["id"]]);
175 
176  // #18223
177  if ($wrong_parent) {
178  return '';
179  }
180 
181  $item_obj_title = trim(ilObject::_lookupTitle((int) $a_item["context_obj_id"]));
182  $item_obj_type = $a_item["context_obj_type"];
183 
184  // sub-items
185  $sub = null;
186  if ($a_item["aggregation"] ?? false) {
187  $do_sub = true;
188  if ($item_obj_type === "file" &&
189  count($a_item["aggregation"]) === 1) {
190  $do_sub = false;
191  }
192  if ($do_sub) {
193  $sub = array();
194  foreach ($a_item["aggregation"] as $subitem) {
195  $sub_res = $this->parseNewsItem($a_parent_ref_id, $a_filter_map, $subitem, true, $a_user_id);
196  if ($sub_res) {
197  $sub[md5($sub_res)] = $sub_res;
198  }
199  }
200  }
201  }
202 
203  if (!$a_is_sub) {
205  $a_item["context_obj_type"],
206  (string) $a_item["title"],
207  (bool) (int) $a_item["content_is_lang_var"],
208  (int) ($a_item["agg_ref_id"] ?? 0),
209  $a_item["aggregation"] ?? [],
210  $lng
211  );
212  } else {
214  $a_item["context_obj_type"],
215  (string) $a_item["title"],
216  (bool) (int) $a_item["content_is_lang_var"],
217  0,
218  [],
219  $lng
220  );
221  }
222 
224  $a_item["context_obj_type"],
225  (string) $a_item["content"],
226  (bool) (int) $a_item["content_text_is_lang_var"],
227  $lng
228  );
229 
230  $title = trim($title);
231 
232  // #18067 / #18186
233  $content = ilStr::shortenTextExtended(trim(strip_tags($content)), 200, true);
234 
235  $res = "";
236  switch ($item_obj_type) {
237  case "frm":
238  if (!$a_is_sub) {
239  $res = $lng->txt("obj_" . $item_obj_type) .
240  ' "' . $item_obj_title . '": ' . $title;
241  } else {
242  $res .= '"' . $title . '": "' . $content . '"';
243  }
244  break;
245 
246  case "file":
247  if (!isset($a_item["aggregation"]) ||
248  count($a_item["aggregation"]) === 1) {
249  $res = $lng->txt("obj_" . $item_obj_type) .
250  ' "' . $item_obj_title . '" - ' . $title;
251  } else {
252  // if files were removed from aggregation update summary count
253  $title = str_replace(
254  " " . count($a_item["aggregation"]) . " ",
255  " " . count($sub) . " ",
256  $title
257  );
258  $res = $title;
259  }
260  break;
261 
262  default:
263  $type_txt = ($obj_definiton->isPlugin($item_obj_type))
264  ? ilObjectPlugin::lookupTxtById($item_obj_type, "obj_" . $item_obj_type)
265  : $lng->txt("obj_" . $item_obj_type);
266  $res = $type_txt .
267  ' "' . $item_obj_title . '"';
268  if ($title) {
269  $res .= ': "' . $title . '"';
270  }
271  if ($content) {
272  $res .= ' - ' . $content;
273  }
274  break;
275  }
276 
277  // comments
278  $comments = $this->data->getComments((int) $a_item["id"], $a_user_id);
279  if (count($comments) > 0) {
280  $res .= "\n" . $lng->txt("news_new_comments") . " (" . count($comments) . ")";
281  }
283  foreach ($comments as $c) {
284  $res .= "\n* " .
286  new ilDateTime($c->getCreationDate(), IL_CAL_DATETIME)
287  ) . ": " .
288  ilStr::shortenTextExtended(trim(strip_tags($c->getText())), 60, true, true);
289  }
290 
291  // likes
292  $likes = $this->data->getLikes((int) $a_item["id"], $a_user_id);
293  if (count($likes) > 0) {
294  $res .= "\n" . $lng->txt("news_new_reactions") . " (" . count($likes) . ")";
295  }
296  foreach ($likes as $l) {
297  $res .= "\n* " .
299  new ilDateTime($l["timestamp"], IL_CAL_DATETIME)
300  ) . ": " .
301  ilLikeGUI::getExpressionText((int) $l["expression"]);
302  }
303 
304  $res = $a_is_sub
305  ? "- " . $res
306  : "# " . $res;
307 
308  if (is_array($sub) && count($sub)) {
309  $res .= "\n" . implode("\n", $sub);
310  }
311  // see 29967
312  $res = str_replace("<br />", " ", $res);
313  $res = strip_tags($res);
314 
315  return trim($res);
316  }
317 
321  protected function filterDuplicateItems(array $a_objects): array
322  {
323  $parent_map = $news_map = $parsed_map = array();
324 
325  // gather news ref ids and news parent ref ids
326  foreach ($a_objects as $parent_ref_id => $news) {
327  foreach ($news as $item) {
328  $news_map[$item["id"]] = (int) ($item["ref_id"] ?? 0);
329  $parent_map[$item["id"]][$parent_ref_id] = $parent_ref_id;
330 
331  if ($item["aggregation"] ?? false) {
332  foreach ($item["aggregation"] as $subitem) {
333  $news_map[$subitem["id"]] = (int) ($subitem["ref_id"] ?? 0);
334  $parent_map[$subitem["id"]][$parent_ref_id] = $parent_ref_id;
335  }
336  }
337  }
338  }
339  // if news has multiple parents find "lowest" parent in path
340  foreach ($parent_map as $news_id => $parents) {
341  if (count($parents) > 1 && $news_map[$news_id] > 0) {
342  $path = $this->tree->getPathId((int) $news_map[$news_id]);
343  $lookup = array_flip($path);
344 
345  $level = 0;
346  foreach ($parents as $parent_ref_id) {
347  $level = max($level, (int) ($lookup[$parent_ref_id] ?? 0));
348  }
349 
350  $parsed_map[$news_id] = $path[$level];
351  }
352  }
353 
354  return $parsed_map;
355  }
356 
361  protected function sendMail(int $a_user_id, array $a_objects, string $a_last_run): void
362  {
363  global $DIC;
364 
365  $ilClientIniFile = $DIC['ilClientIniFile'];
366 
367  $ntf = new ilSystemNotification();
368  $ntf->setLangModules(array("crs", "news"));
369  // no single object anymore
370  // $ntf->setRefId($a_ref_id);
371  // $ntf->setGotoLangId('url');
372  // $ntf->setSubjectLangId('crs_subject_course_group_notification');
373 
374  // user specific language
375  $lng = $ntf->getUserLanguage($a_user_id);
376  $filter_map = $this->filterDuplicateItems($a_objects);
377 
378  $tmp = array();
379 
380  foreach ($a_objects as $parent_ref_id => $items) {
381  $parent = array();
382 
383  // path
384  $path = array();
385  foreach ($this->tree->getPathId($parent_ref_id) as $node) {
386  $path[] = $node;
387  }
388  $path = implode("-", $path);
389 
390  $parent_obj_id = ilObject::_lookupObjId((int) $parent_ref_id);
391  $parent_type = ilObject::_lookupType($parent_obj_id);
392 
393  $parent["title"] = $lng->txt("obj_" . $parent_type) . ' "' . ilObject::_lookupTitle($parent_obj_id) . '"';
394  $parent["url"] = " " . $lng->txt("crs_course_group_notification_link") . " " . ilLink::_getStaticLink($parent_ref_id);
395 
396  $this->logger->debug("ref id: " . $parent_ref_id . ", items: " . count($items));
397 
398  // news summary
399  $parsed = array();
400  if (is_array($items)) {
401  foreach ($items as $news_item) {
402  if ($news_item === null) {
403  continue;
404  }
405 
406  // # Type "<Object Title>": "<News Title>" - News Text
407  $parsed_item = $this->parseNewsItem($parent_ref_id, $filter_map, $news_item, false, $a_user_id);
408  if ($parsed_item) {
409  $parsed[md5($parsed_item)] = $parsed_item;
410  }
411  }
412  }
413 
414  // any news?
415  if (count($parsed)) {
416  $parent["news"] = implode("\n\n", $parsed);
417  $tmp[$path] = $parent;
418  }
419  }
420 
421  // any objects?
422  if (!count($tmp)) {
423  $this->logger->debug("returning");
424  return;
425  }
426 
427  ksort($tmp);
428  $counter = 0;
429  $obj_index = array();
430  $txt = "";
431  foreach ($tmp as $item) {
432  $counter++;
433 
434  $txt .= "(" . $counter . ") " . $item["title"] . "\n" .
435  $item["url"] . "\n\n" .
436  $item["news"] . "\n\n";
437 
438  $obj_index[] = "(" . $counter . ") " . $item["title"];
439  }
440 
441  $ntf->setIntroductionLangId("crs_intro_course_group_notification_for");
442 
443  // index
444  $period = sprintf(
445  $lng->txt("crs_intro_course_group_notification_index"),
448  );
449  $ntf->addAdditionalInfo(
450  $period,
451  trim(implode("\n", $obj_index)),
452  true,
453  true
454  );
455 
456  // object list
457  $ntf->addAdditionalInfo(
458  "",
459  trim($txt),
460  true
461  );
462 
463  // :TODO: does it make sense to add client to subject?
464  $client = $ilClientIniFile->readVariable('client', 'name');
465  $subject = sprintf($lng->txt("crs_subject_course_group_notification"), $client);
466 
467  $mail_content = $ntf->composeAndGetMessage($a_user_id, null, "read", true);
468  $this->logger->debug("sending mail content: " . $mail_content);
469 
470  // #10044
471  $mail = new ilMail(ANONYMOUS_USER_ID);
472  $mail->enqueue(
473  ilObjUser::_lookupLogin($a_user_id),
474  (string) null,
475  (string) null,
476  $subject,
477  $mail_content,
478  []
479  );
480  }
481 
482  public function addToExternalSettingsForm(int $a_form_id, array &$a_fields, bool $a_is_active): void
483  {
484  switch ($a_form_id) {
487  $a_fields["enable_course_group_notifications"] = $a_is_active ?
488  $this->lng->txt("enabled") :
489  $this->lng->txt("disabled");
490  break;
491  }
492  }
493 
494  public function activationWasToggled(ilDBInterface $db, ilSetting $setting, bool $a_currently_active): void
495  {
496  $setting->set("crsgrp_ntf", (string) ((int) $a_currently_active));
497  }
498 }
static array static setUseRelativeDates(bool $a_status)
set use relative dates
$res
Definition: ltiservices.php:69
static determineNewsContent(string $a_context_obj_type, string $a_content, bool $a_is_lang_var, ?ilLanguage $lng=null)
Determine new content.
static formatDate(ilDateTime $date, bool $a_skip_day=false, bool $a_include_wd=false, bool $include_seconds=false, ilObjUser $user=null,)
const IL_CAL_DATETIME
const ANONYMOUS_USER_ID
Definition: constants.php:27
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
txt(string $a_topic, string $a_default_lang_fallback_mod="")
gets the text for a given topic if the topic is not in the list, the topic itself with "-" will be re...
getUserLanguage()
Return language of user.
set(string $a_key, string $a_val)
addToExternalSettingsForm(int $a_form_id, array &$a_fields, bool $a_is_active)
static getExpressionText(int $a_const)
Get expression text for const.
Manage data for ilMembershipCronNotifications cron job.
activationWasToggled(ilDBInterface $db, ilSetting $setting, bool $a_currently_active)
loadLanguageModule(string $a_module)
Load language module.
filterDuplicateItems(array $a_objects)
Filter duplicate news items from structure*.
const IL_CAL_UNIX
$c
Definition: deliver.php:9
$path
Definition: ltiservices.php:30
static _lookupObjId(int $ref_id)
$client
static getNamePresentation( $a_user_id, bool $a_user_image=false, bool $a_profile_link=false, string $a_profile_back_link='', bool $a_force_first_lastname=false, bool $a_omit_login=false, bool $a_sortable=true, bool $a_return_data_array=false, $a_ctrl_path='ilpublicuserprofilegui')
Default behaviour is:
static _lookupTitle(int $obj_id)
static _getLanguageOfUser(int $a_usr_id)
Get language object of user.
final const STATUS_NO_ACTION
static determineNewsTitle(string $a_context_obj_type, string $a_title, bool $a_content_is_lang_var, int $a_agg_ref_id=0, array $a_aggregation=[], ?ilLanguage $lng=null)
Determine title for news item entry.
global $DIC
Definition: shib_login.php:25
$comments
$txt
Definition: error.php:30
static lookupTxtById(string $plugin_id, string $lang_var)
static shortenTextExtended(string $a_str, int $a_len, bool $a_dots=false, bool $a_next_blank=false, bool $a_keep_extension=false)
sendMail(int $a_user_id, array $a_objects, string $a_last_run)
Send news mail for 1 user and n objects.
static _lookupType(int $id, bool $reference=false)
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
static _lookupLogin(int $a_user_id)