See Release Notes
Long Term Support Release
Differences Between: [Versions 401 and 403]
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 access local files 19 * 20 * @since Moodle 2.0 21 * @package repository_local 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 require_once($CFG->dirroot . '/repository/lib.php'); 26 27 /** 28 * repository_local class is used to browse moodle files 29 * 30 * @since Moodle 2.0 31 * @package repository_local 32 * @copyright 2012 Marina Glancy 33 * @copyright 2009 Dongsheng Cai {@link http://dongsheng.org} 34 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 35 */ 36 class repository_local extends repository { 37 /** 38 * Get file listing 39 * 40 * @param string $encodedpath 41 * @param string $page no paging is used in repository_local 42 * @return mixed 43 */ 44 public function get_listing($encodedpath = '', $page = '') { 45 global $CFG, $USER, $OUTPUT; 46 $ret = array(); 47 $ret['dynload'] = true; 48 $ret['nosearch'] = false; 49 $ret['nologin'] = true; 50 $ret['list'] = array(); 51 52 $itemid = null; 53 $filename = null; 54 $filearea = null; 55 $filepath = null; 56 $component = null; 57 58 if (!empty($encodedpath)) { 59 $params = json_decode(base64_decode($encodedpath), true); 60 if (is_array($params) && isset($params['contextid'])) { 61 $component = is_null($params['component']) ? NULL : clean_param($params['component'], PARAM_COMPONENT); 62 $filearea = is_null($params['filearea']) ? NULL : clean_param($params['filearea'], PARAM_AREA); 63 $itemid = is_null($params['itemid']) ? NULL : clean_param($params['itemid'], PARAM_INT); 64 $filepath = is_null($params['filepath']) ? NULL : clean_param($params['filepath'], PARAM_PATH); 65 $filename = is_null($params['filename']) ? NULL : clean_param($params['filename'], PARAM_FILE); 66 $context = context::instance_by_id(clean_param($params['contextid'], PARAM_INT)); 67 } 68 } 69 if (empty($context) && !empty($this->context)) { 70 $context = $this->context->get_course_context(false); 71 } 72 if (empty($context)) { 73 $context = context_system::instance(); 74 } 75 76 // prepare list of allowed extensions: $extensions is either string '*' 77 // or array of lowercase extensions, i.e. array('.gif','.jpg') 78 $extensions = optional_param_array('accepted_types', '', PARAM_RAW); 79 if (empty($extensions) || $extensions === '*' || (is_array($extensions) && in_array('*', $extensions))) { 80 $extensions = '*'; 81 } else { 82 if (!is_array($extensions)) { 83 $extensions = array($extensions); 84 } 85 $extensions = array_map('core_text::strtolower', $extensions); 86 } 87 88 // build file tree 89 $browser = get_file_browser(); 90 if (!($fileinfo = $browser->get_file_info($context, $component, $filearea, $itemid, $filepath, $filename))) { 91 // if file doesn't exist, build path nodes root of current context 92 $fileinfo = $browser->get_file_info($context, null, null, null, null, null); 93 } 94 $ret['list'] = $this->get_non_empty_children($fileinfo, $extensions); 95 96 // build path navigation 97 $path = array(); 98 for ($level = $fileinfo; $level; $level = $level->get_parent()) { 99 array_unshift($path, $level); 100 } 101 array_unshift($path, null); 102 $ret['path'] = array(); 103 for ($i=1; $i<count($path); $i++) { 104 if ($path[$i] == $fileinfo || !$this->can_skip($path[$i], $extensions, $path[$i-1])) { 105 $ret['path'][] = $this->get_node_path($path[$i]); 106 } 107 } 108 return $ret; 109 } 110 111 /** 112 * Tells how the file can be picked from this repository 113 * 114 * @return int 115 */ 116 public function supported_returntypes() { 117 return FILE_INTERNAL | FILE_REFERENCE; 118 } 119 120 /** 121 * Does this repository used to browse moodle files? 122 * 123 * @return boolean 124 */ 125 public function has_moodle_files() { 126 return true; 127 } 128 129 /** 130 * Returns all children elements that have one of the specified extensions 131 * 132 * This function may skip subfolders and recursively add their children 133 * {@link repository_local::can_skip()} 134 * 135 * @param file_info $fileinfo 136 * @param string|array $extensions, for example '*' or array('.gif','.jpg') 137 * @return array array of file_info elements 138 */ 139 private function get_non_empty_children(file_info $fileinfo, $extensions) { 140 $nonemptychildren = $fileinfo->get_non_empty_children($extensions); 141 $list = array(); 142 foreach ($nonemptychildren as $child) { 143 if ($this->can_skip($child, $extensions, $fileinfo)) { 144 $list = array_merge($list, $this->get_non_empty_children($child, $extensions)); 145 } else { 146 $list[] = $this->get_node($child); 147 } 148 } 149 return $list; 150 } 151 152 /** 153 * Whether this folder may be skipped in folder hierarchy 154 * 155 * 1. Skip the name of a single filearea in a module 156 * 2. Skip course categories for non-admins who do not have navshowmycoursecategories setting 157 * 158 * @param file_info $fileinfo 159 * @param string|array $extensions, for example '*' or array('.gif','.jpg') 160 * @param file_info|int $parent specify parent here if we know it to avoid creating extra objects 161 * @return bool 162 */ 163 private function can_skip(file_info $fileinfo, $extensions, $parent = -1) { 164 global $CFG; 165 if (!$fileinfo->is_directory()) { 166 // do not skip files 167 return false; 168 } 169 if ($fileinfo instanceof file_info_context_course || 170 $fileinfo instanceof file_info_context_user || 171 $fileinfo instanceof file_info_area_course_legacy || 172 $fileinfo instanceof file_info_context_module || 173 $fileinfo instanceof file_info_context_system) { 174 // These instances can never be filearea inside an activity, they will never be skipped. 175 return false; 176 } else if ($fileinfo instanceof file_info_context_coursecat) { 177 // This is a course category. For non-admins we do not display categories 178 return empty($CFG->navshowmycoursecategories) && 179 !has_capability('moodle/course:update', context_system::instance()); 180 } else { 181 $params = $fileinfo->get_params(); 182 if (strlen($params['filearea']) && 183 ($params['filepath'] === '/' || empty($params['filepath'])) && 184 ($params['filename'] === '.' || empty($params['filename'])) && 185 context::instance_by_id($params['contextid'])->contextlevel == CONTEXT_MODULE) { 186 if ($parent === -1) { 187 $parent = $fileinfo->get_parent(); 188 } 189 // This is a filearea inside an activity, it can be skipped if it has no non-empty siblings 190 if ($parent && ($parent instanceof file_info_context_module)) { 191 if ($parent->count_non_empty_children($extensions, 2) <= 1) { 192 return true; 193 } 194 } 195 } 196 } 197 return false; 198 } 199 200 /** 201 * Converts file_info object to element of repository return list 202 * 203 * @param file_info $fileinfo 204 * @return array 205 */ 206 private function get_node(file_info $fileinfo) { 207 global $OUTPUT; 208 $encodedpath = base64_encode(json_encode($fileinfo->get_params())); 209 $node = array( 210 'title' => $fileinfo->get_visible_name(), 211 'datemodified' => $fileinfo->get_timemodified(), 212 'datecreated' => $fileinfo->get_timecreated() 213 ); 214 if ($fileinfo->is_directory()) { 215 $node['path'] = $encodedpath; 216 $node['thumbnail'] = $OUTPUT->image_url(file_folder_icon(90))->out(false); 217 $node['children'] = array(); 218 } else { 219 $node['size'] = $fileinfo->get_filesize(); 220 $node['author'] = $fileinfo->get_author(); 221 $node['license'] = $fileinfo->get_license(); 222 $node['isref'] = $fileinfo->is_external_file(); 223 if ($fileinfo->get_status() == 666) { 224 $node['originalmissing'] = true; 225 } 226 $node['source'] = $encodedpath; 227 $node['thumbnail'] = $OUTPUT->image_url(file_file_icon($fileinfo, 90))->out(false); 228 $node['icon'] = $OUTPUT->image_url(file_file_icon($fileinfo, 24))->out(false); 229 if ($imageinfo = $fileinfo->get_imageinfo()) { 230 // what a beautiful picture, isn't it 231 $fileurl = new moodle_url($fileinfo->get_url()); 232 $node['realthumbnail'] = $fileurl->out(false, array('preview' => 'thumb', 'oid' => $fileinfo->get_timemodified())); 233 $node['realicon'] = $fileurl->out(false, array('preview' => 'tinyicon', 'oid' => $fileinfo->get_timemodified())); 234 $node['image_width'] = $imageinfo['width']; 235 $node['image_height'] = $imageinfo['height']; 236 } 237 } 238 return $node; 239 } 240 241 /** 242 * Converts file_info object to element of repository return path 243 * 244 * @param file_info $fileinfo 245 * @return array 246 */ 247 private function get_node_path(file_info $fileinfo) { 248 $encodedpath = base64_encode(json_encode($fileinfo->get_params())); 249 return array( 250 'path' => $encodedpath, 251 'name' => $fileinfo->get_visible_name() 252 ); 253 } 254 255 /** 256 * Search through all the files. 257 * 258 * This method will do a raw search through the database, then will try 259 * to match with files that a user can access. A maximum of 50 files will be 260 * returned at a time, excluding possible duplicates found along the way. 261 * 262 * Queries are done in chunk of 100 files to prevent too many records to be fetched 263 * at once. When too many files are not included, or a maximum of 10 queries are 264 * performed we consider that this was the last page. 265 * 266 * @param String $q The query string. 267 * @param integer $page The page number. 268 * @return array of results. 269 */ 270 public function search($q, $page = 1) { 271 global $DB, $SESSION; 272 273 // Because the repository API is weird, the first page is 0, but it should be 1. 274 if (!$page) { 275 $page = 1; 276 } 277 278 if (!isset($SESSION->repository_local_search)) { 279 $SESSION->repository_local_search = array(); 280 } 281 282 $fs = get_file_storage(); 283 $fb = get_file_browser(); 284 285 $max = 50; 286 $limit = 100; 287 if ($page <= 1) { 288 $SESSION->repository_local_search['query'] = $q; 289 $SESSION->repository_local_search['from'] = 0; 290 $from = 0; 291 } else { 292 // Yes, the repository does not send the query again... 293 $q = $SESSION->repository_local_search['query']; 294 $from = (int) $SESSION->repository_local_search['from']; 295 } 296 297 $count = $fs->search_server_files('%' . $DB->sql_like_escape($q) . '%', null, null, true); 298 $remaining = $count - $from; 299 $maxloops = 3000; 300 $loops = 0; 301 302 $results = array(); 303 while (count($results) < $max && $maxloops > 0 && $remaining > 0) { 304 if (empty($files)) { 305 $files = $fs->search_server_files('%' . $DB->sql_like_escape($q) . '%', $from, $limit); 306 $from += $limit; 307 }; 308 309 $remaining--; 310 $maxloops--; 311 $loops++; 312 313 $file = array_shift($files); 314 if (!$file) { 315 // This should not happen. 316 throw new coding_exception('Unexpected end of files list.'); 317 } 318 319 $key = $file->get_contenthash() . ':' . $file->get_filename(); 320 if (isset($results[$key])) { 321 // We found the file with same content and same name, let's skip it. 322 continue; 323 } 324 325 $ctx = context::instance_by_id($file->get_contextid()); 326 $fileinfo = $fb->get_file_info($ctx, $file->get_component(), $file->get_filearea(), $file->get_itemid(), 327 $file->get_filepath(), $file->get_filename()); 328 if ($fileinfo) { 329 $results[$key] = $this->get_node($fileinfo); 330 } 331 332 } 333 334 // Save the position for the paging to work. 335 if ($maxloops > 0 && $remaining > 0) { 336 $SESSION->repository_local_search['from'] += $loops; 337 $pages = -1; 338 } else { 339 $SESSION->repository_local_search['from'] = 0; 340 $pages = 0; 341 } 342 343 $return = array( 344 'list' => array_values($results), 345 'dynload' => true, 346 'pages' => $pages, 347 'page' => $page 348 ); 349 350 return $return; 351 } 352 353 /** 354 * Is this repository accessing private data? 355 * 356 * @return bool 357 */ 358 public function contains_private_data() { 359 return false; 360 } 361 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body