ILIAS  trunk Revision v11.0_alpha-3011-gc6b235a2e85
class.ilMembershipNotifications.php
Go to the documentation of this file.
1<?php
2
19declare(strict_types=1);
20
27{
28 protected const VALUE_OFF = 0;
29 protected const VALUE_ON = 1;
30 protected const VALUE_BLOCKED = 2;
31 protected const MODE_SELF = 1;
32 protected const MODE_ALL = 2;
33 protected const MODE_ALL_BLOCKED = 3;
34 protected const MODE_CUSTOM = 4;
35
36 protected int $ref_id;
37 protected int $mode;
39 protected array $custom;
40 protected ?ilParticipants $participants = null;
42 protected ilDBInterface $db;
43 protected ilTree $tree;
44 protected ilObjUser $user;
45
46 public function __construct(int $a_ref_id)
47 {
48 global $DIC;
49
50 $this->db = $DIC->database();
51 $this->ref_id = $a_ref_id;
52 $this->custom = [];
53 $this->setting = $DIC->settings();
54 $this->tree = $DIC->repositoryTree();
55 $this->user = $DIC->user();
56
57 $this->setMode(self::MODE_SELF);
58 if ($this->ref_id !== 0) {
59 $this->read();
60 }
61 }
62
63 public static function isActive(): bool
64 {
65 global $DIC;
66
67 $ilSetting = $DIC['ilSetting'];
68 if (!$ilSetting->get("block_activated_news") || !$ilSetting->get("crsgrp_ntf")) {
69 return false;
70 }
71 return true;
72 }
73
74 public static function isActiveForRefId(int $ref_id): bool
75 {
76 if (!self::isActive()) {
77 return false;
78 }
79 // see #31471, #30687, and ilNewsItem::getNewsForRefId
81 if (
82 !ilContainer::_lookupContainerSetting($obj_id, 'cont_use_news', "1") ||
83 (
84 !ilContainer::_lookupContainerSetting($obj_id, 'cont_show_news', "1") &&
85 !ilContainer::_lookupContainerSetting($obj_id, 'news_timeline')
86 )
87 ) {
88 return false;
89 }
90 return true;
91 }
92
93 protected function read(): void
94 {
95 $set = $this->db->query("SELECT nmode mode" .
96 " FROM member_noti" .
97 " WHERE ref_id = " . $this->db->quote($this->ref_id, "integer"));
98 if ($this->db->numRows($set)) {
99 $row = $this->db->fetchAssoc($set);
100 $this->setMode((int) $row["mode"]);
101
102 if ((int) $row["mode"] === self::MODE_CUSTOM) {
103 $set = $this->db->query("SELECT *" .
104 " FROM member_noti_user" .
105 " WHERE ref_id = " . $this->db->quote($this->ref_id, "integer"));
106 while ($row = $this->db->fetchAssoc($set)) {
107 $this->custom[(int) $row["user_id"]] = (int) $row["status"];
108 }
109 }
110 }
111 }
112
113 public function getMode(): int
114 {
115 return $this->mode;
116 }
117
118 protected function setMode(int $a_value): void
119 {
120 if ($this->isValidMode($a_value)) {
121 $this->mode = $a_value;
122 }
123 }
124
125 protected function isValidMode(int $a_value): bool
126 {
127 $valid = array(
128 self::MODE_SELF
129 ,
130 self::MODE_ALL
131 ,
132 self::MODE_ALL_BLOCKED
133 );
134 return in_array($a_value, $valid);
135 }
136
137 public function switchMode(int $a_new_mode): void
138 {
139 if (!$this->ref_id) {
140 return;
141 }
142
143 if (
144 $this->mode &&
145 $this->mode !== $a_new_mode &&
146 $this->isValidMode($a_new_mode)
147 ) {
148 $this->db->manipulate("DELETE FROM member_noti" .
149 " WHERE ref_id = " . $this->db->quote($this->ref_id, "integer"));
150
151 // no custom data
152 if ($a_new_mode !== self::MODE_CUSTOM) {
153 $this->db->manipulate("DELETE FROM member_noti_user" .
154 " WHERE ref_id = " . $this->db->quote($this->ref_id, "integer"));
155 }
156
157 // mode self is default
158 if ($a_new_mode !== self::MODE_SELF) {
159 $this->db->insert("member_noti", array(
160 "ref_id" => array("integer", $this->ref_id),
161 "nmode" => array("integer", $a_new_mode)
162 ));
163 }
164
165 // remove all user settings (all active is preset, optional opt out)
166 if ($a_new_mode === self::MODE_ALL) {
167 $this->db->manipulate("DELETE FROM usr_pref" .
168 " WHERE " . $this->db->like("keyword", "text", "grpcrs_ntf_" . $this->ref_id));
169 }
170 }
171 $this->setMode($a_new_mode);
172 }
173
174 protected function getParticipants(): ?\ilParticipants
175 {
176 if ($this->participants === null) {
177 $grp_ref_id = $this->tree->checkForParentType($this->ref_id, "grp");
178 if ($grp_ref_id) {
179 $this->participants = ilGroupParticipants::_getInstanceByObjId(ilObject::_lookupObjId($grp_ref_id));
180 }
181
182 if (!$this->participants) {
183 $crs_ref_id = $this->tree->checkForParentType($this->ref_id, "crs");
184 if ($crs_ref_id) {
185 $this->participants = ilCourseParticipants::_getInstanceByObjId(ilObject::_lookupObjId($crs_ref_id));
186 }
187 }
188 }
189 return $this->participants;
190 }
191
192 public function getActiveUsers(): array
193 {
194 $users = $all = array();
195 $part_obj = $this->getParticipants();
196 if ($part_obj) {
197 $all = $part_obj->getParticipants();
198 }
199 if (!$all) {
200 return [];
201 }
202
203 switch ($this->getMode()) {
204 // users decide themselves
205 case self::MODE_SELF:
206 $set = $this->db->query("SELECT usr_id" .
207 " FROM usr_pref" .
208 " WHERE keyword = " . $this->db->quote("grpcrs_ntf_" . $this->ref_id, "text") .
209 " AND value = " . $this->db->quote(self::VALUE_ON, "text"));
210 while ($row = $this->db->fetchAssoc($set)) {
211 $users[] = (int) $row["usr_id"];
212 }
213 break;
214
215 // all members, mind opt-out
216 case self::MODE_ALL:
217 // users who did opt-out
218 $inactive = array();
219 $set = $this->db->query("SELECT usr_id" .
220 " FROM usr_pref" .
221 " WHERE keyword = " . $this->db->quote("grpcrs_ntf_" . $this->ref_id, "text") .
222 " AND value = " . $this->db->quote(self::VALUE_OFF, "text"));
223 while ($row = $this->db->fetchAssoc($set)) {
224 $inactive[] = (int) $row["usr_id"];
225 }
226 $users = array_diff($all, $inactive);
227 break;
228
229 // all members, no opt-out
231 $users = $all;
232 break;
233
234 // custom settings
236 foreach ($this->custom as $user_id => $status) {
237 if ($status !== self::VALUE_OFF) {
238 $users[] = $user_id;
239 }
240 }
241 break;
242 }
243 return array_intersect($all, $users);
244 }
245
246 public function activateUser(?int $a_user_id = null): bool
247 {
248 return $this->toggleUser(true, $a_user_id);
249 }
250
251 public function deactivateUser(?int $a_user_id = null): bool
252 {
253 return $this->toggleUser(false, $a_user_id);
254 }
255
256 protected function getUser(?int $a_user_id = null): ?ilObjUser
257 {
258 if (
259 $a_user_id === null ||
260 $a_user_id === $this->user->getId()
261 ) {
262 $user = $this->user;
263 } else {
264 $user = new ilObjUser($a_user_id);
265 }
266
267 if (
268 $user->getId() &&
270 ) {
271 return $user;
272 }
273 return null;
274 }
275
276 protected function toggleUser(bool $a_status, ?int $a_user_id = null): bool
277 {
278 if (!self::isActive()) {
279 return false;
280 }
281
282 switch ($this->getMode()) {
283 case self::MODE_ALL:
284 case self::MODE_SELF:
285 // current user!
286 $user = $this->getUser();
287 if ($user instanceof ilObjUser) {
288 // blocked value not supported in user pref!
289 if ($a_status === true) {
290 $string_value = '1';
291 } else {
292 $string_value = '0';
293 }
294
295 $user->setPref("grpcrs_ntf_" . $this->ref_id, $string_value);
296 $user->writePrefs();
297 return true;
298 }
299 break;
300
302 $user = $this->getUser($a_user_id);
303 if ($user instanceof ilObjUser) {
304 $user_id = $user->getId();
305
306 // did status change at all?
307 if (!array_key_exists($user_id, $this->custom) || $this->custom[$user_id] !== (int) $a_status) {
308 $this->custom[$user_id] = (int) $a_status;
309
310 $this->db->replace(
311 "member_noti_user",
312 array(
313 "ref_id" => array("integer", $this->ref_id),
314 "user_id" => array("integer", $user_id),
315 ),
316 array(
317 "status" => array("integer", (int) $a_status)
318 )
319 );
320 }
321 return true;
322 }
323 break;
324
326 // no individual settings
327 break;
328 }
329 return false;
330 }
331
332 public function isCurrentUserActive(): bool
333 {
334 return in_array($this->user->getId(), $this->getActiveUsers());
335 }
336
337 public function canCurrentUserEdit(): bool
338 {
339 $user_id = $this->user->getId();
340 if ($user_id === ANONYMOUS_USER_ID) {
341 return false;
342 }
343
344 switch ($this->getMode()) {
345 case self::MODE_SELF:
346 case self::MODE_ALL:
347 return true;
348
350 return false;
351
353 return !(array_key_exists($user_id, $this->custom) && $this->custom[$user_id] === self::VALUE_BLOCKED);
354 }
355 return false;
356 }
357
361 public static function getActiveUsersforAllObjects(): array
362 {
363 global $DIC;
364
365 $ilDB = $DIC->database();
366 $tree = $DIC->repositoryTree();
367 $log = $DIC->logger()->mmbr();
368
369 $res = [];
370 if (self::isActive()) {
371 $objects = [];
372
373 // user-preference data (MODE_SELF)
374 $log->debug("read usr_pref");
375 $set = $ilDB->query("SELECT DISTINCT(keyword) keyword" .
376 " FROM usr_pref" .
377 " WHERE " . $ilDB->like("keyword", "text", "grpcrs_ntf_%") .
378 " AND value = " . $ilDB->quote("1", "text"));
379 while ($row = $ilDB->fetchAssoc($set)) {
380 $ref_id = substr($row["keyword"], 11);
381 $objects[(int) $ref_id] = (int) $ref_id;
382 }
383
384 // all other modes
385 $log->debug("read member_noti");
386 $set = $ilDB->query("SELECT ref_id" .
387 " FROM member_noti");
388 while ($row = $ilDB->fetchAssoc($set)) {
389 $objects[(int) $row["ref_id"]] = (int) $row["ref_id"];
390 }
391
392 // this might be slow but it is to be used in CRON JOB ONLY!
393 foreach (array_unique($objects) as $ref_id) {
394 // :TODO: enough checking?
395 if (!$tree->isDeleted($ref_id)) {
396 $log->debug("get active users");
397 $noti = new self($ref_id);
398 $active = $noti->getActiveUsers();
399 if (count($active)) {
400 $res[$ref_id] = $active;
401 }
402 }
403 }
404 }
405 return $res;
406 }
407
411 public static function addToSettingsForm(
412 int $a_ref_id,
413 ?ilPropertyFormGUI $a_form = null,
414 ?ilFormPropertyGUI $a_input = null
415 ): void {
416 global $DIC;
417
418 $lng = $DIC->language();
419
420 if (self::isActive() &&
421 $a_ref_id) {
422 $lng->loadLanguageModule("membership");
423 $noti = new self($a_ref_id);
424
425 $force_noti = new ilRadioGroupInputGUI($lng->txt("mem_force_notification"), "force_noti");
426 $force_noti->setRequired(true);
427 if ($a_form) {
428 $a_form->addItem($force_noti);
429 } else {
430 $a_input->addSubItem($force_noti);
431 }
432
433 if ($noti->isValidMode(self::MODE_SELF)) {
434 $option = new ilRadioOption($lng->txt("mem_force_notification_mode_self"), (string) self::MODE_SELF);
435 $force_noti->addOption($option);
436 }
437 if ($noti->isValidMode(self::MODE_ALL_BLOCKED)) {
438 $option = new ilRadioOption(
439 $lng->txt("mem_force_notification_mode_blocked"),
440 (string) self::MODE_ALL_BLOCKED
441 );
442 $force_noti->addOption($option);
443
444 if ($noti->isValidMode(self::MODE_ALL)) {
445 $changeable = new ilCheckboxInputGUI(
446 $lng->txt("mem_force_notification_mode_all_sub_blocked"),
447 "force_noti_allblk"
448 );
449 $option->addSubItem($changeable);
450 }
451 } elseif ($noti->isValidMode(self::MODE_ALL)) {
452 $option = new ilRadioOption($lng->txt("mem_force_notification_mode_all"), (string) self::MODE_ALL);
453 $force_noti->addOption($option);
454 }
455
456 // set current mode
457 $current_mode = $noti->getMode();
458 $has_changeable_cb = ($noti->isValidMode(self::MODE_ALL_BLOCKED) &&
459 $noti->isValidMode(self::MODE_ALL));
460 if (!$has_changeable_cb) {
461 $force_noti->setValue((string) $current_mode);
462 } else {
463 switch ($current_mode) {
464 case self::MODE_SELF:
465 $force_noti->setValue((string) $current_mode);
467 $changeable->setChecked(true); // checked as "default" on selection of parent
468 break;
469
471 $force_noti->setValue((string) $current_mode);
472 break;
473
474 case self::MODE_ALL:
475 $force_noti->setValue((string) self::MODE_ALL_BLOCKED);
477 $changeable->setChecked(true);
478 break;
479 }
480 }
481 }
482 }
483
484 public static function importFromForm(int $a_ref_id, ?ilPropertyFormGUI $a_form = null): void
485 {
486 global $DIC;
487
488 $http = $DIC->http();
489 $refinery = $DIC->refinery();
490
491 if ($a_ref_id && self::isActive()) {
492 $noti = new self($a_ref_id);
493 $has_changeable_cb = ($noti->isValidMode(self::MODE_ALL_BLOCKED) && $noti->isValidMode(self::MODE_ALL));
494 $changeable = null;
495 if (!$a_form) {
496 $mode = 0;
497 if ($http->wrapper()->post()->has('force_noti')) {
498 $mode = $http->wrapper()->post()->retrieve(
499 'force_noti',
500 $refinery->kindlyTo()->int()
501 );
502 }
503
504 if ($has_changeable_cb) {
505 $changeable = 0;
506 if ($http->wrapper()->post()->has('force_noti_allblk')) {
507 $changeable = $http->wrapper()->post()->retrieve(
508 'force_noti_allblk',
509 $refinery->kindlyTo()->int()
510 );
511 }
512 }
513 } else {
514 $mode = (int) $a_form->getInput("force_noti");
515 if ($has_changeable_cb) {
516 $changeable = $a_form->getInput("force_noti_allblk");
517 }
518 }
519 // checkbox (all) is subitem of all_blocked
520 if ($changeable && $mode === self::MODE_ALL_BLOCKED) {
521 $mode = self::MODE_ALL;
522 }
523 $noti->switchMode($mode);
524 }
525 }
526
527 public function cloneSettings(int $new_ref_id): void
528 {
529 $set = $this->db->queryF(
530 "SELECT * FROM member_noti " .
531 " WHERE ref_id = %s ",
532 array("integer"),
533 array($this->ref_id)
534 );
535 while ($rec = $this->db->fetchAssoc($set)) {
536 $this->db->insert("member_noti", array(
537 "ref_id" => array("integer", $new_ref_id),
538 "nmode" => array("integer", $rec["nmode"])
539 ));
540 }
541 }
542}
This class represents a checkbox property in a property form.
static _lookupContainerSetting(int $a_id, string $a_keyword, ?string $a_default_value=null)
static _getInstanceByObjId(int $a_obj_id)
This class represents a property in a property form.
static _getInstanceByObjId(int $a_obj_id)
Get singleton instance.
Membership notification settings.
static importFromForm(int $a_ref_id, ?ilPropertyFormGUI $a_form=null)
static addToSettingsForm(int $a_ref_id, ?ilPropertyFormGUI $a_form=null, ?ilFormPropertyGUI $a_input=null)
Add notification settings to form.
toggleUser(bool $a_status, ?int $a_user_id=null)
static getActiveUsersforAllObjects()
Get active notifications for all objects.
User class.
setPref(string $a_keyword, ?string $a_value)
static _lookupObjId(int $ref_id)
Base class for course and group participants.
This class represents a property form user interface.
This class represents a property in a property form.
This class represents an option in a radio group.
ILIAS Setting Class.
Tree class data representation in hierachical trees using the Nested Set Model with Gaps by Joe Celco...
isDeleted(int $a_node_id)
This is a wrapper for isSaved() with a more useful name.
const ANONYMOUS_USER_ID
Definition: constants.php:27
$http
Definition: deliver.php:30
$valid
Interface ilDBInterface.
$log
Definition: ltiresult.php:34
$res
Definition: ltiservices.php:69
global $lng
Definition: privfeed.php:31
global $ilSetting
Definition: privfeed.php:31
global $DIC
Definition: shib_login.php:26