/** * Check if the given file is capable of being imported by this plugin. * As {@link file_storage::mimetype()} now uses finfo PHP extension if available, * the value returned by $file->get_mimetype for a .dat file is not the same on all servers. * So we must made 2 checks to verify if the plugin can import the file. * @param stored_file $file the file to check * @return bool whether this plugin can import the file */ public function can_import_file($file) { $mimetypes = array( mimeinfo('type', '.dat'), mimeinfo('type', '.zip') ); return in_array($file->get_mimetype(), $mimetypes) || in_array(mimeinfo('type', $file->get_filename()), $mimetypes); }
/** * Generates a preview image for the stored file * * @param stored_file $file the file we want to preview * @param string $mode preview mode, eg. 'thumb' * @return stored_file|bool the newly created preview file or false */ protected function create_file_preview(stored_file $file, $mode) { $mimetype = $file->get_mimetype(); if ($mimetype === 'image/gif' or $mimetype === 'image/jpeg' or $mimetype === 'image/png') { // make a preview of the image $data = $this->create_imagefile_preview($file, $mode); } else { // unable to create the preview of this mimetype yet return false; } if (empty($data)) { return false; } $context = context_system::instance(); $record = array('contextid' => $context->id, 'component' => 'core', 'filearea' => 'preview', 'itemid' => 0, 'filepath' => '/' . trim($mode, '/') . '/', 'filename' => $file->get_contenthash()); $imageinfo = getimagesizefromstring($data); if ($imageinfo) { $record['mimetype'] = $imageinfo['mime']; } return $this->create_file_from_string($record, $data); }
/** * Check if the given file is capable of being imported by this plugin. * * Note that expensive or detailed integrity checks on the file should * not be performed by this method. Simple file type or magic-number tests * would be suitable. * * @param stored_file $file the file to check * @return bool whether this plugin can import the file */ public function can_import_file($file) { return $file->get_mimetype() == $this->mime_type(); }
/** * if we already know we have exactly one file, * bypass set_formats and just pass the file * so we can detect the formats by mimetype. * * @param stored_file $file file to set the format from * @param mixed $extraformats any additional formats other than by mimetype * eg leap2a etc */ public function set_format_by_file(stored_file $file, $extraformats = null) { $this->file = $file; $fileformat = portfolio_format_from_mimetype($file->get_mimetype()); if (is_string($extraformats)) { $extraformats = array($extraformats); } else { if (!is_array($extraformats)) { $extraformats = array(); } } $this->set_formats(array_merge(array($fileformat), $extraformats)); }
/** * Deduce export format from file mimetype * * This function returns the revelant portfolio export format * which is used to determine which portfolio plugins can be used * for exporting this content * according to the mime type of the given file * this only works when exporting exactly <b>one</b> file * * @param stored_file $file file to check mime type for * * @return string the format constant (see PORTFOLIO_FORMAT_XXX constants) */ function portfolio_format_from_file(stored_file $file) { static $alreadymatched; if (empty($alreadymatched)) { $alreadymatched = array(); } if (!$file instanceof stored_file) { throw new portfolio_exception('invalidfileargument', 'portfolio'); } $mimetype = $file->get_mimetype(); if (array_key_exists($mimetype, $alreadymatched)) { return $alreadymatched[$mimetype]; } $allformats = portfolio_supported_formats(); foreach ($allformats as $format => $classname) { $supportedmimetypes = call_user_func(array($classname, 'mimetypes')); if (!is_array($supportedmimetypes)) { debugging("one of the portfolio format classes, {$classname}, said it supported something funny for mimetypes, should have been array..."); debugging(print_r($supportedmimetypes, true)); continue; } if (in_array($mimetype, $supportedmimetypes)) { $alreadymatched[$mimetype] = $format; return $format; } } return PORTFOLIO_FORMAT_FILE; // base case for files... }
/** * Checks to see if a passed file is indexable. * * @param \stored_file $file The file to check * @return bool True if the file can be indexed */ protected function file_is_indexable($file) { if (!empty($this->config->maxindexfilekb) && $file->get_filesize() > $this->config->maxindexfilekb * 1024) { // The file is too big to index. return false; } $mime = $file->get_mimetype(); if ($mime == 'application/vnd.moodle.backup') { // We don't index Moodle backup files. There is nothing usefully indexable in them. return false; } return true; }
/** * Resize the image, if required, then generate an img tag and, if required, a link to the full-size image * @param stored_file $file the image file to process * @param int $maxwidth the maximum width allowed for the image * @param int $maxheight the maximum height allowed for the image * @return string HTML fragment to add to the label */ function label_generate_resized_image(stored_file $file, $maxwidth, $maxheight) { global $CFG; $fullurl = moodle_url::make_draftfile_url($file->get_itemid(), $file->get_filepath(), $file->get_filename()); $link = null; $attrib = array('alt' => $file->get_filename(), 'src' => $fullurl); if ($imginfo = $file->get_imageinfo()) { // Work out the new width / height, bounded by maxwidth / maxheight $width = $imginfo['width']; $height = $imginfo['height']; if (!empty($maxwidth) && $width > $maxwidth) { $height *= (double) $maxwidth / $width; $width = $maxwidth; } if (!empty($maxheight) && $height > $maxheight) { $width *= (double) $maxheight / $height; $height = $maxheight; } $attrib['width'] = $width; $attrib['height'] = $height; // If the size has changed and the image is of a suitable mime type, generate a smaller version if ($width != $imginfo['width']) { $mimetype = $file->get_mimetype(); if ($mimetype === 'image/gif' or $mimetype === 'image/jpeg' or $mimetype === 'image/png') { require_once $CFG->libdir . '/gdlib.php'; $tmproot = make_temp_directory('mod_label'); $tmpfilepath = $tmproot . '/' . $file->get_contenthash(); $file->copy_content_to($tmpfilepath); $data = generate_image_thumbnail($tmpfilepath, $width, $height); unlink($tmpfilepath); if (!empty($data)) { $fs = get_file_storage(); $record = array('contextid' => $file->get_contextid(), 'component' => $file->get_component(), 'filearea' => $file->get_filearea(), 'itemid' => $file->get_itemid(), 'filepath' => '/', 'filename' => 's_' . $file->get_filename()); $smallfile = $fs->create_file_from_string($record, $data); // Replace the image 'src' with the resized file and link to the original $attrib['src'] = moodle_url::make_draftfile_url($smallfile->get_itemid(), $smallfile->get_filepath(), $smallfile->get_filename()); $link = $fullurl; } } } } else { // Assume this is an image type that get_imageinfo cannot handle (e.g. SVG) $attrib['width'] = $maxwidth; } $img = html_writer::empty_tag('img', $attrib); if ($link) { return html_writer::link($link, $img); } else { return $img; } }
/** * Generates a preview image for the stored file * * @param stored_file $file the file we want to preview * @param string $mode preview mode, eg. 'thumb' * @return stored_file|bool the newly created preview file or false */ protected function create_file_preview(stored_file $file, $mode) { $mimetype = $file->get_mimetype(); if ($mimetype === 'image/gif' or $mimetype === 'image/jpeg' or $mimetype === 'image/png') { // make a preview of the image $data = $this->create_imagefile_preview($file, $mode); } else { // unable to create the preview of this mimetype yet return false; } if (empty($data)) { return false; } // getimagesizefromstring() is available from PHP 5.4 but we need to support // lower versions, so... $tmproot = make_temp_directory('thumbnails'); $tmpfilepath = $tmproot . '/' . $file->get_contenthash() . '_' . $mode; file_put_contents($tmpfilepath, $data); $imageinfo = getimagesize($tmpfilepath); unlink($tmpfilepath); $context = context_system::instance(); $record = array('contextid' => $context->id, 'component' => 'core', 'filearea' => 'preview', 'itemid' => 0, 'filepath' => '/' . trim($mode, '/') . '/', 'filename' => $file->get_contenthash()); if ($imageinfo) { $record['mimetype'] = $imageinfo['mime']; } return $this->create_file_from_string($record, $data); }
/** * Create portfolio tag * * @param stored_file $file file information object * @param string $path file path * @param array $attributes portfolio attributes * @return string */ public static function make_tag($file, $path, $attributes) { $srcattr = 'href'; $tag = 'a'; $content = $file->get_filename(); if (in_array($file->get_mimetype(), portfolio_format_image::mimetypes())) { $srcattr = 'src'; $tag = 'img'; $content = ''; } $attributes[$srcattr] = $path; // this will override anything we might have been passed (which is good) $dom = new DomDocument(); $elem = null; if ($content) { $elem = $dom->createElement($tag, $content); } else { $elem = $dom->createElement($tag); } foreach ($attributes as $key => $value) { $elem->setAttribute($key, $value); } $dom->appendChild($elem); return $dom->saveXML($elem); }
/** * Handles the sending of file data to the user's browser, including support for * byteranges etc. * * @category files * @global stdClass $CFG * @global stdClass $COURSE * @global moodle_session $SESSION * @param stored_file $stored_file local file object * @param int $lifetime Number of seconds before the file should expire from caches (default 24 hours) * @param int $filter 0 (default)=no filtering, 1=all files, 2=html files only * @param bool $forcedownload If true (default false), forces download of file rather than view in browser/plugin * @param string $filename Override filename * @param bool $dontdie - return control to caller afterwards. this is not recommended and only used for cleanup tasks. * if this is passed as true, ignore_user_abort is called. if you don't want your processing to continue on cancel, * you must detect this case when control is returned using connection_aborted. Please not that session is closed * and should not be reopened. * @return null script execution stopped unless $dontdie is true */ function send_stored_file($stored_file, $lifetime = 86400, $filter = 0, $forcedownload = false, $filename = null, $dontdie = false) { global $CFG, $COURSE, $SESSION; if (!$stored_file or $stored_file->is_directory()) { // nothing to serve if ($dontdie) { return; } die; } if ($dontdie) { ignore_user_abort(true); } session_get_instance()->write_close(); // unlock session during fileserving // Use given MIME type if specified, otherwise guess it using mimeinfo. // IE, Konqueror and Opera open html file directly in browser from web even when directed to save it to disk :-O // only Firefox saves all files locally before opening when content-disposition: attachment stated $filename = is_null($filename) ? $stored_file->get_filename() : $filename; $isFF = check_browser_version('Firefox', '1.5'); // only FF > 1.5 properly tested $mimetype = ($forcedownload and !$isFF) ? 'application/x-forcedownload' : ($stored_file->get_mimetype() ? $stored_file->get_mimetype() : mimeinfo('type', $filename)); $lastmodified = $stored_file->get_timemodified(); $filesize = $stored_file->get_filesize(); if ($lifetime > 0 && !empty($_SERVER['HTTP_IF_MODIFIED_SINCE'])) { // get unixtime of request header; clip extra junk off first $since = strtotime(preg_replace('/;.*$/', '', $_SERVER["HTTP_IF_MODIFIED_SINCE"])); if ($since && $since >= $lastmodified) { header('HTTP/1.1 304 Not Modified'); header('Expires: ' . gmdate('D, d M Y H:i:s', time() + $lifetime) . ' GMT'); header('Cache-Control: max-age=' . $lifetime); header('Content-Type: ' . $mimetype); if ($dontdie) { return; } die; } } //do not put '@' before the next header to detect incorrect moodle configurations, //error should be better than "weird" empty lines for admins/users header('Last-Modified: ' . gmdate('D, d M Y H:i:s', $lastmodified) . ' GMT'); // if user is using IE, urlencode the filename so that multibyte file name will show up correctly on popup if (check_browser_version('MSIE')) { $filename = rawurlencode($filename); } if ($forcedownload) { header('Content-Disposition: attachment; filename="' . $filename . '"'); } else { header('Content-Disposition: inline; filename="' . $filename . '"'); } if ($lifetime > 0) { header('Cache-Control: max-age=' . $lifetime); header('Expires: ' . gmdate('D, d M Y H:i:s', time() + $lifetime) . ' GMT'); header('Pragma: '); if (empty($CFG->disablebyteserving) && $mimetype != 'text/plain' && $mimetype != 'text/html') { header('Accept-Ranges: bytes'); if (!empty($_SERVER['HTTP_RANGE']) && strpos($_SERVER['HTTP_RANGE'], 'bytes=') !== FALSE) { // byteserving stuff - for acrobat reader and download accelerators // see: http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.35 // inspired by: http://www.coneural.org/florian/papers/04_byteserving.php $ranges = false; if (preg_match_all('/(\\d*)-(\\d*)/', $_SERVER['HTTP_RANGE'], $ranges, PREG_SET_ORDER)) { foreach ($ranges as $key => $value) { if ($ranges[$key][1] == '') { //suffix case $ranges[$key][1] = $filesize - $ranges[$key][2]; $ranges[$key][2] = $filesize - 1; } else { if ($ranges[$key][2] == '' || $ranges[$key][2] > $filesize - 1) { //fix range length $ranges[$key][2] = $filesize - 1; } } if ($ranges[$key][2] != '' && $ranges[$key][2] < $ranges[$key][1]) { //invalid byte-range ==> ignore header $ranges = false; break; } //prepare multipart header $ranges[$key][0] = "\r\n--" . BYTESERVING_BOUNDARY . "\r\nContent-Type: {$mimetype}\r\n"; $ranges[$key][0] .= "Content-Range: bytes {$ranges[$key][1]}-{$ranges[$key][2]}/{$filesize}\r\n\r\n"; } } else { $ranges = false; } if ($ranges) { byteserving_send_file($stored_file->get_content_file_handle(), $mimetype, $ranges, $filesize); } } } else { /// Do not byteserve (disabled, strings, text and html files). header('Accept-Ranges: none'); } } else { // Do not cache files in proxies and browsers if (strpos($CFG->wwwroot, 'https://') === 0) { //https sites - watch out for IE! KB812935 and KB316431 header('Cache-Control: max-age=10'); header('Expires: ' . gmdate('D, d M Y H:i:s', 0) . ' GMT'); header('Pragma: '); } else { //normal http - prevent caching at all cost header('Cache-Control: private, must-revalidate, pre-check=0, post-check=0, max-age=0'); header('Expires: ' . gmdate('D, d M Y H:i:s', 0) . ' GMT'); header('Pragma: no-cache'); } header('Accept-Ranges: none'); // Do not allow byteserving when caching disabled } if (empty($filter)) { if ($mimetype == 'text/plain') { header('Content-Type: Text/plain; charset=utf-8'); //add encoding } else { header('Content-Type: ' . $mimetype); } header('Content-Length: ' . $filesize); //flush the buffers - save memory and disable sid rewrite //this also disables zlib compression prepare_file_content_sending(); // send the contents $stored_file->readfile(); } else { // Try to put the file through filters if ($mimetype == 'text/html') { $options = new stdClass(); $options->noclean = true; $options->nocache = true; // temporary workaround for MDL-5136 $text = $stored_file->get_content(); $text = file_modify_html_header($text); $output = format_text($text, FORMAT_HTML, $options, $COURSE->id); header('Content-Length: ' . strlen($output)); header('Content-Type: text/html'); //flush the buffers - save memory and disable sid rewrite //this also disables zlib compression prepare_file_content_sending(); // send the contents echo $output; } else { if ($mimetype == 'text/plain' and $filter == 1) { // only filter text if filter all files is selected $options = new stdClass(); $options->newlines = false; $options->noclean = true; $text = $stored_file->get_content(); $output = '<pre>' . format_text($text, FORMAT_MOODLE, $options, $COURSE->id) . '</pre>'; header('Content-Length: ' . strlen($output)); header('Content-Type: text/html; charset=utf-8'); //add encoding //flush the buffers - save memory and disable sid rewrite //this also disables zlib compression prepare_file_content_sending(); // send the contents echo $output; } else { // Just send it out raw header('Content-Length: ' . $filesize); header('Content-Type: ' . $mimetype); //flush the buffers - save memory and disable sid rewrite //this also disables zlib compression prepare_file_content_sending(); // send the contents $stored_file->readfile(); } } } if ($dontdie) { return; } die; //no more chars to output!!! }