ILIAS  release_8 Revision v8.24
class.ilMembershipCronNotifications.php
Go to the documentation of this file.
1<?php
2
3declare(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 {
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
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}
const IL_CAL_UNIX
const IL_CAL_DATETIME
const SCHEDULE_TYPE_DAILY
@depracated This will be replaced with an ENUM in ILIAS 9
static setUseRelativeDates(bool $a_status)
set use relative dates
static formatDate(ilDateTime $date, bool $a_skip_day=false, bool $a_include_wd=false, bool $include_seconds=false)
@classDescription Date and time handling
language handling
static getExpressionText(int $a_const)
Get expression text for const.
Component logger with individual log levels by component id.
Manage data for ilMembershipCronNotifications cron job.
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
addToExternalSettingsForm(int $a_form_id, array &$a_fields, bool $a_is_active)
activationWasToggled(ilDBInterface $db, ilSetting $setting, bool $a_currently_active)
Important: This method is (also) called from the setup process, where the constructor of an ilCronJob...
filterDuplicateItems(array $a_objects)
Filter duplicate news items from structure*.
sendMail(int $a_user_id, array $a_objects, string $a_last_run)
Send news mail for 1 user and n objects.
hasAutoActivation()
Is to be activated on "installation", does only work for ILIAS core cron jobs.
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.
static determineNewsContent(string $a_context_obj_type, string $a_content, bool $a_is_lang_var)
Determine new content.
static _lookupLogin(int $a_user_id)
static lookupTxtById(string $plugin_id, string $lang_var)
static _lookupType(int $id, bool $reference=false)
static _lookupObjId(int $ref_id)
static _lookupTitle(int $obj_id)
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
set(string $a_key, string $a_val)
static shortenTextExtended(string $a_str, int $a_len, bool $a_dots=false, bool $a_next_blank=false, bool $a_keep_extension=false)
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
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:
$c
Definition: cli.php:38
const ANONYMOUS_USER_ID
Definition: constants.php:27
$client
$txt
Definition: error.php:13
global $DIC
Definition: feed.php:28
Interface ilDBInterface.
$path
Definition: ltiservices.php:32
$res
Definition: ltiservices.php:69
$lng
$comments