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