Sindbad~EG File Manager
<?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/>.
/**
* Snap course renderer.
* Overrides core course renderer.
*
* @package theme_snap
* @copyright Copyright (c) 2015 Open LMS (https://www.openlms.net)
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace theme_snap\output\core;
defined('MOODLE_INTERNAL') || die();
use cm_info;
use context_course;
use context_module;
use html_writer;
use moodle_url;
use coursecat;
use stdClass;
use theme_snap\activity;
use theme_snap\activity_meta;
require_once($CFG->dirroot . "/mod/book/locallib.php");
require_once($CFG->libdir . "/gradelib.php");
require_once($CFG->dirroot . '/course/renderer.php');
require_once("$CFG->libdir/resourcelib.php");
class course_renderer extends \core_course_renderer {
/**
* Output frontpage summary text and frontpage modules (stored as section 1 in site course)
*
* This may be disabled in settings
* Copied from course/renderer.php in 3.11. This is an old implementation that needs our constant review.
*
* @return string
*/
public function frontpage_section1() {
global $SITE, $USER;
$output = '';
$editing = $this->page->user_is_editing();
if ($editing) {
// Make sure section with number 1 exists.
course_create_sections_if_missing($SITE, 1);
}
$modinfo = get_fast_modinfo($SITE);
$section = $modinfo->get_section_info(1);
if (($section && (!empty($modinfo->sections[1]) || !empty($section->summary))) || $editing) {
$output .= $this->box_start('generalbox sitetopic');
// If currently moving a file then show the current clipboard.
if (ismoving($SITE->id)) {
$stractivityclipboard = strip_tags(get_string('activityclipboard', '', $USER->activitycopyname));
$output .= '<p><font size="2">';
$cancelcopyurl = new moodle_url('/course/mod.php', ['cancelcopy' => 'true', 'sesskey' => sesskey()]);
$output .= "$stractivityclipboard (" . html_writer::link($cancelcopyurl, get_string('cancel')) .')';
$output .= '</font></p>';
}
$context = context_course::instance(SITEID);
// If the section name is set we show it.
if (trim($section->name ?? '') !== '') {
$output .= $this->heading(
format_string($section->name, true, ['context' => $context]),
2,
'sectionname'
);
}
$summarytext = file_rewrite_pluginfile_urls($section->summary,
'pluginfile.php',
$context->id,
'course',
'section',
$section->id);
$summaryformatoptions = new stdClass();
$summaryformatoptions->noclean = true;
$summaryformatoptions->overflowdiv = true;
$output .= format_text($summarytext, $section->summaryformat, $summaryformatoptions);
if ($editing && has_capability('moodle/course:update', $context)) {
$streditsummary = get_string('editsummary');
$editsectionurl = new moodle_url('/course/editsection.php', ['id' => $section->id]);
$attributes = ['title' => $streditsummary, 'aria-label' => $streditsummary];
$output .= html_writer::link($editsectionurl, $this->pix_icon('t/edit', ''), $attributes) .
"<br /><br />";
}
$output .= $this->course_section_cm_list($SITE, $section);
$output .= $this->course_section_add_cm_control($SITE, $section->section);
$output .= $this->box_end();
}
return $output;
}
/**
* override course render for course module list items
* add additional classes to list item (see $modclass)
*
* @author: SL / GT
* @param stdClass $course
* @param \completion_info $completioninfo
* @param cm_info $mod
* @param int|null $sectionreturn
* @param array $displayoptions
* @return String
*/
public function course_section_cm_list_item($course,
&$completioninfo,
cm_info $mod,
$sectionreturn,
$displayoptions = []
) {
$output = '';
if ($modulehtml = $this->course_section_cm($course, $completioninfo, $mod, $sectionreturn, $displayoptions)) {
list($snapmodtype, $mimetype) = $this->get_mod_type($mod);
if ($mod->modname === 'resource') {
// Default for resources/attatchments e.g. pdf, doc, etc.
$modclasses = ['snap-resource', 'snap-mime-'.$mimetype, 'snap-resource-long'];
$resourcedisplayincourse = true;
$modresourceneoptions = [
RESOURCELIB_DISPLAY_EMBED,
RESOURCELIB_DISPLAY_FRAME,
RESOURCELIB_DISPLAY_NEW,
RESOURCELIB_DISPLAY_DOWNLOAD,
RESOURCELIB_DISPLAY_POPUP,
];
if (!empty($mod->customdata['display'])) {
if (in_array($mod->customdata['display'], $modresourceneoptions)) {
$resourcedisplayincourse = false;
}
}
if (in_array($mimetype, $this->snap_multimedia()) && $resourcedisplayincourse) {
$modclasses[] = 'js-snap-media';
}
// For images we overwrite with the native class.
if ($this->is_image_mod($mod)) {
$modclasses = ['snap-native-image', 'snap-image', 'snap-mime-'.$mimetype];
}
} else if ($mod->modname === 'folder' && !$mod->url) {
// Folder mod set to display on page.
$modclasses = ['snap-activity'];
} else if (plugin_supports('mod', $mod->modname, FEATURE_MOD_ARCHETYPE) === MOD_ARCHETYPE_RESOURCE) {
$modclasses = ['snap-resource'];
if ($mod->modname !== 'label') {
$modclasses = ['snap-resource', 'snap-resource-long'];
}
} else if ($mod->modname === 'scorm') {
$modclasses = ['snap-resource', 'snap-resource-long'];
} else if ($mod->modname !== 'label') {
$modclasses = ['snap-activity'];
}
// Special classes for native html elements.
if (in_array($mod->modname, ['page', 'book'])) {
$modclasses = ['snap-native', 'snap-mime-'.$mod->modname];
$attr['aria-expanded'] = "false";
} else if ($modurl = $mod->url) {
// For snap cards, js uses this to make the whole card clickable.
if ($mod->uservisible) {
$attr['data-href'] = $modurl;
}
}
// Is this mod draft?
// We don't need visibleold as a condition here since it can affect a
// module merged from a course to another, and the draft class won't be applied.
// The "Not published to students" message won't be displayed next to the course module so teachers do not
// realize that the content is not available to students.
$section = $mod->get_section_info();
if (!$mod->visible) {
// If the section is hidden check the visibleold to prevent
// the message will be displayed in all modules.
if ($section->visible || (!$section->visible && !$mod->visibleold)) {
$modclasses[] = 'draft';
}
}
// Is this mod stealth?
if ($mod->is_stealth()) {
$modclasses[] = 'stealth';
}
if ($mod->visible && $section && !$section->visible) {
$modclasses[] = 'stealth-section-hidden';
}
$canviewhidden = has_capability('moodle/course:viewhiddenactivities', $mod->context);
// If the module isn't available, or we are a teacher (can view hidden activities) then get availability
// info.
$availabilityinfo = '';
if (!$mod->available || $canviewhidden) {
$availabilityinfo = $this->course_section_cm_availability($mod, $displayoptions);
}
if ($availabilityinfo !== '' && !$mod->uservisible || $canviewhidden) {
$modclasses[] = 'conditional';
}
if (!$mod->available && !$mod->uservisible) {
$modclasses[] = 'unavailable';
}
// TODO - can we add completion data.
if (has_any_capability(['moodle/course:update', 'moodle/course:manageactivities'], $mod->context)) {
$modclasses[] = 'snap-can-edit';
}
if (has_capability('moodle/course:viewhiddenactivities', $mod->context)) {
$modclasses[] = 'snap-can-view-hidden';
}
$modclasses[] = 'snap-asset'; // Added to stop conflicts in flexpage.
$modclasses[] = 'activity'; // Moodle needs this for drag n drop.
$modclasses[] = $mod->modname;
$modclasses[] = "modtype_$mod->modname";
$modclasses[] = $mod->extraclasses;
$attr['data-type'] = $snapmodtype;
$attr['class'] = implode(' ', $modclasses);
$attr['id'] = 'module-' . $mod->id;
$attr['data-modcontext'] = $mod->context->id;
$output .= html_writer::tag('li', $modulehtml, $attr);
}
return $output;
}
/**
* Renders HTML to show course module availability information
*
* @param cm_info $mod
* @param array $displayoptions
* @return string
*/
public function course_section_cm_availability(cm_info $mod, $displayoptions = []) {
// If we have available info, always spit it out.
if (!$mod->uservisible && !empty($mod->availableinfo)) {
$availinfo = $mod->availableinfo;
} else {
$ci = new \core_availability\info_module($mod);
$availinfo = $ci->get_full_information();
}
if ($availinfo) {
$formattedinfo = \core_availability\info::format_info(
$availinfo, $mod->get_course());
return $formattedinfo;
}
return '';
}
/**
* Renders HTML for completion tracking box on course page
*
* If completion is disabled, returns empty string
* If completion is automatic, returns an icon of the current completion state
* If completion is manual, returns a form (with an icon inside) that allows user to
* toggle completion
*
* @param stdClass $course course object
* @param \completion_info $completioninfo completion info for the course, it is recommended
* to fetch once for all modules in course/section for performance
* @param cm_info $mod module to show completion for
* @param array $displayoptions display options, not used in core
* @return string
* @throws \dml_exception
*/
public function course_section_cm_completion($course, &$completioninfo, cm_info $mod, $displayoptions = []) {
global $CFG, $USER, $DB;
$output = '';
$istrackeduser = $completioninfo->is_tracked_user($USER->id);
$isediting = $this->page->user_is_editing();
if (!empty($displayoptions['hidecompletion']) || !isloggedin() || isguestuser() || !$mod->uservisible) {
return $output;
}
if ($completioninfo === null) {
$completioninfo = new \completion_info($course);
}
$completion = $completioninfo->is_enabled($mod);
if ($completion == COMPLETION_TRACKING_NONE) {
if ($isediting) {
$output .= html_writer::span(' ', 'filler');
}
return $output;
}
$completionicon = '';
if ($isediting || !$istrackeduser) {
switch ($completion) {
case COMPLETION_TRACKING_MANUAL :
$completionicon = 'manual-enabled';
break;
case COMPLETION_TRACKING_AUTOMATIC :
$completionicon = 'auto-enabled';
break;
}
} else {
$completiondata = $completioninfo->get_data($mod, true);
if ($completion == COMPLETION_TRACKING_MANUAL) {
switch($completiondata->completionstate) {
case COMPLETION_INCOMPLETE:
$completionicon = 'manual-n' . ($completiondata->overrideby ? '-override' : '');
break;
case COMPLETION_COMPLETE:
$completionicon = 'manual-y' . ($completiondata->overrideby ? '-override' : '');
break;
}
} else { // Automatic completion.
switch($completiondata->completionstate) {
case COMPLETION_INCOMPLETE:
$completionicon = 'auto-n' . ($completiondata->overrideby ? '-override' : '');
break;
case COMPLETION_COMPLETE:
$completionicon = 'auto-y' . ($completiondata->overrideby ? '-override' : '');
break;
case COMPLETION_COMPLETE_PASS:
$completionicon = 'auto-pass';
break;
case COMPLETION_COMPLETE_FAIL:
$completionicon = 'auto-fail';
break;
}
}
}
if ($completionicon) {
$formattedname = html_entity_decode($mod->get_formatted_name(), ENT_QUOTES, 'UTF-8');
if (!$isediting && $istrackeduser && $completiondata->overrideby) {
$args = new stdClass();
$args->modname = $formattedname;
$overridebyuser = \core_user::get_user($completiondata->overrideby, '*', MUST_EXIST);
$args->overrideuser = fullname($overridebyuser);
$imgalt = get_string('completion-alt-' . $completionicon, 'completion', $args);
} else {
$imgalt = get_string('completion-alt-' . $completionicon, 'completion', $formattedname);
}
if ($isediting || !$istrackeduser || !has_capability('moodle/course:togglecompletion', $mod->context)) {
// When editing, the icon is just an image.
$completionpixicon = new \pix_icon('i/completion-'.$completionicon, $imgalt, '',
['title' => $imgalt, 'class' => 'iconsmall']);
$output .= html_writer::tag('span', $this->output->render($completionpixicon),
['class' => 'autocompletion']);
} else if ($completion == COMPLETION_TRACKING_MANUAL) {
$newstate =
$completiondata->completionstate == COMPLETION_COMPLETE
? COMPLETION_INCOMPLETE
: COMPLETION_COMPLETE;
// In manual mode the icon is a toggle form...
// If this completion state is used by the
// conditional activities system, we need to turn
// off the JS.
$extraclass = '';
if (!empty($CFG->enableavailability) &&
\core_availability\info::completion_value_used($course, $mod->id)) {
$extraclass = ' preventjs';
}
// Check if we should force reload current page to trigger PLD events.
$conditions = ['plugin' => 'local_pld', 'name' => 'version'];
$pld = $DB->record_exists('config_plugins', $conditions);
if (!empty($pld)) {
require_once($CFG->dirroot.'/local/pld/model/action.php');
$actionpld = \local_pld_action::course_should_reload($course->id);
if (is_array($actionpld) && !empty($actionpld['forcereload'])) {
$extraclass .= ' forcereload';
}
}
$output .= html_writer::start_tag('form', ['method' => 'post',
'action' => new moodle_url('/course/togglecompletion.php'),
'class' => 'togglecompletion'. $extraclass, ]);
$output .= html_writer::start_tag('div');
$output .= html_writer::empty_tag('input', [
'type' => 'hidden', 'name' => 'id', 'value' => $mod->id, ]);
$output .= html_writer::empty_tag('input', [
'type' => 'hidden', 'name' => 'sesskey', 'value' => sesskey(), ]);
$output .= html_writer::empty_tag('input', [
'type' => 'hidden', 'name' => 'modulename', 'value' => $formattedname, ]);
$output .= html_writer::empty_tag('input', [
'type' => 'hidden', 'name' => 'completionstate', 'value' => $newstate, ]);
$output .= html_writer::tag('button',
$this->output->pix_icon('i/completion-' . $completionicon, $imgalt),
['class' => 'btn btn-link', 'aria-live' => 'assertive']);
$output .= html_writer::end_tag('div');
$output .= html_writer::end_tag('form');
} else {
// In auto mode, the icon is just an image.
$completionpixicon = new \pix_icon('i/completion-'.$completionicon, $imgalt, '',
['title' => $imgalt]);
$output .= html_writer::tag('span', $this->output->render($completionpixicon),
['class' => 'autocompletion']);
}
}
return $output;
}
/**
* Renders HTML to display one course module in a course section
*
* This includes link, content, availability, completion info and additional information
* that module type wants to display (i.e. number of unread forum posts)
*
* This function calls:
* {@link core_course_renderer::course_section_cm_name()}
* {@link cm_info::get_after_link()}
* {@link core_course_renderer::course_section_cm_text()}
* {@link core_course_renderer::course_section_cm_availability()}
* {@link core_course_renderer::course_section_cm_completion()}
* {@link course_get_cm_edit_actions()}
* {@link core_course_renderer::course_section_cm_edit_actions()}
*
* @param \stdClass $course
* @param \completion_info $completioninfo
* @param \cm_info $mod
* @param int|null $sectionreturn
* @param array $displayoptions
* @return string
*/
public function course_section_cm($course, &$completioninfo, cm_info $mod, $sectionreturn, $displayoptions = []) {
global $COURSE, $CFG;
$output = '';
// We return empty string (because course module will not be displayed at all)
// when
// 1) The activity is not visible to users
// and also
// 2) The 'availableinfo' is empty, i.e. the activity was
// hidden in a way that leaves no info, such as using the
// eye icon.
if (!$mod->uservisible
&& (empty($mod->availableinfo))) {
return $output;
}
if (!$mod->is_visible_on_course_page()) {
return $output;
}
$arialabelasset = $mod->get_module_type_name() . ' ' . get_string('activity', 'theme_snap');
$output .= '<div class="asset-wrapper" role="group" aria-label="'.$arialabelasset.'">';
// Drop section notice.
if (has_capability('moodle/course:update', $mod->context)) {
$output .= '<a class="snap-move-note" href="#">'.get_string('movehere', 'theme_snap').'</a>';
}
// Start the div for the activity content.
$output .= "<div class='activityinstance'>";
// Display the link to the module (or do nothing if module has no url).
$cmname = $this->course_section_cm_name($mod, $displayoptions);
$assetlink = '';
if (!empty($cmname)) {
// Activity/resource type.
$assetlink .= '<h3 class="snap-asset-link">'.$cmname.'</h3>';
}
// Asset content.
$contentpart = $this->course_section_cm_text($mod, $displayoptions);
// Asset metadata - groups, completion etc.
// Due date, feedback available and all the nice snap things.
$snapcompletionmeta = '';
$snapcompletiondata = $this->module_meta_html($mod);
if ($snapcompletiondata) {
$snapcompletionmeta = '<div class="snap-completion-meta">'.$snapcompletiondata.'</div>';
}
// Completion tracking.
$completiontracking = '<div class="snap-asset-completion-tracking">';
$completiontracking .= $this->course_section_cm_completion($course, $completioninfo, $mod, $displayoptions);
$completiontracking .= '</div>';
// Add specific class if the completion tracking is disabled for an activity.
$completion = $completioninfo->is_enabled($mod);
if ($completion == COMPLETION_TRACKING_NONE) {
$completiontracking = '<div class="disabled-snap-asset-completion-tracking">';
$completiontracking .= $this->course_section_cm_completion($course, $completioninfo, $mod, $displayoptions);
$completiontracking .= '</div>';
}
// Draft & Stealth tags.
$stealthtag = '';
$drafttag = '';
// Stealth tag.
$stealthtag = '<div class="snap-stealth-tag">'.get_string('hiddenoncoursepage', 'moodle').'</div>';
// Draft status - always output, shown via css of parent.
$drafttag = '<div class="snap-draft-tag">'.get_string('draft', 'theme_snap').'</div>';
// Group.
$groupmeta = '';
// Resources cannot have groups/groupings.
if ($mod->modname !== 'resource') {
$canmanagegroups = has_capability('moodle/course:managegroups', context_course::instance($mod->course));
if ($canmanagegroups && $mod->effectivegroupmode != NOGROUPS) {
if ($mod->effectivegroupmode == VISIBLEGROUPS) {
$groupinfo = get_string('groupsvisible');
} else if ($mod->effectivegroupmode == SEPARATEGROUPS) {
$groupinfo = get_string('groupsseparate');
}
$groupmeta .= '<div class="snap-group-tag">'.$groupinfo.'</div>';
}
// This will show a grouping (group of groups) name against a module if one has been assigned to the module instance.
if ($canmanagegroups && !empty($mod->groupingid)) {
// Grouping label.
$groupings = groups_get_all_groupings($mod->course);
$groupmeta .= '<div class="snap-grouping-tag">'.format_string($groupings[$mod->groupingid]->name).'</div>';
}
}
$canviewhidden = has_capability('moodle/course:viewhiddenactivities', $mod->context);
// If the module isn't available, or we are a teacher (can view hidden activities) then get availability
// info. Restrictions will appear on click over a lock image inside the activity header.
$coursetoolsicon = '';
if (!$mod->available || $canviewhidden) {
$availabilityinfo = $this->course_section_cm_availability($mod, $displayoptions);
if ($availabilityinfo) {
$ariaconditionaltag = get_string('activityrestriction', 'theme_snap');
$conditionaltagsrc = $this->output->image_url('lock', 'theme');
$datamodcontext = $mod->context->id;
$conditionaliconid = "snap-restriction-$datamodcontext";
$restrictionsource = html_writer::tag('img', '', [
'class' => 'svg-icon',
'title' => $ariaconditionaltag,
'aria-hidden' => 'true',
'src' => $conditionaltagsrc,
]);
$coursetoolsicon = html_writer::tag('a', $restrictionsource, [
'tabindex' => '0',
'class' => 'snap-conditional-tag',
'role' => 'button',
'data-toggle' => 'popover',
'data-trigger' => 'focus',
'data-placement' => 'right',
'id' => $conditionaliconid,
'data-html' => 'true',
'clickable' => 'true',
'data-content' => $availabilityinfo,
'aria-label' => $ariaconditionaltag,
]);
}
}
// Add draft, conditional.
$assetmeta = $stealthtag.$drafttag;
// Add edit menu to Snap.
$snapmenu = '';
$snapmenu .= $this->course_section_menu_actions($mod);
// Build output.
$postcontent = '<div class="snap-asset-meta" data-cmid="'.$mod->id.'">'.$assetmeta.$mod->afterlink.'</div>';
$content = '<div class="snap-asset-content">'.$postcontent.$contentpart.$snapcompletionmeta.$groupmeta.'</div>';
$cardicons = '<div class="snap-header-card-icons">'.$completiontracking.$coursetoolsicon.$snapmenu.'</div>';
$output .= '<div class="snap-header-card">'.$assetlink.$cardicons.'</div>'.$content;
// Bail at this point if we aren't using a supported format. (Folder view is only partially supported).
$supported = ['topics', 'weeks', 'site'];
if (!in_array($COURSE->format, $supported)) {
return parent::course_section_cm($course, $completioninfo, $mod, $sectionreturn, $displayoptions).$assetmeta;
}
$output .= "</div>"; // Close .asset-wrapper.
return $output;
}
/**
* Renders html to display the module content on the course page (i.e. text of the labels)
*
* @param cm_info $mod
* @param array $displayoptions
* @return string
*/
public function course_section_cm_text(cm_info $mod, $displayoptions = []) {
$output = '';
if (!$mod->uservisible && empty($mod->availableinfo)) {
// Nothing to be displayed to the user.
return $output;
}
// Get custom module content for Snap, or get modules own content.
$modmethod = 'mod_'.$mod->modname.'_html';
if ($this->is_image_mod($mod)) {
$content = $this->mod_image_html($mod);
} else if (method_exists($this, $modmethod )) {
$content = call_user_func([$this, $modmethod], $mod);
} else {
$content = $mod->get_formatted_content(['overflowdiv' => false, 'noclean' => true]);
}
$accesstext = '';
$textclasses = '';
if ($mod->uservisible) {
$conditionalhidden = $this->is_cm_conditionally_hidden($mod);
$accessiblebutdim = (!$mod->visible || $conditionalhidden) &&
has_capability('moodle/course:viewhiddenactivities',
context_course::instance($mod->course));
if ($accessiblebutdim) {
if ($conditionalhidden) {
$textclasses .= ' conditionalhidden';
}
// Show accessibility note only if user can access the module himself.
$accesstext = get_accesshide(get_string('hiddenfromstudents').':'. $mod->modfullname);
}
}
if ($mod->url) {
if ($content) {
// If specified, display extra content after link.
$output = html_writer::tag('div', $content, ['class' => trim('contentafterlink ' . $textclasses)]);
// Add chevron icon to content.
$output .= '<div class="mt-3">
<a href="'.$mod->url.'&forceview=1"><i class="fa fa-chevron-down" aria-hidden="true"></i></a>
</div>';
}
} else {
$snapmodtype = $this->get_mod_type($mod)[0];
// Label title should not be displayed in the activity card header.
$labelcurrentstr = get_string('modulename', 'mod_label');
if (strcmp($snapmodtype, $labelcurrentstr) == 0) {
$snapmodtype = '';
}
$assettype = '<div class="snap-assettype">'.$snapmodtype.'</div>';
// No link, so display only content.
$output = html_writer::tag('div', $assettype . $accesstext . $content,
['class' => 'contentwithoutlink text-break' . $textclasses]);
}
return $output;
}
/*
***** SNAP SPECIFIC DISPLAY OF RESOURCES *******
*/
/**
* Get module type
* Note, if module is a resource, get the actual file type
*
* @author Guy Thomas
* @date 2014-06-16
* @param cm_info $mod
* @return array
*/
protected function get_mod_type(cm_info $mod): array {
if ($mod->modname === 'resource') {
$fs = get_file_storage();
$files = $fs->get_area_files($mod->context->id, 'mod_resource', 'content', 0, 'sortorder DESC, id ASC', false);
$mainfile = $files ? reset($files) : null;
$ext = strtolower(pathinfo($mainfile->get_filename(), PATHINFO_EXTENSION));
$filetypegroups = get_mimetypes_array();
$extension = [
'powerpoint' => 'ppt',
'document' => 'doc',
'spreadsheet' => 'xls',
'archive' => 'zip',
'pdf' => 'pdf',
'text' => 'txt',
];
$mimetype = $ext;
if (isset($filetypegroups[$ext])) {
$mimetype = $filetypegroups[$ext];
$mimetype = $icon['string'] ?? $mimetype['icon'];
}
$ext = $extension[$mimetype] ?? $ext;
return [$ext, $mimetype];
} else {
return [$mod->modfullname, null];
}
}
/**
* Is this an image module
* @param cm_info $mod
* @return bool
*/
protected function is_image_mod(cm_info $mod) {
if ($mod->modname == 'resource') {
$fs = get_file_storage();
$files = $fs->get_area_files($mod->context->id, 'mod_resource', 'content', 0, 'sortorder DESC, id ASC', false);
$mainfile = $files ? reset($files) : null;
if (file_extension_in_typegroup($mainfile->get_filename(), 'web_image')) {
return true;
}
return false;
}
return false;
}
/**
* Submission call to action.
*
* @param cm_info $mod
* @param activity_meta $meta
* @return string
* @throws \coding_exception
*/
public static function submission_cta(cm_info $mod, activity_meta $meta) {
global $CFG;
if (empty($meta->submissionnotrequired)) {
$url = $CFG->wwwroot.'/mod/'.$mod->modname.'/view.php?id='.$mod->id;
if ($meta->submitted) {
if (empty($meta->timesubmitted)) {
$submittedonstr = '';
} else {
$submittedonstr = ' '.userdate($meta->timesubmitted, get_string('strftimedate', 'langconfig'));
}
$message = $meta->submittedstr.$submittedonstr;
} else {
$warningstr = $meta->draft ? $meta->draftstr : $meta->notsubmittedstr;
$warningstr = $meta->reopened ? $meta->reopenedstr : $warningstr;
$message = $warningstr;
}
return html_writer::link($url, $message);
}
return '';
}
/**
* Get the module meta data for a specific module.
*
* @param cm_info $mod
* @return string
*/
protected function module_meta_html(cm_info $mod) {
global $COURSE;
$content = '';
if (is_guest(context_course::instance($COURSE->id))) {
return '';
}
// Do we have an activity function for this module for returning meta data?
// @todo - check module lib.php for a meta function (won't work for core mods but will for ours if we wish).
$meta = activity::module_meta($mod);
if (!$meta->is_set(true)) {
// Can't get meta data for this module.
return '';
}
if ($meta->isteacher) {
// Teacher - useful teacher meta data.
$engagementmeta = [];
// Below, !== false means we get 0 out of x submissions.
if (!$meta->submissionnotrequired && $meta->numsubmissions !== false) {
$engagementmeta[] = get_string('xofy'.$meta->submitstrkey, 'theme_snap',
(object) [
'completed' => $meta->numsubmissions,
'participants' => \theme_snap\local::course_participant_count($COURSE->id, $mod->modname),
]
);
}
if ($meta->numrequiregrading) {
$engagementmeta[] = get_string('xungraded', 'theme_snap', $meta->numrequiregrading);
}
if (!empty($engagementmeta)) {
$engagementstr = implode(', ', $engagementmeta);
$params = [
'action' => 'grading',
'id' => $mod->id,
'tsort' => 'timesubmitted',
'filter' => 'require_grading',
];
$url = new moodle_url("/mod/{$mod->modname}/view.php", $params);
$content .= html_writer::link($url, $engagementstr);
}
$suspended = \theme_snap\local::suspended_participant_count($COURSE->id, $mod->id);
if ($suspended) {
$content .= html_writer::tag('p', get_string("quizattemptswarn", "theme_snap"));
}
} else {
// Feedback meta.
if (!empty($meta->grade)) {
// Note - the link that a module takes you to would be better off defined by a function in
// theme/snap/activity - for now its just hard coded.
$url = new \moodle_url('/grade/report/user/index.php', ['id' => $COURSE->id]);
if (in_array($mod->modname, ['quiz', 'assign'])) {
$url = new \moodle_url('/mod/'.$mod->modname.'/view.php?id='.$mod->id);
}
$feedbackavailable = get_string('feedbackavailable', 'theme_snap');
if ($mod->modname != 'lesson') {
$content .= html_writer::link($url, $feedbackavailable);
}
}
// If submissions are not allowed, return the content.
if (!empty($meta->timeopen) && $meta->timeopen > time()) {
// TODO - spit out a 'submissions allowed from' tag.
return $content;
}
// @codingStandardsIgnoreLine
/* @var cm_info $mod */
$content .= self::submission_cta($mod, $meta);
}
// Activity due date.
if (!empty($meta->extension) || !empty($meta->timeclose)) {
$dateformat = get_string('strftimedate', 'langconfig');
if (!empty($meta->extension)) {
$field = 'extension';
} else if (!empty($meta->timeclose)) {
$field = 'timeclose';
}
$labeltext = get_string('due', 'theme_snap', userdate($meta->$field, $dateformat));
$pastdue = $meta->$field < time();
$url = new \moodle_url("/mod/{$mod->modname}/view.php", ['id' => $mod->id]);
$dateclass = $pastdue ? 'tag-danger' : 'tag-success';
$content .= html_writer::link($url, $labeltext,
[
'class' => 'snap-due-date tag '.$dateclass,
'data-from-cache' => $meta->timesfromcache ? 1 : 0,
]);
}
return $content;
}
/**
* Get resource module image html
*
* @param stdClass $mod
* @return string
*/
protected function mod_image_html($mod) {
if (!$mod->uservisible) {
return "";
}
$fs = get_file_storage();
$context = \context_module::instance($mod->id);
// TODO: this is not very efficient!!
$files = $fs->get_area_files($context->id, 'mod_resource', 'content', 0, 'sortorder DESC, id ASC', false);
if (count($files) > 0) {
foreach ($files as $file) {
$imgsrc = \moodle_url::make_pluginfile_url(
$file->get_contextid(),
$file->get_component(),
$file->get_filearea(),
$file->get_itemid(),
$file->get_filepath(),
$file->get_filename(),
);
}
}
$summary = '';
$summary = $mod->get_formatted_content(['overflowdiv' => false, 'noclean' => true]);
$modname = format_string($mod->name);
$img = format_text('<img src="' .$imgsrc. '" alt="' .$modname. '"/>');
$icon = '<img title="' .get_string('vieworiginalimage', 'theme_snap'). '"
alt="' .get_string('vieworiginalimage', 'theme_snap'). '"
src="' .$this->output->image_url('arrow-expand', 'theme'). '">';
$imglink = '<a class="snap-expand-link" href="' .$imgsrc. '" target="_blank">' .$icon. '</a>';
$output = '<figure class="snap-resource-figure figure">'
.$img.$imglink.
'<figcaption class="snap-resource-figure-caption figure-caption">'
.$modname.$summary.
'</figcaption>
</figure>';
return $output;
}
/**
* Get page module html
* @param cm_info $mod
* @return string
*/
protected function mod_page_html(cm_info $mod) {
if (!$mod->uservisible) {
return "";
}
$page = \theme_snap\local::get_page_mod($mod);
$imgarr = \theme_snap\local::extract_first_image($page->content);
$thumbnail = '';
$preview = $page->summary;
// Check that the image is not a TeX equation.
if ($imgarr && (strpos($imgarr['src'], "filter/tex/pix.php") == false)) {
$img = html_writer::img($imgarr['src'], $imgarr['alt']);
$thumbnail = "<div class=summary-figure>$img</div>";
} else if (!$page->intro) {
$preview = shorten_text($page->content, 200);
}
$readmore = get_string('readmore', 'theme_snap');
$close = get_string('collapseicon', 'theme_snap');
$expand = get_string('expandicon', 'theme_snap');
$content = '';
$contentloaded = 0;
if (empty(get_config('theme_snap', 'lazyload_mod_page'))) {
// Identify content elements which should force an AJAX lazy load.
$elcontentblist = ['iframe', 'video', 'object', 'embed', 'model-viewer'];
$content = $page->content;
$lazyload = false;
foreach ($elcontentblist as $el) {
if (stripos($content, '<'.$el) !== false) {
$content = ''; // Don't include the content as it is likely to slow the page load down considerably.
$lazyload = true;
}
}
$contentloaded = !$lazyload ? 1 : 0;
}
// With previous design, we allow displaying videos.
if ($content == '') {
if (stripos($page->content, '<video') !== false) {
$content = $page->content;
$contentloaded = 1;
}
}
$pmcontextattribute = 'data-pagemodcontext="'.$mod->context->id.'"';
$expandpagebutton = "
<button
class='btn collapsed w-100 pagemod-readmore readmore-button snap-action-icon'
{$pmcontextattribute}
aria-expanded='false'>
<i aria-hidden='true' class='icon fa fa-chevron-down fa-fw' title='{$expand} {$page->name}'></i>
</button>
";
$o = "
<div class='summary-container'>
{$thumbnail}
<div class='summary-text'>
{$preview}
</div>
</div>
<div class='readmore-container'>
{$expandpagebutton}
</div>
<div class=pagemod-content tabindex='-1' data-content-loaded={$contentloaded}>
<div id='pagemod-content-container'>
{$content}
</div>
<div class='d-block'><a class='snap-action-icon' href='#' role='button' aria-expanded='true' title='{$close} {$page->name}'>
<i class='fa fa-chevron-up' aria-hidden=''true'></i></a></div>
</div>";
return $o;
}
protected function mod_book_html($mod) {
if (!$mod->uservisible) {
return "";
}
global $DB;
$cm = get_coursemodule_from_id('book', $mod->id, 0, false, MUST_EXIST);
$book = $DB->get_record('book', ['id' => $cm->instance], '*', MUST_EXIST);
$chapters = book_preload_chapters($book);
if ($book->intro) {
$context = context_module::instance($mod->id);
$content = file_rewrite_pluginfile_urls($book->intro, 'pluginfile.php', $context->id, 'mod_book', 'intro', null);
$formatoptions = new stdClass;
$formatoptions->noclean = true;
$formatoptions->overflowdiv = true;
$formatoptions->context = $context;
$content = format_text($content, $book->introformat, $formatoptions);
$o = '<div class="summary-text row">';
$o .= '<div class="content-row col-sm-6">' .$content. '</div>';
$o .= '<div class="chapters-row col-sm-6">' .$this->book_get_toc($chapters, $book, $cm) . '</div>';
$o .= '</div>';
return $o;
}
return $this->book_get_toc($chapters, $book, $cm);
}
/**
* Simplified book toc Get assignment module html (includes meta data);
*
* Based on the function of same name in mod/book/localib.php
* @param $mod
* @return string
*/
public function book_get_toc($chapters, $book, $cm) {
$context = context_module::instance($cm->id);
switch ($book->numbering) {
case BOOK_NUM_BULLETS :
$numclass = 'list-bullets';
break;
case BOOK_NUM_INDENTED:
$numclass = 'list-indented';
break;
case BOOK_NUM_NONE:
$numclass = 'list-none';
break;
case BOOK_NUM_NUMBERS :
default :
$numclass = 'list-numbers';
}
$toc = "<h4>".get_string('chapters', 'theme_snap')."</h4>";
$toc .= '<ol class="bookmod-chapters '.$numclass.'">';
$closemeflag = false; // Control for indented lists.
$chapterlist = '';
foreach ($chapters as $ch) {
$title = trim(format_string($ch->title, true, ['context' => $context]));
if (!$ch->hidden) {
if ($closemeflag && !$ch->parent) {
$chapterlist .= "</ul></li>";
$closemeflag = false;
}
$chapterlist .= "<li>";
$chapterlist .= html_writer::link(new moodle_url('/mod/book/view.php',
['id' => $cm->id, 'chapterid' => $ch->id]), $title, []);
if ($ch->subchapters) {
$chapterlist .= "<ul>";
$closemeflag = true;
} else {
$chapterlist .= "</li>";
}
}
}
$toc .= $chapterlist.'</ol>';
return $toc;
}
/**
* Every mime type we consider to be multimedia.
* @return array
*/
protected function snap_multimedia() {
return ['mp3', 'wav', 'audio', 'mov', 'wmv', 'video', 'mpeg', 'avi', 'quicktime', 'flash'];
}
/**
* Renders html to display a name with the link to the course module on a course page
*
* If module is unavailable for user but still needs to be displayed
* in the list, just the name is returned without a link
*
* Note, that for course modules that never have separate pages (i.e. labels)
* this function return an empty string
*
* @param cm_info $mod
* @param array $displayoptions
* @return string
*/
public function course_section_cm_name(cm_info $mod, $displayoptions = []) {
global $DB, $CFG;
$output = '';
// Nothing to be displayed to the user.
if (!$mod->uservisible && empty($mod->availableinfo)) {
return $output;
}
// Is this for labels or something with no other page url to point to?
$url = $mod->url;
if (!$url) {
return $output;
}
// Get asset name.
$instancename = $mod->get_formatted_name();
$groupinglabel = $mod->get_grouping_label();
$target = '';
$cmname = $mod->modname;
$iconurl = $mod->get_icon_url();
// Resources icon and colors depending on File Extension.
if (strpos($cmname, 'resource') !== false) {
list(, $resourcetype) = $this->get_mod_type($mod);
$standardicons = [
'mp3' => 'audio',
'wav' => 'audio',
'writer' => 'document',
'oth' => 'document',
'flash' => 'pdf',
'eps' => 'pdf',
'impress' => 'powerpoint',
'chart' => 'spreadsheet',
'database' => 'spreadsheet',
'calc' => 'spreadsheet',
'sourcecode' => 'text',
'html' => 'text',
'markup' => 'text',
'mpeg' => 'video',
'wmv' => 'video',
'avi' => 'video',
'quicktime' => 'video',
];
$resourcetype = $standardicons[$resourcetype] ?? $resourcetype;
$icons = ['audio', 'document', 'pdf', 'powerpoint', 'spreadsheet', 'text', 'video'];
if ((in_array($resourcetype, $icons))) {
$resourceiconurl = $this->output->image_url('resource/'.$resourcetype, 'theme');
$iconurl = $resourceiconurl ?? $iconurl;
}
}
$activityimg = "<div class='activityiconcontainer ".$cmname."'>";
if (strpos($iconurl, $CFG->wwwroot) !== 0) { // For LTI activities with custom icon URLs.
$activityimg = "<div class='activityiconcontainer ".$cmname."' style='background-color:transparent;'>";
}
$activityimg .= "<img class='iconlarge activityicon' alt='' role='presentation' src='".$iconurl."' />";
$activityimg .= "</div>";
// Multimedia mods we want to open in the same window.
$snapmultimedia = $this->snap_multimedia();
$resourcedisplay = get_config('theme_snap', 'resourcedisplay');
$displaydescription = get_config('theme_snap', 'displaydescription');
$resourceonclick = "";
if ($mod->modname === 'resource') {
$extension = $this->get_mod_type($mod)[1];
if (in_array($extension, $snapmultimedia)) {
// For multimedia we need to handle the popup setting.
// If popup add a redirect param to prevent the intermediate page.
if ($mod->onclick) {
$resourceonclick = "onclick=\"{$mod->onclick}\"";
$url = '';
}
} else {
if ($resourcedisplay == 'card' && $displaydescription) {
$url .= "&forceview=1";
} else {
if ($mod->onclick) {
$resourceonclick = "onclick=\"{$mod->onclick}\"";
$url = '';
}
}
}
}
$onclicklti = $this->theme_snap_lti_get_launch_container($mod);
if ($mod->modname === 'url') {
$urlmod = $DB->get_record('url', ['id' => $mod->instance], '*', MUST_EXIST);
$cm = get_coursemodule_from_instance('url', $urlmod->id);
$fullurl = new moodle_url('/mod/url/view.php', ['id' => $cm->id]);
if ($urlmod->display == RESOURCELIB_DISPLAY_POPUP) {
// In-pop display.
$fullurl .= "&redirect=1";
$options = empty($urlmod->displayoptions) ? [] : (array)unserialize_array($urlmod->displayoptions);
$width = empty($options['popupwidth']) ? 620 : $options['popupwidth'];
$height = empty($options['popupheight']) ? 450 : $options['popupheight'];
$wh = "width={$width},height={$height},toolbar=no,location=no,menubar=no,copyhistory=no,status=no,";
$wh .= "directories=no,scrollbars=yes,resizable=yes";
$onclickurl = "event.preventDefault(); window.open('{$fullurl}', '', '{$wh}'); return false;";
$onclicklti = "onclick=\"{$onclickurl}\"";
$url = '';
} else if ($urlmod->display == RESOURCELIB_DISPLAY_NEW) {
// New Window display.
$fullurl .= "&redirect=1";
$onclickurl = "window.open('{$fullurl}'); return false;";
$onclicklti = "onclick=\"{$onclickurl}\"";
$url = '';
} else {
$url = $fullurl;
}
}
// Activity/resource type.
$snapmodtype = $this->get_mod_type($mod)[0];
$assettype = '<div class="snap-assettype">'.$snapmodtype.'</div>';
if ($mod->uservisible) {
$output .= $activityimg;
$output .= "<div class='snap-header-container'>"
.$assettype.
"<a $target $onclicklti $resourceonclick class='mod-link' href='$url' title='$instancename'>".
"<p class='instancename'>$instancename</p>
</a>
</div>";
$output .= $groupinglabel;
} else {
// We may be displaying this just in order to show information
// about visibility, without the actual link ($mod->uservisible).
$output .= "<div>$activityimg $instancename</div> $groupinglabel";
}
return $output;
}
/**
* Wrapper around course_get_cm_edit_actions
*
* @param cm_info $mod The module
* @param int $sr The section to link back to (used for creating the links)
* @return array Of action_link or pix_icon objects
*/
protected function course_get_cm_edit_actions(cm_info $mod, $sr = null) {
$actions = course_get_cm_edit_actions($mod, -1, $sr);
$actions = array_filter($actions, function($action) {
return !($action instanceof \action_menu_filler);
});
$rename = core_course_inplace_editable($mod, $mod->indent, $sr);
$edittitle = get_string('edittitle');
$rename = str_replace('</a>', "$edittitle</a>", $rename);
$actions['edit-rename'] = $rename;
return $actions;
}
/**
* Return move notice.
* @return bool|string
* @throws moodle_exception
*/
public function snap_footer_alert() {
return $this->output->render_from_template('theme_snap/footer_alert', null);
}
/**
* Generates a notification if course format is not topics or weeks the user is editing and is a teacher/mananger.
*
* @return string
* @throws \coding_exception
*/
public function course_format_warning() {
global $COURSE;
$format = $COURSE->format;
if (in_array($format, ['weeks', 'topics', 'tiles'])) {
return '';
}
if (!$this->page->user_is_editing()) {
return '';
}
if (!has_capability('moodle/course:manageactivities', context_course::instance($COURSE->id))) {
return '';
}
$url = new moodle_url('/course/edit.php', ['id' => $COURSE->id]);
return $this->output->notification(get_string('courseformatnotification', 'theme_snap', $url->out()));
}
/**
* Renders html to display a course search form.
*
* @param string $value default value to populate the search field
* @param string $format display format - 'plain' (default), 'short' or 'navbar'
* @return string
*/
public function course_search_form($value = '', $format = 'plain') {
static $count = 0;
$formid = 'coursesearch';
if ((++$count) > 1) {
$formid .= $count;
}
switch ($format) {
case 'navbar' :
$formid = 'coursesearchnavbar';
$inputid = 'navsearchbox';
$inputsize = 20;
break;
case 'short' :
$inputid = 'shortsearchbox';
$inputsize = 12;
break;
default :
$inputid = 'coursesearchbox';
$inputsize = 30;
}
$data = (object) [
'searchurl' => (new moodle_url('/course/search.php'))->out(false),
'id' => $formid,
'inputid' => $inputid,
'inputsize' => $inputsize,
'value' => $value,
];
return $this->render_from_template('theme_snap/course_search_form', $data);
}
/**
* Renders HTML to display particular course category - list of it's subcategories and courses
*
* Invoked from /course/index.php
*
* @param int|stdClass|coursecat $category
*/
public function course_category($category) {
global $CFG;
$this->page->blocks->add_region('side-pre');
$basecategory = \core_course_category::get(0);
$coursecat = \core_course_category::get(is_object($category) ? $category->id : $category);
$site = get_site();
$output = '';
$categoryselector = '';
// NOTE - we output manage catagory button in the layout file in Snap.
if (!$coursecat->id) {
if (\core_course_category::is_simple_site() == 1) {
// There exists only one category in the system, do not display link to it.
$coursecat = \core_course_category::get_default();
$strfulllistofcourses = get_string('fulllistofcourses');
$this->page->set_title("$site->shortname: $strfulllistofcourses");
} else {
$strcategories = get_string('categories');
$this->page->set_title("$site->shortname: $strcategories");
}
} else {
$title = $site->shortname;
if ($basecategory->get_children_count() > 1) {
$title .= ": ". $coursecat->get_formatted_name();
}
$this->page->set_title($title);
// Print the category selector.
if ($basecategory->get_children_count() > 1) {
$select = new \single_select(new moodle_url('/course/index.php'), 'categoryid',
\core_course_category::make_categories_list(), $coursecat->id, null, 'switchcategory');
$select->set_label(get_string('category').':');
$categoryselector .= $this->render($select);
}
}
$output .= '<div class="row">';
$output .= '<div class="col-sm-4">';
// Add course search form.
$output .= $this->course_search_form();
$output .= '</div>';
// Add cat select box if available.
$output .= '<div class="col-sm-8 text-right">';
$output .= $categoryselector;
$output .= '</div>';
$chelper = new \coursecat_helper();
// Prepare parameters for courses and categories lists in the tree.
$atts = ['class' => 'category-browse category-browse-'.$coursecat->id];
$chelper->set_show_courses(self::COURSECAT_SHOW_COURSES_AUTO)->set_attributes($atts);
$coursedisplayoptions = [];
$catdisplayoptions = [];
$browse = optional_param('browse', null, PARAM_ALPHA);
$perpage = optional_param('perpage', $CFG->coursesperpage, PARAM_INT);
$page = optional_param('page', 0, PARAM_INT);
$baseurl = new moodle_url('/course/index.php');
if ($coursecat->id) {
$baseurl->param('categoryid', $coursecat->id);
}
if ($perpage != $CFG->coursesperpage) {
$baseurl->param('perpage', $perpage);
}
$coursedisplayoptions['limit'] = $perpage;
$catdisplayoptions['limit'] = $perpage;
if ($browse === 'courses' || !$coursecat->has_children()) {
$coursedisplayoptions['offset'] = $page * $perpage;
$coursedisplayoptions['paginationurl'] = new moodle_url($baseurl, ['browse' => 'courses']);
$catdisplayoptions['nodisplay'] = true;
$catdisplayoptions['viewmoreurl'] = new moodle_url($baseurl, ['browse' => 'categories']);
$catdisplayoptions['viewmoretext'] = new \lang_string('viewallsubcategories');
} else if ($browse === 'categories' || !$coursecat->has_courses()) {
$coursedisplayoptions['nodisplay'] = true;
$catdisplayoptions['offset'] = $page * $perpage;
$catdisplayoptions['paginationurl'] = new moodle_url($baseurl, ['browse' => 'categories']);
$coursedisplayoptions['viewmoreurl'] = new moodle_url($baseurl, ['browse' => 'courses']);
$coursedisplayoptions['viewmoretext'] = new \lang_string('viewallcourses');
} else {
// We have a category that has both subcategories and courses, display pagination separately.
$coursedisplayoptions['viewmoreurl'] = new moodle_url($baseurl, ['browse' => 'courses', 'page' => 1]);
$catdisplayoptions['viewmoreurl'] = new moodle_url($baseurl, ['browse' => 'categories', 'page' => 1]);
}
$chelper->set_courses_display_options($coursedisplayoptions)->set_categories_display_options($catdisplayoptions);
// Display course category tree.
$output .= $this->coursecat_tree($chelper, $coursecat);
// Add action buttons.
$context = get_category_or_system_context($coursecat->id);
if (has_capability('moodle/course:create', $context)) {
// Print link to create a new course, for the 1st available category.
if ($coursecat->id) {
$url = new moodle_url('/course/edit.php', ['category' => $coursecat->id, 'returnto' => 'category']);
} else {
$url = new moodle_url('/course/edit.php', ['category' => $CFG->defaultrequestcategory, 'returnto' => 'topcat']);
}
$output .= '<div class="add-course-btn-container"><a class="btn btn-secondary" href="'.$url.'">'.
get_string('addnewcourse', 'moodle').'</a></div>';
}
$output .= $this->container_start('buttons');
ob_start();
if (\core_course_category::is_simple_site() == 1) {
snap_print_course_request_buttons(\context_system::instance());
} else {
snap_print_course_request_buttons($context);
}
$output .= ob_get_contents();
ob_end_clean();
$output .= $this->container_end();
return $output;
}
/**
* Prints a course footer with course contacts, course description and recent updates.
*
* @return string
*/
public function course_footer() {
global $DB, $COURSE, $CFG;
// Check toggle switch.
if (empty($this->page->theme->settings->coursefootertoggle)) {
return false;
}
$context = context_course::instance($COURSE->id);
$courseteachers = '';
$coursesummary = '';
$clist = new \core_course_list_element($COURSE);
$teachers = $clist->get_course_contacts();
if (!empty($teachers)) {
// Get all teacher user records in one go.
$teacherids = [];
foreach ($teachers as $teacher) {
$teacherids[] = $teacher['user']->id;
}
$teacherusers = $DB->get_records_list('user', 'id', $teacherids);
// Course contacts.
$courseteachers .= '<h5>'.get_string('coursecontacts', 'theme_snap').'</h5>';
foreach ($teachers as $teacher) {
if (!isset($teacherusers[$teacher['user']->id])) {
continue;
}
$teacheruser = $teacherusers[$teacher['user']->id];
$courseteachers .= $this->print_teacher_profile($teacheruser);
}
}
// If user can edit add link to manage users.
if (has_capability('moodle/course:enrolreview', $context)) {
if (empty($courseteachers)) {
$courseteachers = "<h5>".get_string('coursecontacts', 'theme_snap')."</h5>";
}
$courseteachers .= '<br><a id="enrolled-users" class="btn btn-outline-secondary btn-sm"
href="'.$CFG->wwwroot.'/user/index.php?id='.$COURSE->id.'">'.get_string('enrolledusers', 'enrol').'</a>';
}
// Course cummary.
if (!empty($COURSE->summary)) {
$coursesummary = '<h5>'.get_string('aboutcourse', 'theme_snap').'</h5>';
$formatoptions = new stdClass;
$formatoptions->noclean = true;
$formatoptions->overflowdiv = true;
$formatoptions->context = $context;
$coursesummarycontent = file_rewrite_pluginfile_urls($COURSE->summary,
'pluginfile.php', $context->id, 'course', 'summary', null);
$coursesummarycontent = format_text($coursesummarycontent, $COURSE->summaryformat, $formatoptions);
$coursesummary .= '<div id="snap-course-footer-summary">'.$coursesummarycontent.'</div>';
}
// If able to edit add link to edit summary.
if (has_capability('moodle/course:update', $context)) {
if (empty($coursesummary)) {
$coursesummary = '<h5>'.get_string('aboutcourse', 'theme_snap').'</h5>';
}
$coursesummary .= '<br><a id="edit-summary" class="btn btn-outline-secondary btn-sm"
href="'.$CFG->wwwroot.'/course/edit.php?id='.$COURSE->id.'#id_descriptionhdr">'.get_string('editsummary').'</a>';
}
// Get recent activities on mods in the course.
$courserecentactivities = $this->get_mod_recent_activity($context);
$courserecentactivity = '';
if ($courserecentactivities) {
$courserecentactivity = '<h5>'.get_string('recentactivity').'</h5>';
if (!empty($courserecentactivities)) {
$courserecentactivity .= $courserecentactivities;
}
}
// If user can edit add link to moodle recent activity stuff.
if (has_capability('moodle/course:update', $context)) {
if (empty($courserecentactivities)) {
$courserecentactivity = '<h5>'.get_string('recentactivity').'</h5>';
$courserecentactivity .= get_string('norecentactivity');
}
$courserecentactivity .= '<div class="col-xs-12 clearfix"><a href="'.$CFG->wwwroot.'/course/recent.php?id='
.$COURSE->id.'">'.get_string('showmore', 'form').'</a></div>';
}
if (!empty($courserecentactivity)) {
$columns[] = $courserecentactivity;
}
if (!empty($courseteachers)) {
$columns[] = $courseteachers;
}
if (!empty($coursesummary)) {
$columns[] = $coursesummary;
}
$output = '';
if (empty($columns)) {
return $output;
} else {
$output .= '<div class="row">';
$output .= '<div class="col-lg-3 col-md-4"><div id="snap-course-footer-contacts">'.$courseteachers.'</div></div>';
$output .= '<div class="col-lg-9 col-md-8"><div id="snap-course-footer-about">'.$coursesummary.'</div></div>';
$output .= '<div class="col-sm-12"><div id="snap-course-footer-recent-activity">'.$courserecentactivity.'</div></div>';
$output .= '</div>';
}
return $output;
}
/**
* Print teacher profile
* Prints a media object with the techers photo, name (links to profile) and desctiption.
*
* @param stdClass $user
* @return string
*/
public function print_teacher_profile($user) {
global $CFG, $USER;
$userpicture = new \user_picture($user);
$userpicture->link = false;
$userpicture->alttext = false;
$userpicture->size = 100;
$picture = $this->render($userpicture);
$fullname = '<a href="' .$CFG->wwwroot. '/user/profile.php?id=' .$user->id. '">'.format_string(fullname($user)).'</a>';
$data = (object) [
'image' => $picture,
'content' => $fullname,
];
if ($USER->id != $user->id) {
$messageicon = '<img class="svg-icon" alt="" role="presentation" src="'
.$this->output->image_url('messages', 'theme').' ">';
$message = '<br><small><a href="'.$CFG->wwwroot.
'/message/index.php?id='.$user->id.'">message'.$messageicon.'</a></small>';
$data->content .= $message;
}
return $this->render_from_template('theme_snap/media_object', $data);
}
/**
* Print recent activites for a course
*
* @param stdClass $context
* @return string
*/
public function get_mod_recent_activity($context) {
global $COURSE;
$viewfullnames = has_capability('moodle/site:viewfullnames', $context);
$recentactivity = [];
$timestart = time() - (86400 * 7); // Only show last 7 days activity.
if (optional_param('testing', false, PARAM_BOOL)) {
$timestart = time() - (86400 * 3000); // 3000 days ago for testing.
}
$modinfo = get_fast_modinfo($COURSE);
$usedmodules = $modinfo->get_used_module_names();
// Don't show activity for folder mod.
unset($usedmodules['folder']);
if (empty($usedmodules)) {
// No used modules so return null string.
return '';
}
foreach ($usedmodules as $modname => $modfullname) {
// Each module gets it's own logs and prints them.
ob_start();
$hascontent = component_callback('mod_'. $modname, 'print_recent_activity',
[$COURSE, $viewfullnames, $timestart], false);
if ($hascontent) {
$content = ob_get_contents();
if (!empty($content)) {
$recentactivity[$modname] = $content;
}
}
ob_end_clean();
}
$output = '';
if (!empty($recentactivity)) {
foreach ($recentactivity as $modname => $moduleactivity) {
// Get mod icon, empty alt as title already there.
$img = html_writer::tag('img', '', [
'src' => $this->output->image_url('icon', $modname),
'alt' => '',
]);
// Create media object for module activity.
$data = (object) [
'image' => $img,
'content' => $moduleactivity,
'class' => $modname,
];
$output .= $this->render_from_template('theme_snap/media_object', $data);
}
}
return $output;
}
/**
* Renders HTML to display a list of course modules in a course section
* Also displays "move here" controls in Javascript-disabled mode.
* Copied from course/rederer.php
*
* @deprecated since 4.0 MDL-72656 - use core_course output components instead.
*
* This function calls {@link core_course_renderer::course_section_cm()}
*
* @param stdClass $course course object
* @param int|stdClass|section_info $section relative section number or section object
* @param int $sectionreturn section number to return to
* @param int $displayoptions
* @return void
*/
public function course_section_cm_list($course, $section, $sectionreturn = null, $displayoptions = []) {
global $USER;
$output = '';
$format = course_get_format($course);
$modinfo = $format->get_modinfo();
if (is_object($section)) {
$section = $modinfo->get_section_info($section->section);
} else {
$section = $modinfo->get_section_info($section);
}
$completioninfo = new \completion_info($course);
// Check if we are currently in the process of moving a module with JavaScript disabled.
$ismoving = $format->show_editor() && ismoving($course->id);
if ($ismoving) {
$strmovefull = strip_tags(get_string("movefull", "", "'$USER->activitycopyname'"));
}
// Get the list of modules visible to user (excluding the module being moved if there is one).
$moduleshtml = [];
if (!empty($modinfo->sections[$section->section])) {
foreach ($modinfo->sections[$section->section] as $modnumber) {
$mod = $modinfo->cms[$modnumber];
if ($ismoving && $mod->id == $USER->activitycopy) {
// Do not display moving mod.
continue;
}
if ($modulehtml = $this->course_section_cm_list_item($course,
$completioninfo, $mod, $sectionreturn, $displayoptions)) {
$moduleshtml[$modnumber] = $modulehtml;
}
}
}
$sectionoutput = '';
if (!empty($moduleshtml) || $ismoving) {
foreach ($moduleshtml as $modnumber => $modulehtml) {
if ($ismoving) {
$movingurl = new \moodle_url('/course/mod.php', ['moveto' => $modnumber, 'sesskey' => sesskey()]);
$sectionoutput .= html_writer::tag('li',
html_writer::link($movingurl, '', ['title' => $strmovefull, 'class' => 'movehere']),
['class' => 'movehere']);
}
$sectionoutput .= $modulehtml;
}
if ($ismoving) {
$movingurl = new \moodle_url('/course/mod.php', ['movetosection' => $section->id, 'sesskey' => sesskey()]);
$sectionoutput .= html_writer::tag('li',
html_writer::link($movingurl, '', ['title' => $strmovefull, 'class' => 'movehere']),
['class' => 'movehere']);
}
}
// Always output the section module list.
$output .= html_writer::tag('ul', $sectionoutput, ['class' => 'section img-text']);
return $output;
}
/**
* Checks if course module has any conditions that may make it unavailable for
* all or some of the students
* Copied from course/rederer.php
* @deprecated since Moodle 4.0 MDL-72656 - please do not use this function any more.
*
* @param cm_info $mod
* @return bool
*/
public function is_cm_conditionally_hidden(cm_info $mod) {
global $CFG;
$conditionalhidden = false;
if (!empty($CFG->enableavailability)) {
$info = new \core_availability\info_module($mod);
$conditionalhidden = !$info->is_available_for_all();
}
return $conditionalhidden;
}
/**
* Get LTI launch container.
*
* @param cm_info $mod
* @return string
*/
public function theme_snap_lti_get_launch_container(cm_info $mod) {
global $DB, $CFG;
require_once($CFG->dirroot.'/mod/lti/lib.php');
require_once($CFG->dirroot.'/mod/lti/locallib.php');
// LTI launch container for Snap.
if (!$lti = $DB->get_record('lti', ['id' => $mod->instance],
'icon, secureicon, intro, introformat, name, typeid, toolurl, launchcontainer')) {
return null;
}
$info = new \cached_cm_info();
if ($mod->showdescription) {
// Convert intro to html. Do not filter cached version, filters run at display time.
$info->content = format_module_intro('lti', $lti, $mod->id, false);
}
if (!empty($lti->typeid)) {
$toolconfig = lti_get_type_config($lti->typeid);
} else if ($tool = lti_get_tool_by_url_match($lti->toolurl)) {
$toolconfig = lti_get_type_config($tool->id);
} else {
$toolconfig = [];
}
// We want to use the right icon based on whether the
// current page is being requested over http or https.
if (lti_request_is_using_ssl() &&
(!empty($lti->secureicon) || (isset($toolconfig['secureicon']) && !empty($toolconfig['secureicon'])))) {
if (!empty($lti->secureicon)) {
$info->iconurl = new moodle_url($lti->secureicon);
} else {
$info->iconurl = new moodle_url($toolconfig['secureicon']);
}
} else if (!empty($lti->icon)) {
$info->iconurl = new moodle_url($lti->icon);
} else if (isset($toolconfig['icon']) && !empty($toolconfig['icon'])) {
$info->iconurl = new moodle_url($toolconfig['icon']);
}
// For some reason Snap wasn't doing this right with some external tools,
// with this we are creating the same behavior that core does, launching the content in a new window on click.
$launchcontainer = lti_get_launch_container($lti, $toolconfig);
$onclicklti = '';
if (($launchcontainer == LTI_LAUNCH_CONTAINER_WINDOW) && ($mod->modname === 'lti')) {
if ($mod->onclick) {
$launchurl = new moodle_url('/mod/lti/launch.php', ['id' => $mod->id]);
$onclickltiurl = 'window.open("' . $launchurl->out(false) . '", "lti-'.$mod->id.'"); return false;';
$onclicklti = "onclick='$onclickltiurl'";
}
}
return $onclicklti;
}
/**
* Renders HTML for displaying the sequence of course module editing buttons
*
* @param cm_info $mod The module we are displaying actions for.
* @return string
*
* @see course_get_cm_edit_actions()
*
*/
public function course_section_menu_actions(cm_info $mod = null) {
global $CFG;
// Build up edit actions.
$actions = '';
$actionsadvanced = [];
$coursecontext = context_course::instance($mod->course);
$modcontext = context_module::instance($mod->id);
$baseurl = new moodle_url('/course/mod.php', ['sesskey' => sesskey()]);
$str = get_strings(['editsettings', 'delete', 'move', 'duplicate', 'hide', 'show', 'roles',
'makeavailable', 'makeunavailable', ], 'moodle');
// Move, Edit, Delete.
if (has_capability('moodle/course:manageactivities', $modcontext)) {
// Update button.
$actionsadvanced[] = '<li><a href="'.new moodle_url($baseurl, ['update' => $mod->id]).
'" data-action="update" role="button" class="snap-edit-asset dropdown-item" role="button">'.
'<i class="icon fa fa-pencil fa-fw "></i>'.$str->editsettings.'</a></li>';
// Move button.
$movealt = s(get_string('move', 'theme_snap', $mod->get_formatted_name()));
$actionsadvanced[] = '<li><a><label role="button" class="snap-asset-move dropdown-item" for="snap-move-mod-'
.$mod->id.'"><input id="snap-move-mod-'.$mod->id.'" aria-label="'.$movealt.
'"class="js-snap-asset-move sr-only" role="button" type="checkbox">'.
'<i class="icon fa fa-arrows fa-fw "></i>'.$str->move.'</label></a></li>';
// Delete button.
$actionsadvanced[] = '<li><a href="'.new moodle_url($baseurl, ['delete' => $mod->id]).
'" data-action="delete" role="button" class="js_snap_delete dropdown-item">'.
'<i class="icon fa fa-trash fa-fw "></i>'.$str->delete.'</a></li>';
}
// Hide/Show.
if (has_capability('moodle/course:activityvisibility', $modcontext)) {
$ariacbaction = get_string('hideandshowactioncb', 'theme_snap');
$actions .= '<input class="sr-only" type="checkbox" aria-label="'.$ariacbaction.'">';
$hideaction = '<li><a href="'.new moodle_url($baseurl, ['hide' => $mod->id]);
$hideaction .= '" data-action="hide" role="button" class="dropdown-item editing_hide js_snap_hide">'.
'<i class="icon fa fa-eye fa-fw "></i>'.$str->hide.'</a></li>';
$actionsadvanced[] = $hideaction;
$showaction = '<li><a href="'.new moodle_url($baseurl, ['show' => $mod->id]);
$showaction .= '" data-action="show" role="button" class="dropdown-item editing_show js_snap_show">'.
'<i class="icon fa fa-eye-slash fa-fw "></i>'.$str->show.'</a></li>';
$actionsadvanced[] = $showaction;
// Stealth action.
$courseformat = course_get_format($mod->get_course());
$makeunavailable = '<li><a href="'.new moodle_url($baseurl, ['hide' => $mod->id]);
$makeunavailable .= '" data-action="hide" role="button" class="dropdown-item editing_makeunavailable js_snap_hide">' .
'<i class="icon fa fa-commenting fa-fw "></i>'.$str->makeunavailable . '</a></li>';
$actionsadvanced[] = $makeunavailable;
if (!empty($CFG->allowstealth) && $mod->has_view()) {
$action = 'stealth';
$actionstealth = '<li><a href="'.new moodle_url($baseurl, [$action => $mod->id]);
$actionstealth .= '" data-action="' . $action . '"
role="button" class="dropdown-item editing_makeavailable js_snap_stealth">' .
'<i class="icon fa fa-ban fa-fw "></i>'.$str->makeavailable.'</a></li>';
$actionsadvanced[] = $actionstealth;
$action = 'show';
$actionstealthshow = '<li><a href="'.new moodle_url($baseurl, [$action => $mod->id]);
$actionstealthshow .= '" data-action="' . $action .
'" role="button" class="dropdown-item editing_makeavailable js_snap_stealthshow">' .
'<i class="icon fa fa-ban fa-fw "></i>'.$str->makeavailable.'</a></li>';
$actionsadvanced[] = $actionstealthshow;
}
}
// Duplicate.
$dupecaps = ['moodle/backup:backuptargetimport', 'moodle/restore:restoretargetimport'];
if (has_all_capabilities($dupecaps, $coursecontext) &&
plugin_supports('mod', $mod->modname, FEATURE_BACKUP_MOODLE2) &&
plugin_supports('mod', $mod->modname, 'duplicate', true)) {
$actionsadvanced[] = "<li><a href='".new moodle_url($baseurl, ['duplicate' => $mod->id]).
"' data-action='duplicate' role='button' class='dropdown-item js_snap_duplicate'>".
"<i class='icon fa fa-copy fa-fw'></i>$str->duplicate</a></li>";
}
// Assign roles.
if (has_capability('moodle/role:assign', $modcontext)) {
$actionsadvanced[] = "<li><a role='button' class='dropdown-item' href='".
new moodle_url('/admin/roles/assign.php', ['contextid' => $modcontext->id]).
"'><i class='icon fa fa-user-circle fa-fw'></i>$str->roles</a></li>";
}
// Give local plugins a chance to add icons.
$localplugins = [];
foreach (get_plugin_list_with_function('local', 'extend_module_editing_buttons') as $function) {
$localplugins = array_merge($localplugins, $function($mod));
}
foreach (get_plugin_list_with_function('block', 'extend_module_editing_buttons') as $function) {
// The Sharing Cart block should only be available for user with capability to manage activities.
if ($function === 'block_sharing_cart_extend_module_editing_buttons') {
if (has_capability('moodle/course:manageactivities', $modcontext)) {
$localplugins = array_merge($localplugins, $function($mod));
}
} else {
$localplugins = array_merge($localplugins, $function($mod));
}
}
// TODO - pld string is far too long....
$locallinks = '';
foreach ($localplugins as $localplugin) {
$url = $localplugin->url;
$text = $localplugin->text;
$icon = $localplugin->icon;
$iconhtml = $this->pix_icon($icon->pix, $text, $icon->component);
$class = 'dropdown-item ' . $localplugin->attributes['class'];
$actionsadvanced[] = "<a href='$url' class='$class'>$iconhtml $text</a>";
}
$advancedactions = '';
if (!empty($actionsadvanced)) {
$moreicons = '<i aria-hidden="true" class="icon fa fa-chevron-down fa-fw"></i>'.
'<i aria-hidden="true" class="icon fa fa-chevron-up fa-fw"></i>';
$advancedactions = '<div class="dropdown snap-edit-more-dropdown">';
$advancedactions .= '<button class="snap-edit-asset-more" ';
$advancedactions .= 'data-toggle="dropdown" data-boundary="window" data-offset="-10,12"';
$advancedactions .= 'aria-label="' . get_string('moreoptionslabel', 'theme_snap') . '" aria-expanded="false"';
$advancedactions .= 'aria-controls="#snap-asset-menu">'.$moreicons.'</button>';
$advancedactions .= '<ul id="snap-asset-menu" class="dropdown-menu asset-edit-menu">';
foreach ($actionsadvanced as $action) {
$advancedactions .= "$action";
}
$advancedactions .= "</ul></div>";
}
// Add actions menu.
$output = '';
if ($advancedactions) {
$output .= "<div class='js-only snap-asset-actions' role='region' aria-label='" .
get_string('courseactionslabel', 'theme_snap') . "'>";
$output .= $advancedactions;
$output .= "</div>";
}
return $output;
}
}
Sindbad File Manager Version 1.0, Coded By Sindbad EG ~ The Terrorists