Mini Shell

Direktori : /var/www/vhosts/ccp.ac.th/smtp.ccp.ac.th/httpdocs/new/moodle/theme/learnr/
Upload File :
Current File : /var/www/vhosts/ccp.ac.th/smtp.ccp.ac.th/httpdocs/new/moodle/theme/learnr/locallib.php

<?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/>.

/**
 * Theme LearnR - Local library
 *
 * @package    theme_learnr
 * @copyright  2022 Alexander Bias, lern.link GmbH <[email protected]>
 * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
 */

/**
 * Build the course related hints HTML code.
 * This function evaluates and composes all course related hints which may appear on a course page below the course header.
 *
 * @copyright  2022 Alexander Bias, lern.link GmbH <[email protected]>
 * @copyright  based on code from theme_boost_campus by Kathrin Osswald.
 *
 * @return string.
 */
function theme_learnr_get_course_related_hints() {
    global $CFG, $COURSE, $PAGE, $USER, $OUTPUT;

    // Require user library.
    require_once($CFG->dirroot.'/user/lib.php');

    // Initialize HTML code.
    $html = '';

    // If the setting showhintcoursehidden is set and the visibility of the course is hidden and
    // a hint for the visibility will be shown.
    if (get_config('theme_learnr', 'showhintcoursehidden') == THEME_LEARNR_SETTING_SELECT_YES
            && has_capability('theme/learnr:viewhintinhiddencourse', \context_course::instance($COURSE->id))
            && $PAGE->has_set_url()
            && $PAGE->url->compare(new moodle_url('/course/view.php'), URL_MATCH_BASE)
            && $COURSE->visible == false) {

        // Prepare template context.
        $templatecontext = array('courseid' => $COURSE->id);

        // If the user has the capability to change the course settings, an additional link to the course settings is shown.
        if (has_capability('moodle/course:update', context_course::instance($COURSE->id))) {
            $templatecontext['showcoursesettingslink'] = true;
        } else {
            $templatecontext['showcoursesettingslink'] = false;
        }

        // Render template and add it to HTML code.
        $html .= $OUTPUT->render_from_template('theme_learnr/course-hint-hidden', $templatecontext);
    }

    // If the setting showhintcourseguestaccess is set and the user is accessing the course with guest access,
    // a hint for users is shown.
    // We also check that the user did not switch the role. This is a special case for roles that can fully access the course
    // without being enrolled. A role switch would show the guest access hint additionally in that case and this is not
    // intended.
    if (get_config('theme_learnr', 'showhintcourseguestaccess') == THEME_LEARNR_SETTING_SELECT_YES
            && is_guest(\context_course::instance($COURSE->id), $USER->id)
            && $PAGE->has_set_url()
            && $PAGE->url->compare(new moodle_url('/course/view.php'), URL_MATCH_BASE)
            && !is_role_switched($COURSE->id)) {

        // Require self enrolment library.
        require_once($CFG->dirroot . '/enrol/self/lib.php');

        // Prepare template context.
        $templatecontext = array('courseid' => $COURSE->id,
                'role' => role_get_name(get_guest_role()));

        // Search for an available self enrolment link in this course.
        $templatecontext['showselfenrollink'] = false;
        $instances = enrol_get_instances($COURSE->id, true);
        $plugins = enrol_get_plugins(true);
        foreach ($instances as $instance) {
            // If the enrolment plugin isn't enabled currently, skip it.
            if (!isset($plugins[$instance->enrol])) {
                continue;
            }

            // Remember the enrolment plugin.
            $plugin = $plugins[$instance->enrol];

            // If there is a self enrolment link.
            if ($plugin->show_enrolme_link($instance)) {
                $templatecontext['showselfenrollink'] = true;
                break;
            }
        }

        // Render template and add it to HTML code.
        $html .= $OUTPUT->render_from_template('theme_learnr/course-hint-guestaccess', $templatecontext);
    }

    // If the setting showhintcourseselfenrol is set, a hint for users is shown that the course allows unrestricted self
    // enrolment. This hint is only shown if the course is visible, the self enrolment is visible and if the user has the
    // capability "theme/learnr:viewhintcourseselfenrol".
    if (get_config('theme_learnr', 'showhintcourseselfenrol') == THEME_LEARNR_SETTING_SELECT_YES
            && has_capability('theme/learnr:viewhintcourseselfenrol', \context_course::instance($COURSE->id))
            && $PAGE->has_set_url()
            && $PAGE->url->compare(new moodle_url('/course/view.php'), URL_MATCH_BASE)
            && $COURSE->visible == true) {

        // Get the active enrol instances for this course.
        $enrolinstances = enrol_get_instances($COURSE->id, true);

        // Prepare to remember when self enrolment is / will be possible.
        $selfenrolmentpossiblecurrently = false;
        $selfenrolmentpossiblefuture = false;
        foreach ($enrolinstances as $instance) {
            // Check if unrestricted self enrolment is possible currently or in the future.
            $now = (new \DateTime("now", \core_date::get_server_timezone_object()))->getTimestamp();
            if ($instance->enrol == 'self' && empty($instance->password) && $instance->customint6 == 1 &&
                    (empty($instance->enrolenddate) || $instance->enrolenddate > $now)) {

                // Build enrol instance object with all necessary information for rendering the note later.
                $instanceobject = new stdClass();

                // Remember instance name.
                if (empty($instance->name)) {
                    $instanceobject->name = get_string('pluginname', 'enrol_self') .
                            " (" . get_string('defaultcoursestudent', 'core') . ")";
                } else {
                    $instanceobject->name = $instance->name;
                }

                // Remember type of unrestrictedness.
                if (empty($instance->enrolenddate) && empty($instance->enrolstartdate)) {
                    $instanceobject->unrestrictedness = 'unlimited';
                    $selfenrolmentpossiblecurrently = true;
                } else if (empty($instance->enrolstartdate) &&
                        !empty($instance->enrolenddate) && $instance->enrolenddate > $now) {
                    $instanceobject->unrestrictedness = 'until';
                    $selfenrolmentpossiblecurrently = true;
                } else if (empty($instance->enrolenddate) &&
                        !empty($instance->enrolstartdate) && $instance->enrolstartdate > $now) {
                    $instanceobject->unrestrictedness = 'from';
                    $selfenrolmentpossiblefuture = true;
                } else if (empty($instance->enrolenddate) &&
                        !empty($instance->enrolstartdate) && $instance->enrolstartdate <= $now) {
                    $instanceobject->unrestrictedness = 'since';
                    $selfenrolmentpossiblecurrently = true;
                } else if (!empty($instance->enrolstartdate) && $instance->enrolstartdate > $now &&
                        !empty($instance->enrolenddate) && $instance->enrolenddate > $now) {
                    $instanceobject->unrestrictedness = 'fromuntil';
                    $selfenrolmentpossiblefuture = true;
                } else if (!empty($instance->enrolstartdate) && $instance->enrolstartdate <= $now &&
                        !empty($instance->enrolenddate) && $instance->enrolenddate > $now) {
                    $instanceobject->unrestrictedness = 'sinceuntil';
                    $selfenrolmentpossiblecurrently = true;
                } else {
                    // This should not happen, thus continue to next instance.
                    continue;
                }

                // Remember enrol start date.
                if (!empty($instance->enrolstartdate)) {
                    $instanceobject->startdate = $instance->enrolstartdate;
                } else {
                    $instanceobject->startdate = null;
                }

                // Remember enrol end date.
                if (!empty($instance->enrolenddate)) {
                    $instanceobject->enddate = $instance->enrolenddate;
                } else {
                    $instanceobject->enddate = null;
                }

                // Remember this instance.
                $selfenrolinstances[$instance->id] = $instanceobject;
            }
        }

        // If there is at least one unrestricted enrolment instance,
        // show the hint with information about each unrestricted active self enrolment in the course.
        if (!empty($selfenrolinstances) &&
                ($selfenrolmentpossiblecurrently == true || $selfenrolmentpossiblefuture == true)) {

            // Prepare template context.
            $templatecontext = array();

            // Add the start of the hint t the template context
            // depending on the fact if enrolment is already possible currently or will be in the future.
            if ($selfenrolmentpossiblecurrently == true) {
                $templatecontext['selfenrolhintstart'] = get_string('showhintcourseselfenrolstartcurrently', 'theme_learnr');
            } else if ($selfenrolmentpossiblefuture == true) {
                $templatecontext['selfenrolhintstart'] = get_string('showhintcourseselfenrolstartfuture', 'theme_learnr');
            }

            // Iterate over all enrolment instances to output the details.
            foreach ($selfenrolinstances as $selfenrolinstanceid => $selfenrolinstanceobject) {
                // If the user has the capability to config self enrolments, enrich the instance name with the settings link.
                if (has_capability('enrol/self:config', \context_course::instance($COURSE->id))) {
                    $url = new moodle_url('/enrol/editinstance.php', array('courseid' => $COURSE->id,
                            'id' => $selfenrolinstanceid, 'type' => 'self'));
                    $selfenrolinstanceobject->name = html_writer::link($url, $selfenrolinstanceobject->name);
                }

                // Add the enrolment instance information to the template context depending on the instance configuration.
                if ($selfenrolinstanceobject->unrestrictedness == 'unlimited') {
                    $templatecontext['selfenrolinstances'][] = get_string('showhintcourseselfenrolunlimited', 'theme_learnr',
                            array('name' => $selfenrolinstanceobject->name));
                } else if ($selfenrolinstanceobject->unrestrictedness == 'until') {
                    $templatecontext['selfenrolinstances'][] = get_string('showhintcourseselfenroluntil', 'theme_learnr',
                            array('name' => $selfenrolinstanceobject->name,
                                    'until' => userdate($selfenrolinstanceobject->enddate)));
                } else if ($selfenrolinstanceobject->unrestrictedness == 'from') {
                    $templatecontext['selfenrolinstances'][] = get_string('showhintcourseselfenrolfrom', 'theme_learnr',
                            array('name' => $selfenrolinstanceobject->name,
                                    'from' => userdate($selfenrolinstanceobject->startdate)));
                } else if ($selfenrolinstanceobject->unrestrictedness == 'since') {
                    $templatecontext['selfenrolinstances'][] = get_string('showhintcourseselfenrolsince', 'theme_learnr',
                            array('name' => $selfenrolinstanceobject->name,
                                    'since' => userdate($selfenrolinstanceobject->startdate)));
                } else if ($selfenrolinstanceobject->unrestrictedness == 'fromuntil') {
                    $templatecontext['selfenrolinstances'][] = get_string('showhintcourseselfenrolfromuntil', 'theme_learnr',
                            array('name' => $selfenrolinstanceobject->name,
                                    'until' => userdate($selfenrolinstanceobject->enddate),
                                    'from' => userdate($selfenrolinstanceobject->startdate)));
                } else if ($selfenrolinstanceobject->unrestrictedness == 'sinceuntil') {
                    $templatecontext['selfenrolinstances'][] = get_string('showhintcourseselfenrolsinceuntil', 'theme_learnr',
                            array('name' => $selfenrolinstanceobject->name,
                                    'until' => userdate($selfenrolinstanceobject->enddate),
                                    'since' => userdate($selfenrolinstanceobject->startdate)));
                }
            }

            // If the user has the capability to config self enrolments, add the call for action to the template context.
            if (has_capability('enrol/self:config', \context_course::instance($COURSE->id))) {
                $templatecontext['calltoaction'] = true;
            } else {
                $templatecontext['calltoaction'] = false;
            }

            // Render template and add it to HTML code.
            $html .= $OUTPUT->render_from_template('theme_learnr/course-hint-selfenrol', $templatecontext);
        }
    }

    // If the setting showswitchedroleincourse is set and the user has switched his role,
    // a hint for the role switch will be shown.
    if (get_config('theme_learnr', 'showswitchedroleincourse') === THEME_LEARNR_SETTING_SELECT_YES
            && is_role_switched($COURSE->id) ) {

        // Get the role name switched to.
        $opts = \user_get_user_navigation_info($USER, $PAGE);
        $role = $opts->metadata['rolename'];

        // Get the URL to switch back (normal role).
        $url = new moodle_url('/course/switchrole.php',
                array('id' => $COURSE->id,
                        'sesskey' => sesskey(),
                        'switchrole' => 0,
                        'returnurl' => $PAGE->url->out_as_local_url(false)));

        // Prepare template context.
        $templatecontext = array('role' => $role,
                'url' => $url->out());

        // Render template and add it to HTML code.
        $html .= $OUTPUT->render_from_template('theme_learnr/course-hint-switchedrole', $templatecontext);
    }

    // Return HTML code.
    return $html;
}

/**
 * Build the link to a static page.
 *
 * @param string $page The static page's identifier.
 * @return string.
 */
function theme_learnr_get_staticpage_link($page) {
    // Compose the URL object.
    $url = new moodle_url('/theme/learnr/pages/'.$page.'.php');

    // Return the string representation of the URL.
    return $url->out();
}

/**
 * Build the page title of a static page.
 *
 * @param string $page The static page's identifier.
 * @return string.
 */
function theme_learnr_get_staticpage_pagetitle($page) {
    // Get the configured page title.
    $pagetitleconfig = format_string(get_config('theme_learnr', $page.'pagetitle'), true,
    ['context' => \context_system::instance()]);

    // If there is a string configured.
    if ($pagetitleconfig) {
        // Return this setting.
        return $pagetitleconfig;

        // Otherwise.
    } else {
        // Return the default string.
        return get_string($page.'pagetitledefault', 'theme_learnr');
    }
}

/**
 * Helper function to check if a given info banner should be shown on this page.
 * This function checks
 * a) if the banner is enabled at all
 * b) if the banner has any content (i.e. is not empty)
 * b) if the banner is configured to be shown on the given page
 * c) if the banner is configured to be shown now (in case it is a time-based banner)
 *
 * @copyright  2022 Alexander Bias, lern.link GmbH <[email protected]>
 * @copyright  based on code from theme_boost_campus by Kathrin Osswald.
 *
 * @param int $bannerno The counting number of the info banner.
 *
 * @return boolean.
 */
function theme_learnr_infobanner_is_shown_on_page($bannerno) {
    global $PAGE;

    // Get theme config.
    $config = get_config('theme_learnr');

    // If the info banner is enabled.
    $enabledsettingname = 'infobanner'.$bannerno.'enabled';
    if ($config->{$enabledsettingname} == THEME_LEARNR_SETTING_SELECT_YES) {
        // If the info banner has any content.
        $contentsettingname = 'infobanner'.$bannerno.'content';
        if (!empty($config->{$contentsettingname})) {
            // If the info banner should be shown on this page.
            $pagessettingname = 'infobanner'.$bannerno.'pages';
            $showonpage = false;
            $pages = explode(',', $config->{$pagessettingname});
            foreach ($pages as $page) {
                if ($PAGE->pagelayout == $page) {
                    $showonpage = true;
                    break;
                }
            }
            if ($showonpage == true) {
                // If this is a time-based-banner.
                $modesettingname = 'infobanner'.$bannerno.'mode';
                if ($config->{$modesettingname} == THEME_LEARNR_SETTING_INFOBANNERMODE_TIMEBASED) {
                    $startsettingname = 'infobanner'.$bannerno.'start';
                    $endsettingname = 'infobanner'.$bannerno.'end';
                    // Check if time settings are empty and try to convert the time strings to a unix timestamp.
                    if (empty($config->{$startsettingname})) {
                        $startempty = true;
                        $start = 0;
                    } else {
                        $startempty = false;
                        $start = $config->{$startsettingname};
                    }
                    if (empty($config->{$endsettingname})) {
                        $endempty = true;
                        $end = 0;
                    } else {
                        $endempty = false;
                        $end = $config->{$endsettingname};
                    }

                    // The banner is shown if
                    // a) now is between start and end time OR
                    // b) start is not set but end is not reached yet OR
                    // c) end is not set, but start lies in the past OR
                    // d) no dates are set, so there's no time restriction.
                    $now = time();
                    if (($now >= $start && $now <= $end ||
                            ($now <= $end && $startempty) ||
                            ($now >= $start && $endempty) ||
                            ($startempty && $endempty))) {
                        return true;
                    }

                    // Otherwise this is a perpetual banner.
                } else {
                    // If the banner was not dismissed by the user.
                    if (get_user_preferences('theme_learnr_infobanner'.$bannerno.'_dismissed') != true) {
                        return true;
                    }
                }
            }
        }
    }

    // Apparently, the banner should not be shown on this page.
    return false;
}

/**
 * Helper function to compare either infobanner or tiles orders.
 *
 * @param int $a The first value
 * @param int $b The second value
 *
 * @return boolean.
 */
function theme_learnr_compare_order($a, $b) {
    // If the same 'order' attribute is given to both items.
    if ($a->order == $b->order) {
        // We have to compare the 'no' attribute.
        // This way, we make sure that the item which is presented first in the admin settings is still placed first in the
        // ordered list even if the same order is configured.
        return ($a->no < $b->no) ? -1 : 1;
    }

    // Otherwise, compare both items based on their 'order' attribute.
    return ($a->order < $b->order) ? -1 : 1;
}

/**
 * Helper function to reset the visibility of a given info banner.
 *
 * @param int $no The number of the info banner.
 *
 * @return bool True if everything went fine, false if at least one user couldn't be resetted.
 */
function theme_learnr_infobanner_reset_visibility($no) {
    global $DB;

    // Clean the no parameter, just to be sure as we will use it within a user preference label (hence in a SQL query).
    $no = clean_param($no, PARAM_INT);

    // Get all users that have dismissed the info banner once and therefore the user preference.
    $whereclause = 'name = :name AND value = :value';
    $params = ['name' => 'theme_learnr_infobanner'.$no.'_dismissed', 'value' => '1'];
    $users = $DB->get_records_select('user_preferences', $whereclause, $params, '', 'userid');

    // Initialize variable for feedback messages.
    $somethingwentwrong = false;
    // Store coding exception.
    $codingexception[] = array();

    foreach ($users as $user) {
        try {
            unset_user_preference('theme_learnr_infobanner'.$no.'_dismissed', $user->userid);
        } catch (coding_exception $e) {
            $somethingwentwrong = true;
        }
    }

    if (!$somethingwentwrong) {
        return true;
    } else {
        return false;
    }
}

/**
 * Get the random number for displaying the background image on the login page randomly.
 *
 * @return int|null
 * @throws coding_exception
 * @throws dml_exception
 */
function theme_learnr_get_random_loginbackgroundimage_number() {
    // Static variable.
    static $number = null;

    if ($number == null) {
        // Get all files for loginbackgroundimages.
        $files = theme_learnr_get_loginbackgroundimage_files();

        // Get count of array elements.
        $filecount = count($files);

        // We only return a number if images are uploaded to the loginbackgroundimage file area.
        if ($filecount > 0) {
            // If Behat tests are running.
            if (defined('BEHAT_SITE_RUNNING')) {
                // Select the last image (to make Behat tests work).
                $number = $filecount;
            } else {
                // Generate random number.
                $number = rand(1, $filecount);
            }
        }
    }

    return $number;
}

/**
 * Get a random class for body tag for the background image of the login page.
 *
 * @return string
 */
function theme_learnr_get_random_loginbackgroundimage_class() {
    // Get the static random number.
    $number = theme_learnr_get_random_loginbackgroundimage_number();

    // Only create the class name with the random number if there is a number (=files uploaded to the file area).
    if ($number != null) {
        return 'loginbackgroundimage'.$number;
    } else {
        return '';
    }
}

/**
 * Return the files from the loginbackgroundimage file area.
 * This function always loads the files from the filearea which is not really performant.
 * However, we accept this at the moment as it is only invoked on the login page.
 *
 * @return array|null
 * @throws coding_exception
 * @throws dml_exception
 */
function theme_learnr_get_loginbackgroundimage_files() {
    // Static variable to remember the files for subsequent calls of this function.
    static $files = null;

    if ($files == null) {
        // Get the system context.
        $systemcontext = \context_system::instance();

        // Get filearea.
        $fs = get_file_storage();

        // Get all files from filearea.
        $files = $fs->get_area_files($systemcontext->id, 'theme_learnr', 'loginbackgroundimage',
                false, 'itemid', false);
    }

    return $files;
}

/**
 *
 * Get the advertisement tile's background image URL from the filearea 'tilebackgroundimage'.tileno.
 *
 * Note:
 * Calling this function for each tile separately is maybe not performant. Originally it was planed to put
 * all files in one filearea. However, at the time of development
 * https://github.com/moodle/moodle/blob/master/lib/outputlib.php#L2062
 * did not support itemids in setting-files of themes.
 *
 * @param int $tileno The tile number.
 * @return string|null
 */
function theme_learnr_get_urloftilebackgroundimage($tileno) {
    // If the tile number is apparently not valid, return.
    // Note: We just check the tile's number, we do not check if the tile is enabled or not.
    if ($tileno < 0 || $tileno > THEME_LEARNR_SETTING_ADVERTISEMENTTILES_COUNT) {
        return null;
    }

    // Get the background image config for this tile.
    $bgconfig = get_config('theme_learnr', 'tile'.$tileno.'backgroundimage');

    // If a background image is configured.
    if (!empty($bgconfig)) {
        // Get the system context.
        $systemcontext = context_system::instance();

        // Get filearea.
        $fs = get_file_storage();

        // Get all files from filearea.
        $files = $fs->get_area_files($systemcontext->id, 'theme_learnr', 'tilebackgroundimage'.$tileno,
                false, 'itemid', false);

        // Just pick the first file - we are sure that there is just one file.
        $file = reset($files);

        // Build and return the image URL.
        return moodle_url::make_pluginfile_url($file->get_contextid(), $file->get_component(), $file->get_filearea(),
                $file->get_itemid(), $file->get_filepath(), $file->get_filename());
    }

    // As no image was found, return null.
    return null;
}

/**
 * Add background images from setting 'loginbackgroundimage' to SCSS.
 *
 * @return string
 */
function theme_learnr_get_loginbackgroundimage_scss() {
    // Initialize variables.
    $count = 0;
    $scss = '';

    // Get all files from filearea.
    $files = theme_learnr_get_loginbackgroundimage_files();

    // Add URL of uploaded images to equivalent class.
    foreach ($files as $file) {
        $count++;
        // Get url from file.
        $url = moodle_url::make_pluginfile_url($file->get_contextid(), $file->get_component(), $file->get_filearea(),
                $file->get_itemid(), $file->get_filepath(), $file->get_filename());
        // Add this url to the body class loginbackgroundimage[n] as a background image.
        $scss .= 'body.pagelayout-login.loginbackgroundimage'.$count.' {';
        $scss .= 'background-image: url("'.$url.'");';
        $scss .= '}';
    }

    return $scss;
}

/**
 * Get the text that should be displayed for the randomly displayed background image on the login page.
 *
 * @return array (of two strings, holding the text and the text color)
 * @throws coding_exception
 * @throws dml_exception
 */
function theme_learnr_get_loginbackgroundimage_text() {
    // Get the random number.
    $number = theme_learnr_get_random_loginbackgroundimage_number();

    // Only search for the text if there's a background image.
    if ($number != null) {

        // Get the files from the filearea loginbackgroundimage.
        $files = theme_learnr_get_loginbackgroundimage_files();
        // Get the file for the selected random number.
        $file = array_slice($files, ($number - 1), 1, false);
        // Get the filename.
        $filename = array_pop($file)->get_filename();

        // Get the config for loginbackgroundimagetext and make an array out of the lines.
        $lines = explode("\n", get_config('theme_learnr', 'loginbackgroundimagetext'));

        // Process the lines.
        foreach ($lines as $line) {
            $settings = explode("|", $line);
            // If the line does not have three items, skip it.
            if (count($settings) != 3) {
                continue;
            }
            // Compare the filenames for a match.
            if (strcmp($filename, trim($settings[0])) == 0) {
                // Trim the second parameter as we need it more than once.
                $settings[2] = trim($settings[2]);

                // If the color value is not acceptable, replace it with dark.
                if ($settings[2] != 'dark' && $settings[2] != 'light') {
                    $settings[2] = 'dark';
                }

                // Return the text + text color that belongs to the randomly selected image.
                return array(format_string(trim($settings[1])), $settings[2]);
            }
        }
    }

    return '';
}

/**
 * Return the files from the additionalresources file area as templatecontext structure.
 * It was designed to compose the files for the settings-additionalresources-filelist.mustache template.
 * This function always loads the files from the filearea which is not really performant.
 * Thus, you have to take care where and how often you use it (or add some caching).
 *
 * @return array|null
 * @throws coding_exception
 * @throws dml_exception
 */
function theme_learnr_get_additionalresources_templatecontext() {
    global $OUTPUT;

    // Static variable to remember the files for subsequent calls of this function.
    static $filesforcontext = null;

    if ($filesforcontext == null) {
        // Get the system context.
        $systemcontext = \context_system::instance();

        // Get filearea.
        $fs = get_file_storage();

        // Get all files from filearea.
        $files = $fs->get_area_files($systemcontext->id, 'theme_learnr', 'additionalresources', false, 'itemid', false);

        // Iterate over the files and fill the templatecontext of the file list.
        $filesforcontext = array();
        foreach ($files as $af) {
            $urlpersistent = new moodle_url('/pluginfile.php/1/theme_learnr/additionalresources/0/'.$af->get_filename());
            $urlrevisioned = new moodle_url('/pluginfile.php/1/theme_learnr/additionalresources/'.theme_get_revision().
                    '/'.$af->get_filename());
            $filesforcontext[] = array('filename' => $af->get_filename(),
                                        'filetype' => $af->get_mimetype(),
                                        'filesize' => display_size($af->get_filesize()),
                                        'fileicon' => $OUTPUT->image_icon(file_file_icon($af, 64), get_mimetype_description($af)),
                                        'fileurlpersistent' => $urlpersistent->out(),
                                        'fileurlrevisioned' => $urlrevisioned->out());
        }
    }

    return $filesforcontext;
}

/**
 * Return the files from the customfonts file area as templatecontext structure.
 * It was designed to compose the files for the settings-customfonts-filelist.mustache template.
 * This function always loads the files from the filearea which is not really performant.
 * Thus, you have to take care where and how often you use it (or add some caching).
 *
 * @return array|null
 * @throws coding_exception
 * @throws dml_exception
 */
function theme_learnr_get_customfonts_templatecontext() {
    global $OUTPUT;

    // Static variable to remember the files for subsequent calls of this function.
    static $filesforcontext = null;

    if ($filesforcontext == null) {
        // Get the system context.
        $systemcontext = \context_system::instance();

        // Get filearea.
        $fs = get_file_storage();

        // Get all files from filearea.
        $files = $fs->get_area_files($systemcontext->id, 'theme_learnr', 'customfonts', false, 'itemid', false);

        // Get the webfonts extensions list.
        $webfonts = theme_learnr_get_webfonts_extensions();

        // Iterate over the files.
        $filesforcontext = array();
        foreach ($files as $af) {
            // Get the filename.
            $filename = $af->get_filename();

            // Check if the file is really a font file (as we can't really rely on the upload restriction in settings.php)
            // according to its file suffix (as the filetype might not have a known mimetype).
            // If it isn't a font file, skip it.
            $filenamesuffix = pathinfo($filename, PATHINFO_EXTENSION);
            if (!in_array('.'.$filenamesuffix, $webfonts)) {
                continue;
            }

            // Otherwise, fill the templatecontext of the file list.
            $urlpersistent = new moodle_url('/pluginfile.php/1/theme_learnr/customfonts/0/'.$filename);
            $filesforcontext[] = array('filename' => $filename,
                    'fileurlpersistent' => $urlpersistent->out());
        }
    }

    return $filesforcontext;
}

/**
 * Helper function which returns an array of accepted webfonts extensions (including the dots).
 *
 * @return array
 */
function theme_learnr_get_webfonts_extensions() {
    return array('.eot', '.otf', '.svg', '.ttf', '.woff', '.woff2');
}

/**
 * Helper function which makes sure that all webfont file types are registered in the system.
 * The webfont file types need to be registered in the system, otherwise the admin settings filepicker wouldn't allow restricting
 * the uploadable file types to webfonts only.
 *
 * Please note: If custom filetypes are defined in config.php, registering additional filetypes is not possible
 * due to a restriction in the set_custom_types() function in Moodle core. In this case, this function does not
 * register anything and will return false.
 *
 * @return boolean true if the filetypes were registered, false if not.
 * @throws coding_exception
 */
function theme_learnr_register_webfonts_filetypes() {
    global $CFG;

    // If customfiletypes are set in config.php, we can't do anything.
    if (array_key_exists('customfiletypes', $CFG->config_php_settings)) {
        return false;
    }

    // Our array of webfont file types to register.
    // As we want to keep things simple, we do not set a particular icon for these file types.
    // Likewise, we do not set any type groups or use descriptions from the language pack.
    $webfonts = array(
            'eot' => array(
                    'extension' => 'eot',
                    'mimetype' => 'application/vnd.ms-fontobject',
                    'coreicon' => 'unknown'
            ),
            'otf' => array(
                    'extension' => 'otf',
                    'mimetype' => 'font/otf',
                    'coreicon' => 'unknown'
            ),
            'svg' => array(
                    'extension' => 'svg',
                    'mimetype' => 'image/svg+xml',
                    'coreicon' => 'unknown'
            ),
            'ttf' => array(
                    'extension' => 'ttf',
                    'mimetype' => 'font/ttf',
                    'coreicon' => 'unknown'
            ),
            'woff' => array(
                    'extension' => 'woff',
                    'mimetype' => 'font/woff',
                    'coreicon' => 'unknown'
            ),
            'woff2' => array(
                    'extension' => 'woff2',
                    'mimetype' => 'font/woff2',
                    'coreicon' => 'unknown'
            ),
    );

    // First, get the list of currently registered file types.
    $currenttypes = core_filetypes::get_types();

    // Iterate over the webfonts file types.
    foreach ($webfonts as $f) {
        // If the file type is already registered, skip it.
        if (array_key_exists($f['extension'], $currenttypes)) {
            continue;
        }

        // Otherwise, register the file type.
        core_filetypes::add_type($f['extension'], $f['mimetype'], $f['coreicon']);
    }

    return true;
}

/**
 * Helper function to render a preview of a HTML email to be shown on the theme settings page.
 *
 * If E-Mails have been branded, an E-Mail preview will be returned as string.
 * Otherwise, null will be returned.
 *
 * @return string|null
 */
function theme_learnr_get_emailbrandinghtmlpreview() {
    global $OUTPUT;

    // Get branding snippets.
    $htmlprefix = get_string('templateemailhtmlprefix', 'theme_learnr');
    $htmlsuffix = get_string('templateemailhtmlsuffix', 'theme_learnr');

    // If no snippet was customized, return null.
    if (trim($htmlprefix) == '' && trim($htmlsuffix) == '') {
        return null;
    }

    // Otherwise, compose mail text.
    $mailtemplatecontext = array('body' => get_string('emailbrandinghtmldemobody', 'theme_learnr'));
    $mail = $OUTPUT->render_from_template('core/email_html', $mailtemplatecontext);

    // And compose mail preview.
    $previewtemplatecontext = array('mail' => $mail);
    $preview = $OUTPUT->render_from_template('theme_learnr/emailpreview', $previewtemplatecontext);

    return $preview;
}

/**
 * Helper function to render a preview of a plaintext email to be shown on the theme settings page.
 *
 * If E-Mails have been branded, an E-Mail preview will be returned as string.
 * Otherwise, null will be returned.
 *
 * @return string|null
 */
function theme_learnr_get_emailbrandingtextpreview() {
    global $OUTPUT;

    // Get branding snippets.
    $textprefix = get_string('templateemailtextprefix', 'theme_learnr');
    $textsuffix = get_string('templateemailtextsuffix', 'theme_learnr');

    // If no snippet was customized, return null.
    if (trim($textprefix) == '' && trim($textsuffix) == '') {
        return null;
    }

    // Otherwise, compose mail text.
    $mailtemplatecontext = array('body' => get_string('emailbrandingtextdemobody', 'theme_learnr'));
    $mail = nl2br($OUTPUT->render_from_template('core/email_text', $mailtemplatecontext));
    $mail = '<div class="text-monospace">'.$mail.'</div>';

    // And compose mail preview.
    $previewtemplatecontext = array('mail' => $mail);
    $preview = $OUTPUT->render_from_template('theme_learnr/emailpreview', $previewtemplatecontext);

    return $preview;
}

/**
 * Callback function which is called from settings.php if the FontAwesome files setting has changed.
 *
 * It gets all files from the files setting, picks all the expected files (and ignores all others)
 * and stores them into an application cache for quicker access.
 *
 * @return void
 */
function theme_learnr_fontawesome_checkin() {
    // Create cache for FontAwesome files.
    $cache = cache::make('theme_learnr', 'fontawesome');

    // Purge the existing cache values as we will refill the cache now.
    $cache->purge();

    // Get FontAwesome version config.
    $faconfig = get_config('theme_learnr', 'fontawesomeversion');

    // If a FontAwesome version is enabled.
    if ($faconfig != THEME_LEARNR_SETTING_FAVERSION_NONE && $faconfig != null) {

        // Get the system context.
        $systemcontext = \context_system::instance();

        // Get filearea.
        $fs = get_file_storage();

        // Get FontAwesome file structure.
        $filestructure = theme_learnr_get_fontawesome_filestructure($faconfig);

        // If a valid file structure could be retrieved.
        if ($filestructure != null) {

            // Iterate over the folder structure.
            foreach ($filestructure as $folder => $files) {

                // Initialize a folder list.
                $folderlist = array();

                // Iterate over the files in the folder.
                foreach ($files as $file => $expected) {

                    // Try to get the file from the filearea.
                    $fsfile = $fs->get_file($systemcontext->id, 'theme_learnr', 'fontawesome', 0, '/'.$folder.'/', $file);

                    // If the file exists.
                    if ($fsfile != false) {
                        // Add the file to the folder list.
                        $folderlist[] = $file;
                    }
                }

                // Add the folder to the cache.
                $cache->set($folder, $folderlist);
            }
        }
    }

    // Add a marker value to the cache which indicates that the files have been checked into the cache completely.
    // This will help to decide later if the cache is really empty (and should be refilled) or if there aren't just any
    // files uploaded.
    $cache->set('checkedin', true);
}

/**
 * Helper function which returns an array of accepted fontawesome file extensions (including the dots).
 *
 * @return array
 */
function theme_learnr_get_fontawesome_extensions() {
    return array('.css', '.eot', '.svg', '.ttf', '.woff', '.woff2');
}

/**
 * Helper function which returns the files which are expected to be provided for a given FontAwesome version.
 *
 * @param string $version The FontAwesome version, given as THEME_LEARNR_SETTING_FAVERSION_* constant.
 *
 * @return array|null The array of files or null if an invalid FontAwesome version was provided.
 */
function theme_learnr_get_fontawesome_filestructure($version) {
    // Pick the files for the selected FA version.
    switch ($version) {
        case THEME_LEARNR_SETTING_FAVERSION_FA6FREE:
            $files = array('css' => array('fontawesome.min.css' => THEME_LEARNR_SETTING_FAFILES_MANDATORY,
                            'solid.min.css' => THEME_LEARNR_SETTING_FAFILES_MANDATORY,
                            'regular.min.css' => THEME_LEARNR_SETTING_FAFILES_OPTIONAL,
                            'brands.min.css' => THEME_LEARNR_SETTING_FAFILES_OPTIONAL,
                            'v4-font-face.min.css' => THEME_LEARNR_SETTING_FAFILES_MANDATORY),
                    'webfonts' => array('fa-solid-900.woff2' => THEME_LEARNR_SETTING_FAFILES_MANDATORY,
                            'fa-solid-900.ttf' => THEME_LEARNR_SETTING_FAFILES_MANDATORY,
                            'fa-regular-400.woff2' => THEME_LEARNR_SETTING_FAFILES_OPTIONAL,
                            'fa-regular-400.ttf' => THEME_LEARNR_SETTING_FAFILES_OPTIONAL,
                            'fa-brands-400.woff2' => THEME_LEARNR_SETTING_FAFILES_OPTIONAL,
                            'fa-brands-400.ttf' => THEME_LEARNR_SETTING_FAFILES_OPTIONAL,
                            'fa-v4compatibility.woff2' => THEME_LEARNR_SETTING_FAFILES_MANDATORY,
                            'fa-v4compatibility.ttf' => THEME_LEARNR_SETTING_FAFILES_MANDATORY));
            break;
        default:
            // This only happens if an invalid version was provided.
            $files = null;
    }

    // Return the file structure.
    return $files;
}

/**
 * Helper function which return the files from the fontawesome file area as templatecontext structure.
 * It was designed to compose the files for the settings-fontawesome-filelist.mustache template.
 * This function uses the fontawesome cache definition, i.e. it does not load the files from the filearea directly.
 * This means it uses the same data source as the theme_learnr_add_fontawesome_to_page() function which adds
 * the fontawesome files to the page.
 *
 * @return array|null
 * @throws coding_exception
 * @throws dml_exception
 */
function theme_learnr_get_fontawesome_templatecontext() {
    // Create cache for FontAwesome files.
    $cache = cache::make('theme_learnr', 'fontawesome');

    // If the cache is completely empty, check the files in on-the-fly.
    if ($cache->get('checkedin') != true) {
        theme_learnr_fontawesome_checkin();
    }

    // Get FontAwesome version config.
    $faconfig = get_config('theme_learnr', 'fontawesomeversion');

    // If a FontAwesome version is enabled.
    if ($faconfig != THEME_LEARNR_SETTING_FAVERSION_NONE && $faconfig != null) {

        // Initialize context variable.
        $filesforcontext = array();

        // Get FontAwesome file structure.
        $filestructure = theme_learnr_get_fontawesome_filestructure($faconfig);

        // If a valid file structure could be retrieved.
        if ($filestructure != null) {

            // Iterate over the folder structure.
            foreach ($filestructure as $folder => $files) {

                // Get the cached data for this folder.
                $cachedfolder = $cache->get($folder);

                // Iterate over the files in the folder structure.
                foreach ($files as $file => $expected) {

                    // Deduce the mandatory value.
                    if ($expected == THEME_LEARNR_SETTING_FAFILES_MANDATORY) {
                        $mandatory = true;
                    } else {
                        $mandatory = false;
                    }

                    // Compose the file path.
                    $filepath = $folder . '/' . $file;

                    // Get the description of the file.
                    $fileidentifier = str_replace('/', '-', $filepath);
                    $description = get_string('fontawesomelistfileinfo-' . $faconfig . '-' . $fileidentifier, 'theme_learnr');

                    // If the folder was not uploaded at all or if the folder is empty, we do not need to check if the file exists.
                    // We can add the file as non-existent right away.
                    if ($cachedfolder == null || ($cachedfolder == array()) && count($cachedfolder) < 1) {
                        $exists = false;

                        // Otherwise, we have to check the file it was uploaded.
                    } else {
                        $exists = in_array($file, $cachedfolder);
                    }

                    // Add the file to the template structure.
                    $filesforcontext[] = array('filepath' => $filepath, 'exists' => $exists, 'mandatory' => $mandatory,
                            'description' => $description);
                }
            }
        }
    }

    return $filesforcontext;
}

/**
 * Helper function which returns the visual checks for the configured FontAwesome version.
 *
 * @return array|null The array of checks or null if an invalid FontAwesome version is configured.
 */
function theme_learnr_get_fontawesome_checks_templatecontext() {
    global $CFG;

    // Get FontAwesome version config.
    $version = get_config('theme_learnr', 'fontawesomeversion');

    // Pick the checks for the selected FA version.
    switch ($version) {
        case THEME_LEARNR_SETTING_FAVERSION_FA6FREE:
            $checks = array(
                    array('icon' => '<i class="fa fa-check-circle-o fa-3x fa-fw"></i>',
                            'title' => get_string('fontawesomecheck-fa6free-general-title', 'theme_learnr'),
                            'description' => get_string('fontawesomecheck-fa6free-general-description', 'theme_learnr')),
                    array('icon' => '<i class="fa fa-map-o fa-3x fa-fw"></i>',
                            'title' => get_string('fontawesomecheck-fa6free-fallback-title', 'theme_learnr'),
                            'description' => get_string('fontawesomecheck-fa6free-fallback-description', 'theme_learnr')),
                    array('icon' => '<i class="fa-solid fa-virus-covid fa-3x fa-fw"></i>',
                            'title' => get_string('fontawesomecheck-fa6free-newstuff-title', 'theme_learnr'),
                            'description' => get_string('fontawesomecheck-fa6free-newstuff-description', 'theme_learnr')),
            );
            break;
        default:
            // This only happens if an invalid version was provided.
            $checks = null;
    }

    // If the filter_fontawesome plugin is installed, add a check for filtering the icons.
    if (file_exists($CFG->dirroot.'/filter/fontawesome/version.php')) {
        $checks[] = array('icon' => format_text('[fa-solid fa-users-line fa-3x fa-fw]'),
                'title' => get_string('fontawesomecheck-fa6free-filter-title', 'theme_learnr'),
                'description' => get_string('fontawesomecheck-fa6free-filter-description', 'theme_learnr'));
    }

    // Return the checks structure.
    return $checks;
}

/**
 * Helper function to compose the title of an external admin page.
 * This is adopted from /admin/settings.php and done to make sure that the external admin pages look as similar as possible
 * to the standard admin pages.
 *
 * @param string $pagename The page's name.
 *
 * @return string
 */
function theme_learnr_get_externaladminpage_title($pagename) {
    global $SITE;

    $title = $SITE->shortname.': ';
    $title .= get_string('administration', 'core').': ';
    $title .= get_string('appearance', 'core').': ';
    $title .= get_string('themes', 'core').': ';
    $title .= get_string('pluginname', 'theme_learnr').': ';
    $title .= $pagename;

    return $title;
}

/**
 * Helper function to compose the heading of an external admin page.
 * This is adopted from /admin/settings.php and done to make sure that the external admin pages look as similar as possible
 * to the standard admin pages.
 *
 * @return string
 */
function theme_learnr_get_externaladminpage_heading() {
    global $SITE;

    return $SITE->fullname;
}

/**
 * Helper function which adds the CSS files from the fontawesome file area to the Moodle page.
 * This function uses the fontawesome cache definition, i.e. it does not load the files from the filearea directly.
 * It's meant to be called by theme_learnr_before_standard_html_head() only.
 * *
 * @throws coding_exception
 * @throws dml_exception
 * @throws moodle_exception
 */
function theme_learnr_add_fontawesome_to_page() {
    global $PAGE;

    // Create cache for FontAwesome files.
    $cache = cache::make('theme_learnr', 'fontawesome');

    // If the cache is completely empty, check the files in on-the-fly.
    if ($cache->get('checkedin') != true) {
        theme_learnr_fontawesome_checkin();
    }

    // Get FontAwesome version config.
    $faconfig = get_config('theme_learnr', 'fontawesomeversion');

    // If a FontAwesome version is enabled.
    if ($faconfig != THEME_LEARNR_SETTING_FAVERSION_NONE && $faconfig != null) {

        // Get the cached data for the CSS folder (we do not need to add files from any other folders in the cache).
        $cachedfolder = $cache->get('css');

        // Iterate over the files in the cached folder structure.
        foreach ($cachedfolder as $cachedfile) {

            // Build the FontAwesome CSS file URL.
            $facssurl = new moodle_url('/pluginfile.php/1/theme_learnr/fontawesome/' .
                    theme_get_revision().'/css/'.$cachedfile);

            // Add the CSS file to the page.
            $PAGE->requires->css($facssurl);
        }
    }
}

/**
 * Helper function which adds the CSS file from the flavour to the Moodle page.
 * It's meant to be called by theme_learnr_before_standard_html_head() only.
 * *
 * @throws coding_exception
 * @throws dml_exception
 * @throws moodle_exception
 */
function theme_learnr_add_flavourcss_to_page() {
    global $CFG, $PAGE;

    // Require flavours library.
    require_once($CFG->dirroot . '/theme/learnr/flavours/flavourslib.php');

    // If any flavour applies to this page.
    $flavour = theme_learnr_get_flavour_which_applies();
    if ($flavour != null) {
        // Build the flavour CSS file URL.
        $flavourcssurl = new moodle_url('/theme/learnr/flavours/styles.php',
                array('id' => $flavour->id, 'rev' => theme_get_revision()));

        // Add the CSS file to the page.
        $PAGE->requires->css($flavourcssurl);
    }
}

/**
 * Helper function which returns the course header image url, picking the current course from the course settings
 * or the fallback image from the theme.
 * If no course header image can should be shown for the current course, the function returns null.
 *
 * @return null | string
 */
function theme_learnr_get_course_header_image_url() {
    global $PAGE;

    // If the current course is the frontpage course (which means that we are not within any real course),
    // directly return null.
    if (isset($PAGE->course->id) && $PAGE->course->id == SITEID) {
        return null;
    }

    // Get the course image.
    $courseimage = \core_course\external\course_summary_exporter::get_course_image($PAGE->course);

    // If the course has a course image.
    if ($courseimage) {
        // Then return it directly.
        return $courseimage;

        // Otherwise, if a fallback image is configured.
    } else if (get_config('theme_learnr', 'courseheaderimagefallback')) {
        // Get the system context.
        $systemcontext = \context_system::instance();

        // Get filearea.
        $fs = get_file_storage();

        // Get all files from filearea.
        $files = $fs->get_area_files($systemcontext->id, 'theme_learnr', 'courseheaderimagefallback',
            false, 'itemid', false);

        // Just pick the first file - we are sure that there is just one file.
        $file = reset($files);

        // Build and return the image URL.
        return moodle_url::make_pluginfile_url($file->get_contextid(), $file->get_component(), $file->get_filearea(),
            $file->get_itemid(), $file->get_filepath(), $file->get_filename());
    }

    // Begin DBN Update.
    elseif ($hasheaderbg) {
        return $PAGE->theme->image_url('noimg', 'theme')->out();
    }
    // End DBN Update.

    // As no picture was found, return null.
    return null;
}

/**
 * Helper function which sets the URL to the CSS file as soon as the theme's mobilescss setting has any CSS code.
 * It's meant to be called as callback when changing the admin setting only.
 * *
 * @throws coding_exception
 * @throws dml_exception
 * @throws moodle_exception
 */
function theme_learnr_set_mobilecss_url() {
    // Check if the admin has set any CSS code for the Mobile app.
    $csscode = get_config('theme_learnr', 'mobilescss');
    if (!empty($csscode)) {
        // Build the Mobile app CSS file URL and especially add the current time as rev parameter.
        // This parameter isn't the theme revision as the theme cache is not cleared when this setting is stored.
        // It is just the time when the setting is saved.
        // This is the best we can do to make the Mobile app load the new styles when needed.
        $mobilescssurl = new moodle_url('/theme/learnr/mobile/styles.php', array('rev' => time()));

        // Set the $CFG->mobilecssurl setting.
        set_config('mobilecssurl', $mobilescssurl->out());

        // Otherwise.
    } else {
        // Clear the $CFG->mobilecssurl setting.
        set_config('mobilecssurl', '');
    }
}

/**
 * Returns an array of the defined additional block regions.
 *
 * @param array $pageregions List of page regions.
 * @return array $regions
 */
function theme_learnr_get_additional_regions($pageregions=[]) {
    $regions = [
            'footerleft' => 'footer-left',
            'footerright' => 'footer-right',
            'footercenter' => 'footer-center',
            'offcanvasleft' => 'offcanvas-left',
            'offcanvasright' => 'offcanvas-right',
            'offcanvascenter' => 'offcanvas-center',
            'outsideleft' => 'outside-left',
            'outsideright' => 'outside-right',
            'outsidetop' => 'outside-top',
            'outsidebottom' => 'outside-bottom',
            'contentupper' => 'content-upper',
            'contentlower' => 'content-lower',
            'header' => 'header'
    ];

    return ($pageregions) ? array_intersect($regions, $pageregions) : $regions;
}

/**
 * Get the defined regions for the page layout.
 *
 * @param string $layout Pagelayout name.
 * @return array $regions
 */
function theme_learnr_get_block_regions($layout) {

    // Get the admin setting for the layout.
    $regionsettings = get_config('theme_learnr', 'blockregionsfor'.$layout);

    // Explode the admin setting to get the block regions.
    $settings = !empty($regionsettings) ? explode(',', $regionsettings) : [];

    // Add the configured regions to the side-pre region (which is always provided by Boost core).
    $regions = array_merge(['side-pre'], $settings);

    // Return.
    return $regions;
}
// Begin DBN Update.
function theme_learnr_get_course_activities() {
    GLOBAL $CFG, $PAGE, $OUTPUT;
    // A copy of block_activity_modules.
    $course = $PAGE->course;
    $content = new stdClass();
    $modinfo = get_fast_modinfo($course);
    $modfullnames = array();

    $archetypes = array();

    foreach ($modinfo->cms as $cm) {
        // Exclude activities which are not visible or have no link (=label).
        if (!$cm->uservisible or !$cm->has_view()) {
            continue;
        }
        if (array_key_exists($cm->modname, $modfullnames)) {
            continue;
        }
        if (!array_key_exists($cm->modname, $archetypes)) {
            $archetypes[$cm->modname] = plugin_supports('mod', $cm->modname, FEATURE_MOD_ARCHETYPE, MOD_ARCHETYPE_OTHER);
        }
        if ($archetypes[$cm->modname] == MOD_ARCHETYPE_RESOURCE) {
            if (!array_key_exists('resources', $modfullnames)) {
                $modfullnames['resources'] = get_string('resources');
            }
        } else {
            $modfullnames[$cm->modname] = $cm->modplural;
        }
    }
    core_collator::asort($modfullnames);

    return $modfullnames;
}
// End DBN Update.

Zerion Mini Shell 1.0