/** * 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; } }
/** * Updates all files that are referencing this file with the new contenthash * and filesize * * @param stored_file $storedfile */ public function update_references_to_storedfile(stored_file $storedfile) { global $CFG, $DB; $params = array(); $params['contextid'] = $storedfile->get_contextid(); $params['component'] = $storedfile->get_component(); $params['filearea'] = $storedfile->get_filearea(); $params['itemid'] = $storedfile->get_itemid(); $params['filename'] = $storedfile->get_filename(); $params['filepath'] = $storedfile->get_filepath(); $reference = self::pack_reference($params); $referencehash = sha1($reference); $sql = "SELECT repositoryid, id FROM {files_reference}\n WHERE referencehash = ?"; $rs = $DB->get_recordset_sql($sql, array($referencehash)); $now = time(); foreach ($rs as $record) { $this->update_references($record->id, $now, null, $storedfile->get_contenthash(), $storedfile->get_filesize(), 0, $storedfile->get_timemodified()); } $rs->close(); }
/** * Returns the number of aliases that link to the given stored_file * * Aliases in user draft areas are not counted. * * @param stored_file $storedfile * @return int */ public function get_references_count_by_storedfile(stored_file $storedfile) { global $DB; $params = array(); $params['contextid'] = $storedfile->get_contextid(); $params['component'] = $storedfile->get_component(); $params['filearea'] = $storedfile->get_filearea(); $params['itemid'] = $storedfile->get_itemid(); $params['filename'] = $storedfile->get_filename(); $params['filepath'] = $storedfile->get_filepath(); return $this->search_references_count(self::pack_reference($params)); }
/** * Overwrite an existing file in a draft area. * * @param stored_file $newfile the new file with the new content and meta-data * @param stored_file $existingfile the file that will be overwritten * @throws moodle_exception * @since Moodle 3.2 */ function file_overwrite_existing_draftfile(stored_file $newfile, stored_file $existingfile) { if ($existingfile->get_component() != 'user' or $existingfile->get_filearea() != 'draft') { throw new coding_exception('The file to overwrite is not in a draft area.'); } $fs = get_file_storage(); // Remember original file source field. $source = @unserialize($existingfile->get_source()); // Remember the original sortorder. $sortorder = $existingfile->get_sortorder(); if ($newfile->is_external_file()) { // New file is a reference. Check that existing file does not have any other files referencing to it if (isset($source->original) && $fs->search_references_count($source->original)) { throw new moodle_exception('errordoublereference', 'repository'); } } // Delete existing file to release filename. $newfilerecord = array('contextid' => $existingfile->get_contextid(), 'component' => 'user', 'filearea' => 'draft', 'itemid' => $existingfile->get_itemid(), 'timemodified' => time()); $existingfile->delete(); // Create new file. $newfile = $fs->create_file_from_storedfile($newfilerecord, $newfile); // Preserve original file location (stored in source field) for handling references. if (isset($source->original)) { if (!($newfilesource = @unserialize($newfile->get_source()))) { $newfilesource = new stdClass(); } $newfilesource->original = $source->original; $newfile->set_source(serialize($newfilesource)); } $newfile->set_sortorder($sortorder); }
/** * Return the count files referring to provided stored_file instance * This won't work for draft files * * @param stored_file $storedfile * @return int */ public function get_references_count_by_storedfile($storedfile) { global $DB; $params = array(); $params['contextid'] = $storedfile->get_contextid(); $params['component'] = $storedfile->get_component(); $params['filearea'] = $storedfile->get_filearea(); $params['itemid'] = $storedfile->get_itemid(); $params['filename'] = $storedfile->get_filename(); $params['filepath'] = $storedfile->get_filepath(); $params['userid'] = $storedfile->get_userid(); $reference = self::pack_reference($params); $sql = "SELECT COUNT(f.id)\n FROM {files} f\n LEFT JOIN {files_reference} r\n ON f.referencefileid = r.id\n WHERE " . $DB->sql_compare_text('r.reference') . ' = ' . $DB->sql_compare_text('?') . "\n AND (f.component <> ? OR f.filearea <> ?)"; $count = $DB->count_records_sql($sql, array($reference, 'user', 'draft')); return $count; }
/** * xmldb_hotpot_move_file * * move a file or folder (within the same context) * if $file is a directory, then all subfolders and files will also be moved * if the destination file/folder already exists, then $file will be deleted * * @param stored_file $file * @param string $new_filepath * @param string $new_filename (optional, default='') * @return void, but may update filearea */ function xmldb_hotpot_move_file($file, $new_filepath, $new_filename = '') { $fs = get_file_storage(); $contextid = $file->get_contextid(); $component = $file->get_component(); $filearea = $file->get_filearea(); $itemid = $file->get_itemid(); $old_filepath = $file->get_filepath(); $old_filename = $file->get_filename(); if ($file->is_directory()) { $children = $fs->get_directory_files($contextid, $component, $filearea, $itemid, $old_filepath); $old_filepath = '/^' . preg_quote($old_filepath, '/') . '/'; foreach ($children as $child) { xmldb_hotpot_move_file($child, preg_replace($old_filepath, $new_filepath, $child->get_filepath(), 1)); } } if ($new_filename == '') { $new_filename = $old_filename; } if ($fs->file_exists($contextid, $component, $filearea, $itemid, $new_filepath, $new_filename)) { $file->delete(); // new file already exists } else { $file->rename($new_filepath, $new_filename); } }
/** * Updates all files that are referencing this file with the new contenthash * and filesize * * @param stored_file $storedfile */ public function update_references_to_storedfile(stored_file $storedfile) { global $CFG, $DB; $params = array(); $params['contextid'] = $storedfile->get_contextid(); $params['component'] = $storedfile->get_component(); $params['filearea'] = $storedfile->get_filearea(); $params['itemid'] = $storedfile->get_itemid(); $params['filename'] = $storedfile->get_filename(); $params['filepath'] = $storedfile->get_filepath(); $reference = self::pack_reference($params); $referencehash = sha1($reference); $sql = "SELECT repositoryid, id FROM {files_reference}\n WHERE referencehash = ? and reference = ?"; $rs = $DB->get_recordset_sql($sql, array($referencehash, $reference)); $now = time(); foreach ($rs as $record) { require_once $CFG->dirroot . '/repository/lib.php'; $repo = repository::get_instance($record->repositoryid); $lifetime = $repo->get_reference_file_lifetime($reference); $this->update_references($record->id, $now, $lifetime, $storedfile->get_contenthash(), $storedfile->get_filesize(), 0); } $rs->close(); }
/** * Get the image details from a file and return them. * @param stored_file $file * @param $pagecount * @return mixed array|false */ protected static function get_image_details($file, $pagecount) { if ($imageinfo = $file->get_imageinfo()) { $imgurl = moodle_url::make_pluginfile_url($file->get_contextid(), $file->get_component(), $file->get_filearea(), $file->get_itemid(), $file->get_filepath(), $file->get_filename()); // Prevent browser from caching image if it has changed. $imgurl->param('ts', $file->get_timemodified()); return array($imgurl, $imageinfo['width'], $imageinfo['height'], $pagecount); } // Something went wrong. return false; }
/** * Utility function for getting a file URL * * @param stored_file $file * @return string file url */ private function util_get_file_url($file) { return moodle_url::make_pluginfile_url($file->get_contextid(), $file->get_component(), $file->get_filearea(), $file->get_itemid(), $file->get_filepath(), $file->get_filename(), false); }
/** * Based on a stored_file objects, adds either that file (if it's a file) or * all its children (if it's a directory) into the list of files to * archive. * * If a progress indicator is supplied and if this corresponds to a * directory, then it will be repeatedly called with the same values. This * allows the progress handler to respond in some way to avoid timeouts * if required. * * @param array $expandedfiles List of all files to archive (output) * @param string $archivepath Current path within archive * @param stored_file $file File object */ protected function list_files_stored(array &$expandedfiles, $archivepath, stored_file $file) { if ($file->is_directory()) { // Add a directory-creation record. $expandedfiles[$archivepath . '/'] = null; // Loop through directory contents (this is a recursive collection // of all children not just one directory). $fs = get_file_storage(); $baselength = strlen($file->get_filepath()); $files = $fs->get_directory_files($file->get_contextid(), $file->get_component(), $file->get_filearea(), $file->get_itemid(), $file->get_filepath(), true, true); foreach ($files as $childfile) { // Get full pathname after original part. $path = $childfile->get_filepath(); $path = substr($path, $baselength); $path = $archivepath . '/' . $path; if ($childfile->is_directory()) { $childfile = null; } else { $path .= $childfile->get_filename(); } $expandedfiles[$path] = $childfile; } } else { // Just add it to list. $expandedfiles[$archivepath] = $file; } }
static function ensure_pdf_compatible(stored_file $file) { global $CFG; $fp = $file->get_content_file_handle(); $ident = fread($fp, 10); if (substr_compare('%PDF-', $ident, 0, 5) !== 0) { return false; // This is not a PDF file at all } $ident = substr($ident, 5); // Remove the '%PDF-' part $ident = explode('\\x0A', $ident); // Truncate to first '0a' character list($major, $minor) = explode('.', $ident[0]); // Split the major / minor version $major = intval($major); $minor = intval($minor); if ($major == 0 || $minor == 0) { return false; // Not a valid PDF version number } if ($major = 1 && $minor <= 4) { return true; // We can handle this version - nothing else to do } $temparea = $CFG->dataroot . '/temp/uploadpdf'; $hash = $file->get_contenthash(); $tempsrc = $temparea . "/src-{$hash}.pdf"; $tempdst = $temparea . "/dst-{$hash}.pdf"; if (!file_exists($temparea)) { if (!mkdir($temparea, 0777, true)) { die("Unable to create temporary folder {$temparea}"); } } $file->copy_content_to($tempsrc); // Copy the file $gsexec = $CFG->gs_path; $command = "{$gsexec} -q -sDEVICE=pdfwrite -dBATCH -dNOPAUSE -sOutputFile=\"{$tempdst}\" \"{$tempsrc}\" 2>&1"; $result = exec($command); if (!file_exists($tempdst)) { return false; // Something has gone wrong in the conversion } $file->delete(); // Delete the original file $fs = get_file_storage(); $fileinfo = array('contextid' => $file->get_contextid(), 'component' => $file->get_component(), 'filearea' => $file->get_filearea(), 'itemid' => $file->get_itemid(), 'filename' => $file->get_filename(), 'filepath' => $file->get_filepath()); $fs->create_file_from_pathname($fileinfo, $tempdst); // Create replacement file @unlink($tempsrc); // Delete the temporary files @unlink($tempdst); return true; }
/** * Shame that this was nicked from gdlib.php and that there isn't a function I could have used from there. * Creates a resized version of image and stores copy in file area * * @param context $context * @param string $component * @param string filearea * @param int $itemid * @param stored_file $originalfile * @param int $newwidth; * @param int $newheight; * @return stored_file */ public static function resize(\stored_file $originalfile, $resizefilename = false, $newwidth = false, $newheight = false, $jpgquality = 90) { if ($resizefilename === false) { $resizefilename = $originalfile->get_filename(); } if (!$newwidth && !$newheight) { return false; } $contextid = $originalfile->get_contextid(); $component = $originalfile->get_component(); $filearea = $originalfile->get_filearea(); $itemid = $originalfile->get_itemid(); $imageinfo = (object) $originalfile->get_imageinfo(); $imagefnc = ''; if (empty($imageinfo)) { return false; } // Create temporary image for processing. $tmpimage = tempnam(sys_get_temp_dir(), 'tmpimg'); \file_put_contents($tmpimage, $originalfile->get_content()); if (!$newheight) { $m = $imageinfo->height / $imageinfo->width; // Multiplier to work out $newheight. $newheight = $newwidth * $m; } else { if (!$newwidth) { $m = $imageinfo->width / $imageinfo->height; // Multiplier to work out $newwidth. $newwidth = $newheight * $m; } } $t = null; switch ($imageinfo->mimetype) { case 'image/gif': if (\function_exists('imagecreatefromgif')) { $im = \imagecreatefromgif($tmpimage); } else { \debugging('GIF not supported on this server'); unlink($tmpimage); return false; } // Guess transparent colour from GIF. $transparent = \imagecolortransparent($im); if ($transparent != -1) { $t = \imagecolorsforindex($im, $transparent); } break; case 'image/jpeg': if (\function_exists('imagecreatefromjpeg')) { $im = \imagecreatefromjpeg($tmpimage); } else { \debugging('JPEG not supported on this server'); unlink($tmpimage); return false; } // If the user uploads a jpeg them we should process as a jpeg if possible. if (\function_exists('imagejpeg')) { $imagefnc = 'imagejpeg'; $filters = null; // Not used. $quality = $jpgquality; } else { if (\function_exists('imagepng')) { $imagefnc = 'imagepng'; $filters = PNG_NO_FILTER; $quality = 1; } else { \debugging('Jpeg and png not supported on this server, please fix server configuration'); unlink($tmpimage); return false; } } break; case 'image/png': if (\function_exists('imagecreatefrompng')) { $im = \imagecreatefrompng($tmpimage); } else { \debugging('PNG not supported on this server'); unlink($tmpimage); return false; } break; default: unlink($tmpimage); return false; } unlink($tmpimage); // The default for all images other than jpegs is to try imagepng first. if (empty($imagefnc)) { if (\function_exists('imagepng')) { $imagefnc = 'imagepng'; $filters = PNG_NO_FILTER; $quality = 1; } else { if (\function_exists('imagejpeg')) { $imagefnc = 'imagejpeg'; $filters = null; // Not used. $quality = $jpgquality; } else { \debugging('Jpeg and png not supported on this server, please fix server configuration'); return false; } } } if (\function_exists('imagecreatetruecolor')) { $newimage = \imagecreatetruecolor($newwidth, $newheight); if ($imageinfo->mimetype != 'image/jpeg' and $imagefnc === 'imagepng') { if ($t) { // Transparent GIF hacking... $transparentcolour = \imagecolorallocate($newimage, $t['red'], $t['green'], $t['blue']); \imagecolortransparent($newimage, $transparentcolour); } \imagealphablending($newimage, false); $color = \imagecolorallocatealpha($newimage, 0, 0, 0, 127); \imagefill($newimage, 0, 0, $color); \imagesavealpha($newimage, true); } } else { $newimage = \imagecreate($newwidth, $newheight); } \imagecopybicubic($newimage, $im, 0, 0, 0, 0, $newwidth, $newheight, $imageinfo->width, $imageinfo->height); $fs = \get_file_storage(); $newimageparams = array('contextid' => $contextid, 'component' => $component, 'filearea' => $filearea, 'itemid' => $itemid, 'filepath' => '/'); \ob_start(); if (!$imagefnc($newimage, null, $quality, $filters)) { return false; } $data = \ob_get_clean(); \imagedestroy($newimage); $newimageparams['filename'] = $resizefilename; if ($resizefilename == $originalfile->get_filename()) { $originalfile->delete(); } $file1 = $fs->create_file_from_string($newimageparams, $data); return $file1; }
/** * Perform archiving file from stored file * * @param zip_archive $ziparch zip archive instance * @param string $archivepath file path to archive * @param stored_file $file stored_file object */ private function archive_stored($ziparch, $archivepath, $file) { $file->archive_file($ziparch, $archivepath); if (!$file->is_directory()) { return; } $baselength = strlen($file->get_filepath()); $fs = get_file_storage(); $files = $fs->get_directory_files($file->get_contextid(), $file->get_component(), $file->get_filearea(), $file->get_itemid(), $file->get_filepath(), true, true); foreach ($files as $file) { $path = $file->get_filepath(); $path = substr($path, $baselength); $path = $archivepath . '/' . $path; if (!$file->is_directory()) { $path = $path . $file->get_filename(); } $file->archive_file($ziparch, $path); } }
/** * Get the URL of a file that belongs to a response variable of this * question_attempt. * @param stored_file $file the file to link to. * @return string the URL of that file. */ public function get_response_file_url(stored_file $file) { return file_encode_url(new moodle_url('/pluginfile.php'), '/' . implode('/', array($file->get_contextid(), $file->get_component(), $file->get_filearea(), $this->usageid, $this->slot, $file->get_itemid())) . $file->get_filepath() . $file->get_filename(), true); }
function emarking_create_anonymous_page_from_storedfile(stored_file $file, $student) { if (!$file) { throw new Exception('Stored file does not exist'); } // Get file storage and copy file to temp folder. $fs = get_file_storage(); $tmppath = $file->copy_content_to_temp('emarking', 'anonymous'); // Treat the file as an image, get its size and draw a white rectangle. $size = getimagesize($tmppath, $info); $imagemime = exif_imagetype($tmppath); if ($imagemime == IMAGETYPE_PNG) { $image = imagecreatefrompng($tmppath); } elseif ($imagemime == IMAGETYPE_JPEG) { $image = imagecreatefromjpeg($tmppath); } else { throw new Exception('Unsupported file type for pages'); } if (!$image) { throw new Exception('Could not read image from ' . $info[0] . ' ' . $tmppath); } $white = imagecolorallocate($image, 255, 255, 255); $y2 = round($size[1] / 10, 0); imagefilledrectangle($image, 0, 0, $size[0], $y2, $white); // Save the new image to replace the file. if ($imagemime == IMAGETYPE_PNG) { if (!imagepng($image, $tmppath)) { throw new Exception('Could not save image as PNG to ' . $tmppath); } } elseif ($imagemime == IMAGETYPE_JPEG) { if (!imagejpeg($image, $tmppath, 100)) { throw new Exception('Could not save image as JPEG to ' . $tmppath); } } else { throw new Exception('Unsupported file type for saving page'); } clearstatcache(); $filenameanonymous = emarking_get_anonymous_filename($file->get_filename()); // Copy file from temp folder to Moodle's filesystem. $filerecordanonymous = array('contextid' => $file->get_contextid(), 'component' => 'mod_emarking', 'filearea' => 'pages', 'itemid' => $file->get_itemid(), 'filepath' => '/', 'filename' => $filenameanonymous, 'timecreated' => $file->get_timecreated(), 'timemodified' => time(), 'userid' => $student->id, 'author' => $student->firstname . ' ' . $student->lastname, 'license' => 'allrightsreserved'); $previousfile = $fs->get_file($file->get_contextid(), 'mod_emarking', 'pages', $file->get_itemid(), '/', $filenameanonymous); if ($previousfile) { $previousfile->delete(); } $fileinfo = $fs->create_file_from_pathname($filerecordanonymous, $tmppath); if (!$fileinfo) { throw new Exception('Could not create anonymous version of a stored file'); } return $fileinfo; }
/** * Perform archiving file from stored file. * * @param zip_archive $ziparch zip archive instance * @param string $archivepath file path to archive * @param stored_file $file stored_file object * @param file_progress $progress Progress indicator callback or null if not required * @return bool success */ private function archive_stored($ziparch, $archivepath, $file, file_progress $progress = null) { $result = $file->archive_file($ziparch, $archivepath); if (!$result) { return false; } if (!$file->is_directory()) { return true; } $baselength = strlen($file->get_filepath()); $fs = get_file_storage(); $files = $fs->get_directory_files($file->get_contextid(), $file->get_component(), $file->get_filearea(), $file->get_itemid(), $file->get_filepath(), true, true); foreach ($files as $file) { // Record progress for each file. if ($progress) { $progress->progress(); } $path = $file->get_filepath(); $path = substr($path, $baselength); $path = $archivepath . '/' . $path; if (!$file->is_directory()) { $path = $path . $file->get_filename(); } // Ignore result here, partial zipping is ok for now. $file->archive_file($ziparch, $path); } return true; }
function emarking_create_anonymous_page_from_storedfile(stored_file $file) { if (!$file) { throw new Exception('Stored file does not exist'); } // Get file storage and copy file to temp folder. $fs = get_file_storage(); $tmppath = $file->copy_content_to_temp('emarking', 'anonymous'); // Treat the file as an image, get its size and draw a white rectangle. $size = getimagesize($tmppath); $image = imagecreatefrompng($tmppath); $white = imagecolorallocate($image, 255, 255, 255); $y2 = round($size[1] / 10, 0); imagefilledrectangle($image, 0, 0, $size[0], $y2, $white); // Save the new image to replace the file. if (!imagepng($image, $tmppath)) { return false; } clearstatcache(); $filenameanonymous = emarking_get_anonymous_filename($file->get_filename()); // Copy file from temp folder to Moodle's filesystem. $filerecordanonymous = array('contextid' => $file->get_contextid(), 'component' => 'mod_emarking', 'filearea' => 'pages', 'itemid' => $file->get_itemid(), 'filepath' => '/', 'filename' => $filenameanonymous, 'timecreated' => $file->get_timecreated(), 'timemodified' => time(), 'userid' => $file->get_userid(), 'author' => $file->get_author(), 'license' => 'allrightsreserved'); $previousfile = $fs->get_file($file->get_contextid(), 'mod_emarking', 'pages', $file->get_itemid(), '/', $filenameanonymous); if ($previousfile) { $previousfile->delete(); } $fileinfo = $fs->create_file_from_pathname($filerecordanonymous, $tmppath); return $fileinfo; }