PageRenderTime 391ms CodeModel.GetById 39ms RepoModel.GetById 0ms app.codeStats 0ms

/lib/filebrowser/file_info_context_course.php

https://bitbucket.org/moodle/moodle
PHP | 997 lines | 533 code | 116 blank | 348 comment | 87 complexity | 3c993910e8d861556a7753dac8dcac43 MD5 | raw file
Possible License(s): Apache-2.0, LGPL-2.1, BSD-3-Clause, MIT, GPL-3.0
  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. * Utility class for browsing of course files.
  18. *
  19. * @package core_files
  20. * @copyright 2008 Petr Skoda (http://skodak.org)
  21. * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  22. */
  23. defined('MOODLE_INTERNAL') || die();
  24. /**
  25. * Represents a course context in the tree navigated by {@link file_browser}.
  26. *
  27. * @package core_files
  28. * @copyright 2008 Petr Skoda (http://skodak.org)
  29. * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  30. */
  31. class file_info_context_course extends file_info {
  32. /** @var stdClass course object */
  33. protected $course;
  34. /** @var file_info_context_module[] cached child modules. See {@link get_child_module()} */
  35. protected $childrenmodules = [];
  36. /**
  37. * Constructor
  38. *
  39. * @param file_browser $browser file browser instance
  40. * @param stdClass $context context object
  41. * @param stdClass $course course object
  42. */
  43. public function __construct($browser, $context, $course) {
  44. parent::__construct($browser, $context);
  45. $this->course = $course;
  46. }
  47. /**
  48. * Return information about this specific context level
  49. *
  50. * @param string $component component
  51. * @param string $filearea file area
  52. * @param int $itemid item ID
  53. * @param string $filepath file path
  54. * @param string $filename file name
  55. * @return file_info|null file_info instance or null if not found or access not allowed
  56. */
  57. public function get_file_info($component, $filearea, $itemid, $filepath, $filename) {
  58. // try to emulate require_login() tests here
  59. if (!isloggedin()) {
  60. return null;
  61. }
  62. if (!$this->course->visible and !has_capability('moodle/course:viewhiddencourses', $this->context)) {
  63. return null;
  64. }
  65. if (!is_viewing($this->context) and !$this->browser->is_enrolled($this->course->id)) {
  66. // no peaking here if not enrolled or inspector
  67. return null;
  68. }
  69. if (empty($component)) {
  70. return $this;
  71. }
  72. $methodname = "get_area_{$component}_{$filearea}";
  73. if (method_exists($this, $methodname)) {
  74. return $this->$methodname($itemid, $filepath, $filename);
  75. }
  76. return null;
  77. }
  78. /**
  79. * Returns list of areas inside this course
  80. *
  81. * @param string $extensions Only return areas that have files with these extensions
  82. * @param bool $returnemptyfolders return all areas always, if true it will ignore the previous argument
  83. * @return array
  84. */
  85. protected function get_course_areas($extensions = '*', $returnemptyfolders = false) {
  86. global $DB;
  87. $allareas = [
  88. 'course_summary',
  89. 'course_overviewfiles',
  90. 'course_section',
  91. 'backup_section',
  92. 'backup_course',
  93. 'backup_automated',
  94. 'course_legacy'
  95. ];
  96. if ($returnemptyfolders) {
  97. return $allareas;
  98. }
  99. $params1 = ['contextid' => $this->context->id, 'emptyfilename' => '.'];
  100. $sql1 = "SELECT " . $DB->sql_concat('f.component', "'_'", 'f.filearea') . "
  101. FROM {files} f
  102. WHERE f.filename <> :emptyfilename AND f.contextid = :contextid ";
  103. $sql3 = ' GROUP BY f.component, f.filearea';
  104. list($sql2, $params2) = $this->build_search_files_sql($extensions);
  105. $areaswithfiles = $DB->get_fieldset_sql($sql1 . $sql2 . $sql3, array_merge($params1, $params2));
  106. return array_intersect($allareas, $areaswithfiles);
  107. }
  108. /**
  109. * Gets a stored file for the course summary filearea directory
  110. *
  111. * @param int $itemid item ID
  112. * @param string $filepath file path
  113. * @param string $filename file name
  114. * @return file_info|null file_info instance or null if not found or access not allowed
  115. */
  116. protected function get_area_course_summary($itemid, $filepath, $filename) {
  117. global $CFG;
  118. if (!has_capability('moodle/course:update', $this->context)) {
  119. return null;
  120. }
  121. if (is_null($itemid)) {
  122. return $this;
  123. }
  124. $fs = get_file_storage();
  125. $filepath = is_null($filepath) ? '/' : $filepath;
  126. $filename = is_null($filename) ? '.' : $filename;
  127. if (!$storedfile = $fs->get_file($this->context->id, 'course', 'summary', 0, $filepath, $filename)) {
  128. if ($filepath === '/' and $filename === '.') {
  129. $storedfile = new virtual_root_file($this->context->id, 'course', 'summary', 0);
  130. } else {
  131. // not found
  132. return null;
  133. }
  134. }
  135. $urlbase = $CFG->wwwroot.'/pluginfile.php';
  136. return new file_info_stored($this->browser, $this->context, $storedfile, $urlbase, get_string('areacourseintro', 'repository'), false, true, true, false);
  137. }
  138. /**
  139. * Gets a stored file for the course images filearea directory
  140. *
  141. * @param int $itemid item ID
  142. * @param string $filepath file path
  143. * @param string $filename file name
  144. * @return file_info|null file_info instance or null if not found or access not allowed
  145. */
  146. protected function get_area_course_overviewfiles($itemid, $filepath, $filename) {
  147. global $CFG;
  148. if (!has_capability('moodle/course:update', $this->context)) {
  149. return null;
  150. }
  151. if (is_null($itemid)) {
  152. return $this;
  153. }
  154. $fs = get_file_storage();
  155. $filepath = is_null($filepath) ? '/' : $filepath;
  156. $filename = is_null($filename) ? '.' : $filename;
  157. if (!$storedfile = $fs->get_file($this->context->id, 'course', 'overviewfiles', 0, $filepath, $filename)) {
  158. if ($filepath === '/' and $filename === '.') {
  159. $storedfile = new virtual_root_file($this->context->id, 'course', 'overviewfiles', 0);
  160. } else {
  161. // not found
  162. return null;
  163. }
  164. }
  165. $urlbase = $CFG->wwwroot.'/pluginfile.php';
  166. return new file_info_stored($this->browser, $this->context, $storedfile, $urlbase, get_string('areacourseoverviewfiles', 'repository'), false, true, true, false);
  167. }
  168. /**
  169. * Gets a stored file for the course section filearea directory
  170. *
  171. * @param int $itemid item ID
  172. * @param string $filepath file path
  173. * @param string $filename file name
  174. * @return file_info|null file_info instance or null if not found or access not allowed
  175. */
  176. protected function get_area_course_section($itemid, $filepath, $filename) {
  177. global $CFG, $DB;
  178. if (!has_capability('moodle/course:update', $this->context)) {
  179. return null;
  180. }
  181. if (empty($itemid)) {
  182. // list all sections
  183. return new file_info_area_course_section($this->browser, $this->context, $this->course, $this);
  184. }
  185. if (!$section = $DB->get_record('course_sections', array('course'=>$this->course->id, 'id'=>$itemid))) {
  186. return null; // does not exist
  187. }
  188. $fs = get_file_storage();
  189. $filepath = is_null($filepath) ? '/' : $filepath;
  190. $filename = is_null($filename) ? '.' : $filename;
  191. if (!$storedfile = $fs->get_file($this->context->id, 'course', 'section', $itemid, $filepath, $filename)) {
  192. if ($filepath === '/' and $filename === '.') {
  193. $storedfile = new virtual_root_file($this->context->id, 'course', 'section', $itemid);
  194. } else {
  195. // not found
  196. return null;
  197. }
  198. }
  199. $urlbase = $CFG->wwwroot.'/pluginfile.php';
  200. require_once($CFG->dirroot.'/course/lib.php');
  201. $sectionname = get_section_name($this->course, $section);
  202. return new file_info_stored($this->browser, $this->context, $storedfile, $urlbase, $sectionname, true, true, true, false);
  203. }
  204. /**
  205. * Gets a stored file for the course legacy filearea directory
  206. *
  207. * @param int $itemid item ID
  208. * @param string $filepath file path
  209. * @param string $filename file name
  210. * @return file_info|null file_info instance or null if not found or access not allowed
  211. */
  212. protected function get_area_course_legacy($itemid, $filepath, $filename) {
  213. if (!has_capability('moodle/course:managefiles', $this->context)) {
  214. return null;
  215. }
  216. if ($this->course->id != SITEID and $this->course->legacyfiles != 2) {
  217. // bad luck, legacy course files not used any more
  218. }
  219. if (is_null($itemid)) {
  220. return $this;
  221. }
  222. $fs = get_file_storage();
  223. $filepath = is_null($filepath) ? '/' : $filepath;
  224. $filename = is_null($filename) ? '.' : $filename;
  225. if (!$storedfile = $fs->get_file($this->context->id, 'course', 'legacy', 0, $filepath, $filename)) {
  226. if ($filepath === '/' and $filename === '.') {
  227. $storedfile = new virtual_root_file($this->context->id, 'course', 'legacy', 0);
  228. } else {
  229. // not found
  230. return null;
  231. }
  232. }
  233. return new file_info_area_course_legacy($this->browser, $this->context, $storedfile);
  234. }
  235. /**
  236. * Gets a stored file for the backup course filearea directory
  237. *
  238. * @param int $itemid item ID
  239. * @param string $filepath file path
  240. * @param string $filename file name
  241. * @return file_info|null file_info instance or null if not found or access not allowed
  242. */
  243. protected function get_area_backup_course($itemid, $filepath, $filename) {
  244. global $CFG;
  245. if (!has_capability('moodle/backup:backupcourse', $this->context) and !has_capability('moodle/restore:restorecourse', $this->context)) {
  246. return null;
  247. }
  248. if (is_null($itemid)) {
  249. return $this;
  250. }
  251. $fs = get_file_storage();
  252. $filepath = is_null($filepath) ? '/' : $filepath;
  253. $filename = is_null($filename) ? '.' : $filename;
  254. if (!$storedfile = $fs->get_file($this->context->id, 'backup', 'course', 0, $filepath, $filename)) {
  255. if ($filepath === '/' and $filename === '.') {
  256. $storedfile = new virtual_root_file($this->context->id, 'backup', 'course', 0);
  257. } else {
  258. // not found
  259. return null;
  260. }
  261. }
  262. $downloadable = has_capability('moodle/backup:downloadfile', $this->context);
  263. $uploadable = has_capability('moodle/restore:uploadfile', $this->context);
  264. $urlbase = $CFG->wwwroot.'/pluginfile.php';
  265. return new file_info_stored($this->browser, $this->context, $storedfile, $urlbase, get_string('coursebackup', 'repository'), false, $downloadable, $uploadable, false);
  266. }
  267. /**
  268. * Gets a stored file for the automated backup filearea directory
  269. *
  270. * @param int $itemid item ID
  271. * @param string $filepath file path
  272. * @param string $filename file name
  273. * @return file_info|null
  274. */
  275. protected function get_area_backup_automated($itemid, $filepath, $filename) {
  276. global $CFG;
  277. if (!has_capability('moodle/restore:viewautomatedfilearea', $this->context)) {
  278. return null;
  279. }
  280. if (is_null($itemid)) {
  281. return $this;
  282. }
  283. $fs = get_file_storage();
  284. $filepath = is_null($filepath) ? '/' : $filepath;
  285. $filename = is_null($filename) ? '.' : $filename;
  286. if (!$storedfile = $fs->get_file($this->context->id, 'backup', 'automated', 0, $filepath, $filename)) {
  287. if ($filepath === '/' and $filename === '.') {
  288. $storedfile = new virtual_root_file($this->context->id, 'backup', 'automated', 0);
  289. } else {
  290. // not found
  291. return null;
  292. }
  293. }
  294. // Automated backup files are only downloadable if the user has both 'backup:downloadfile and 'restore:userinfo'.
  295. $downloadable = has_capability('moodle/backup:downloadfile', $this->context) &&
  296. has_capability('moodle/restore:userinfo', $this->context);
  297. $uploadable = false;
  298. $urlbase = $CFG->wwwroot.'/pluginfile.php';
  299. return new file_info_stored($this->browser, $this->context, $storedfile, $urlbase, get_string('automatedbackup', 'repository'), true, $downloadable, $uploadable, false);
  300. }
  301. /**
  302. * Gets a stored file for the backup section filearea directory
  303. *
  304. * @param int $itemid item ID
  305. * @param string $filepath file path
  306. * @param string $filename file name
  307. * @return file_info|null file_info instance or null if not found or access not allowed
  308. */
  309. protected function get_area_backup_section($itemid, $filepath, $filename) {
  310. global $CFG, $DB;
  311. if (!has_capability('moodle/backup:backupcourse', $this->context) and !has_capability('moodle/restore:restorecourse', $this->context)) {
  312. return null;
  313. }
  314. if (empty($itemid)) {
  315. // list all sections
  316. return new file_info_area_backup_section($this->browser, $this->context, $this->course, $this);
  317. }
  318. if (!$section = $DB->get_record('course_sections', array('course'=>$this->course->id, 'id'=>$itemid))) {
  319. return null; // does not exist
  320. }
  321. $fs = get_file_storage();
  322. $filepath = is_null($filepath) ? '/' : $filepath;
  323. $filename = is_null($filename) ? '.' : $filename;
  324. if (!$storedfile = $fs->get_file($this->context->id, 'backup', 'section', $itemid, $filepath, $filename)) {
  325. if ($filepath === '/' and $filename === '.') {
  326. $storedfile = new virtual_root_file($this->context->id, 'backup', 'section', $itemid);
  327. } else {
  328. // not found
  329. return null;
  330. }
  331. }
  332. $downloadable = has_capability('moodle/backup:downloadfile', $this->context);
  333. $uploadable = has_capability('moodle/restore:uploadfile', $this->context);
  334. $urlbase = $CFG->wwwroot.'/pluginfile.php';
  335. return new file_info_stored($this->browser, $this->context, $storedfile, $urlbase, $section->id, true, $downloadable, $uploadable, false);
  336. }
  337. /**
  338. * Returns localised visible name.
  339. *
  340. * @return string
  341. */
  342. public function get_visible_name() {
  343. return ($this->course->id == SITEID) ? get_string('frontpage', 'admin') : format_string(get_course_display_name_for_list($this->course), true, array('context'=>$this->context));
  344. }
  345. /**
  346. * Whether or not new files or directories can be added
  347. *
  348. * @return bool
  349. */
  350. public function is_writable() {
  351. return false;
  352. }
  353. /**
  354. * Whether or not this is a directory
  355. *
  356. * @return bool
  357. */
  358. public function is_directory() {
  359. return true;
  360. }
  361. /**
  362. * Returns list of children.
  363. *
  364. * @return array of file_info instances
  365. */
  366. public function get_children() {
  367. return $this->get_filtered_children('*', false, true);
  368. }
  369. /**
  370. * Returns the child module if it is accessible by the current user
  371. *
  372. * @param cm_info|int $cm
  373. * @return file_info_context_module|null
  374. */
  375. protected function get_child_module($cm) {
  376. $cmid = is_object($cm) ? $cm->id : $cm;
  377. if (!array_key_exists($cmid, $this->childrenmodules)) {
  378. $this->childrenmodules[$cmid] = null;
  379. if (!($cm instanceof cm_info)) {
  380. $cms = get_fast_modinfo($this->course)->cms;
  381. $cm = array_key_exists($cmid, $cms) ? $cms[$cmid] : null;
  382. }
  383. if ($cm && $cm->uservisible) {
  384. $this->childrenmodules[$cmid] = new file_info_context_module($this->browser,
  385. $cm->context, $this->course, $cm, $cm->modname);
  386. }
  387. }
  388. return $this->childrenmodules[$cmid];
  389. }
  390. /**
  391. * Help function to return files matching extensions or their count
  392. *
  393. * @param string|array $extensions, either '*' or array of lowercase extensions, i.e. array('.gif','.jpg')
  394. * @param bool|int $countonly if false returns the children, if an int returns just the
  395. * count of children but stops counting when $countonly number of children is reached
  396. * @param bool $returnemptyfolders if true returns items that don't have matching files inside
  397. * @return array|int array of file_info instances or the count
  398. */
  399. private function get_filtered_children($extensions = '*', $countonly = false, $returnemptyfolders = false) {
  400. $children = array();
  401. $courseareas = $this->get_course_areas($extensions, $returnemptyfolders);
  402. foreach ($courseareas as $areaname) {
  403. $area = explode('_', $areaname, 2);
  404. if ($child = $this->get_file_info($area[0], $area[1], 0, '/', '.')) {
  405. $children[] = $child;
  406. if (($countonly !== false) && count($children) >= $countonly) {
  407. return $countonly;
  408. }
  409. }
  410. }
  411. $cnt = count($children);
  412. if (!has_capability('moodle/course:managefiles', $this->context)) {
  413. // 'managefiles' capability is checked in every activity module callback.
  414. // Don't even waste time on retrieving the modules if we can't browse the files anyway
  415. } else {
  416. if ($returnemptyfolders) {
  417. $modinfo = get_fast_modinfo($this->course);
  418. foreach ($modinfo->cms as $cminfo) {
  419. if ($child = $this->get_child_module($cminfo)) {
  420. $children[] = $child;
  421. $cnt++;
  422. }
  423. }
  424. } else if ($moduleareas = $this->get_module_areas_with_files($extensions)) {
  425. // We found files in some of the modules.
  426. // Create array of children modules ordered with the same way as cms in modinfo.
  427. $modulechildren = array_fill_keys(array_keys(get_fast_modinfo($this->course)->get_cms()), null);
  428. foreach ($moduleareas as $area) {
  429. if ($modulechildren[$area->cmid]) {
  430. // We already found non-empty area within the same module, do not analyse other areas.
  431. continue;
  432. }
  433. if ($child = $this->get_child_module($area->cmid)) {
  434. if ($child->get_file_info($area->component, $area->filearea, $area->itemid, null, null)) {
  435. $modulechildren[$area->cmid] = $child;
  436. $cnt++;
  437. if (($countonly !== false) && $cnt >= $countonly) {
  438. return $cnt;
  439. }
  440. }
  441. }
  442. }
  443. $children = array_merge($children, array_values(array_filter($modulechildren)));
  444. }
  445. }
  446. if ($countonly !== false) {
  447. return count($children);
  448. }
  449. return $children;
  450. }
  451. /**
  452. * Returns list of areas inside the course modules that have files with the given extension
  453. *
  454. * @param string $extensions
  455. * @return array
  456. */
  457. protected function get_module_areas_with_files($extensions = '*') {
  458. global $DB;
  459. $params1 = ['contextid' => $this->context->id,
  460. 'emptyfilename' => '.',
  461. 'contextlevel' => CONTEXT_MODULE,
  462. 'course' => $this->course->id];
  463. $ctxfieldsas = context_helper::get_preload_record_columns_sql('ctx');
  464. $ctxfields = implode(', ', array_keys(context_helper::get_preload_record_columns('ctx')));
  465. $sql1 = "SELECT
  466. ctx.id AS contextid,
  467. f.component,
  468. f.filearea,
  469. f.itemid,
  470. ctx.instanceid AS cmid,
  471. {$ctxfieldsas}
  472. FROM {files} f
  473. INNER JOIN {context} ctx ON ctx.id = f.contextid
  474. INNER JOIN {course_modules} cm ON cm.id = ctx.instanceid
  475. WHERE f.filename <> :emptyfilename
  476. AND cm.course = :course
  477. AND ctx.contextlevel = :contextlevel";
  478. $sql3 = "
  479. GROUP BY ctx.id, f.component, f.filearea, f.itemid, {$ctxfields}
  480. ORDER BY ctx.id, f.component, f.filearea, f.itemid";
  481. list($sql2, $params2) = $this->build_search_files_sql($extensions);
  482. $areas = [];
  483. if ($rs = $DB->get_recordset_sql($sql1. $sql2 . $sql3, array_merge($params1, $params2))) {
  484. foreach ($rs as $record) {
  485. context_helper::preload_from_record($record);
  486. $areas[] = $record;
  487. }
  488. $rs->close();
  489. }
  490. // Sort areas so 'backup' and 'intro' are in the beginning of the list, they are the easiest to check access to.
  491. usort($areas, function($a, $b) {
  492. $aeasy = ($a->filearea === 'intro' && substr($a->component, 0, 4) === 'mod_') ||
  493. ($a->filearea === 'activity' && $a->component === 'backup');
  494. $beasy = ($b->filearea === 'intro' && substr($b->component, 0, 4) === 'mod_') ||
  495. ($b->filearea === 'activity' && $b->component === 'backup');
  496. return $aeasy == $beasy ? 0 : ($aeasy ? -1 : 1);
  497. });
  498. return $areas;
  499. }
  500. /**
  501. * Returns list of children which are either files matching the specified extensions
  502. * or folders that contain at least one such file.
  503. *
  504. * @param string|array $extensions, either '*' or array of lowercase extensions, i.e. array('.gif','.jpg')
  505. * @return array of file_info instances
  506. */
  507. public function get_non_empty_children($extensions = '*') {
  508. return $this->get_filtered_children($extensions, false);
  509. }
  510. /**
  511. * Returns the number of children which are either files matching the specified extensions
  512. * or folders containing at least one such file.
  513. *
  514. * @param string|array $extensions, for example '*' or array('.gif','.jpg')
  515. * @param int $limit stop counting after at least $limit non-empty children are found
  516. * @return int
  517. */
  518. public function count_non_empty_children($extensions = '*', $limit = 1) {
  519. return $this->get_filtered_children($extensions, $limit);
  520. }
  521. /**
  522. * Returns parent file_info instance
  523. *
  524. * @return file_info or null for root
  525. */
  526. public function get_parent() {
  527. $parent = $this->context->get_parent_context();
  528. return $this->browser->get_file_info($parent);
  529. }
  530. }
  531. /**
  532. * Subclass of file_info_stored for files in the course files area.
  533. *
  534. * @package core_files
  535. * @copyright 2008 Petr Skoda (http://skodak.org)
  536. * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  537. */
  538. class file_info_area_course_legacy extends file_info_stored {
  539. /**
  540. * Constructor
  541. *
  542. * @param file_browser $browser file browser instance
  543. * @param stdClass $context context object
  544. * @param stored_file $storedfile stored_file instance
  545. */
  546. public function __construct($browser, $context, $storedfile) {
  547. global $CFG;
  548. $urlbase = $CFG->wwwroot.'/file.php';
  549. parent::__construct($browser, $context, $storedfile, $urlbase, get_string('coursefiles'), false, true, true, false);
  550. }
  551. /**
  552. * Returns file download url
  553. *
  554. * @param bool $forcedownload whether or not force download
  555. * @param bool $https whether or not force https
  556. * @return string url
  557. */
  558. public function get_url($forcedownload=false, $https=false) {
  559. if (!$this->is_readable()) {
  560. return null;
  561. }
  562. if ($this->lf->is_directory()) {
  563. return null;
  564. }
  565. $filepath = $this->lf->get_filepath();
  566. $filename = $this->lf->get_filename();
  567. $courseid = $this->context->instanceid;
  568. $path = '/'.$courseid.$filepath.$filename;
  569. return file_encode_url($this->urlbase, $path, $forcedownload, $https);
  570. }
  571. /**
  572. * Returns list of children.
  573. *
  574. * @return array of file_info instances
  575. */
  576. public function get_children() {
  577. if (!$this->lf->is_directory()) {
  578. return array();
  579. }
  580. $result = array();
  581. $fs = get_file_storage();
  582. $storedfiles = $fs->get_directory_files($this->context->id, 'course', 'legacy', 0, $this->lf->get_filepath(), false, true, "filepath ASC, filename ASC");
  583. foreach ($storedfiles as $file) {
  584. $result[] = new file_info_area_course_legacy($this->browser, $this->context, $file);
  585. }
  586. return $result;
  587. }
  588. /**
  589. * Returns list of children which are either files matching the specified extensions
  590. * or folders that contain at least one such file.
  591. *
  592. * @param string|array $extensions, either '*' or array of lowercase extensions, i.e. array('.gif','.jpg')
  593. * @return array of file_info instances
  594. */
  595. public function get_non_empty_children($extensions = '*') {
  596. if (!$this->lf->is_directory()) {
  597. return array();
  598. }
  599. $result = array();
  600. $fs = get_file_storage();
  601. $storedfiles = $fs->get_directory_files($this->context->id, 'course', 'legacy', 0,
  602. $this->lf->get_filepath(), false, true, "filepath, filename");
  603. foreach ($storedfiles as $file) {
  604. $extension = core_text::strtolower(pathinfo($file->get_filename(), PATHINFO_EXTENSION));
  605. if ($file->is_directory() || $extensions === '*' || (!empty($extension) && in_array('.'.$extension, $extensions))) {
  606. $fileinfo = new file_info_area_course_legacy($this->browser, $this->context, $file, $this->urlbase, $this->topvisiblename,
  607. $this->itemidused, $this->readaccess, $this->writeaccess, false);
  608. if (!$file->is_directory() || $fileinfo->count_non_empty_children($extensions)) {
  609. $result[] = $fileinfo;
  610. }
  611. }
  612. }
  613. return $result;
  614. }
  615. }
  616. /**
  617. * Represents a course category context in the tree navigated by {@link file_browser}.
  618. *
  619. * @package core_files
  620. * @copyright 2008 Petr Skoda (http://skodak.org)
  621. * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  622. */
  623. class file_info_area_course_section extends file_info {
  624. /** @var stdClass course object */
  625. protected $course;
  626. /** @var file_info_context_course course file info object */
  627. protected $courseinfo;
  628. /**
  629. * Constructor
  630. *
  631. * @param file_browser $browser file browser instance
  632. * @param stdClass $context context object
  633. * @param stdClass $course course object
  634. * @param file_info_context_course $courseinfo file info instance
  635. */
  636. public function __construct($browser, $context, $course, file_info_context_course $courseinfo) {
  637. parent::__construct($browser, $context);
  638. $this->course = $course;
  639. $this->courseinfo = $courseinfo;
  640. }
  641. /**
  642. * Returns list of standard virtual file/directory identification.
  643. * The difference from stored_file parameters is that null values
  644. * are allowed in all fields
  645. *
  646. * @return array with keys contextid, filearea, itemid, filepath and filename
  647. */
  648. public function get_params() {
  649. return array('contextid' => $this->context->id,
  650. 'component' => 'course',
  651. 'filearea' => 'section',
  652. 'itemid' => null,
  653. 'filepath' => null,
  654. 'filename' => null);
  655. }
  656. /**
  657. * Returns localised visible name.
  658. *
  659. * @return string
  660. */
  661. public function get_visible_name() {
  662. //$format = $this->course->format;
  663. $sectionsname = get_string("coursesectionsummaries");
  664. return $sectionsname;
  665. }
  666. /**
  667. * Return whether or not new files or directories can be added
  668. *
  669. * @return bool
  670. */
  671. public function is_writable() {
  672. return false;
  673. }
  674. /**
  675. * Return whether or not this is a empty area
  676. *
  677. * @return bool
  678. */
  679. public function is_empty_area() {
  680. $fs = get_file_storage();
  681. return $fs->is_area_empty($this->context->id, 'course', 'section');
  682. }
  683. /**
  684. * Return whether or not this is a empty area
  685. *
  686. * @return bool
  687. */
  688. public function is_directory() {
  689. return true;
  690. }
  691. /**
  692. * Returns list of children.
  693. *
  694. * @return array of file_info instances
  695. */
  696. public function get_children() {
  697. global $DB;
  698. $children = array();
  699. $course_sections = $DB->get_records('course_sections', array('course'=>$this->course->id), 'section');
  700. foreach ($course_sections as $section) {
  701. if ($child = $this->courseinfo->get_file_info('course', 'section', $section->id, '/', '.')) {
  702. $children[] = $child;
  703. }
  704. }
  705. return $children;
  706. }
  707. /**
  708. * Returns the number of children which are either files matching the specified extensions
  709. * or folders containing at least one such file.
  710. *
  711. * @param string|array $extensions, for example '*' or array('.gif','.jpg')
  712. * @param int $limit stop counting after at least $limit non-empty children are found
  713. * @return int
  714. */
  715. public function count_non_empty_children($extensions = '*', $limit = 1) {
  716. global $DB;
  717. $params1 = array(
  718. 'courseid' => $this->course->id,
  719. 'contextid' => $this->context->id,
  720. 'component' => 'course',
  721. 'filearea' => 'section',
  722. 'emptyfilename' => '.');
  723. $sql1 = "SELECT DISTINCT cs.id FROM {files} f, {course_sections} cs
  724. WHERE cs.course = :courseid
  725. AND f.contextid = :contextid
  726. AND f.component = :component
  727. AND f.filearea = :filearea
  728. AND f.itemid = cs.id
  729. AND f.filename <> :emptyfilename";
  730. list($sql2, $params2) = $this->build_search_files_sql($extensions);
  731. $rs = $DB->get_recordset_sql($sql1. ' '. $sql2, array_merge($params1, $params2));
  732. $cnt = 0;
  733. foreach ($rs as $record) {
  734. if ((++$cnt) >= $limit) {
  735. break;
  736. }
  737. }
  738. $rs->close();
  739. return $cnt;
  740. }
  741. /**
  742. * Returns parent file_info instance
  743. *
  744. * @return file_info|null file_info or null for root
  745. */
  746. public function get_parent() {
  747. return $this->courseinfo;
  748. }
  749. }
  750. /**
  751. * Implementation of course section backup area
  752. *
  753. * @package core_files
  754. * @copyright 2008 Petr Skoda (http://skodak.org)
  755. * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  756. */
  757. class file_info_area_backup_section extends file_info {
  758. /** @var stdClass course object */
  759. protected $course;
  760. /** @var file_info_context_course course file info object */
  761. protected $courseinfo;
  762. /**
  763. * Constructor
  764. *
  765. * @param file_browser $browser file browser instance
  766. * @param stdClass $context context object
  767. * @param stdClass $course course object
  768. * @param file_info_context_course $courseinfo file info instance
  769. */
  770. public function __construct($browser, $context, $course, file_info_context_course $courseinfo) {
  771. parent::__construct($browser, $context);
  772. $this->course = $course;
  773. $this->courseinfo = $courseinfo;
  774. }
  775. /**
  776. * Returns list of standard virtual file/directory identification.
  777. * The difference from stored_file parameters is that null values
  778. * are allowed in all fields
  779. *
  780. * @return array with keys contextid, component, filearea, itemid, filepath and filename
  781. */
  782. public function get_params() {
  783. return array('contextid' => $this->context->id,
  784. 'component' => 'backup',
  785. 'filearea' => 'section',
  786. 'itemid' => null,
  787. 'filepath' => null,
  788. 'filename' => null);
  789. }
  790. /**
  791. * Returns localised visible name.
  792. *
  793. * @return string
  794. */
  795. public function get_visible_name() {
  796. return get_string('sectionbackup', 'repository');
  797. }
  798. /**
  799. * Return whether or not new files and directories can be added
  800. *
  801. * @return bool
  802. */
  803. public function is_writable() {
  804. return false;
  805. }
  806. /**
  807. * Whether or not this is an empty area
  808. *
  809. * @return bool
  810. */
  811. public function is_empty_area() {
  812. $fs = get_file_storage();
  813. return $fs->is_area_empty($this->context->id, 'backup', 'section');
  814. }
  815. /**
  816. * Return whether or not this is a directory
  817. *
  818. * @return bool
  819. */
  820. public function is_directory() {
  821. return true;
  822. }
  823. /**
  824. * Returns list of children.
  825. *
  826. * @return array of file_info instances
  827. */
  828. public function get_children() {
  829. global $DB;
  830. $children = array();
  831. $course_sections = $DB->get_records('course_sections', array('course'=>$this->course->id), 'section');
  832. foreach ($course_sections as $section) {
  833. if ($child = $this->courseinfo->get_file_info('backup', 'section', $section->id, '/', '.')) {
  834. $children[] = $child;
  835. }
  836. }
  837. return $children;
  838. }
  839. /**
  840. * Returns the number of children which are either files matching the specified extensions
  841. * or folders containing at least one such file.
  842. *
  843. * @param string|array $extensions, for example '*' or array('.gif','.jpg')
  844. * @param int $limit stop counting after at least $limit non-empty children are found
  845. * @return int
  846. */
  847. public function count_non_empty_children($extensions = '*', $limit = 1) {
  848. global $DB;
  849. $params1 = array(
  850. 'courseid' => $this->course->id,
  851. 'contextid' => $this->context->id,
  852. 'component' => 'backup',
  853. 'filearea' => 'section',
  854. 'emptyfilename' => '.');
  855. $sql1 = "SELECT DISTINCT cs.id AS sectionid FROM {files} f, {course_sections} cs
  856. WHERE cs.course = :courseid
  857. AND f.contextid = :contextid
  858. AND f.component = :component
  859. AND f.filearea = :filearea
  860. AND f.itemid = cs.id
  861. AND f.filename <> :emptyfilename";
  862. list($sql2, $params2) = $this->build_search_files_sql($extensions);
  863. $rs = $DB->get_recordset_sql($sql1. ' '. $sql2, array_merge($params1, $params2));
  864. $cnt = 0;
  865. foreach ($rs as $record) {
  866. if ((++$cnt) >= $limit) {
  867. break;
  868. }
  869. }
  870. $rs->close();
  871. return $cnt;
  872. }
  873. /**
  874. * Returns parent file_info instance
  875. *
  876. * @return file_info or null for root
  877. */
  878. public function get_parent() {
  879. return $this->browser->get_file_info($this->context);
  880. }
  881. }