Differences Between: [Versions 310 and 402] [Versions 311 and 402] [Versions 39 and 402] [Versions 400 and 402] [Versions 401 and 402]
1 <?php 2 // This file is part of Moodle - http://moodle.org/ 3 // 4 // Moodle is free software: you can redistribute it and/or modify 5 // it under the terms of the GNU General Public License as published by 6 // the Free Software Foundation, either version 3 of the License, or 7 // (at your option) any later version. 8 // 9 // Moodle is distributed in the hope that it will be useful, 10 // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 // GNU General Public License for more details. 13 // 14 // You should have received a copy of the GNU General Public License 15 // along with Moodle. If not, see <http://www.gnu.org/licenses/>. 16 17 /** 18 * This plugin is used to upload files 19 * 20 * @since Moodle 2.0 21 * @package repository_upload 22 * @copyright 2010 Dongsheng Cai {@link http://dongsheng.org} 23 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 24 */ 25 26 defined('MOODLE_INTERNAL') || die(); 27 require_once($CFG->dirroot . '/repository/lib.php'); 28 29 /** 30 * A repository plugin to allow user uploading files 31 * 32 * @since Moodle 2.0 33 * @package repository_upload 34 * @copyright 2009 Dongsheng Cai {@link http://dongsheng.org} 35 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 36 */ 37 38 class repository_upload extends repository { 39 private $mimetypes = array(); 40 41 /** 42 * Print a upload form 43 * @return array 44 */ 45 public function print_login() { 46 return $this->get_listing(); 47 } 48 49 /** 50 * Process uploaded file 51 * @return array|bool 52 */ 53 public function upload($saveasfilename, $maxbytes) { 54 global $CFG; 55 56 $types = optional_param_array('accepted_types', '*', PARAM_RAW); 57 $savepath = optional_param('savepath', '/', PARAM_PATH); 58 $itemid = optional_param('itemid', 0, PARAM_INT); 59 $license = optional_param('license', $CFG->sitedefaultlicense, PARAM_TEXT); 60 $author = optional_param('author', '', PARAM_TEXT); 61 $areamaxbytes = optional_param('areamaxbytes', FILE_AREA_MAX_BYTES_UNLIMITED, PARAM_INT); 62 $overwriteexisting = optional_param('overwrite', false, PARAM_BOOL); 63 64 return $this->process_upload($saveasfilename, $maxbytes, $types, $savepath, $itemid, $license, $author, 65 $overwriteexisting, $areamaxbytes); 66 } 67 68 /** 69 * Do the actual processing of the uploaded file 70 * @param string $saveasfilename name to give to the file 71 * @param int $maxbytes maximum file size 72 * @param mixed $types optional array of file extensions that are allowed or '*' for all 73 * @param string $savepath optional path to save the file to 74 * @param int $itemid optional the ID for this item within the file area 75 * @param string $license optional the license to use for this file 76 * @param string $author optional the name of the author of this file 77 * @param bool $overwriteexisting optional user has asked to overwrite the existing file 78 * @param int $areamaxbytes maximum size of the file area. 79 * @return object containing details of the file uploaded 80 */ 81 public function process_upload($saveasfilename, $maxbytes, $types = '*', $savepath = '/', $itemid = 0, 82 $license = null, $author = '', $overwriteexisting = false, $areamaxbytes = FILE_AREA_MAX_BYTES_UNLIMITED) { 83 global $USER, $CFG; 84 85 \core\session\manager::write_close(); 86 87 if ((is_array($types) and in_array('*', $types)) or $types == '*') { 88 $this->mimetypes = '*'; 89 } else { 90 foreach ($types as $type) { 91 $this->mimetypes[] = mimeinfo('type', $type); 92 } 93 } 94 95 if ($license == null) { 96 $license = $CFG->sitedefaultlicense; 97 } 98 99 $record = new stdClass(); 100 $record->filearea = 'draft'; 101 $record->component = 'user'; 102 $record->filepath = $savepath; 103 $record->itemid = $itemid; 104 $record->license = $license; 105 $record->author = $author; 106 107 $context = context_user::instance($USER->id); 108 $elname = 'repo_upload_file'; 109 110 $fs = get_file_storage(); 111 $sm = get_string_manager(); 112 113 if ($record->filepath !== '/') { 114 $record->filepath = file_correct_filepath($record->filepath); 115 } 116 117 if (!isset($_FILES[$elname])) { 118 throw new moodle_exception('nofile'); 119 } 120 if (!empty($_FILES[$elname]['error'])) { 121 switch ($_FILES[$elname]['error']) { 122 case UPLOAD_ERR_INI_SIZE: 123 throw new moodle_exception('upload_error_ini_size', 'repository_upload'); 124 break; 125 case UPLOAD_ERR_FORM_SIZE: 126 throw new moodle_exception('upload_error_form_size', 'repository_upload'); 127 break; 128 case UPLOAD_ERR_PARTIAL: 129 throw new moodle_exception('upload_error_partial', 'repository_upload'); 130 break; 131 case UPLOAD_ERR_NO_FILE: 132 throw new moodle_exception('upload_error_no_file', 'repository_upload'); 133 break; 134 case UPLOAD_ERR_NO_TMP_DIR: 135 throw new moodle_exception('upload_error_no_tmp_dir', 'repository_upload'); 136 break; 137 case UPLOAD_ERR_CANT_WRITE: 138 throw new moodle_exception('upload_error_cant_write', 'repository_upload'); 139 break; 140 case UPLOAD_ERR_EXTENSION: 141 throw new moodle_exception('upload_error_extension', 'repository_upload'); 142 break; 143 default: 144 throw new moodle_exception('nofile'); 145 } 146 } 147 148 \core\antivirus\manager::scan_file($_FILES[$elname]['tmp_name'], $_FILES[$elname]['name'], true); 149 150 // {@link repository::build_source_field()} 151 $sourcefield = $this->get_file_source_info($_FILES[$elname]['name']); 152 $record->source = self::build_source_field($sourcefield); 153 154 if (empty($saveasfilename)) { 155 $record->filename = clean_param($_FILES[$elname]['name'], PARAM_FILE); 156 } else { 157 $ext = ''; 158 $match = array(); 159 $filename = clean_param($_FILES[$elname]['name'], PARAM_FILE); 160 if (strpos($filename, '.') === false) { 161 // File has no extension at all - do not add a dot. 162 $record->filename = $saveasfilename; 163 } else { 164 if (preg_match('/\.([a-z0-9]+)$/i', $filename, $match)) { 165 if (isset($match[1])) { 166 $ext = $match[1]; 167 } 168 } 169 $ext = !empty($ext) ? $ext : ''; 170 if (preg_match('#\.(' . $ext . ')$#i', $saveasfilename)) { 171 // The saveas filename contains file extension already. 172 $record->filename = $saveasfilename; 173 } else { 174 $record->filename = $saveasfilename . '.' . $ext; 175 } 176 } 177 } 178 179 // Check the file has some non-null contents - usually an indication that a user has 180 // tried to upload a folder by mistake. 181 if (!$this->check_valid_contents($_FILES[$elname]['tmp_name'])) { 182 throw new moodle_exception('upload_error_invalid_file', 'repository_upload', '', $record->filename); 183 } 184 185 if ($this->mimetypes != '*') { 186 // Check filetype. 187 $filemimetype = file_storage::mimetype($_FILES[$elname]['tmp_name'], $record->filename); 188 if (!in_array($filemimetype, $this->mimetypes)) { 189 throw new moodle_exception('invalidfiletype', 'repository', '', 190 get_mimetype_description(array('filename' => $_FILES[$elname]['name']))); 191 } 192 } 193 194 if (empty($record->itemid)) { 195 $record->itemid = 0; 196 } 197 198 $filesize = filesize($_FILES[$elname]['tmp_name']); 199 if (($maxbytes !== -1) && ($filesize > $maxbytes)) { 200 $maxbytesdisplay = display_size($maxbytes, 0); 201 throw new file_exception('maxbytesfile', (object) array('file' => $record->filename, 202 'size' => $maxbytesdisplay)); 203 } 204 205 if (file_is_draft_area_limit_reached($record->itemid, $areamaxbytes, $filesize)) { 206 throw new file_exception('maxareabytes'); 207 } 208 // Ensure the user does not upload too many draft files in a short period. 209 if (file_is_draft_areas_limit_reached($USER->id)) { 210 throw new file_exception('maxdraftitemids'); 211 } 212 213 $record->contextid = $context->id; 214 $record->userid = $USER->id; 215 216 if (repository::draftfile_exists($record->itemid, $record->filepath, $record->filename)) { 217 $existingfilename = $record->filename; 218 $unusedfilename = repository::get_unused_filename($record->itemid, $record->filepath, $record->filename); 219 $record->filename = $unusedfilename; 220 $storedfile = $fs->create_file_from_pathname($record, $_FILES[$elname]['tmp_name']); 221 if ($overwriteexisting) { 222 repository::overwrite_existing_draftfile($record->itemid, $record->filepath, $existingfilename, 223 $record->filepath, $record->filename); 224 $record->filename = $existingfilename; 225 } else { 226 $event = array(); 227 $event['event'] = 'fileexists'; 228 $event['newfile'] = new stdClass; 229 $event['newfile']->filepath = $record->filepath; 230 $event['newfile']->filename = $unusedfilename; 231 $event['newfile']->url = moodle_url::make_draftfile_url($record->itemid, $record->filepath, 232 $unusedfilename)->out(false); 233 234 $event['existingfile'] = new stdClass; 235 $event['existingfile']->filepath = $record->filepath; 236 $event['existingfile']->filename = $existingfilename; 237 $event['existingfile']->url = moodle_url::make_draftfile_url($record->itemid, $record->filepath, 238 $existingfilename)->out(false); 239 return $event; 240 } 241 } else { 242 $storedfile = $fs->create_file_from_pathname($record, $_FILES[$elname]['tmp_name']); 243 } 244 245 $logevent = \core\event\draft_file_added::create([ 246 'objectid' => $storedfile->get_id(), 247 'context' => $context, 248 'other' => [ 249 'itemid' => $record->itemid, 250 'filename' => $record->filename, 251 'filesize' => $filesize, 252 'filepath' => $record->filepath, 253 'contenthash' => $storedfile->get_contenthash(), 254 ], 255 ]); 256 $logevent->trigger(); 257 258 return array( 259 'url' => moodle_url::make_draftfile_url($record->itemid, $record->filepath, $record->filename)->out(false), 260 'id' => $record->itemid, 261 'file' => $record->filename); 262 } 263 264 265 /** 266 * Checks the contents of the given file is not completely NULL - this can happen if a 267 * user drags & drops a folder onto a filemanager / filepicker element 268 * @param string $filepath full path (including filename) to file to check 269 * @return true if file has at least one non-null byte within it 270 */ 271 protected function check_valid_contents($filepath) { 272 $buffersize = 4096; 273 274 $fp = fopen($filepath, 'r'); 275 if (!$fp) { 276 return false; // Cannot read the file - something has gone wrong. 277 } 278 while (!feof($fp)) { 279 // Read the file 4k at a time. 280 $data = fread($fp, $buffersize); 281 if (preg_match('/[^\0]+/', $data)) { 282 fclose($fp); 283 return true; // Return as soon as a non-null byte is found. 284 } 285 } 286 // Entire file is NULL. 287 fclose($fp); 288 return false; 289 } 290 291 /** 292 * Return a upload form 293 * @return array 294 */ 295 public function get_listing($path = '', $page = '') { 296 global $CFG; 297 $ret = array(); 298 $ret['nologin'] = true; 299 $ret['nosearch'] = true; 300 $ret['norefresh'] = true; 301 $ret['list'] = array(); 302 $ret['dynload'] = false; 303 $ret['upload'] = array('label' => get_string('attachment', 'repository'), 'id' => 'repo-form'); 304 $ret['allowcaching'] = true; // Indicates that result of get_listing() can be cached in filepicker.js. 305 return $ret; 306 } 307 308 /** 309 * supported return types 310 * @return int 311 */ 312 public function supported_returntypes() { 313 return FILE_INTERNAL; 314 } 315 316 /** 317 * Is this repository accessing private data? 318 * 319 * @return bool 320 */ 321 public function contains_private_data() { 322 return false; 323 } 324 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body