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