Differences Between: [Versions 310 and 311] [Versions 310 and 400] [Versions 310 and 401] [Versions 310 and 402] [Versions 310 and 403] [Versions 39 and 310]
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 * Private imscp module utility functions 19 * 20 * @package mod_imscp 21 * @copyright 2009 Petr Skoda {@link http://skodak.org} 22 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 23 */ 24 25 defined('MOODLE_INTERNAL') || die(); 26 27 require_once("$CFG->dirroot/mod/imscp/lib.php"); 28 require_once("$CFG->libdir/filelib.php"); 29 require_once("$CFG->libdir/resourcelib.php"); 30 31 /** 32 * Print IMSCP content to page. 33 * 34 * @param stdClass $imscp module instance. 35 * @param stdClass $cm course module. 36 * @param stdClass $course record. 37 */ 38 function imscp_print_content($imscp, $cm, $course) { 39 global $PAGE, $CFG; 40 41 $items = unserialize($imscp->structure); 42 $first = reset($items); 43 $context = context_module::instance($cm->id); 44 $urlbase = "$CFG->wwwroot/pluginfile.php"; 45 $path = '/'.$context->id.'/mod_imscp/content/'.$imscp->revision.'/'.$first['href']; 46 $firsturl = file_encode_url($urlbase, $path, false); 47 48 echo '<div id="imscp_layout">'; 49 echo '<div id="imscp_toc">'; 50 echo '<div id="imscp_tree"><ul>'; 51 foreach ($items as $item) { 52 echo imscp_htmllize_item($item, $imscp, $cm); 53 } 54 echo '</ul></div>'; 55 echo '<div id="imscp_nav" style="display:none">'; 56 echo '<button id="nav_skipprev"><<</button><button id="nav_prev"><</button><button id="nav_up">^</button>'; 57 echo '<button id="nav_next">></button><button id="nav_skipnext">>></button>'; 58 echo '</div>'; 59 echo '</div>'; 60 echo '</div>'; 61 62 $PAGE->requires->js_init_call('M.mod_imscp.init'); 63 return; 64 } 65 66 /** 67 * Internal function - creates htmls structure suitable for YUI tree. 68 */ 69 function imscp_htmllize_item($item, $imscp, $cm) { 70 global $CFG; 71 72 if ($item['href']) { 73 if (preg_match('|^https?://|', $item['href'])) { 74 $url = $item['href']; 75 } else { 76 $context = context_module::instance($cm->id); 77 $urlbase = "$CFG->wwwroot/pluginfile.php"; 78 $path = '/'.$context->id.'/mod_imscp/content/'.$imscp->revision.'/'.$item['href']; 79 $url = file_encode_url($urlbase, $path, false); 80 } 81 $result = "<li><a href=\"$url\">".$item['title'].'</a>'; 82 } else { 83 $result = '<li>'.$item['title']; 84 } 85 if ($item['subitems']) { 86 $result .= '<ul>'; 87 foreach ($item['subitems'] as $subitem) { 88 $result .= imscp_htmllize_item($subitem, $imscp, $cm); 89 } 90 $result .= '</ul>'; 91 } 92 $result .= '</li>'; 93 94 return $result; 95 } 96 97 /** 98 * Parse an IMS content package's manifest file to determine its structure 99 * @param object $imscp 100 * @param object $context 101 * @return array 102 */ 103 function imscp_parse_structure($imscp, $context) { 104 $fs = get_file_storage(); 105 106 if (!$manifestfile = $fs->get_file($context->id, 'mod_imscp', 'content', $imscp->revision, '/', 'imsmanifest.xml')) { 107 return null; 108 } 109 110 return imscp_parse_manifestfile($manifestfile->get_content(), $imscp, $context); 111 } 112 113 /** 114 * Parse the contents of a IMS package's manifest file. 115 * @param string $manifestfilecontents the contents of the manifest file 116 * @return array 117 */ 118 function imscp_parse_manifestfile($manifestfilecontents, $imscp, $context) { 119 $doc = new DOMDocument(); 120 $oldentities = libxml_disable_entity_loader(true); 121 if (!$doc->loadXML($manifestfilecontents, LIBXML_NONET)) { 122 return null; 123 } 124 libxml_disable_entity_loader($oldentities); 125 126 // We put this fake URL as base in order to detect path changes caused by xml:base attributes. 127 $doc->documentURI = 'http://grrr/'; 128 129 $xmlorganizations = $doc->getElementsByTagName('organizations'); 130 if (empty($xmlorganizations->length)) { 131 return null; 132 } 133 $default = null; 134 if ($xmlorganizations->item(0)->attributes->getNamedItem('default')) { 135 $default = $xmlorganizations->item(0)->attributes->getNamedItem('default')->nodeValue; 136 } 137 $xmlorganization = $doc->getElementsByTagName('organization'); 138 if (empty($xmlorganization->length)) { 139 return null; 140 } 141 $organization = null; 142 foreach ($xmlorganization as $org) { 143 if (is_null($organization)) { 144 // Use first if default nor found. 145 $organization = $org; 146 } 147 if (!$org->attributes->getNamedItem('identifier')) { 148 continue; 149 } 150 if ($default === $org->attributes->getNamedItem('identifier')->nodeValue) { 151 // Found default - use it. 152 $organization = $org; 153 break; 154 } 155 } 156 157 // Load all resources. 158 $resources = array(); 159 160 $xmlresources = $doc->getElementsByTagName('resource'); 161 foreach ($xmlresources as $res) { 162 if (!$identifier = $res->attributes->getNamedItem('identifier')) { 163 continue; 164 } 165 $identifier = $identifier->nodeValue; 166 if ($xmlbase = $res->baseURI) { 167 // Undo the fake URL, we are interested in relative links only. 168 $xmlbase = str_replace('http://grrr/', '/', $xmlbase); 169 $xmlbase = rtrim($xmlbase, '/').'/'; 170 } else { 171 $xmlbase = ''; 172 } 173 if (!$href = $res->attributes->getNamedItem('href')) { 174 // If href not found look for <file href="help.htm"/>. 175 $fileresources = $res->getElementsByTagName('file'); 176 foreach ($fileresources as $file) { 177 $href = $file->getAttribute('href'); 178 } 179 if (pathinfo($href, PATHINFO_EXTENSION) == 'xml') { 180 $href = imscp_recursive_href($href, $imscp, $context); 181 } 182 if (empty($href)) { 183 continue; 184 } 185 } else { 186 $href = $href->nodeValue; 187 } 188 if (strpos($href, 'http://') !== 0) { 189 $href = $xmlbase.$href; 190 } 191 // Item href cleanup - Some packages are poorly done and use \ in urls. 192 $href = ltrim(strtr($href, "\\", '/'), '/'); 193 $resources[$identifier] = $href; 194 } 195 196 $items = array(); 197 foreach ($organization->childNodes as $child) { 198 if ($child->nodeName === 'item') { 199 if (!$item = imscp_recursive_item($child, 0, $resources)) { 200 continue; 201 } 202 $items[] = $item; 203 } 204 } 205 206 return $items; 207 } 208 209 function imscp_recursive_href($manifestfilename, $imscp, $context) { 210 $fs = get_file_storage(); 211 212 $dirname = dirname($manifestfilename); 213 $filename = basename($manifestfilename); 214 215 if ($dirname !== '/') { 216 $dirname = "/$dirname/"; 217 } 218 219 if (!$manifestfile = $fs->get_file($context->id, 'mod_imscp', 'content', $imscp->revision, $dirname, $filename)) { 220 return null; 221 } 222 223 $doc = new DOMDocument(); 224 $oldentities = libxml_disable_entity_loader(true); 225 if (!$doc->loadXML($manifestfile->get_content(), LIBXML_NONET)) { 226 return null; 227 } 228 libxml_disable_entity_loader($oldentities); 229 230 $xmlresources = $doc->getElementsByTagName('resource'); 231 foreach ($xmlresources as $res) { 232 if (!$href = $res->attributes->getNamedItem('href')) { 233 $fileresources = $res->getElementsByTagName('file'); 234 foreach ($fileresources as $file) { 235 $href = $file->getAttribute('href'); 236 if (pathinfo($href, PATHINFO_EXTENSION) == 'xml') { 237 $href = imscp_recursive_href($href, $imscp, $context); 238 } 239 240 if (pathinfo($href, PATHINFO_EXTENSION) == 'htm' || pathinfo($href, PATHINFO_EXTENSION) == 'html') { 241 return $href; 242 } 243 } 244 } 245 } 246 247 return $manifestfilename; 248 } 249 250 function imscp_recursive_item($xmlitem, $level, $resources) { 251 $identifierref = ''; 252 if ($identifierref = $xmlitem->attributes->getNamedItem('identifierref')) { 253 $identifierref = $identifierref->nodeValue; 254 } 255 256 $title = '?'; 257 $subitems = array(); 258 259 foreach ($xmlitem->childNodes as $child) { 260 if ($child->nodeName === 'title') { 261 $title = $child->textContent; 262 263 } else if ($child->nodeName === 'item') { 264 if ($subitem = imscp_recursive_item($child, $level + 1, $resources)) { 265 $subitems[] = $subitem; 266 } 267 } 268 } 269 270 return array('href' => isset($resources[$identifierref]) ? $resources[$identifierref] : '', 271 'title' => $title, 272 'level' => $level, 273 'subitems' => $subitems, 274 ); 275 } 276 277 /** 278 * File browsing support class 279 * 280 * @copyright 2009 Petr Skoda {@link http://skodak.org} 281 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 282 */ 283 class imscp_file_info extends file_info { 284 protected $course; 285 protected $cm; 286 protected $areas; 287 protected $filearea; 288 289 public function __construct($browser, $course, $cm, $context, $areas, $filearea) { 290 parent::__construct($browser, $context); 291 $this->course = $course; 292 $this->cm = $cm; 293 $this->areas = $areas; 294 $this->filearea = $filearea; 295 } 296 297 /** 298 * Returns list of standard virtual file/directory identification. 299 * The difference from stored_file parameters is that null values 300 * are allowed in all fields 301 * @return array with keys contextid, filearea, itemid, filepath and filename 302 */ 303 public function get_params() { 304 return array('contextid' => $this->context->id, 305 'component' => 'mod_imscp', 306 'filearea' => $this->filearea, 307 'itemid' => null, 308 'filepath' => null, 309 'filename' => null); 310 } 311 312 /** 313 * Returns localised visible name. 314 * @return string 315 */ 316 public function get_visible_name() { 317 return $this->areas[$this->filearea]; 318 } 319 320 /** 321 * Can I add new files or directories? 322 * @return bool 323 */ 324 public function is_writable() { 325 return false; 326 } 327 328 /** 329 * Is directory? 330 * @return bool 331 */ 332 public function is_directory() { 333 return true; 334 } 335 336 /** 337 * Returns list of children. 338 * @return array of file_info instances 339 */ 340 public function get_children() { 341 return $this->get_filtered_children('*', false, true); 342 } 343 344 /** 345 * Help function to return files matching extensions or their count 346 * 347 * @param string|array $extensions, either '*' or array of lowercase extensions, i.e. array('.gif','.jpg') 348 * @param bool|int $countonly if false returns the children, if an int returns just the 349 * count of children but stops counting when $countonly number of children is reached 350 * @param bool $returnemptyfolders if true returns items that don't have matching files inside 351 * @return array|int array of file_info instances or the count 352 */ 353 private function get_filtered_children($extensions = '*', $countonly = false, $returnemptyfolders = false) { 354 global $DB; 355 $params = array('contextid' => $this->context->id, 356 'component' => 'mod_imscp', 357 'filearea' => $this->filearea); 358 $sql = 'SELECT DISTINCT itemid 359 FROM {files} 360 WHERE contextid = :contextid 361 AND component = :component 362 AND filearea = :filearea'; 363 if (!$returnemptyfolders) { 364 $sql .= ' AND filename <> :emptyfilename'; 365 $params['emptyfilename'] = '.'; 366 } 367 list($sql2, $params2) = $this->build_search_files_sql($extensions); 368 $sql .= ' '.$sql2; 369 $params = array_merge($params, $params2); 370 if ($countonly !== false) { 371 $sql .= ' ORDER BY itemid'; 372 } 373 374 $rs = $DB->get_recordset_sql($sql, $params); 375 $children = array(); 376 foreach ($rs as $record) { 377 if ($child = $this->browser->get_file_info($this->context, 'mod_imscp', $this->filearea, $record->itemid)) { 378 $children[] = $child; 379 if ($countonly !== false && count($children) >= $countonly) { 380 break; 381 } 382 } 383 } 384 $rs->close(); 385 if ($countonly !== false) { 386 return count($children); 387 } 388 return $children; 389 } 390 391 /** 392 * Returns list of children which are either files matching the specified extensions 393 * or folders that contain at least one such file. 394 * 395 * @param string|array $extensions, either '*' or array of lowercase extensions, i.e. array('.gif','.jpg') 396 * @return array of file_info instances 397 */ 398 public function get_non_empty_children($extensions = '*') { 399 return $this->get_filtered_children($extensions, false); 400 } 401 402 /** 403 * Returns the number of children which are either files matching the specified extensions 404 * or folders containing at least one such file. 405 * 406 * @param string|array $extensions, for example '*' or array('.gif','.jpg') 407 * @param int $limit stop counting after at least $limit non-empty children are found 408 * @return int 409 */ 410 public function count_non_empty_children($extensions = '*', $limit = 1) { 411 return $this->get_filtered_children($extensions, $limit); 412 } 413 414 /** 415 * Returns parent file_info instance 416 * @return file_info or null for root 417 */ 418 public function get_parent() { 419 return $this->browser->get_file_info($this->context); 420 } 421 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body