Mini Shell
<?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 - 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
*/
// Constants which are use throughout this theme.
define('THEME_LEARNR_SETTING_SELECT_YES', 'yes');
define('THEME_LEARNR_SETTING_SELECT_NO', 'no');
define('THEME_LEARNR_SETTING_STATICPAGELINKPOSITION_NONE', 'none');
define('THEME_LEARNR_SETTING_STATICPAGELINKPOSITION_FOOTNOTE', 'footnote');
define('THEME_LEARNR_SETTING_STATICPAGELINKPOSITION_FOOTER', 'footer');
define('THEME_LEARNR_SETTING_STATICPAGELINKPOSITION_BOTH', 'both');
define('THEME_LEARNR_SETTING_HIDENODESPRIMARYNAVIGATION_HOME', 'home');
define('THEME_LEARNR_SETTING_HIDENODESPRIMARYNAVIGATION_MYHOME', 'myhome');
define('THEME_LEARNR_SETTING_HIDENODESPRIMARYNAVIGATION_MYCOURSES', 'courses');
define('THEME_LEARNR_SETTING_HIDENODESPRIMARYNAVIGATION_SITEADMIN', 'siteadmin');
define('THEME_LEARNR_SETTING_INFOBANNER_COUNT', 5);
define('THEME_LEARNR_SETTING_INFOBANNERPAGES_MY', 'mydashboard');
define('THEME_LEARNR_SETTING_INFOBANNERPAGES_MYCOURSES', 'mycourses');
define('THEME_LEARNR_SETTING_INFOBANNERPAGES_SITEHOME', 'frontpage');
define('THEME_LEARNR_SETTING_INFOBANNERPAGES_COURSE', 'course');
define('THEME_LEARNR_SETTING_INFOBANNERPAGES_LOGIN', 'login');
define('THEME_LEARNR_SETTING_INFOBANNERMODE_PERPETUAL', 'perp');
define('THEME_LEARNR_SETTING_INFOBANNERMODE_TIMEBASED', 'time');
define('THEME_LEARNR_SETTING_ADVERTISEMENTTILES_COUNT', 12);
define('THEME_LEARNR_SETTING_ADVERTISEMENTTILES_COLUMN_COUNT', 4);
define('THEME_LEARNR_SETTING_ADVERTISEMENTTILES_FRONTPAGEPOSITION_BEFORE', 1);
define('THEME_LEARNR_SETTING_ADVERTISEMENTTILES_FRONTPAGEPOSITION_AFTER', 2);
define('THEME_LEARNR_SETTING_FAVERSION_NONE', 'none');
define('THEME_LEARNR_SETTING_FAVERSION_FA6FREE', 'fa6free');
define('THEME_LEARNR_SETTING_FAFILES_MANDATORY', 'm');
define('THEME_LEARNR_SETTING_FAFILES_OPTIONAL', 'o');
define('THEME_LEARNR_SETTING_HEIGHT_100PX', '100px');
define('THEME_LEARNR_SETTING_HEIGHT_150PX', '150px');
define('THEME_LEARNR_SETTING_HEIGHT_200PX', '200px');
define('THEME_LEARNR_SETTING_HEIGHT_250PX', '250px');
define('THEME_LEARNR_SETTING_IMAGEPOSITION_CENTER_CENTER', 'center center');
define('THEME_LEARNR_SETTING_IMAGEPOSITION_CENTER_TOP', 'center top');
define('THEME_LEARNR_SETTING_IMAGEPOSITION_CENTER_BOTTOM', 'center bottom');
define('THEME_LEARNR_SETTING_IMAGEPOSITION_LEFT_TOP', 'left top');
define('THEME_LEARNR_SETTING_IMAGEPOSITION_LEFT_CENTER', 'left center');
define('THEME_LEARNR_SETTING_IMAGEPOSITION_LEFT_BOTTOM', 'left bottom');
define('THEME_LEARNR_SETTING_IMAGEPOSITION_RIGHT_TOP', 'right top');
define('THEME_LEARNR_SETTING_IMAGEPOSITION_RIGHT_CENTER', 'right center');
define('THEME_LEARNR_SETTING_IMAGEPOSITION_RIGHT_BOTTOM', 'right bottom');
define('THEME_LEARNR_SETTING_COURSEIMAGELAYOUT_STACKEDDARK', 'stackeddark');
define('THEME_LEARNR_SETTING_COURSEIMAGELAYOUT_STACKEDLIGHT', 'stackedlight');
define('THEME_LEARNR_SETTING_COURSEIMAGELAYOUT_HEADINGABOVE', 'headingabove');
define('THEME_LEARNR_SETTING_LINKTARGET_SAMEWINDOW', 'same');
define('THEME_LEARNR_SETTING_LINKTARGET_NEWTAB', 'new');
define('THEME_LEARNR_SETTING_LOGINFORMPOS_CENTER', 'center');
define('THEME_LEARNR_SETTING_LOGINFORMPOS_LEFT', 'left');
define('THEME_LEARNR_SETTING_LOGINFORMPOS_RIGHT', 'right');
define('THEME_LEARNR_SETTING_NAVBARCOLOR_LIGHT', 'light');
define('THEME_LEARNR_SETTING_NAVBARCOLOR_DARK', 'dark');
define('THEME_LEARNR_SETTING_NAVBARCOLOR_PRIMARYLIGHT', 'primarylight');
define('THEME_LEARNR_SETTING_NAVBARCOLOR_PRIMARYDARK', 'primarydark');
define('THEME_LEARNR_SETTING_OUTSIDEREGIONSPLACEMENT_NEXTMAINCONTENT', 'nextmaincontent');
define('THEME_LEARNR_SETTING_OUTSIDEREGIONSPLACEMENT_NEARWINDOW', 'nearwindowedges');
define('THEME_LEARNR_SETTING_OUTSIDEREGIONSWITH_FULLWIDTH', 'fullwidth');
define('THEME_LEARNR_SETTING_OUTSIDEREGIONSWITH_COURSECONTENTWIDTH', 'coursecontentwidth');
define('THEME_LEARNR_SETTING_OUTSIDEREGIONSWITH_HEROWIDTH', 'herowidth');
/**
* Returns the main SCSS content.
*
* @param theme_config $theme The theme config object.
* @return string
*/
function theme_learnr_get_main_scss_content($theme) {
global $CFG;
$scss = '';
$filename = !empty($theme->settings->preset) ? $theme->settings->preset : null;
$fs = get_file_storage();
$context = context_system::instance();
$scss .= file_get_contents($CFG->dirroot . '/theme/learnr/scss/learnr/pre.scss');
if ($filename && ($presetfile = $fs->get_file($context->id, 'theme_learnr', 'preset', 0, '/', $filename))) {
$scss .= $presetfile->get_content();
} else {
// Safety fallback - maybe new installs etc.
$scss .= file_get_contents($CFG->dirroot . '/theme/learnr/scss/preset/default.scss');
}
// Begin DBN Update.
if ($theme->settings->sectionstyle == 1) {
$scss .= file_get_contents($CFG->dirroot . '/theme/learnr/scss/sections/sections-learnr.scss');
}
if ($theme->settings->sectionstyle == 2) {
$scss .= file_get_contents($CFG->dirroot . '/theme/learnr/scss/sections/sections-boxed.scss');
}
if ($theme->settings->sectionstyle == 3) {
$scss .= file_get_contents($CFG->dirroot . '/theme/learnr/scss/sections/sections-boost.scss');
}
if ($theme->settings->sectionstyle == 4) {
$scss .= file_get_contents($CFG->dirroot . '/theme/learnr/scss/sections/sections-bars.scss');
}
// End DBN Update.
$scss .= file_get_contents($CFG->dirroot . '/theme/learnr/scss/learnr/post.scss');
return $scss;
}
/**
* Get SCSS to prepend.
*
* @param theme_config $theme The theme config object.
* @return array
*/
function theme_learnr_get_pre_scss($theme) {
global $CFG;
// Require local library.
require_once($CFG->dirroot . '/theme/learnr/locallib.php');
$scss = '';
// Add SCSS constants for evaluating select setting values in SCSS code.
$scss .= '$boostunionsettingyes: '.THEME_LEARNR_SETTING_SELECT_YES. ";\n";
$scss .= '$boostunionsettingno: '.THEME_LEARNR_SETTING_SELECT_NO. ";\n";
$configurable = [
// Config key => [variableName, ...].
'brandcolor' => ['primary'],
'bootstrapcolorsuccess' => ['success'],
'bootstrapcolorinfo' => ['info'],
'bootstrapcolorwarning' => ['warning'],
'bootstrapcolordanger' => ['danger'],
// Begin DBN Update.
'navbarbg' => ['navbar-bg'],
'navbarlink' => ['navbar-link'],
'navbarlinkhover' => ['navbar-link-hover'],
'navbarsitetitlecolor' => ['navbarsitetitlecolor'],
'drawerbg' => ['drawer-bg'],
'bodybg' => ['body-bg'],
'bgwhite' => ['bg-white'],
'bgdark' => ['bg-dark'],
'courseheaderbg' => ['courseheaderbg'],
'pagenavbuttonsbg' => ['pagenavbuttonsbg'],
'courseboxheight' => ['courseboxheight'],
'courseboxheight' => ['courseboxheight'],
// End DBN Update.
];
// Prepend variables first.
foreach ($configurable as $configkey => $targets) {
$value = get_config('theme_learnr', $configkey);
if (!($value)) {
continue;
}
array_map(function($target) use (&$scss, $value) {
$scss .= '$' . $target . ': ' . $value . ";\n";
}, (array) $targets);
}
// Overwrite Boost core SCSS variables which need units and thus couldn't be added to $configurable above.
// Set variables which are influenced by the coursecontentmaxwidth setting.
if (get_config('theme_learnr', 'coursecontentmaxwidth')) {
$scss .= '$course-content-maxwidth: '.get_config('theme_learnr', 'coursecontentmaxwidth').";\n";
}
// Set variables which are influenced by the mediumcontentmaxwidth setting.
if (get_config('theme_learnr', 'mediumcontentmaxwidth')) {
$scss .= '$medium-content-maxwidth: '.get_config('theme_learnr', 'mediumcontentmaxwidth').";\n";
}
// Set variables which are influenced by the h5pcontentmaxwidth setting.
if (get_config('theme_learnr', 'h5pcontentmaxwidth')) {
$scss .= '$h5p-content-maxwidth: '.get_config('theme_learnr', 'h5pcontentmaxwidth').";\n";
}
// Overwrite Boost core SCSS variables which are stored in a SCSS map and thus couldn't be added to $configurable above.
// Set variables for the activity icon colors.
$activityiconcolors = array();
if (get_config('theme_learnr', 'activityiconcoloradministration')) {
$activityiconcolors[] = '"administration": '.get_config('theme_learnr', 'activityiconcoloradministration');
}
if (get_config('theme_learnr', 'activityiconcolorassessment')) {
$activityiconcolors[] = '"assessment": '.get_config('theme_learnr', 'activityiconcolorassessment');
}
if (get_config('theme_learnr', 'activityiconcolorcollaboration')) {
$activityiconcolors[] = '"collaboration": '.get_config('theme_learnr', 'activityiconcolorcollaboration');
}
if (get_config('theme_learnr', 'activityiconcolorcollaboration')) {
$activityiconcolors[] = '"communication": '.get_config('theme_learnr', 'activityiconcolorcollaboration');
}
if (get_config('theme_learnr', 'activityiconcolorcontent')) {
$activityiconcolors[] = '"content": '.get_config('theme_learnr', 'activityiconcolorcontent');
}
if (get_config('theme_learnr', 'activityiconcolorinterface')) {
$activityiconcolors[] = '"interface": '.get_config('theme_learnr', 'activityiconcolorinterface');
}
if (count($activityiconcolors) > 0) {
$activityiconscss = '$activity-icon-colors: ('."\n";
$activityiconscss .= implode(",\n", $activityiconcolors);
$activityiconscss .= ');';
$scss .= $activityiconscss."\n";
}
// Set custom LearnR SCSS variables.
if (get_config('theme_learnr', 'blockregionoutsideleftwidth')) {
$scss .= '$blockregionoutsideleftwidth: '.get_config('theme_learnr', 'blockregionoutsideleftwidth').";\n";
}
if (get_config('theme_learnr', 'blockregionoutsiderightwidth')) {
$scss .= '$blockregionoutsiderightwidth: '.get_config('theme_learnr', 'blockregionoutsiderightwidth').
";\n";
}
// Prepend pre-scss.
if (get_config('theme_learnr', 'scsspre')) {
$scss .= get_config('theme_learnr', 'scsspre');
}
return $scss;
}
/**
* Inject additional SCSS.
*
* @param theme_config $theme The theme config object.
* @return string
*/
function theme_learnr_get_extra_scss($theme) {
// Initialize extra SCSS.
$content = '';
// You might think that this extra SCSS function is only called for the activated theme.
// However, due to the way how the theme_*_get_extra_scss callback functions are searched and called within Boost child theme
// hierarchy LearnR not only gets the extra SCSS from this function here but only from theme_boost_get_extra_scss as well.
//
// There, the CSS snippets for the background image and the login background images are added already to the SCSS codebase.
// Additionally, the custom SCSS from $theme->settings->scss (which hits the SCSS settings from theme_learnr even though
// the code is within theme_boost) is already added to the SCSS codebase as well.
//
// We have to accept this fact here and must not copy the code from theme_boost_get_extra_scss into this function.
// Instead, we must only add additionally CSS code which is based on any LearnR-only functionality.
// In contrast to Boost core, LearnR should add the login page background to the body element as well.
// Thus, check if a login background image is set.
$loginbackgroundimagepresent = get_config('theme_learnr', 'loginbackgroundimage');
if (!empty($loginbackgroundimagepresent)) {
// We first have to revert the background which is set to #page on the login page by Boost core already.
// Doing this, we also have to make the background of the #page element transparent on the login page.
$content .= 'body.pagelayout-login #page { ';
$content .= "background-image: none !important;";
$content .= "background-color: transparent !important;";
$content .= '}';
// Afterwards, we set the background-size attribute for the body element again.
$content .= 'body.pagelayout-login { ';
$content .= "background-size: cover;";
$content .= '}';
// Finally, we add all possible background image urls which will be picked based on the (random) loginpageimage class.
$content .= theme_learnr_get_loginbackgroundimage_scss();
}
// Boost core has the behaviour that the normal background image is not shown on the login page, only the login background image
// is shown on the login page.
// This is fine, but it is done improperly as the normal background image is still there on the login page and just overlaid
// with a grey color in the #page element. This can result in flickering during the page load.
// We try to avoid this by removing the background image from the body tag if no login background image is set.
if (empty($loginbackgroundimagepresent)) {
$content .= 'body.pagelayout-login { ';
$content .= "background-image: none !important;";
$content .= '}';
}
// Lastly, we make sure that the background image is fixed and not repeated. Just to be sure.
$content .= 'body { ';
$content .= "background-repeat: no-repeat;";
$content .= "background-attachment: fixed;";
$content .= '}';
// Note: LearnR is also capable of overriding the background image in its flavours.
// In contrast to the other flavour assets like the favicon overriding, this isn't done here in place as this function
// is composing Moodle core CSS which has to remain flavour-independent.
// Instead, the flavour is overriding the background image later in flavours/styles.php.
return $content;
}
/**
* Get compiled css.
*
* @return string compiled css
*/
function theme_learnr_get_precompiled_css() {
global $CFG;
// Get the fallback CSS file from Boost Core as long as LearnR does not use a fallback file of its own.
return file_get_contents($CFG->dirroot . '/theme/boost/style/moodle.css');
}
/**
* Serves any files associated with the theme settings.
*
* @param stdClass $course
* @param stdClass $cm
* @param context $context
* @param string $filearea
* @param array $args
* @param bool $forcedownload
* @param array $options
* @return bool
*/
function theme_learnr_pluginfile($course, $cm, $context, $filearea, $args, $forcedownload, array $options = array()) {
global $CFG;
// Serve the (general) logo files or favicon file from the theme settings.
// This code is copied and modified from core_admin_pluginfile() in admin/lib.php.
if (in_array($filearea, ['logo', 'logocompact', 'favicon'])) {
$size = array_shift($args); // The path hides the size.
$itemid = clean_param(array_shift($args), PARAM_INT);
$filename = clean_param(array_shift($args), PARAM_FILE);
$themerev = theme_get_revision();
if ($themerev <= 0) {
// Normalise to 0 as -1 doesn't place well with paths.
$themerev = 0;
}
// Extract the requested width and height.
$maxwidth = 0;
$maxheight = 0;
if (preg_match('/^\d+x\d+$/', $size)) {
list($maxwidth, $maxheight) = explode('x', $size);
$maxwidth = clean_param($maxwidth, PARAM_INT);
$maxheight = clean_param($maxheight, PARAM_INT);
}
$lifetime = 0;
if ($itemid > 0 && $themerev == $itemid) {
// The itemid is $CFG->themerev, when 0 or less no caching. Also no caching when they don't match.
$lifetime = DAYSECS * 60;
}
// Anyone, including guests and non-logged in users, can view the logos.
$options = ['cacheability' => 'public'];
// Check if we've got a cached file to return. When lifetime is 0 then we don't want to cached one.
$candidate = $CFG->localcachedir . "/theme_learnr/$themerev/$filearea/{$maxwidth}x{$maxheight}/$filename";
if (file_exists($candidate) && $lifetime > 0) {
send_file($candidate, $filename, $lifetime, 0, false, false, '', false, $options);
}
// Find the original file.
$fs = get_file_storage();
$filepath = "/{$context->id}/theme_learnr/{$filearea}/0/{$filename}";
if (!$file = $fs->get_file_by_hash(sha1($filepath))) {
send_file_not_found();
}
// No need for resizing, but if the file should be cached we save it so we can serve it fast next time.
if (empty($maxwidth) && empty($maxheight)) {
if ($lifetime) {
file_safe_save_content($file->get_content(), $candidate);
}
send_stored_file($file, $lifetime, 0, false, $options);
}
// Proceed with the resizing.
$filedata = $file->resize_image($maxwidth, $maxheight);
if (!$filedata) {
send_file_not_found();
}
// If we don't want to cached the file, serve now and quit.
if (!$lifetime) {
send_content_uncached($filedata, $filename);
}
// Save, serve and quit.
file_safe_save_content($filedata, $candidate);
send_file($candidate, $filename, $lifetime, 0, false, false, '', false, $options);
// Serve all other (general) image and resource files from the theme settings.
// This code is copied and modified from theme_boost_pluginfile() in theme/boost/lib.php.
} else if ($context->contextlevel == CONTEXT_SYSTEM && ($filearea === 'backgroundimage' ||
$filearea === 'loginbackgroundimage' || $filearea === 'additionalresources' ||
$filearea === 'customfonts' || $filearea === 'fontawesome' || $filearea === 'courseheaderimagefallback' ||
preg_match("/tilebackgroundimage[2-9]|1[0-2]?/", $filearea))) {
$theme = theme_config::load('learnr');
// By default, theme files must be cache-able by both browsers and proxies.
if (!array_key_exists('cacheability', $options)) {
$options['cacheability'] = 'public';
}
return $theme->setting_file_serve($filearea, $args, $forcedownload, $options);
// Serve the files from the theme flavours.
} else if ($filearea === 'flavours_look_logocompact' || $filearea === 'flavours_look_logo' ||
$filearea === 'flavours_look_favicon' || $filearea === 'flavours_look_backgroundimage') {
// Flavour files should not be top secret.
// Even if they apply to particular contexts or cohorts, we do not do any hard checks if a user should be
// allowed to request a file.
// We just make sure that the forcelogin setting is respected. This is ok as there isn't any possibility
// to apply a flavour to the login page / for non-logged-in users at the moment.
if ($CFG->forcelogin) {
require_login();
}
// Get file storage.
$fs = get_file_storage();
// Get the file from the filestorage.
$filename = clean_param(array_pop($args), PARAM_FILE);
array_pop($args); // This is the themerev number in the $args array which is used for browser caching, here we ignore it.
$itemid = clean_param(array_pop($args), PARAM_INT);
if ((!$file = $fs->get_file($context->id, 'theme_learnr', $filearea, $itemid, '/', $filename)) ||
$file->is_directory()) {
send_file_not_found();
}
// Unlock session during file serving.
\core\session\manager::write_close();
// Send stored file (and cache it for 90 days, similar to other static assets within Moodle).
send_stored_file($file, DAYSECS * 90, 0, $forcedownload, $options);
} else {
send_file_not_found();
}
}
/**
* Callback to add head elements.
*
* We use this callback to inject the FontAwesome CSS code and the flavour's CSS code to the page.
*
* @return string
*/
function theme_learnr_before_standard_html_head() {
global $CFG, $PAGE;
// Initialize HTML (even though we do not add any HTML at this stage of the implementation).
$html = '';
// If a theme other than LearnR or a child theme of it is active, return directly.
// This is necessary as the before_standard_html_head() callback is called regardless of the active theme.
if ($PAGE->theme->name != 'learnr' && !in_array('learnr', $PAGE->theme->parents)) {
return $html;
}
// Require local library.
require_once($CFG->dirroot . '/theme/learnr/locallib.php');
// Add the FontAwesome icons to the page.
theme_learnr_add_fontawesome_to_page();
// Add the flavour CSS to the page.
theme_learnr_add_flavourcss_to_page();
// Return an empty string to keep the caller happy.
return $html;
}
//Begin DBN change
function theme_learnr_strip_html_tags( $text ) {
$text = preg_replace(
array(
// Remove invisible content.
'@<head[^>]*?>.*?</head>@siu',
'@<style[^>]*?>.*?</style>@siu',
'@<script[^>]*?.*?</script>@siu',
'@<object[^>]*?.*?</object>@siu',
'@<embed[^>]*?.*?</embed>@siu',
'@<applet[^>]*?.*?</applet>@siu',
'@<noframes[^>]*?.*?</noframes>@siu',
'@<noscript[^>]*?.*?</noscript>@siu',
'@<noembed[^>]*?.*?</noembed>@siu',
// Add line breaks before and after blocks.
'@</?((address)|(blockquote)|(center)|(del))@iu',
'@</?((div)|(h[1-9])|(ins)|(isindex)|(p)|(pre))@iu',
'@</?((dir)|(dl)|(dt)|(dd)|(li)|(menu)|(ol)|(ul))@iu',
'@</?((table)|(th)|(td)|(caption))@iu',
'@</?((form)|(button)|(fieldset)|(legend)|(input))@iu',
'@</?((label)|(select)|(optgroup)|(option)|(textarea))@iu',
'@</?((frameset)|(frame)|(iframe))@iu',
),
array(
' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ',
"\n\$0", "\n\$0", "\n\$0", "\n\$0", "\n\$0", "\n\$0",
"\n\$0", "\n\$0",
),
$text
);
return strip_tags( $text );
}
/**
* Cut the Course content.
*
* @param $str
* @param $n
* @param $end_char
* @return string
*/
function theme_learnr_course_trim_char($str, $n = 500, $endchar = '…') {
if (strlen($str) < $n) {
return $str;
}
$str = preg_replace("/\s+/", ' ', str_replace(array("\r\n", "\r", "\n"), ' ', $str));
if (strlen($str) <= $n) {
return $str;
}
$out = "";
$small = substr($str, 0, $n);
$out = $small.$endchar;
return $out;
}
//End DBN change
Zerion Mini Shell 1.0