Proyectos de Subversion Moodle

Rev

| Ultima modificación | Ver Log |

Rev Autor Línea Nro. Línea
1 efrain 1
<?php
2
// This file is part of Moodle - http://moodle.org/
3
//
4
// Moodle is free software: you can redistribute it and/or modify
5
// it under the terms of the GNU General Public License as published by
6
// the Free Software Foundation, either version 3 of the License, or
7
// (at your option) any later version.
8
//
9
// Moodle is distributed in the hope that it will be useful,
10
// but WITHOUT ANY WARRANTY; without even the implied warranty of
11
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12
// GNU General Public License for more details.
13
//
14
// You should have received a copy of the GNU General Public License
15
// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
16
 
17
/**
18
 * file contains the general utility class for this tool
19
 *
20
 * File         util.php
21
 * Encoding     UTF-8
22
 * @copyright   Sebsoft.nl
23
 * @license     http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
24
 */
25
 
26
namespace tool_usersuspension;
27
 
28
use tool_usersuspension\statustable;
29
 
30
/**
31
 * tool_usersuspension\util
32
 *
33
 * @package     tool_usersuspension
34
 *
35
 * @copyright   Sebsoft.nl
36
 * @author      R.J. van Dongen <rogier@sebsoft.nl>
37
 * @license     http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
38
 */
39
class util {
40
 
41
    /**
42
     * __construct() DO NOT SHOW / ALLOW TO BE CALLED: Open source version
43
     */
44
    private function __construct() {
45
        // Open source version.
46
    }
47
 
48
    /**
49
     * Nasty function to try and assure UNIQUE prefixes.
50
     * Only use this to prefix named query params.
51
     *
52
     * @return string
53
     */
54
    public static function get_prefix() {
55
        static $counter = 0;
56
        $counter++;
57
        return 'pfx' . $counter;
58
    }
59
 
60
    /**
61
     * Return a more humanly readable timespan string from a timespan
62
     *
63
     * @param float $size
64
     * @return string
65
     */
66
    final public static function format_timespan($size) {
67
        $neg = ($size < 0);
68
        $size = (float) abs($size);
69
        if ($size > 7 * 86400) {
70
            return ($neg ? '-' : '') . sprintf('%d %s', floor($size / (7 * 86400)), get_string('weeks'));
71
        } else if ($size > 86400) {
72
            return ($neg ? '-' : '') . sprintf('%d %s', floor($size / 86400), get_string('days'));
73
        } else if ($size > 3600) {
74
            return ($neg ? '-' : '') . sprintf('%d %s', floor($size / 3600), get_string('hours'));
75
        } else if ($size > 60) {
76
            return ($neg ? '-' : '') . sprintf('%d %s', floor($size / 60), get_string('minutes'));
77
        } else {
78
            return ($neg ? '-' : '') . sprintf('%d %s', $size, get_string('seconds'));
79
        }
80
    }
81
 
82
    /**
83
     * Count the number of activly monitored users.
84
     * Do note this method will not count users configured to be excluded.
85
     *
86
     * @return int number of actively monitored users
87
     */
88
    public static function count_monitored_users() {
89
        global $DB;
90
        $where = 'deleted = :deleted';
91
        $params = array('deleted' => 0);
92
        static::append_user_exclusion($where, $params, 'u.');
93
        return $DB->count_records_sql('SELECT COUNT(*) FROM {user} u WHERE ' . $where, $params);
94
    }
95
 
96
    /**
97
     * Count the number of suspended users.
98
     * Do note this method will not count users configured to be excluded.
99
     *
100
     * @return int number of suspended users
101
     */
102
    public static function count_suspended_users() {
103
        global $DB;
104
        $where = 'suspended = :suspended AND deleted = :deleted';
105
        $params = array('suspended' => 1, 'deleted' => 0);
106
        static::append_user_exclusion($where, $params, 'u.');
107
        return $DB->count_records_sql('SELECT COUNT(*) FROM {user} u WHERE ' . $where, $params);
108
    }
109
 
110
    /**
111
     * Count the number of users that are to be suspended.
112
     * Do note this method will not count users configured to be excluded.
113
     *
114
     * @return int number of suspendable users
115
     */
116
    public static function count_users_to_suspend() {
117
        global $DB;
118
        list($where, $params) = static::get_suspension_query(false);
119
        list($where2, $params2) = static::get_suspension_query(true);
120
        $sql = 'SELECT COUNT(*) FROM {user} u WHERE ' . "({$where}) OR ({$where2})";
121
        return $DB->count_records_sql($sql, $params + $params2);
122
 
123
    }
124
 
125
    /**
126
     * Count the number of users that are to be deleted.
127
     * Do note this method will not count users configured to be excluded.
128
     *
129
     * @return int number of deleteable users
130
     */
131
    public static function count_users_to_delete() {
132
        global $DB;
133
        list($where, $params) = static::get_deletion_query(false);
134
        list($where2, $params2) = static::get_deletion_query(true);
135
        $sql = 'SELECT COUNT(*) FROM {user} u WHERE ' . "({$where}) OR ({$where2})";
136
        return $DB->count_records_sql($sql, $params + $params2);
137
    }
138
 
139
    /**
140
     * Marks inactive users as suspended according to configuration settings.
141
     *
142
     * @return boolean
143
     */
144
    final public static function mark_users_to_suspend() {
145
        global $DB;
146
        if (!(bool)config::get('enabled')) {
147
            return false;
148
        }
149
        if (!(bool)config::get('enablesmartdetect')) {
150
            return false;
151
        }
152
        $lastrun = static::get_lastrun_config('smartdetect', 0, true);
153
        $deltatime = time() - $lastrun;
154
        if ($deltatime < config::get('smartdetect_interval')) {
155
            return false;
156
        }
157
        list($where, $params) = static::get_suspension_query(true);
158
        $sql = "SELECT * FROM {user} u WHERE $where";
159
        $users = $DB->get_records_sql($sql, $params);
160
        foreach ($users as $user) {
161
            // Suspend user here.
162
            static::do_suspend_user($user);
163
        }
164
    }
165
 
166
    /**
167
     * Warns inactive users that they will be suspended soon. This must be run *AFTER* user suspension is done,
168
     * or it will email suspended users if this is the first run.
169
     */
170
    final public static function warn_users_of_suspension() {
171
        global $DB;
172
 
173
        if (!(bool)config::get('enabled')) {
174
            return false;
175
        }
176
        if (!(bool)config::get('enablesmartdetect')) {
177
            return false;
178
        }
179
        if (!(bool)config::get('enablesmartdetect_warning')) {
180
            return false;
181
        }
182
        // Run in parallel with the suspensions.
183
        $lastrun = static::get_lastrun_config('smartdetect', 0, true);
184
        $deltatime = time() - $lastrun;
185
        if ($deltatime < config::get('smartdetect_interval')) {
186
            return false;
187
        }
188
 
189
        // Do nothing if warningtime is 0.
190
        $warningtime = (int)config::get('smartdetect_warninginterval');
191
        if ($warningtime <= 0) {
192
            return false;
193
        }
194
 
195
        // Get the query for users to warn.
196
        $warningthreshold = (time() - (int)config::get('smartdetect_suspendafter')) + $warningtime;
197
        list($where, $params) = static::get_suspension_query(true, $warningthreshold);
198
        $sql = "SELECT * FROM {user} u WHERE $where";
199
        $users = $DB->get_records_sql($sql, $params);
200
        foreach ($users as $user) {
201
            // Check whether the user was already warned.
202
            if ((get_user_preferences('tool_usersuspension_warned', false, $user))) {
203
                continue;
204
            }
205
 
206
            static::process_user_warning_email($user);
207
            // Mark the user as warned. This will be reset on their first successful login post warning.
208
            set_user_preference('tool_usersuspension_warned', true, $user);
209
        }
210
    }
211
 
212
    /**
213
     * Deletes suspended users according to configuration settings.
214
     *
215
     * @return boolean
216
     */
217
    final public static function delete_suspended_users() {
218
        global $DB;
219
        if (!(bool)config::get('enabled')) {
220
            return false;
221
        }
222
        if (!(bool)config::get('enablecleanup')) {
223
            return false;
224
        }
225
        $lastrun = static::get_lastrun_config('cleanup', 0, true);
226
        $deltatime = time() - $lastrun;
227
        if ($deltatime < config::get('cleanup_interval')) {
228
            return false;
229
        }
230
        list($where, $params) = static::get_deletion_query(true);
231
        $sql = "SELECT * FROM {user} u WHERE $where";
232
        $users = $DB->get_records_sql($sql, $params);
233
        foreach ($users as $user) {
234
            // Delete user here.
235
            static::do_delete_user($user);
236
        }
237
    }
238
 
239
    /**
240
     * Gets last run configuration for a specific type
241
     *
242
     * @param string $type
243
     * @param mixed $default default value to return if this config is not set
244
     * @param bool $autosetnew if true, automatically insert current time for the last run configuration
245
     */
246
    final protected static function get_lastrun_config($type, $default = null, $autosetnew = true) {
247
        $value = get_config('tool_usersuspension', $type . '_lastrun');
248
        if ($autosetnew) {
249
            static::set_lastrun_config($type);
250
        }
251
        return (($value === false) ? $default : $value);
252
    }
253
 
254
    /**
255
     * Sets last run configuration for a specific type
256
     *
257
     * @param string $type
258
     */
259
    final protected static function set_lastrun_config($type) {
260
        set_config($type . '_lastrun', time(), 'tool_usersuspension');
261
    }
262
 
263
    /**
264
     * Performs the actual user suspension by updating the users table
265
     *
266
     * @param \stdClass $user
267
     * @param bool $automated true if a result of automated suspension, false if suspending
268
     *              is a result of a manual action
269
     */
270
    final public static function do_suspend_user($user, $automated = true) {
271
        global $USER, $CFG;
272
        require_once($CFG->dirroot . '/user/lib.php');
273
        // Piece of code taken from /admin/user.php so we dance just like moodle does.
274
        if (!is_siteadmin($user) && $USER->id != $user->id && $user->suspended != 1) {
275
            $user->suspended = 1;
276
            // Force logout.
277
            \core\session\manager::kill_user_sessions($user->id);
278
            user_update_user($user, false, true);
279
            // Process email if applicable.
280
            $user->suspended = 0; // This is to prevent mail from not sending.
281
            $emailsent = (static::process_user_suspended_email($user, $automated) === true);
282
            // Create status record.
283
            static::process_status_record($user, 'suspended', $emailsent);
284
            // Trigger event.
285
            $event = event\user_suspended::create(
286
                    array(
287
                        'objectid' => $user->id,
288
                        'relateduserid' => $user->id,
289
                        'context' => \context_user::instance($user->id),
290
                        'other' => array()
291
                        )
292
                    );
293
            $event->trigger();
294
            return true;
295
        }
296
        return false;
297
    }
298
 
299
    /**
300
     * Performs the actual user unsuspension by updating the users table
301
     *
302
     * @param \stdClass $user
303
     */
304
    final public static function do_unsuspend_user($user) {
305
        global $CFG, $DB;
306
        require_once($CFG->dirroot . '/user/lib.php');
307
        // Piece of code taken from /admin/user.php so we dance just like moodle does.
308
        if ($user = $DB->get_record('user', array('id' => $user->id,
309
                'mnethostid' => $CFG->mnet_localhost_id, 'deleted' => 0))) {
310
            if ($user->suspended != 0) {
311
                $user->suspended = 0;
312
                user_update_user($user, false, true);
313
                // Process email id applicable.
314
                $emailsent = (static::process_user_unsuspended_email($user) === true);
315
                // Trigger event.
316
                $event = event\user_unsuspended::create(
317
                        array(
318
                            'objectid' => $user->id,
319
                            'relateduserid' => $user->id,
320
                            'context' => \context_user::instance($user->id),
321
                            'other' => array()
322
                            )
323
                        );
324
                $event->trigger();
325
                // Create status record.
326
                static::process_status_record($user, 'unsuspended', $emailsent);
327
                return true;
328
            }
329
        }
330
        return false;
331
    }
332
 
333
    /**
334
     * Performs the actual user deletion
335
     *
336
     * @param \stdClass $user
337
     * @return bool true if successful, false otherwise
338
     */
339
    final public static function do_delete_user($user) {
340
        global $USER, $CFG;
341
        require_once($CFG->dirroot . '/user/lib.php');
342
        // Piece of code taken from /admin/user.php so we dance just like moodle does.
343
        if (!is_siteadmin($user) && $USER->id != $user->id && $user->deleted != 1) {
344
            // Force logout.
345
            \core\session\manager::kill_user_sessions($user->id);
346
            user_delete_user($user);
347
            // Process email id applicable.
348
            $user->suspended = 0; // This is to prevent mail from not sending.
349
            $emailsent = (static::process_user_deleted_email($user) === true);
350
            // Create status record.
351
            static::process_status_record($user, 'deleted', $emailsent);
352
            return true;
353
        }
354
        return false;
355
    }
356
 
357
    /**
358
     * Process a status record.
359
     * This will insert a new status record and move all existing status records for the given user to the logs.
360
     *
361
     * @param \stdClass $user user record
362
     * @param string $status status string
363
     * @param bool $emailsent whether or not the email was sent
364
     */
365
    final public static function process_status_record($user, $status, $emailsent) {
366
        global $DB;
367
        // Move existing record to log.
368
        $recordstolog = $DB->get_records('tool_usersuspension_status', array('userid' => $user->id));
369
        foreach ($recordstolog as $record) {
370
            unset($record->id);
371
            $DB->insert_record('tool_usersuspension_log', $record);
372
        }
373
        $DB->delete_records('tool_usersuspension_status', array('userid' => $user->id));
374
        // Insert new record.
375
        $statusrecord = (object) array(
376
            'userid' => $user->id,
377
            'status' => $status,
378
            'mailsent' => ($emailsent ? 1 : 0),
379
            'mailedto' => $user->email,
380
            'timecreated' => time()
381
        );
382
        $DB->insert_record('tool_usersuspension_status', $statusrecord);
383
    }
384
 
385
    /**
386
     * Add user exclusion to the query.
387
     * This will, at the very least, exclude the site administrators and the guest account
388
     *
389
     * @param string $where
390
     * @param array $params
391
     * @param string $useraliasprefix alias prefix for users (e.g. 'u.' to indicate u.id)
392
     */
393
    public static function append_user_exclusion(&$where, &$params, $useraliasprefix = '') {
394
        global $CFG, $DB;
395
        // Set standard exclusions.
396
        $excludeids = array(1, $CFG->siteguest); // Guest account.
397
        $excludeids = array_merge($excludeids, array_keys(get_admins()));
398
        // Now append configured exclusions.
399
        $excludeids = array_merge($excludeids, static::get_user_exclusion_list());
400
        $excludeids = array_unique($excludeids);
401
 
402
        list($notinsql, $uparams) = $DB->get_in_or_equal($excludeids, SQL_PARAMS_NAMED, 'uidexc', false, 0);
403
        $where .= ' AND ' . $useraliasprefix . 'id '.$notinsql;
404
        $params = $params + $uparams;
405
    }
406
 
407
    /**
408
     * Get a list of userids to exclude.
409
     * This will load all relevant userids from the tool's exclusion table
410
     *
411
     * @return array list of user ids
412
     */
413
    public static function get_user_exclusion_list() {
414
        global $DB;
415
        // First load users.
416
        $userids = $DB->get_fieldset_select('tool_usersuspension_excl', 'refid',
417
                'type = :type', array('type' => 'user'));
418
        $cohortids = $DB->get_fieldset_select('tool_usersuspension_excl', 'refid',
419
                'type = :type', array('type' => 'cohort'));
420
        foreach ($cohortids as $cohortid) {
421
            $cohortuserids = $DB->get_fieldset_select('cohort_members', 'userid',
422
                    'cohortid = :cohid', array('cohid' => $cohortid));
423
            $userids = array_merge($userids, $cohortuserids);
424
        }
425
 
426
        return array_unique($userids);
427
    }
428
 
429
    /**
430
     * Return the query to load users applicable for suspension.
431
     *
432
     * @param bool $pastsuspensiondate if true, this return the query for users
433
     *          that are past their date of suspension (i.e. should be suspended).
434
     *          If false, this returns the query for users that are not past their
435
     *          date of suspension yet. The latter can be used for statistics on
436
     *          when users would get suspended.
437
     * @param int $customtime A custom timestamp to perform the comparison against.
438
     * @return array A list containing the constructed where part of the sql and an array of parameters.
439
     */
440
    public static function get_suspension_query($pastsuspensiondate = true, $customtime = null) {
441
        global $CFG;
442
        $uniqid = static::get_prefix();
443
        $detectoperator = $pastsuspensiondate ? '<' : '>';
444
        $timecheck = !empty($customtime) ? $customtime : time() - (config::get('smartdetect_suspendafter'));
445
        $where = "u.confirmed = 1 AND u.suspended = 0 AND u.deleted = 0 AND u.mnethostid = :{$uniqid}mnethost ";
446
        $where .= "AND (";
447
        $where .= "(u.lastaccess = 0 AND u.firstaccess > 0 AND u.firstaccess $detectoperator :{$uniqid}time1)";
448
        $where .= " OR (u.lastaccess > 0 AND u.lastaccess $detectoperator :{$uniqid}time2)";
449
        $where .= " OR (u.auth = 'manual' AND u.firstaccess = 0 AND u.lastaccess = 0 ";
450
        $where .= "     AND u.timemodified > 0 AND u.timemodified $detectoperator :{$uniqid}time3)";
451
        $where .= ")";
452
        $params = array("{$uniqid}mnethost" => $CFG->mnet_localhost_id,
453
            "{$uniqid}time1" => $timecheck,
454
            "{$uniqid}time2" => $timecheck,
455
            "{$uniqid}time3" => $timecheck);
456
        // Append user exclusion.
457
        static::append_user_exclusion($where, $params, 'u.');
458
        return array($where, $params);
459
    }
460
 
461
    /**
462
     * Return the query to load users applicable for deletion.
463
     *
464
     * @param bool $pastdeletiondate if true, this return the query for users
465
     *          that are past their date of deletion (i.e. should be deleted).
466
     *          If false, this returns the query for users that are not past their
467
     *          date of deletion yet. The latter can be used for statistics on
468
     *          when users would get deleted.
469
     * @return array A list containing the constructed where part of the sql and an array of parameters.
470
     */
471
    public static function get_deletion_query($pastdeletiondate = true) {
472
        global $CFG;
473
        $detectoperator = $pastdeletiondate ? '<' : '>';
474
        $uniqid = static::get_prefix();
475
        $params = array("{$uniqid}mnethost" => $CFG->mnet_localhost_id,
476
            "{$uniqid}" => time() - (int)config::get('cleanup_deleteafter'));
477
        $where = "u.suspended = 1 AND u.confirmed = 1 AND u.deleted = 0 "
478
                . "AND u.mnethostid = :{$uniqid}mnethost AND u.timemodified $detectoperator :{$uniqid}";
479
        static::append_user_exclusion($where, $params, 'u.');
480
        return array($where, $params);
481
    }
482
 
483
    /**
484
     * Process the view for cohort exclusion.
485
     * This will display or process the exclusion form for cohort exclusion.
486
     *
487
     * @param \moodle_url $url
488
     */
489
    public static function view_process_cohort_exclusion($url) {
490
        global $CFG, $OUTPUT;
491
        require_once($CFG->dirroot . '/' . $CFG->admin . '/tool/usersuspension/classes/forms/exclude/cohort.php');
492
        $formurl = clone $url;
493
        $formurl->param('action', 'add');
494
        $formurl->param('addtype', 'cohort');
495
        $mform = new forms\exclude\cohort($formurl);
496
        if ($mform->is_cancelled()) {
497
            redirect($url);
498
        } else if ($data = $mform->get_data()) {
499
            echo $OUTPUT->header();
500
            echo '<div id="tool-usersuspension-form-container">';
501
            $mform->process();
502
            echo '<br/>';
503
            echo static::continue_button($url, get_string('button:backtoexclusions', 'tool_usersuspension'));
504
            echo '</div>';
505
            echo $OUTPUT->footer();
506
        } else {
507
            echo $OUTPUT->header();
508
            echo '<div id="tool-usersuspension-form-container">';
509
            echo '<div>';
510
            static::print_view_tabs($url->params(), 'exclusions');
511
            echo '</div>';
512
            echo $mform->display();
513
            echo '</div>';
514
            echo $OUTPUT->footer();
515
        }
516
    }
517
 
518
    /**
519
     * Process the view for user exclusion.
520
     * This will display or process the exclusion form for user exclusion.
521
     *
522
     * @param \moodle_url $url
523
     */
524
    public static function view_process_user_exclusion($url) {
525
        global $CFG, $OUTPUT;
526
        require_once($CFG->dirroot . '/' . $CFG->admin . '/tool/usersuspension/classes/forms/exclude/user.php');
527
        $formurl = clone $url;
528
        $formurl->param('action', 'add');
529
        $formurl->param('addtype', 'user');
530
        $mform = new forms\exclude\user($formurl);
531
        if ($mform->is_cancelled()) {
532
            redirect($url);
533
        } else if ($data = $mform->get_data()) {
534
            echo $OUTPUT->header();
535
            echo '<div id="tool-usersuspension-form-container">';
536
            $mform->process();
537
            echo '<br/>';
538
            echo static::continue_button($url, get_string('button:backtoexclusions', 'tool_usersuspension'));
539
            echo '</div>';
540
            echo $OUTPUT->footer();
541
        } else {
542
            echo $OUTPUT->header();
543
            echo '<div id="tool-usersuspension-form-container">';
544
            echo '<div>';
545
            static::print_view_tabs($url->params(), 'exclusions');
546
            echo '</div>';
547
            echo $mform->display();
548
            echo '</div>';
549
            echo $OUTPUT->footer();
550
        }
551
    }
552
 
553
    /**
554
     * Send an e-mail due to a user being suspended
555
     *
556
     * @param \stdClass $user
557
     * @param bool $automated true if a result of automated suspension, false if suspending
558
     *              is a result of a manual action
559
     * @return void
560
     */
561
    public static function process_user_suspended_email($user, $automated = true) {
562
        if (!(bool)config::get('send_suspend_email')) {
563
            return false;
564
        }
565
        // Prepare and send email.
566
        $from = \core_user::get_support_user();
567
        $a = new \stdClass();
568
        $a->name = fullname($user);
569
        $a->timeinactive = static::format_timespan(config::get('smartdetect_suspendafter'));
570
        $a->contact = $from->email;
571
        $a->signature = fullname($from);
572
        $subject = get_string_manager()->get_string('email:user:suspend:subject',
573
                'tool_usersuspension', $a, $user->lang);
574
        if ($automated) {
575
            $messagehtml = get_string_manager()->get_string('email:user:suspend:auto:body',
576
                    'tool_usersuspension', $a, $user->lang);
577
        } else {
578
            $messagehtml = get_string_manager()->get_string('email:user:suspend:manual:body',
579
                    'tool_usersuspension', $a, $user->lang);
580
        }
581
        $messagetext = format_text_email($messagehtml, FORMAT_HTML);
582
        return email_to_user($user, $from, $subject, $messagetext, $messagehtml);
583
    }
584
 
585
    /**
586
     * Send an e-mail due to a user facing suspension due to inactivity.
587
     *
588
     * @param \stdClass $user
589
     * @return void
590
     */
591
    public static function process_user_warning_email($user) {
592
        // Prepare and send email.
593
        $from = \core_user::get_support_user();
594
        $a = new \stdClass();
595
        $a->name = fullname($user);
596
        $a->suspendinterval = static::format_timespan(config::get('smartdetect_suspendafter'));
597
        $a->warningperiod = static::format_timespan(config::get('smartdetect_warninginterval'));
598
        $a->contact = $from->email;
599
        $a->signature = fullname($from);
600
        $subject = get_string('email:user:warning:subject', 'tool_usersuspension', $a);
601
        $messagehtml = get_string('email:user:warning:body', 'tool_usersuspension', $a);
602
 
603
        $messagetext = format_text_email($messagehtml, FORMAT_HTML);
604
        return email_to_user($user, $from, $subject, $messagetext, $messagehtml);
605
    }
606
 
607
    /**
608
     * Send an e-mail due to a user being unsuspended
609
     *
610
     * @param \stdClass $user
611
     * @return void
612
     */
613
    public static function process_user_unsuspended_email($user) {
614
        if (!(bool)config::get('send_suspend_email')) {
615
            return false;
616
        }
617
        // Prepare and send email.
618
        $from = \core_user::get_support_user();
619
        $a = new \stdClass();
620
        $a->name = fullname($user);
621
        $a->contact = $from->email;
622
        $a->signature = fullname($from);
623
        $subject = get_string_manager()->get_string('email:user:unsuspend:subject',
624
                'tool_usersuspension', $a, $user->lang);
625
        $messagehtml = get_string_manager()->get_string('email:user:unsuspend:body',
626
                'tool_usersuspension', $a, $user->lang);
627
        $messagetext = format_text_email($messagehtml, FORMAT_HTML);
628
        return email_to_user($user, $from, $subject, $messagetext, $messagehtml);
629
    }
630
 
631
    /**
632
     * Send an e-mail due to a user being deleted
633
     *
634
     * @param \stdClass $user
635
     * @return bool true if sent, false if disabled or error
636
     */
637
    public static function process_user_deleted_email($user) {
638
        if (!(bool)config::get('send_delete_email')) {
639
            return false;
640
        }
641
        // Prepare and send email.
642
        $from = \core_user::get_support_user();
643
        $a = new \stdClass();
644
        $a->name = fullname($user);
645
        $a->timesuspended = static::format_timespan(config::get('cleanup_deleteafter'));
646
        $a->contact = $from->email;
647
        $a->signature = fullname($from);
648
        $subject = get_string_manager()->get_string('email:user:delete:subject',
649
                'tool_usersuspension', $a, $user->lang);
650
        $messagehtml = get_string_manager()->get_string('email:user:delete:body',
651
                'tool_usersuspension', $a, $user->lang);
652
        $messagetext = format_text_email($messagehtml, FORMAT_HTML);
653
        return email_to_user($user, $from, $subject, $messagetext, $messagehtml);
654
    }
655
 
656
    /**
657
     * Clean history logs (if enabled in global config) older than the configured duration.
658
     *
659
     * @return boolean
660
     */
661
    public static function clean_logs() {
662
        global $DB;
663
        if (!(bool)config::get('enablecleanlogs')) {
664
            return false;
665
        }
666
        $DB->delete_records_select('tool_usersuspension_log', 'timecreated < ?',
667
                array(time() - (int)config::get('cleanlogsafter')));
668
        return true;
669
    }
670
 
671
    /**
672
     * Print a notification message.
673
     *
674
     * @param string $msg the notification message to display
675
     * @param string $class class or type of message. Please use either 'success' or 'error'
676
     * @return void
677
     */
678
    public static function print_notification($msg, $class = 'success') {
679
        global $OUTPUT;
680
        $pix = '<img src="' . $OUTPUT->image_url('msg_' . $class, 'tool_usersuspension') . '"/>';
681
        echo '<div class="tool-usersuspension-notification-' . $class . '">' . $pix . ' ' . $msg . '</div>';
682
    }
683
 
684
    /**
685
     * Returns HTML to display a continue button that goes to a particular URL.
686
     *
687
     * @param string|moodle_url $url The url the button goes to.
688
     * @param string $buttontext the text to show on the button.
689
     * @return string the HTML to output.
690
     */
691
    public static function continue_button($url, $buttontext) {
692
        global $OUTPUT;
693
        if (!($url instanceof \moodle_url)) {
694
            $url = new \moodle_url($url);
695
        }
696
        $button = new \single_button($url, $buttontext, 'get');
697
        $button->class = 'continuebutton';
698
 
699
        return $OUTPUT->render($button);
700
    }
701
 
702
    /**
703
     * Create a tab object with a nice image view, instead of just a regular tabobject
704
     *
705
     * @param string $id unique id of the tab in this tree, it is used to find selected and/or inactive tabs
706
     * @param string $pix image name
707
     * @param string $component component where the image will be looked for
708
     * @param string|moodle_url $link
709
     * @param string $text text on the tab
710
     * @param string $title title under the link, by defaul equals to text
711
     * @param bool $linkedwhenselected whether to display a link under the tab name when it's selected
712
     * @return \tabobject
713
     */
714
    public static function pictabobject($id, $pix = null, $component = 'tool_usersuspension', $link = null,
715
            $text = '', $title = '', $linkedwhenselected = false) {
716
        global $OUTPUT;
717
        $img = '';
718
        if ($pix !== null) {
719
            $img = '<img src="' . $OUTPUT->image_url($pix, $component) . '"> ';
720
        }
721
        return new \tabobject($id, $link, $img . $text, empty($title) ? $text : $title, $linkedwhenselected);
722
    }
723
 
724
    /**
725
     * print the tabs for the overview pages.
726
     *
727
     * @param array $params basic url parameters
728
     * @param string $selected id of the selected tab
729
     */
730
    public static function print_view_tabs($params, $selected) {
731
        global $CFG, $OUTPUT;
732
        $tabs = array();
733
        // Add exclusions.
734
        $exclusions = static::pictabobject('exclusions', 'exclusions', 'tool_usersuspension',
735
            new \moodle_url('/' . $CFG->admin . '/tool/usersuspension/view/exclude.php', $params),
736
                get_string('table:exclusions', 'tool_usersuspension'));
737
        $exclusions->subtree[] = static::pictabobject('excludeaddcohort', null, 'tool_usersuspension',
738
            new \moodle_url('/' . $CFG->admin . '/tool/usersuspension/view/exclude.php',
739
                    $params + array('action' => 'add', 'addtype' => 'cohort', 'sesskey' => sesskey())),
740
                get_string('action:exclude:add:cohort', 'tool_usersuspension'));
741
        $exclusions->subtree[] = static::pictabobject('excludeadduser', null, 'tool_usersuspension',
742
            new \moodle_url('/' . $CFG->admin . '/tool/usersuspension/view/exclude.php',
743
                    $params + array('action' => 'add', 'addtype' => 'user', 'sesskey' => sesskey())),
744
                get_string('action:exclude:add:user', 'tool_usersuspension'));
745
        $tabs[] = $exclusions;
746
        // Add statuslist tabs.
747
        foreach (statustable::get_viewtypes() as $type) {
748
            $url = new \moodle_url('/' . $CFG->admin . '/tool/usersuspension/view/statuslist.php', $params);
749
            $url->param('type', $type);
750
            $counter = '';
751
            switch ($type) {
752
                case statustable::DELETE:
753
                    $counter = ' (' . static::count_users_to_delete() . ')';
754
                    break;
755
                case statustable::SUSPENDED:
756
                    $counter = ' (' . static::count_suspended_users() . ')';
757
                    break;
758
                case statustable::TOSUSPEND:
759
                    $counter = ' (' . static::count_users_to_suspend() . ')';
760
                    break;
761
                case statustable::STATUS:
762
                    $counter = ' (' . static::count_monitored_users() . ')';
763
                    break;
764
            }
765
            $tabs[] = static::pictabobject($type, 'status_' . $type, 'tool_usersuspension',
766
                    $url, get_string('table:status:' . $type, 'tool_usersuspension') . $counter);
767
        }
768
        // Add upload tab.
769
        if ((bool)config::get('enablefromupload')) {
770
            $upload = static::pictabobject('upload', 'upload', 'tool_usersuspension',
771
                new \moodle_url('/' . $CFG->admin . '/tool/usersuspension/view/upload.php', $params),
772
                    get_string('link:upload', 'tool_usersuspension'));
773
            $tabs[] = $upload;
774
        }
775
 
776
        // Add logs tabs.
777
        $logs = static::pictabobject('logs', 'logs', 'tool_usersuspension',
778
            new \moodle_url('/' . $CFG->admin . '/tool/usersuspension/view/log.php', $params + array('history' => 0)),
779
                get_string('table:logs', 'tool_usersuspension'));
780
        $logs->subtree[] = static::pictabobject('log_latest', null, 'tool_usersuspension',
781
            new \moodle_url('/' . $CFG->admin . '/tool/usersuspension/view/log.php', $params + array('history' => 0)),
782
                get_string('table:log:latest', 'tool_usersuspension'));
783
        $logs->subtree[] = static::pictabobject('log_all', null, 'tool_usersuspension',
784
            new \moodle_url('/' . $CFG->admin . '/tool/usersuspension/view/log.php', $params + array('history' => 1)),
785
                get_string('table:log:all', 'tool_usersuspension'));
786
        $tabs[] = $logs;
787
 
788
        // Add tests.
789
        $testfromfolder = static::pictabobject('testfromfolder', null, 'tool_usersuspension',
790
            new \moodle_url('/' . $CFG->admin . '/tool/usersuspension/view/testfromfolder.php', $params),
791
                get_string('testfromfolder', 'tool_usersuspension'));
792
        $tabs[] = $testfromfolder;
793
 
794
        // Add notifications tabs.
795
        $notifications = static::pictabobject('notifications', null, 'tool_usersuspension',
796
            new \moodle_url('/' . $CFG->admin . '/tool/usersuspension/view/notifications.php', $params),
797
                get_string('tab:notifications', 'tool_usersuspension'));
798
        $tabs[] = $notifications;
799
 
800
        echo $OUTPUT->tabtree($tabs, $selected);
801
    }
802
 
803
    /**
804
     * Generate messages (using core notifications) for (every) frontpage in this tool.
805
     */
806
    public static function generate_notifications() {
807
        $messages = [];
808
        // Main plugin enabled.
809
        if (!(bool)config::get('enabled')) {
810
            $messages[] = get_string('config:tool:disabled', 'tool_usersuspension');
811
        }
812
        // Auto-suspend enabled.
813
        if (!(bool)config::get('enablesmartdetect')) {
814
            $messages[] = get_string('config:smartdetect:disabled', 'tool_usersuspension');
815
        }
816
        // Auto-delete enabled.
817
        if (!(bool)config::get('enablecleanup')) {
818
            $messages[] = get_string('config:cleanup:disabled', 'tool_usersuspension');
819
        }
820
        // Task(s).
821
        if (!(bool)config::get('enableunsuspendfromfolder')) {
822
            $messages[] = get_string('config:unsuspendfromfolder:disabled', 'tool_usersuspension');
823
        }
824
        if (!(bool)config::get('enablefromfolder')) {
825
            $messages[] = get_string('config:fromfolder:disabled', 'tool_usersuspension');
826
        }
827
        // Folder(s).
828
        $uploadedfolder = config::get('uploadfolder');
829
        if (!file_exists($uploadedfolder) || !is_dir($uploadedfolder)) {
830
            $messages[] = 'CSV upload folder "'.$uploadedfolder.'" does not exist';
831
        }
832
        if (!is_readable($uploadedfolder) || !is_dir($uploadedfolder)) {
833
            $messages[] = 'CSV upload folder "'.$uploadedfolder.'" is not readable';
834
        }
835
        if (!empty($messages)) {
836
            return \html_writer::div(implode('<br/>', $messages), 'alert alert-warning');
837
        } else {
838
            return \html_writer::div(get_string('notifications:allok', 'tool_usersuspension'), 'alert alert-success');
839
        }
840
    }
841
 
842
}