ILIAS  trunk Revision v11.0_alpha-3011-gc6b235a2e85
class.ilMembershipCronNotifications.php
Go to the documentation of this file.
1<?php
2
3declare(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"];
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}
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
$txt
Definition: error.php:31
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