Proyectos de Subversion Moodle

Rev

Autoría | Ultima modificación | Ver Log |

<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.

/**
 * file contains the general utility class for this tool
 *
 * File         util.php
 * Encoding     UTF-8
 * @copyright   Sebsoft.nl
 * @license     http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
 */

namespace tool_usersuspension;

use tool_usersuspension\statustable;

/**
 * tool_usersuspension\util
 *
 * @package     tool_usersuspension
 *
 * @copyright   Sebsoft.nl
 * @author      R.J. van Dongen <rogier@sebsoft.nl>
 * @license     http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
 */
class util {

    /**
     * __construct() DO NOT SHOW / ALLOW TO BE CALLED: Open source version
     */
    private function __construct() {
        // Open source version.
    }

    /**
     * Nasty function to try and assure UNIQUE prefixes.
     * Only use this to prefix named query params.
     *
     * @return string
     */
    public static function get_prefix() {
        static $counter = 0;
        $counter++;
        return 'pfx' . $counter;
    }

    /**
     * Return a more humanly readable timespan string from a timespan
     *
     * @param float $size
     * @return string
     */
    final public static function format_timespan($size) {
        $neg = ($size < 0);
        $size = (float) abs($size);
        if ($size > 7 * 86400) {
            return ($neg ? '-' : '') . sprintf('%d %s', floor($size / (7 * 86400)), get_string('weeks'));
        } else if ($size > 86400) {
            return ($neg ? '-' : '') . sprintf('%d %s', floor($size / 86400), get_string('days'));
        } else if ($size > 3600) {
            return ($neg ? '-' : '') . sprintf('%d %s', floor($size / 3600), get_string('hours'));
        } else if ($size > 60) {
            return ($neg ? '-' : '') . sprintf('%d %s', floor($size / 60), get_string('minutes'));
        } else {
            return ($neg ? '-' : '') . sprintf('%d %s', $size, get_string('seconds'));
        }
    }

    /**
     * Count the number of activly monitored users.
     * Do note this method will not count users configured to be excluded.
     *
     * @return int number of actively monitored users
     */
    public static function count_monitored_users() {
        global $DB;
        $where = 'deleted = :deleted';
        $params = array('deleted' => 0);
        static::append_user_exclusion($where, $params, 'u.');
        return $DB->count_records_sql('SELECT COUNT(*) FROM {user} u WHERE ' . $where, $params);
    }

    /**
     * Count the number of suspended users.
     * Do note this method will not count users configured to be excluded.
     *
     * @return int number of suspended users
     */
    public static function count_suspended_users() {
        global $DB;
        $where = 'suspended = :suspended AND deleted = :deleted';
        $params = array('suspended' => 1, 'deleted' => 0);
        static::append_user_exclusion($where, $params, 'u.');
        return $DB->count_records_sql('SELECT COUNT(*) FROM {user} u WHERE ' . $where, $params);
    }

    /**
     * Count the number of users that are to be suspended.
     * Do note this method will not count users configured to be excluded.
     *
     * @return int number of suspendable users
     */
    public static function count_users_to_suspend() {
        global $DB;
        list($where, $params) = static::get_suspension_query(false);
        list($where2, $params2) = static::get_suspension_query(true);
        $sql = 'SELECT COUNT(*) FROM {user} u WHERE ' . "({$where}) OR ({$where2})";
        return $DB->count_records_sql($sql, $params + $params2);

    }

    /**
     * Count the number of users that are to be deleted.
     * Do note this method will not count users configured to be excluded.
     *
     * @return int number of deleteable users
     */
    public static function count_users_to_delete() {
        global $DB;
        list($where, $params) = static::get_deletion_query(false);
        list($where2, $params2) = static::get_deletion_query(true);
        $sql = 'SELECT COUNT(*) FROM {user} u WHERE ' . "({$where}) OR ({$where2})";
        return $DB->count_records_sql($sql, $params + $params2);
    }

    /**
     * Marks inactive users as suspended according to configuration settings.
     *
     * @return boolean
     */
    final public static function mark_users_to_suspend() {
        global $DB;
        if (!(bool)config::get('enabled')) {
            return false;
        }
        if (!(bool)config::get('enablesmartdetect')) {
            return false;
        }
        $lastrun = static::get_lastrun_config('smartdetect', 0, true);
        $deltatime = time() - $lastrun;
        if ($deltatime < config::get('smartdetect_interval')) {
            return false;
        }
        list($where, $params) = static::get_suspension_query(true);
        $sql = "SELECT * FROM {user} u WHERE $where";
        $users = $DB->get_records_sql($sql, $params);
        foreach ($users as $user) {
            // Suspend user here.
            static::do_suspend_user($user);
        }
    }

    /**
     * Warns inactive users that they will be suspended soon. This must be run *AFTER* user suspension is done,
     * or it will email suspended users if this is the first run.
     */
    final public static function warn_users_of_suspension() {
        global $DB;

        if (!(bool)config::get('enabled')) {
            return false;
        }
        if (!(bool)config::get('enablesmartdetect')) {
            return false;
        }
        if (!(bool)config::get('enablesmartdetect_warning')) {
            return false;
        }
        // Run in parallel with the suspensions.
        $lastrun = static::get_lastrun_config('smartdetect', 0, true);
        $deltatime = time() - $lastrun;
        if ($deltatime < config::get('smartdetect_interval')) {
            return false;
        }

        // Do nothing if warningtime is 0.
        $warningtime = (int)config::get('smartdetect_warninginterval');
        if ($warningtime <= 0) {
            return false;
        }

        // Get the query for users to warn.
        $warningthreshold = (time() - (int)config::get('smartdetect_suspendafter')) + $warningtime;
        list($where, $params) = static::get_suspension_query(true, $warningthreshold);
        $sql = "SELECT * FROM {user} u WHERE $where";
        $users = $DB->get_records_sql($sql, $params);
        foreach ($users as $user) {
            // Check whether the user was already warned.
            if ((get_user_preferences('tool_usersuspension_warned', false, $user))) {
                continue;
            }

            static::process_user_warning_email($user);
            // Mark the user as warned. This will be reset on their first successful login post warning.
            set_user_preference('tool_usersuspension_warned', true, $user);
        }
    }

    /**
     * Deletes suspended users according to configuration settings.
     *
     * @return boolean
     */
    final public static function delete_suspended_users() {
        global $DB;
        if (!(bool)config::get('enabled')) {
            return false;
        }
        if (!(bool)config::get('enablecleanup')) {
            return false;
        }
        $lastrun = static::get_lastrun_config('cleanup', 0, true);
        $deltatime = time() - $lastrun;
        if ($deltatime < config::get('cleanup_interval')) {
            return false;
        }
        list($where, $params) = static::get_deletion_query(true);
        $sql = "SELECT * FROM {user} u WHERE $where";
        $users = $DB->get_records_sql($sql, $params);
        foreach ($users as $user) {
            // Delete user here.
            static::do_delete_user($user);
        }
    }

    /**
     * Gets last run configuration for a specific type
     *
     * @param string $type
     * @param mixed $default default value to return if this config is not set
     * @param bool $autosetnew if true, automatically insert current time for the last run configuration
     */
    final protected static function get_lastrun_config($type, $default = null, $autosetnew = true) {
        $value = get_config('tool_usersuspension', $type . '_lastrun');
        if ($autosetnew) {
            static::set_lastrun_config($type);
        }
        return (($value === false) ? $default : $value);
    }

    /**
     * Sets last run configuration for a specific type
     *
     * @param string $type
     */
    final protected static function set_lastrun_config($type) {
        set_config($type . '_lastrun', time(), 'tool_usersuspension');
    }

    /**
     * Performs the actual user suspension by updating the users table
     *
     * @param \stdClass $user
     * @param bool $automated true if a result of automated suspension, false if suspending
     *              is a result of a manual action
     */
    final public static function do_suspend_user($user, $automated = true) {
        global $USER, $CFG;
        require_once($CFG->dirroot . '/user/lib.php');
        // Piece of code taken from /admin/user.php so we dance just like moodle does.
        if (!is_siteadmin($user) && $USER->id != $user->id && $user->suspended != 1) {
            $user->suspended = 1;
            // Force logout.
            \core\session\manager::kill_user_sessions($user->id);
            user_update_user($user, false, true);
            // Process email if applicable.
            $user->suspended = 0; // This is to prevent mail from not sending.
            $emailsent = (static::process_user_suspended_email($user, $automated) === true);
            // Create status record.
            static::process_status_record($user, 'suspended', $emailsent);
            // Trigger event.
            $event = event\user_suspended::create(
                    array(
                        'objectid' => $user->id,
                        'relateduserid' => $user->id,
                        'context' => \context_user::instance($user->id),
                        'other' => array()
                        )
                    );
            $event->trigger();
            return true;
        }
        return false;
    }

    /**
     * Performs the actual user unsuspension by updating the users table
     *
     * @param \stdClass $user
     */
    final public static function do_unsuspend_user($user) {
        global $CFG, $DB;
        require_once($CFG->dirroot . '/user/lib.php');
        // Piece of code taken from /admin/user.php so we dance just like moodle does.
        if ($user = $DB->get_record('user', array('id' => $user->id,
                'mnethostid' => $CFG->mnet_localhost_id, 'deleted' => 0))) {
            if ($user->suspended != 0) {
                $user->suspended = 0;
                user_update_user($user, false, true);
                // Process email id applicable.
                $emailsent = (static::process_user_unsuspended_email($user) === true);
                // Trigger event.
                $event = event\user_unsuspended::create(
                        array(
                            'objectid' => $user->id,
                            'relateduserid' => $user->id,
                            'context' => \context_user::instance($user->id),
                            'other' => array()
                            )
                        );
                $event->trigger();
                // Create status record.
                static::process_status_record($user, 'unsuspended', $emailsent);
                return true;
            }
        }
        return false;
    }

    /**
     * Performs the actual user deletion
     *
     * @param \stdClass $user
     * @return bool true if successful, false otherwise
     */
    final public static function do_delete_user($user) {
        global $USER, $CFG;
        require_once($CFG->dirroot . '/user/lib.php');
        // Piece of code taken from /admin/user.php so we dance just like moodle does.
        if (!is_siteadmin($user) && $USER->id != $user->id && $user->deleted != 1) {
            // Force logout.
            \core\session\manager::kill_user_sessions($user->id);
            user_delete_user($user);
            // Process email id applicable.
            $user->suspended = 0; // This is to prevent mail from not sending.
            $emailsent = (static::process_user_deleted_email($user) === true);
            // Create status record.
            static::process_status_record($user, 'deleted', $emailsent);
            return true;
        }
        return false;
    }

    /**
     * Process a status record.
     * This will insert a new status record and move all existing status records for the given user to the logs.
     *
     * @param \stdClass $user user record
     * @param string $status status string
     * @param bool $emailsent whether or not the email was sent
     */
    final public static function process_status_record($user, $status, $emailsent) {
        global $DB;
        // Move existing record to log.
        $recordstolog = $DB->get_records('tool_usersuspension_status', array('userid' => $user->id));
        foreach ($recordstolog as $record) {
            unset($record->id);
            $DB->insert_record('tool_usersuspension_log', $record);
        }
        $DB->delete_records('tool_usersuspension_status', array('userid' => $user->id));
        // Insert new record.
        $statusrecord = (object) array(
            'userid' => $user->id,
            'status' => $status,
            'mailsent' => ($emailsent ? 1 : 0),
            'mailedto' => $user->email,
            'timecreated' => time()
        );
        $DB->insert_record('tool_usersuspension_status', $statusrecord);
    }

    /**
     * Add user exclusion to the query.
     * This will, at the very least, exclude the site administrators and the guest account
     *
     * @param string $where
     * @param array $params
     * @param string $useraliasprefix alias prefix for users (e.g. 'u.' to indicate u.id)
     */
    public static function append_user_exclusion(&$where, &$params, $useraliasprefix = '') {
        global $CFG, $DB;
        // Set standard exclusions.
        $excludeids = array(1, $CFG->siteguest); // Guest account.
        $excludeids = array_merge($excludeids, array_keys(get_admins()));
        // Now append configured exclusions.
        $excludeids = array_merge($excludeids, static::get_user_exclusion_list());
        $excludeids = array_unique($excludeids);

        list($notinsql, $uparams) = $DB->get_in_or_equal($excludeids, SQL_PARAMS_NAMED, 'uidexc', false, 0);
        $where .= ' AND ' . $useraliasprefix . 'id '.$notinsql;
        $params = $params + $uparams;
    }

    /**
     * Get a list of userids to exclude.
     * This will load all relevant userids from the tool's exclusion table
     *
     * @return array list of user ids
     */
    public static function get_user_exclusion_list() {
        global $DB;
        // First load users.
        $userids = $DB->get_fieldset_select('tool_usersuspension_excl', 'refid',
                'type = :type', array('type' => 'user'));
        $cohortids = $DB->get_fieldset_select('tool_usersuspension_excl', 'refid',
                'type = :type', array('type' => 'cohort'));
        foreach ($cohortids as $cohortid) {
            $cohortuserids = $DB->get_fieldset_select('cohort_members', 'userid',
                    'cohortid = :cohid', array('cohid' => $cohortid));
            $userids = array_merge($userids, $cohortuserids);
        }

        return array_unique($userids);
    }

    /**
     * Return the query to load users applicable for suspension.
     *
     * @param bool $pastsuspensiondate if true, this return the query for users
     *          that are past their date of suspension (i.e. should be suspended).
     *          If false, this returns the query for users that are not past their
     *          date of suspension yet. The latter can be used for statistics on
     *          when users would get suspended.
     * @param int $customtime A custom timestamp to perform the comparison against.
     * @return array A list containing the constructed where part of the sql and an array of parameters.
     */
    public static function get_suspension_query($pastsuspensiondate = true, $customtime = null) {
        global $CFG;
        $uniqid = static::get_prefix();
        $detectoperator = $pastsuspensiondate ? '<' : '>';
        $timecheck = !empty($customtime) ? $customtime : time() - (config::get('smartdetect_suspendafter'));
        $where = "u.confirmed = 1 AND u.suspended = 0 AND u.deleted = 0 AND u.mnethostid = :{$uniqid}mnethost ";
        $where .= "AND (";
        $where .= "(u.lastaccess = 0 AND u.firstaccess > 0 AND u.firstaccess $detectoperator :{$uniqid}time1)";
        $where .= " OR (u.lastaccess > 0 AND u.lastaccess $detectoperator :{$uniqid}time2)";
        $where .= " OR (u.auth = 'manual' AND u.firstaccess = 0 AND u.lastaccess = 0 ";
        $where .= "     AND u.timemodified > 0 AND u.timemodified $detectoperator :{$uniqid}time3)";
        $where .= ")";
        $params = array("{$uniqid}mnethost" => $CFG->mnet_localhost_id,
            "{$uniqid}time1" => $timecheck,
            "{$uniqid}time2" => $timecheck,
            "{$uniqid}time3" => $timecheck);
        // Append user exclusion.
        static::append_user_exclusion($where, $params, 'u.');
        return array($where, $params);
    }

    /**
     * Return the query to load users applicable for deletion.
     *
     * @param bool $pastdeletiondate if true, this return the query for users
     *          that are past their date of deletion (i.e. should be deleted).
     *          If false, this returns the query for users that are not past their
     *          date of deletion yet. The latter can be used for statistics on
     *          when users would get deleted.
     * @return array A list containing the constructed where part of the sql and an array of parameters.
     */
    public static function get_deletion_query($pastdeletiondate = true) {
        global $CFG;
        $detectoperator = $pastdeletiondate ? '<' : '>';
        $uniqid = static::get_prefix();
        $params = array("{$uniqid}mnethost" => $CFG->mnet_localhost_id,
            "{$uniqid}" => time() - (int)config::get('cleanup_deleteafter'));
        $where = "u.suspended = 1 AND u.confirmed = 1 AND u.deleted = 0 "
                . "AND u.mnethostid = :{$uniqid}mnethost AND u.timemodified $detectoperator :{$uniqid}";
        static::append_user_exclusion($where, $params, 'u.');
        return array($where, $params);
    }

    /**
     * Process the view for cohort exclusion.
     * This will display or process the exclusion form for cohort exclusion.
     *
     * @param \moodle_url $url
     */
    public static function view_process_cohort_exclusion($url) {
        global $CFG, $OUTPUT;
        require_once($CFG->dirroot . '/' . $CFG->admin . '/tool/usersuspension/classes/forms/exclude/cohort.php');
        $formurl = clone $url;
        $formurl->param('action', 'add');
        $formurl->param('addtype', 'cohort');
        $mform = new forms\exclude\cohort($formurl);
        if ($mform->is_cancelled()) {
            redirect($url);
        } else if ($data = $mform->get_data()) {
            echo $OUTPUT->header();
            echo '<div id="tool-usersuspension-form-container">';
            $mform->process();
            echo '<br/>';
            echo static::continue_button($url, get_string('button:backtoexclusions', 'tool_usersuspension'));
            echo '</div>';
            echo $OUTPUT->footer();
        } else {
            echo $OUTPUT->header();
            echo '<div id="tool-usersuspension-form-container">';
            echo '<div>';
            static::print_view_tabs($url->params(), 'exclusions');
            echo '</div>';
            echo $mform->display();
            echo '</div>';
            echo $OUTPUT->footer();
        }
    }

    /**
     * Process the view for user exclusion.
     * This will display or process the exclusion form for user exclusion.
     *
     * @param \moodle_url $url
     */
    public static function view_process_user_exclusion($url) {
        global $CFG, $OUTPUT;
        require_once($CFG->dirroot . '/' . $CFG->admin . '/tool/usersuspension/classes/forms/exclude/user.php');
        $formurl = clone $url;
        $formurl->param('action', 'add');
        $formurl->param('addtype', 'user');
        $mform = new forms\exclude\user($formurl);
        if ($mform->is_cancelled()) {
            redirect($url);
        } else if ($data = $mform->get_data()) {
            echo $OUTPUT->header();
            echo '<div id="tool-usersuspension-form-container">';
            $mform->process();
            echo '<br/>';
            echo static::continue_button($url, get_string('button:backtoexclusions', 'tool_usersuspension'));
            echo '</div>';
            echo $OUTPUT->footer();
        } else {
            echo $OUTPUT->header();
            echo '<div id="tool-usersuspension-form-container">';
            echo '<div>';
            static::print_view_tabs($url->params(), 'exclusions');
            echo '</div>';
            echo $mform->display();
            echo '</div>';
            echo $OUTPUT->footer();
        }
    }

    /**
     * Send an e-mail due to a user being suspended
     *
     * @param \stdClass $user
     * @param bool $automated true if a result of automated suspension, false if suspending
     *              is a result of a manual action
     * @return void
     */
    public static function process_user_suspended_email($user, $automated = true) {
        if (!(bool)config::get('send_suspend_email')) {
            return false;
        }
        // Prepare and send email.
        $from = \core_user::get_support_user();
        $a = new \stdClass();
        $a->name = fullname($user);
        $a->timeinactive = static::format_timespan(config::get('smartdetect_suspendafter'));
        $a->contact = $from->email;
        $a->signature = fullname($from);
        $subject = get_string_manager()->get_string('email:user:suspend:subject',
                'tool_usersuspension', $a, $user->lang);
        if ($automated) {
            $messagehtml = get_string_manager()->get_string('email:user:suspend:auto:body',
                    'tool_usersuspension', $a, $user->lang);
        } else {
            $messagehtml = get_string_manager()->get_string('email:user:suspend:manual:body',
                    'tool_usersuspension', $a, $user->lang);
        }
        $messagetext = format_text_email($messagehtml, FORMAT_HTML);
        return email_to_user($user, $from, $subject, $messagetext, $messagehtml);
    }

    /**
     * Send an e-mail due to a user facing suspension due to inactivity.
     *
     * @param \stdClass $user
     * @return void
     */
    public static function process_user_warning_email($user) {
        // Prepare and send email.
        $from = \core_user::get_support_user();
        $a = new \stdClass();
        $a->name = fullname($user);
        $a->suspendinterval = static::format_timespan(config::get('smartdetect_suspendafter'));
        $a->warningperiod = static::format_timespan(config::get('smartdetect_warninginterval'));
        $a->contact = $from->email;
        $a->signature = fullname($from);
        $subject = get_string('email:user:warning:subject', 'tool_usersuspension', $a);
        $messagehtml = get_string('email:user:warning:body', 'tool_usersuspension', $a);

        $messagetext = format_text_email($messagehtml, FORMAT_HTML);
        return email_to_user($user, $from, $subject, $messagetext, $messagehtml);
    }

    /**
     * Send an e-mail due to a user being unsuspended
     *
     * @param \stdClass $user
     * @return void
     */
    public static function process_user_unsuspended_email($user) {
        if (!(bool)config::get('send_suspend_email')) {
            return false;
        }
        // Prepare and send email.
        $from = \core_user::get_support_user();
        $a = new \stdClass();
        $a->name = fullname($user);
        $a->contact = $from->email;
        $a->signature = fullname($from);
        $subject = get_string_manager()->get_string('email:user:unsuspend:subject',
                'tool_usersuspension', $a, $user->lang);
        $messagehtml = get_string_manager()->get_string('email:user:unsuspend:body',
                'tool_usersuspension', $a, $user->lang);
        $messagetext = format_text_email($messagehtml, FORMAT_HTML);
        return email_to_user($user, $from, $subject, $messagetext, $messagehtml);
    }

    /**
     * Send an e-mail due to a user being deleted
     *
     * @param \stdClass $user
     * @return bool true if sent, false if disabled or error
     */
    public static function process_user_deleted_email($user) {
        if (!(bool)config::get('send_delete_email')) {
            return false;
        }
        // Prepare and send email.
        $from = \core_user::get_support_user();
        $a = new \stdClass();
        $a->name = fullname($user);
        $a->timesuspended = static::format_timespan(config::get('cleanup_deleteafter'));
        $a->contact = $from->email;
        $a->signature = fullname($from);
        $subject = get_string_manager()->get_string('email:user:delete:subject',
                'tool_usersuspension', $a, $user->lang);
        $messagehtml = get_string_manager()->get_string('email:user:delete:body',
                'tool_usersuspension', $a, $user->lang);
        $messagetext = format_text_email($messagehtml, FORMAT_HTML);
        return email_to_user($user, $from, $subject, $messagetext, $messagehtml);
    }

    /**
     * Clean history logs (if enabled in global config) older than the configured duration.
     *
     * @return boolean
     */
    public static function clean_logs() {
        global $DB;
        if (!(bool)config::get('enablecleanlogs')) {
            return false;
        }
        $DB->delete_records_select('tool_usersuspension_log', 'timecreated < ?',
                array(time() - (int)config::get('cleanlogsafter')));
        return true;
    }

    /**
     * Print a notification message.
     *
     * @param string $msg the notification message to display
     * @param string $class class or type of message. Please use either 'success' or 'error'
     * @return void
     */
    public static function print_notification($msg, $class = 'success') {
        global $OUTPUT;
        $pix = '<img src="' . $OUTPUT->image_url('msg_' . $class, 'tool_usersuspension') . '"/>';
        echo '<div class="tool-usersuspension-notification-' . $class . '">' . $pix . ' ' . $msg . '</div>';
    }

    /**
     * Returns HTML to display a continue button that goes to a particular URL.
     *
     * @param string|moodle_url $url The url the button goes to.
     * @param string $buttontext the text to show on the button.
     * @return string the HTML to output.
     */
    public static function continue_button($url, $buttontext) {
        global $OUTPUT;
        if (!($url instanceof \moodle_url)) {
            $url = new \moodle_url($url);
        }
        $button = new \single_button($url, $buttontext, 'get');
        $button->class = 'continuebutton';

        return $OUTPUT->render($button);
    }

    /**
     * Create a tab object with a nice image view, instead of just a regular tabobject
     *
     * @param string $id unique id of the tab in this tree, it is used to find selected and/or inactive tabs
     * @param string $pix image name
     * @param string $component component where the image will be looked for
     * @param string|moodle_url $link
     * @param string $text text on the tab
     * @param string $title title under the link, by defaul equals to text
     * @param bool $linkedwhenselected whether to display a link under the tab name when it's selected
     * @return \tabobject
     */
    public static function pictabobject($id, $pix = null, $component = 'tool_usersuspension', $link = null,
            $text = '', $title = '', $linkedwhenselected = false) {
        global $OUTPUT;
        $img = '';
        if ($pix !== null) {
            $img = '<img src="' . $OUTPUT->image_url($pix, $component) . '"> ';
        }
        return new \tabobject($id, $link, $img . $text, empty($title) ? $text : $title, $linkedwhenselected);
    }

    /**
     * print the tabs for the overview pages.
     *
     * @param array $params basic url parameters
     * @param string $selected id of the selected tab
     */
    public static function print_view_tabs($params, $selected) {
        global $CFG, $OUTPUT;
        $tabs = array();
        // Add exclusions.
        $exclusions = static::pictabobject('exclusions', 'exclusions', 'tool_usersuspension',
            new \moodle_url('/' . $CFG->admin . '/tool/usersuspension/view/exclude.php', $params),
                get_string('table:exclusions', 'tool_usersuspension'));
        $exclusions->subtree[] = static::pictabobject('excludeaddcohort', null, 'tool_usersuspension',
            new \moodle_url('/' . $CFG->admin . '/tool/usersuspension/view/exclude.php',
                    $params + array('action' => 'add', 'addtype' => 'cohort', 'sesskey' => sesskey())),
                get_string('action:exclude:add:cohort', 'tool_usersuspension'));
        $exclusions->subtree[] = static::pictabobject('excludeadduser', null, 'tool_usersuspension',
            new \moodle_url('/' . $CFG->admin . '/tool/usersuspension/view/exclude.php',
                    $params + array('action' => 'add', 'addtype' => 'user', 'sesskey' => sesskey())),
                get_string('action:exclude:add:user', 'tool_usersuspension'));
        $tabs[] = $exclusions;
        // Add statuslist tabs.
        foreach (statustable::get_viewtypes() as $type) {
            $url = new \moodle_url('/' . $CFG->admin . '/tool/usersuspension/view/statuslist.php', $params);
            $url->param('type', $type);
            $counter = '';
            switch ($type) {
                case statustable::DELETE:
                    $counter = ' (' . static::count_users_to_delete() . ')';
                    break;
                case statustable::SUSPENDED:
                    $counter = ' (' . static::count_suspended_users() . ')';
                    break;
                case statustable::TOSUSPEND:
                    $counter = ' (' . static::count_users_to_suspend() . ')';
                    break;
                case statustable::STATUS:
                    $counter = ' (' . static::count_monitored_users() . ')';
                    break;
            }
            $tabs[] = static::pictabobject($type, 'status_' . $type, 'tool_usersuspension',
                    $url, get_string('table:status:' . $type, 'tool_usersuspension') . $counter);
        }
        // Add upload tab.
        if ((bool)config::get('enablefromupload')) {
            $upload = static::pictabobject('upload', 'upload', 'tool_usersuspension',
                new \moodle_url('/' . $CFG->admin . '/tool/usersuspension/view/upload.php', $params),
                    get_string('link:upload', 'tool_usersuspension'));
            $tabs[] = $upload;
        }

        // Add logs tabs.
        $logs = static::pictabobject('logs', 'logs', 'tool_usersuspension',
            new \moodle_url('/' . $CFG->admin . '/tool/usersuspension/view/log.php', $params + array('history' => 0)),
                get_string('table:logs', 'tool_usersuspension'));
        $logs->subtree[] = static::pictabobject('log_latest', null, 'tool_usersuspension',
            new \moodle_url('/' . $CFG->admin . '/tool/usersuspension/view/log.php', $params + array('history' => 0)),
                get_string('table:log:latest', 'tool_usersuspension'));
        $logs->subtree[] = static::pictabobject('log_all', null, 'tool_usersuspension',
            new \moodle_url('/' . $CFG->admin . '/tool/usersuspension/view/log.php', $params + array('history' => 1)),
                get_string('table:log:all', 'tool_usersuspension'));
        $tabs[] = $logs;

        // Add tests.
        $testfromfolder = static::pictabobject('testfromfolder', null, 'tool_usersuspension',
            new \moodle_url('/' . $CFG->admin . '/tool/usersuspension/view/testfromfolder.php', $params),
                get_string('testfromfolder', 'tool_usersuspension'));
        $tabs[] = $testfromfolder;

        // Add notifications tabs.
        $notifications = static::pictabobject('notifications', null, 'tool_usersuspension',
            new \moodle_url('/' . $CFG->admin . '/tool/usersuspension/view/notifications.php', $params),
                get_string('tab:notifications', 'tool_usersuspension'));
        $tabs[] = $notifications;

        echo $OUTPUT->tabtree($tabs, $selected);
    }

    /**
     * Generate messages (using core notifications) for (every) frontpage in this tool.
     */
    public static function generate_notifications() {
        $messages = [];
        // Main plugin enabled.
        if (!(bool)config::get('enabled')) {
            $messages[] = get_string('config:tool:disabled', 'tool_usersuspension');
        }
        // Auto-suspend enabled.
        if (!(bool)config::get('enablesmartdetect')) {
            $messages[] = get_string('config:smartdetect:disabled', 'tool_usersuspension');
        }
        // Auto-delete enabled.
        if (!(bool)config::get('enablecleanup')) {
            $messages[] = get_string('config:cleanup:disabled', 'tool_usersuspension');
        }
        // Task(s).
        if (!(bool)config::get('enableunsuspendfromfolder')) {
            $messages[] = get_string('config:unsuspendfromfolder:disabled', 'tool_usersuspension');
        }
        if (!(bool)config::get('enablefromfolder')) {
            $messages[] = get_string('config:fromfolder:disabled', 'tool_usersuspension');
        }
        // Folder(s).
        $uploadedfolder = config::get('uploadfolder');
        if (!file_exists($uploadedfolder) || !is_dir($uploadedfolder)) {
            $messages[] = 'CSV upload folder "'.$uploadedfolder.'" does not exist';
        }
        if (!is_readable($uploadedfolder) || !is_dir($uploadedfolder)) {
            $messages[] = 'CSV upload folder "'.$uploadedfolder.'" is not readable';
        }
        if (!empty($messages)) {
            return \html_writer::div(implode('<br/>', $messages), 'alert alert-warning');
        } else {
            return \html_writer::div(get_string('notifications:allok', 'tool_usersuspension'), 'alert alert-success');
        }
    }

}