ILIAS  trunk Revision v12.0_alpha-1227-g7ff6d300864
class.ilMembershipCronNotifications.php
Go to the documentation of this file.
1<?php
2
19declare(strict_types=1);
20
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 JobScheduleType::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(): JobResult
87 {
88 global $DIC;
89
90 $this->logger->debug("===Member Notifications=== start");
91
92 $status = JobResult::STATUS_NO_ACTION;
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 = JobResult::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 JobResult();
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"];
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}
const IL_CAL_UNIX
const IL_CAL_DATETIME
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, ?ilObjUser $user=null,)
@classDescription Date and time handling
static _getLanguageOfUser(int $a_usr_id)
Get language object of user.
language handling
loadLanguageModule(string $a_module)
Load language module.
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 getExpressionText(int $a_const)
Get expression text for const.
Component logger with individual log levels by component id.
Manage data for ilMembershipCronNotifications cron job.
addToExternalSettingsForm(int $a_form_id, array &$a_fields, bool $a_is_active)
activationWasToggled(ilDBInterface $db, ilSetting $setting, bool $a_currently_active)
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=[], ?ilLanguage $lng=null)
Determine title for news item entry.
static determineNewsContent(string $a_context_obj_type, string $a_content, bool $a_is_lang_var, ?ilLanguage $lng=null)
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)
ILIAS Setting Class.
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...
Tree class data representation in hierachical trees using the Nested Set Model with Gaps by Joe Celco...
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=null)
Default behaviour is:
const ANONYMOUS_USER_ID
Definition: constants.php:27
$c
Definition: deliver.php:25
$client
Interface ilDBInterface.
$path
Definition: ltiservices.php:30
$res
Definition: ltiservices.php:69
global $lng
Definition: privfeed.php:31
global $DIC
Definition: shib_login.php:26
$counter
$comments