Course information
Whoops \ Exception \ ErrorException
(E_WARNING)
Stack frames (11)
10
Whoops\Exception\ErrorException
…/public/lib/filestorage/file_system.php:410
9
getimagesize
…/public/lib/filestorage/file_system.php:410
8
file_system
get_imageinfo_from_path
…/public/local/alternative_file_system/classes/storages/storage_file_system.php:155
7
local_alternative_file_system\storages\storage_file_system
get_imageinfo
…/public/local/alternative_file_system/classes/external_file_system.php:219
6
local_alternative_file_system\external_file_system
get_imageinfo
…/public/lib/filestorage/stored_file.php:586
5
stored_file
get_imageinfo
…/public/lib/filestorage/stored_file.php:601
4
stored_file
is_valid_image
…/public/course/renderer.php:542
3
core_course_renderer
course_overview_files
…/public/course/renderer.php:617
2
core_course_renderer
coursecat_coursebox_content
…/public/course/renderer.php:480
1
core_course_renderer
coursecat_coursebox
…/public/course/renderer.php:86
0
core_course_renderer
course_info_box
…/public/course/info.php:76
/var/www/html/moodle/public/lib/filestorage/file_system.php
// The mimetype does not include image.
return false;
}
// If it looks like an image, and it smells like an image, perhaps it's an image!
return true;
}
/**
* Returns image information relating to the specified path or URL.
*
* @param string $path The full path of the image file.
* @return array|bool array that containing width, height, and mimetype or false if cannot get the image info.
*/
protected function get_imageinfo_from_path($path) {
$imagemimetype = file_storage::mimetype_from_file($path);
$issvgimage = file_is_svg_image_from_mimetype($imagemimetype);
if (!$issvgimage) {
$imageinfo = getimagesize($path);
if (!is_array($imageinfo)) {
return false; // Nothing to process, the file was not recognised as image by GD.
}
$image = [
'width' => $imageinfo[0],
'height' => $imageinfo[1],
'mimetype' => image_type_to_mime_type($imageinfo[2]),
];
} else {
// Since SVG file is actually an XML file, GD cannot handle.
$svgcontent = @simplexml_load_file($path);
if (!$svgcontent) {
// Cannot parse the file.
return false;
}
$svgattrs = $svgcontent->attributes();
if (!empty($svgattrs->viewBox)) {
// We have viewBox.
$viewboxval = explode(' ', $svgattrs->viewBox);
More info
http://docs.moodle.org/501/en/error/moodle/generalexceptionmessage
/var/www/html/moodle/public/lib/filestorage/file_system.php
// The mimetype does not include image.
return false;
}
// If it looks like an image, and it smells like an image, perhaps it's an image!
return true;
}
/**
* Returns image information relating to the specified path or URL.
*
* @param string $path The full path of the image file.
* @return array|bool array that containing width, height, and mimetype or false if cannot get the image info.
*/
protected function get_imageinfo_from_path($path) {
$imagemimetype = file_storage::mimetype_from_file($path);
$issvgimage = file_is_svg_image_from_mimetype($imagemimetype);
if (!$issvgimage) {
$imageinfo = getimagesize($path);
if (!is_array($imageinfo)) {
return false; // Nothing to process, the file was not recognised as image by GD.
}
$image = [
'width' => $imageinfo[0],
'height' => $imageinfo[1],
'mimetype' => image_type_to_mime_type($imageinfo[2]),
];
} else {
// Since SVG file is actually an XML file, GD cannot handle.
$svgcontent = @simplexml_load_file($path);
if (!$svgcontent) {
// Cannot parse the file.
return false;
}
$svgattrs = $svgcontent->attributes();
if (!empty($svgattrs->viewBox)) {
// We have viewBox.
$viewboxval = explode(' ', $svgattrs->viewBox);
/var/www/html/moodle/public/local/alternative_file_system/classes/storages/storage_file_system.php
* @param stored_file $file The file to inspect
*
* @return mixed array with width, height and mimetype; false if not an image
*
* @throws Exception
*/
public function get_imageinfo(stored_file $file) {
if (!$this->is_image_from_storedfile($file)) {
return false;
}
$hash = $file->get_contenthash();
$cache = cache::make("core", "file_imageinfo");
$info = $cache->get($hash);
if ($info !== false) {
return $info;
}
$path = $this->get_remote_path_from_hash($file->get_contenthash());
$info = $this->get_imageinfo_from_path($path);
$cache->set($hash, $info);
return $info;
}
/**
* Function get_remote_file_size
*
* @param $contenthash
* @return int
* @throws dml_exception
*/
public function get_remote_file_size($contenthash) {
$url = $this->get_remote_path_from_hash($contenthash);
$curl = curl_init($url);
// Issue a HEAD request and follow any redirects.
curl_setopt($curl, CURLOPT_NOBODY, true);
curl_setopt($curl, CURLOPT_HEADER, true);
curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
/var/www/html/moodle/public/local/alternative_file_system/classes/external_file_system.php
* Determine whether the file is present on the local file system somewhere.
*
* @param stored_file $file The file to ensure is available.
* @return bool
* @throws Exception
*/
public function is_file_readable_remotely_by_storedfile(stored_file $file) {
return $this->filesysteminstance->is_file_readable_remotely_by_storedfile($file);
}
/**
* Returns information about image.
* Information is determined from the file content
*
* @param stored_file $file The file to inspect
* @return mixed array with width, height and mimetype; false if not an image
* @throws Exception
*/
public function get_imageinfo(stored_file $file) {
return $this->filesysteminstance->get_imageinfo($file);
}
/**
* Returns file handle - read only mode, no writing allowed into pool files!
*
* When a consumer needs a seekable stream (for example requests using Range
* headers in the mobile app), a signed remote URL is not enough because
* functions like fseek() may fail. In those cases, download the file to a
* request-local path first and open the local copy.
*
* @param stored_file $file The file to retrieve a handle for
* @param int $type Type of file handle (FILE_HANDLE_xx constant)
* @return resource file handle
* @throws file_exception
* @throws coding_exception
*/
public function get_content_file_handle(stored_file $file, $type = stored_file::FILE_HANDLE_FOPEN) {
if ($file->get_filesize() >= LOCAL_ALTERNATIVE_FILE_SYSTEM_MIN_DOWNLOAD_SIZE) {
$localpath = $this->get_local_file_for_stored_file($file);
return self::get_file_handle_for_path($localpath, $type);
/var/www/html/moodle/public/lib/filestorage/stored_file.php
// This file is not stored locally - attempt to retrieve it from the repository.
// This may happen if the repository deliberately does not fetch files, or if there is a failure with the sync.
$fileinfo = $this->repository->get_file($this->get_reference());
if (isset($fileinfo['path'])) {
return $filearch->add_file_from_pathname($archivepath, $fileinfo['path']);
}
}
}
return $this->filesystem->add_storedfile_to_archive($this, $filearch, $archivepath);
}
/**
* Returns information about image,
* information is determined from the file content
*
* @return mixed array with width, height and mimetype; false if not an image
*/
public function get_imageinfo() {
return $this->filesystem->get_imageinfo($this);
}
/**
* Verifies the file is a valid web image - gif, png and jpeg only.
*
* It should be ok to serve this image from server without any other security workarounds.
*
* @return bool true if file ok
*/
public function is_valid_image() {
$mimetype = $this->get_mimetype();
if (!file_mimetype_in_typegroup($mimetype, 'web_image')) {
return false;
}
if (!$info = $this->get_imageinfo()) {
return false;
}
if ($info['mimetype'] !== $mimetype) {
return false;
}
/var/www/html/moodle/public/lib/filestorage/stored_file.php
*
* @return mixed array with width, height and mimetype; false if not an image
*/
public function get_imageinfo() {
return $this->filesystem->get_imageinfo($this);
}
/**
* Verifies the file is a valid web image - gif, png and jpeg only.
*
* It should be ok to serve this image from server without any other security workarounds.
*
* @return bool true if file ok
*/
public function is_valid_image() {
$mimetype = $this->get_mimetype();
if (!file_mimetype_in_typegroup($mimetype, 'web_image')) {
return false;
}
if (!$info = $this->get_imageinfo()) {
return false;
}
if ($info['mimetype'] !== $mimetype) {
return false;
}
// ok, GD likes this image
return true;
}
/**
* Returns parent directory, creates missing parents if needed.
*
* @return stored_file
*/
public function get_parent_directory() {
if ($this->file_record->filepath === '/' and $this->file_record->filename === '.') {
//root dir does not have parent
return null;
}
/var/www/html/moodle/public/course/renderer.php
);
$content .= html_writer::tag('li', $name);
}
$content .= html_writer::end_tag('ul');
}
return $content;
}
/**
* Returns HTML to display course overview files.
*
* @param core_course_list_element $course
* @return string
*/
protected function course_overview_files(core_course_list_element $course): string {
global $CFG;
$contentimages = $contentfiles = '';
foreach ($course->get_course_overviewfiles() as $file) {
$isimage = $file->is_valid_image();
$url = moodle_url::make_file_url("$CFG->wwwroot/pluginfile.php",
'/' . $file->get_contextid() . '/' . $file->get_component() . '/' .
$file->get_filearea() . $file->get_filepath() . $file->get_filename(), !$isimage);
if ($isimage) {
$contentimages .= html_writer::tag('div',
html_writer::empty_tag('img', ['src' => $url, 'alt' => '']),
['class' => 'courseimage']);
} else {
$image = $this->output->pix_icon(file_file_icon($file), $file->get_filename(), 'moodle');
$filename = html_writer::tag('span', $image, ['class' => 'fp-icon']).
html_writer::tag('span', $file->get_filename(), ['class' => 'fp-filename']);
$contentfiles .= html_writer::tag('span',
html_writer::link($url, $filename),
['class' => 'coursefile fp-filename-icon text-break']);
}
}
return $contentimages . $contentfiles;
}
/**
/var/www/html/moodle/public/course/renderer.php
}
/**
* Returns HTML to display course content (summary, course contacts and optionally category name)
*
* This method is called from coursecat_coursebox() and may be re-used in AJAX
*
* @param coursecat_helper $chelper various display options
* @param stdClass|core_course_list_element $course
* @return string
*/
protected function coursecat_coursebox_content(coursecat_helper $chelper, $course) {
if ($chelper->get_show_courses() < self::COURSECAT_SHOW_COURSES_EXPANDED) {
return '';
}
if ($course instanceof stdClass) {
$course = new core_course_list_element($course);
}
$content = \html_writer::start_tag('div', ['class' => 'd-flex']);
$content .= $this->course_overview_files($course);
$content .= \html_writer::start_tag('div', ['class' => 'flex-grow-1']);
$content .= $this->course_summary($chelper, $course);
$content .= $this->course_contacts($course);
$content .= $this->course_category_name($chelper, $course);
$content .= $this->course_custom_fields($course);
$content .= \html_writer::end_tag('div');
$content .= \html_writer::end_tag('div');
return $content;
}
/**
* Renders the list of courses
*
* This is internal function, please use {@link core_course_renderer::courses_list()} or another public
* method from outside of the class
*
* If list of courses is specified in $courses; the argument $chelper is only used
* to retrieve display options and attributes, only methods get_show_courses(),
* get_courses_display_option() and get_and_erase_attributes() are called.
*
/var/www/html/moodle/public/course/renderer.php
$content = '';
$classes = trim('coursebox clearfix '. $additionalclasses);
if ($chelper->get_show_courses() < self::COURSECAT_SHOW_COURSES_EXPANDED) {
$classes .= ' collapsed';
}
// .coursebox
$content .= html_writer::start_tag('div', array(
'class' => $classes,
'data-courseid' => $course->id,
'data-type' => self::COURSECAT_TYPE_COURSE,
));
$content .= html_writer::start_tag('div', array('class' => 'info'));
$content .= $this->course_name($chelper, $course);
$content .= $this->course_enrolment_icons($course);
$content .= html_writer::end_tag('div');
$content .= html_writer::start_tag('div', array('class' => 'content'));
$content .= $this->coursecat_coursebox_content($chelper, $course);
$content .= html_writer::end_tag('div');
$content .= html_writer::end_tag('div'); // .coursebox
return $content;
}
/**
* Returns HTML to display course summary.
*
* @param coursecat_helper $chelper
* @param core_course_list_element $course
* @return string
*/
protected function course_summary(coursecat_helper $chelper, core_course_list_element $course): string {
$content = '';
if ($course->has_summary()) {
$content .= html_writer::start_tag('div', ['class' => 'summary']);
$content .= $chelper->get_course_formatted_summary($course,
array('overflowdiv' => true, 'noclean' => true, 'para' => false));
$content .= html_writer::end_tag('div');
/var/www/html/moodle/public/course/renderer.php
* @param string $target
*/
public function __construct(moodle_page $page, $target) {
$this->strings = new stdClass;
$courseid = $page->course->id;
parent::__construct($page, $target);
}
/**
* Renders course info box.
*
* @param stdClass $course
* @return string
*/
public function course_info_box(stdClass $course) {
$content = '';
$content .= $this->output->box_start('generalbox info');
$chelper = new coursecat_helper();
$chelper->set_show_courses(self::COURSECAT_SHOW_COURSES_EXPANDED);
$content .= $this->coursecat_coursebox($chelper, $course);
$content .= $this->output->box_end();
return $content;
}
/**
* Renders course info box.
*
* @param stdClass $course course object
* @param string[] $widgets array of enrolment widgets
* @param \core\url|null $returnurl return url
* @return string
*/
public function enrolment_options(stdClass $course, array $widgets, ?\core\url $returnurl = null): string {
if (!$widgets) {
if (isguestuser()) {
$message = get_string('noguestaccess', 'enrol');
$continuebutton = $this->output->continue_button(get_login_url());
} else if ($returnurl) {
$message = get_string('notenrollable', 'enrol');
$continuebutton = $this->output->continue_button($returnurl);
/var/www/html/moodle/public/course/info.php
$PAGE->set_url('/course/info.php', array('id' => $course->id));
$strcourseinfo = get_string('courseinfo');
$PAGE->set_title(implode(moodle_page::TITLE_SEPARATOR, [$strcourseinfo, $course->fullname]));
$PAGE->set_heading($strcourseinfo);
$PAGE->navbar->add(get_string('summary'));
echo $OUTPUT->header();
// print enrol info
if ($texts = enrol_get_course_description_texts($course)) {
echo $OUTPUT->box_start('generalbox icons');
echo implode($texts);
echo $OUTPUT->box_end();
}
/** @var core_course_renderer $courserenderer */
$courserenderer = $PAGE->get_renderer('core', 'course');
echo $courserenderer->course_info_box($course);
echo "<br />";
// Trigger event, course information viewed.
$eventparams = array('context' => $context, 'objectid' => $course->id);
$event = \core\event\course_information_viewed::create($eventparams);
$event->trigger();
echo $OUTPUT->footer();
Environment & details:
| Key | Value |
| id | 561 |
| lang | en |
empty
empty
empty
| Key | Value |
| USER | stdClass Object ( [id] => 0 [mnethostid] => 1 [access] => Array ( [ra] => Array ( [/1] => Array ( [6] => 6 ) ) [time] => 1783367791 [rsw] => Array ( ) ) [enrol] => Array ( [enrolled] => Array ( ) [tempguest] => Array ( ) ) [sesskey] => XW4PI75Nel [preference] => Array ( ) ) |
| SESSION | stdClass Object ( [isnewsessioncookie] => 1 [lang] => en [cachestore_session] => Array ( [default_session-core/courseeditorstate] => Array ( ) [default_session-core/navigation_cache] => Array ( [__lastaccess__u0_dv25q2ut6hs0s4igpfl5odhel6] => Array ( [0] => 1783367791 [1] => 1783367791 ) ) [default_session-core/coursecat] => Array ( [__lastaccess__u0_dv25q2ut6hs0s4igpfl5odhel6] => Array ( [0] => 1783367791 [1] => 1783367791 ) [u0_dv25q2ut6hs0s4igpfl5odhel6_75fd03523f0d57863066cdcf0321eb7494bce9ba] => Array ( [0] => 1783367791.0665-6a4c086f103f82.55413756 [1] => 1783367791 ) [u0_dv25q2ut6hs0s4igpfl5odhel6_d66165f584d395826c25c45acc50225b57540591] => Array ( [0] => Array ( ) [1] => 1783367791 ) [u0_dv25q2ut6hs0s4igpfl5odhel6_f9d3f89d57599acea9ec4f8e2aa65c0a1d054c4a] => Array ( [0] => Array ( [0] => 1 [1] => 14 [2] => 12 [3] => 40 ) [1] => 1783367791 ) ) [default_session-core/navigation_expandcourse] => Array ( [__lastaccess__u0_dv25q2ut6hs0s4igpfl5odhel6] => Array ( [0] => 1783367791 [1] => 0 ) [u0_dv25q2ut6hs0s4igpfl5odhel6_561-d50a9667e989a5c6e417a634c58f68ec] => Array ( [0] => 0 [1] => 0 ) [u0_dv25q2ut6hs0s4igpfl5odhel6_566-d50a9667e989a5c6e417a634c58f68ec] => Array ( [0] => 0 [1] => 0 ) [u0_dv25q2ut6hs0s4igpfl5odhel6_565-d50a9667e989a5c6e417a634c58f68ec] => Array ( [0] => 0 [1] => 0 ) [u0_dv25q2ut6hs0s4igpfl5odhel6_564-d50a9667e989a5c6e417a634c58f68ec] => Array ( [0] => 0 [1] => 0 ) [u0_dv25q2ut6hs0s4igpfl5odhel6_563-d50a9667e989a5c6e417a634c58f68ec] => Array ( [0] => 0 [1] => 0 ) [u0_dv25q2ut6hs0s4igpfl5odhel6_562-d50a9667e989a5c6e417a634c58f68ec] => Array ( [0] => 0 [1] => 0 ) [u0_dv25q2ut6hs0s4igpfl5odhel6_560-d50a9667e989a5c6e417a634c58f68ec] => Array ( [0] => 0 [1] => 0 ) [u0_dv25q2ut6hs0s4igpfl5odhel6_557-d50a9667e989a5c6e417a634c58f68ec] => Array ( [0] => 0 [1] => 0 ) [u0_dv25q2ut6hs0s4igpfl5odhel6_556-d50a9667e989a5c6e417a634c58f68ec] => Array ( [0] => 0 [1] => 0 ) [u0_dv25q2ut6hs0s4igpfl5odhel6_555-d50a9667e989a5c6e417a634c58f68ec] => Array ( [0] => 0 [1] => 0 ) [u0_dv25q2ut6hs0s4igpfl5odhel6_554-d50a9667e989a5c6e417a634c58f68ec] => Array ( [0] => 0 [1] => 0 ) ) ) ) |
| Key | Value |
| USER | www-data |
| HOME | /var/www |
| SCRIPT_NAME | /course/info.php |
| REQUEST_URI | /course/info.php?id=561&lang=en |
| QUERY_STRING | id=561&lang=en |
| REQUEST_METHOD | GET |
| SERVER_PROTOCOL | HTTP/1.1 |
| GATEWAY_INTERFACE | CGI/1.1 |
| REMOTE_PORT | 55660 |
| SCRIPT_FILENAME | /var/www/html/moodle/public/course/info.php |
| SERVER_ADMIN | admincollecto |
| CONTEXT_DOCUMENT_ROOT | /var/www/html/moodle/public |
| CONTEXT_PREFIX | |
| REQUEST_SCHEME | http |
| DOCUMENT_ROOT | /var/www/html/moodle/public |
| REMOTE_ADDR | 216.73.217.58 |
| SERVER_PORT | 443 |
| SERVER_ADDR | 192.168.2.203 |
| SERVER_NAME | lab22.moodle.decclic.qc.ca |
| SERVER_SOFTWARE | Apache |
| SERVER_SIGNATURE | |
| PATH | /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin |
| HTTP_X_FORWARDED_PROTO | https |
| HTTP_X_CONCAT | lab22.moodle.decclic.qc.ca_::ffff:216.73.217.58_Mozilla/5.0 AppleWebKit/537.36 (KHTML, like Gecko; compatible; ClaudeBot/1.0; +claudebot@anthropic.com) |
| HTTP_X_GEOIP_CONTINENT | NA |
| HTTP_X_GEOIP_COUNTRY | US |
| HTTP_X_CRAWLER_IPDOMAIN | ::ffff:216.73.217.58| |
| HTTP_ACCEPT_ENCODING | gzip, br, zstd, deflate |
| HTTP_USER_AGENT | Mozilla/5.0 AppleWebKit/537.36 (KHTML, like Gecko; compatible; ClaudeBot/1.0; +claudebot@anthropic.com) |
| HTTP_HOST | lab22.moodle.decclic.qc.ca |
| proxy-nokeepalive | 1 |
| SCRIPT_URI | http://lab22.moodle.decclic.qc.ca/course/info.php |
| SCRIPT_URL | /course/info.php |
| HTTP_ACCEPT | */* |
| HTTP_CONTENT_TYPE | |
| HTTP_AUTHORIZATION | |
| FCGI_ROLE | RESPONDER |
| PHP_SELF | /course/info.php |
| REQUEST_TIME_FLOAT | 1783367791.0588 |
| REQUEST_TIME | 1783367791 |
| HTTPS | on |
empty
0. Whoops\Handler\PrettyPageHandler
1. Whoops\Handler\CallbackHandler