mirror of
https://github.com/moodle/moodle.git
synced 2025-01-18 14:03:52 +01:00
MDL-35668 filebrowser: perf improvements for courses
When searching for non-empty children first retrieve list of all non-empty fileareas in modules and then check access for each of them. This will be much faster than checking access for each module and then look for files and areas inside this module. Do the same with course file areas
This commit is contained in:
parent
10122f009e
commit
84d815611c
@ -36,6 +36,9 @@ class file_info_context_course extends file_info {
|
||||
/** @var stdClass course object */
|
||||
protected $course;
|
||||
|
||||
/** @var file_info_context_module[] cached child modules. See {@link get_child_module()} */
|
||||
protected $childrenmodules = [];
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
@ -86,6 +89,41 @@ class file_info_context_course extends file_info {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns list of areas inside this course
|
||||
*
|
||||
* @param string $extensions Only return areas that have files with these extensions
|
||||
* @param bool $returnemptyfolders return all areas always, if true it will ignore the previous argument
|
||||
* @return array
|
||||
*/
|
||||
protected function get_course_areas($extensions = '*', $returnemptyfolders = false) {
|
||||
global $DB;
|
||||
|
||||
$allareas = [
|
||||
'course_summary',
|
||||
'course_overviewfiles',
|
||||
'course_section',
|
||||
'backup_section',
|
||||
'backup_course',
|
||||
'backup_automated',
|
||||
'course_legacy'
|
||||
];
|
||||
|
||||
if ($returnemptyfolders) {
|
||||
return $allareas;
|
||||
}
|
||||
|
||||
$params1 = ['contextid' => $this->context->id, 'emptyfilename' => '.'];
|
||||
$sql1 = "SELECT " . $DB->sql_concat('f.component', "'_'", 'f.filearea') . "
|
||||
FROM {files} f
|
||||
WHERE f.filename <> :emptyfilename AND f.contextid = :contextid ";
|
||||
$sql3 = ' GROUP BY f.component, f.filearea';
|
||||
list($sql2, $params2) = $this->build_search_files_sql($extensions);
|
||||
$areaswithfiles = $DB->get_fieldset_sql($sql1 . $sql2 . $sql3, array_merge($params1, $params2));
|
||||
|
||||
return array_intersect($allareas, $areaswithfiles);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a stored file for the course summary filearea directory
|
||||
*
|
||||
@ -387,6 +425,28 @@ class file_info_context_course extends file_info {
|
||||
return $this->get_filtered_children('*', false, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the child module if it is accessible by the current user
|
||||
*
|
||||
* @param cm_info|int $cm
|
||||
* @return file_info_context_module|null
|
||||
*/
|
||||
protected function get_child_module($cm) {
|
||||
$cmid = is_object($cm) ? $cm->id : $cm;
|
||||
if (!array_key_exists($cmid, $this->childrenmodules)) {
|
||||
$this->childrenmodules[$cmid] = null;
|
||||
if (!($cm instanceof cm_info)) {
|
||||
$cms = get_fast_modinfo($this->course)->cms;
|
||||
$cm = array_key_exists($cmid, $cms) ? $cms[$cmid] : null;
|
||||
}
|
||||
if ($cm && $cm->uservisible) {
|
||||
$this->childrenmodules[$cmid] = new file_info_context_module($this->browser,
|
||||
$cm->context, $this->course, $cm, $cm->modname);
|
||||
}
|
||||
}
|
||||
return $this->childrenmodules[$cmid];
|
||||
}
|
||||
|
||||
/**
|
||||
* Help function to return files matching extensions or their count
|
||||
*
|
||||
@ -397,46 +457,52 @@ class file_info_context_course extends file_info {
|
||||
* @return array|int array of file_info instances or the count
|
||||
*/
|
||||
private function get_filtered_children($extensions = '*', $countonly = false, $returnemptyfolders = false) {
|
||||
$areas = array(
|
||||
array('course', 'summary'),
|
||||
array('course', 'overviewfiles'),
|
||||
array('course', 'section'),
|
||||
array('backup', 'section'),
|
||||
array('backup', 'course'),
|
||||
array('backup', 'automated'),
|
||||
array('course', 'legacy')
|
||||
);
|
||||
$children = array();
|
||||
foreach ($areas as $area) {
|
||||
|
||||
$courseareas = $this->get_course_areas($extensions, $returnemptyfolders);
|
||||
foreach ($courseareas as $areaname) {
|
||||
$area = explode('_', $areaname, 2);
|
||||
if ($child = $this->get_file_info($area[0], $area[1], 0, '/', '.')) {
|
||||
if ($returnemptyfolders || $child->count_non_empty_children($extensions)) {
|
||||
$children[] = $child;
|
||||
if (($countonly !== false) && count($children) >= $countonly) {
|
||||
return $countonly;
|
||||
}
|
||||
$children[] = $child;
|
||||
if (($countonly !== false) && count($children) >= $countonly) {
|
||||
return $countonly;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$cnt = count($children);
|
||||
if (!has_capability('moodle/course:managefiles', $this->context)) {
|
||||
// 'managefiles' capability is checked in every activity module callback.
|
||||
// Don't even waste time on retrieving the modules if we can't browse the files anyway
|
||||
} else {
|
||||
// now list all modules
|
||||
$modinfo = get_fast_modinfo($this->course);
|
||||
foreach ($modinfo->cms as $cminfo) {
|
||||
if (empty($cminfo->uservisible)) {
|
||||
continue;
|
||||
}
|
||||
$modcontext = context_module::instance($cminfo->id, IGNORE_MISSING);
|
||||
if ($child = $this->browser->get_file_info($modcontext)) {
|
||||
if ($returnemptyfolders || $child->count_non_empty_children($extensions)) {
|
||||
if ($returnemptyfolders) {
|
||||
$modinfo = get_fast_modinfo($this->course);
|
||||
foreach ($modinfo->cms as $cminfo) {
|
||||
if ($child = $this->get_child_module($cminfo)) {
|
||||
$children[] = $child;
|
||||
if (($countonly !== false) && count($children) >= $countonly) {
|
||||
return $countonly;
|
||||
$cnt++;
|
||||
}
|
||||
}
|
||||
} else if ($moduleareas = $this->get_module_areas_with_files($extensions)) {
|
||||
// We found files in some of the modules.
|
||||
// Create array of children modules ordered with the same way as cms in modinfo.
|
||||
$modulechildren = array_fill_keys(array_keys(get_fast_modinfo($this->course)->get_cms()), null);
|
||||
foreach ($moduleareas as $area) {
|
||||
if ($modulechildren[$area->cmid]) {
|
||||
// We already found non-empty area within the same module, do not analyse other areas.
|
||||
continue;
|
||||
}
|
||||
if ($child = $this->get_child_module($area->cmid)) {
|
||||
if ($child->get_file_info($area->component, $area->filearea, $area->itemid, null, null)) {
|
||||
$modulechildren[$area->cmid] = $child;
|
||||
$cnt++;
|
||||
if (($countonly !== false) && $cnt >= $countonly) {
|
||||
return $cnt;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
$children = array_merge($children, array_values(array_filter($modulechildren)));
|
||||
}
|
||||
}
|
||||
|
||||
@ -446,6 +512,52 @@ class file_info_context_course extends file_info {
|
||||
return $children;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns list of areas inside the course modules that have files with the given extension
|
||||
*
|
||||
* @param string $extensions
|
||||
* @return array
|
||||
*/
|
||||
protected function get_module_areas_with_files($extensions = '*') {
|
||||
global $DB;
|
||||
|
||||
$params1 = ['contextid' => $this->context->id,
|
||||
'emptyfilename' => '.',
|
||||
'contextlevel' => CONTEXT_MODULE,
|
||||
'depth' => $this->context->depth + 1,
|
||||
'pathmask' => $this->context->path . '/%'];
|
||||
$sql1 = "SELECT ctx.id AS contextid, f.component, f.filearea, f.itemid, ctx.instanceid AS cmid, " .
|
||||
context_helper::get_preload_record_columns_sql('ctx') . "
|
||||
FROM {files} f
|
||||
INNER JOIN {context} ctx ON ctx.id = f.contextid
|
||||
WHERE f.filename <> :emptyfilename
|
||||
AND ctx.contextlevel = :contextlevel
|
||||
AND ctx.depth = :depth
|
||||
AND " . $DB->sql_like('ctx.path', ':pathmask') . " ";
|
||||
$sql3 = ' GROUP BY ctx.id, f.component, f.filearea, f.itemid, ctx.instanceid,
|
||||
ctx.path, ctx.depth, ctx.contextlevel
|
||||
ORDER BY ctx.id, f.component, f.filearea, f.itemid';
|
||||
list($sql2, $params2) = $this->build_search_files_sql($extensions);
|
||||
$areas = [];
|
||||
if ($rs = $DB->get_recordset_sql($sql1. $sql2 . $sql3, array_merge($params1, $params2))) {
|
||||
foreach ($rs as $record) {
|
||||
context_helper::preload_from_record($record);
|
||||
$areas[] = $record;
|
||||
}
|
||||
$rs->close();
|
||||
}
|
||||
|
||||
// Sort areas so 'backup' and 'intro' are in the beginning of the list, they are the easiest to check access to.
|
||||
usort($areas, function($a, $b) {
|
||||
$aeasy = ($a->filearea === 'intro' && substr($a->component, 0, 4) === 'mod_') ||
|
||||
($a->filearea === 'activity' && $a->component === 'backup');
|
||||
$beasy = ($b->filearea === 'intro' && substr($b->component, 0, 4) === 'mod_') ||
|
||||
($b->filearea === 'activity' && $b->component === 'backup');
|
||||
return $aeasy == $beasy ? 0 : ($aeasy ? -1 : 1);
|
||||
});
|
||||
return $areas;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns list of children which are either files matching the specified extensions
|
||||
* or folders that contain at least one such file.
|
||||
|
Loading…
x
Reference in New Issue
Block a user