Skip to content

Commit

Permalink
Merge branch 'MDL-53057-master' of git://github.com/FMCorz/moodle
Browse files Browse the repository at this point in the history
  • Loading branch information
David Monllao committed Mar 15, 2016
2 parents 0162087 + 1039b52 commit 8ea783f
Show file tree
Hide file tree
Showing 2 changed files with 269 additions and 53 deletions.
1 change: 1 addition & 0 deletions repository/filesystem/lang/en/repository_filesystem.php
Expand Up @@ -36,6 +36,7 @@
$string['nosubdir'] = 'You need to create at least one folder inside the <b>{$a}</b> directory so you can select it here.';
$string['pluginname_help'] = 'Create repository from local directory';
$string['pluginname'] = 'File system';
$string['searchresults'] = 'Search results';
$string['enablecourseinstances'] = 'Allow admins to add a file system repository instance to a course (configurable only by admins)';
$string['enableuserinstances'] = 'Allow admins to add a file system repository instance for personal use (configurable only by admins)';

321 changes: 268 additions & 53 deletions repository/filesystem/lib.php
Expand Up @@ -60,45 +60,40 @@ public function __construct($repositoryid, $context = SYSCONTEXTID, $options = a
/**
* Get the list of files and directories in that repository.
*
* @param string $path to browse.
* @param string $page page number.
* @return array list of files and folders.
* @param string $fullpath Path to explore. This is assembled by {@link self::build_node_path()}.
* @param string $page Page number.
* @return array List of files and folders.
*/
public function get_listing($path = '', $page = '') {
public function get_listing($fullpath = '', $page = '') {
global $OUTPUT;

$list = array();
$list['list'] = array();
$list['manage'] = false;
$list['dynload'] = true;
$list['nologin'] = true;
$list['nosearch'] = true;
$list['path'] = array(
array('name' => get_string('root', 'repository_filesystem'), 'path' => '')
$list = array(
'list' => array(),
'manage' => false,
'dynload' => true,
'nologin' => true,
'path' => array()
);

// We analyse the path to extract what to browse.
$fullpath = empty($fullpath) ? $this->build_node_path('root') : $fullpath;
$trail = explode('|', $fullpath);
$trail = array_pop($trail);
list($mode, $path, $unused) = $this->explode_node_path($trail);

// Is that a search?
if ($mode === 'search') {
return $this->search($path, $page);
}

// Cleaning up the requested path.
$path = trim($path, '/');
if (!$this->is_in_repository($path)) {
// In case of doubt on the path, reset to default.
$path = '';
}
$abspath = rtrim($this->get_rootpath() . $path, '/') . '/';

// Construct the breadcrumb.
$trail = '';
if ($path !== '') {
$parts = explode('/', $path);
if (count($parts) > 1) {
foreach ($parts as $part) {
if (!empty($part)) {
$trail .= '/' . $part;
$list['path'][] = array('name' => $part, 'path' => $trail);
}
}
} else {
$list['path'][] = array('name' => $path, 'path' => $path);
}
}
$rootpath = $this->get_rootpath();
$abspath = rtrim($rootpath . $path, '/') . '/';

// Retrieve list of files and directories and sort them.
$fileslist = array();
Expand All @@ -117,41 +112,261 @@ public function get_listing($path = '', $page = '') {
core_collator::asort($fileslist, core_collator::SORT_NATURAL);
core_collator::asort($dirslist, core_collator::SORT_NATURAL);

// Fill the $list['list'].
// Fill the results.
foreach ($dirslist as $file) {
$list['list'][] = array(
'title' => $file,
'children' => array(),
'datecreated' => filectime($abspath . $file),
'datemodified' => filemtime($abspath . $file),
'thumbnail' => $OUTPUT->pix_url(file_folder_icon(90))->out(false),
'path' => $path . '/' . $file
);
$list['list'][] = $this->build_node($rootpath, $path, $file, true, $fullpath);
}
foreach ($fileslist as $file) {
$node = array(
'title' => $file,
'source' => $path . '/' . $file,
'size' => filesize($abspath . $file),
'datecreated' => filectime($abspath . $file),
'datemodified' => filemtime($abspath . $file),
'thumbnail' => $OUTPUT->pix_url(file_extension_icon($file, 90))->out(false),
'icon' => $OUTPUT->pix_url(file_extension_icon($file, 24))->out(false)
);
if (file_extension_in_typegroup($file, 'image') && ($imageinfo = @getimagesize($abspath . $file))) {
$list['list'][] = $this->build_node($rootpath, $path, $file, false, $fullpath);
}

$list['path'] = $this->build_breadcrumb($fullpath);
$list['list'] = array_filter($list['list'], array($this, 'filter'));

return $list;
}

/**
* Search files in repository.
*
* This search works by walking through the directories returning the files that match. Once
* the limit of files is reached the walk stops. Whenever more files are requested, the walk
* starts from the beginning until it reaches an additional set of files to return.
*
* @param string $query The query string.
* @param int $page The page number.
* @return mixed
*/
public function search($query, $page = 1) {
global $OUTPUT, $SESSION;

$query = core_text::strtolower($query);
$remainingdirs = 1000;
$remainingobjects = 5000;
$perpage = 50;

// Because the repository API is weird, the first page is 0, but it should be 1.
if (!$page) {
$page = 1;
}

// Initialise the session variable in which we store the search related things.
if (!isset($SESSION->repository_filesystem_search)) {
$SESSION->repository_filesystem_search = array();
}

// Restore, or initialise the session search variables.
if ($page <= 1) {
$SESSION->repository_filesystem_search['query'] = $query;
$SESSION->repository_filesystem_search['from'] = 0;
$from = 0;
} else {
// Yes, the repository does not send the query again...
$query = $SESSION->repository_filesystem_search['query'];
$from = (int) $SESSION->repository_filesystem_search['from'];
}
$limit = $from + $perpage;
$searchpath = $this->build_node_path('search', $query);

// Pre-search initialisation.
$rootpath = $this->get_rootpath();
$found = 0;
$toexplore = array('');

// Retrieve list of matching files and directories.
$matches = array();
while (($path = array_shift($toexplore)) !== null) {
$remainingdirs--;

if ($objects = scandir($rootpath . $path)) {
foreach ($objects as $object) {
$objectabspath = $rootpath . $path . $object;
if ($object == '.' || $object == '..') {
continue;
}

$remainingobjects--;
$isdir = is_dir($objectabspath);

// It is a match!
if (strpos(core_text::strtolower($object), $query) !== false) {
$found++;
$matches[] = array($path, $object, $isdir);

// That's enough, no need to find more.
if ($found >= $limit) {
break 2;
}
}

// I've seen enough files, I give up!
if ($remainingobjects <= 0) {
break 2;
}

// Add the directory to things to explore later.
if ($isdir) {
$toexplore[] = $path . trim($object, '/') . '/';
}
}
}

if ($remainingdirs <= 0) {
break;
}
}

// Extract the results from all the matches.
$matches = array_slice($matches, $from, $perpage);

// If we didn't reach our limits of browsing, and we appear to still have files to find.
if ($remainingdirs > 0 && $remainingobjects > 0 && count($matches) >= $perpage) {
$SESSION->repository_filesystem_search['from'] = $limit;
$pages = -1;

// We reached the end of the repository, or our limits.
} else {
$SESSION->repository_filesystem_search['from'] = 0;
$pages = 0;
}

// Organise the nodes.
$results = array();
foreach ($matches as $match) {
list($path, $name, $isdir) = $match;
$results[] = $this->build_node($rootpath, $path, $name, $isdir, $searchpath);
}

$list = array();
$list['list'] = array_filter($results, array($this, 'filter'));
$list['dynload'] = true;
$list['nologin'] = true;
$list['page'] = $page;
$list['pages'] = $pages;
$list['path'] = $this->build_breadcrumb($searchpath);

return $list;
}

/**
* Build the breadcrumb from a full path.
*
* @param string $path A path generated by {@link self::build_node_path()}.
* @return array
*/
protected function build_breadcrumb($path) {
$breadcrumb = array(array(
'name' => get_string('root', 'repository_filesystem'),
'path' => $this->build_node_path('root')
));

$crumbs = explode('|', $path);
$trail = '';

foreach ($crumbs as $crumb) {
list($mode, $nodepath, $display) = $this->explode_node_path($crumb);
switch ($mode) {
case 'search':
$breadcrumb[] = array(
'name' => get_string('searchresults', 'repository_filesystem'),
'path' => $this->build_node_path($mode, $nodepath, $display, $trail),
);
break;

case 'browse':
$breadcrumb[] = array(
'name' => $display,
'path' => $this->build_node_path($mode, $nodepath, $display, $trail),
);
break;
}

$lastcrumb = end($breadcrumb);
$trail = $lastcrumb['path'];
}

return $breadcrumb;
}

/**
* Build a file or directory node.
*
* @param string $rootpath The absolute path to the repository.
* @param string $path The relative path of the object
* @param string $name The name of the object
* @param string $isdir Is the object a directory?
* @param string $rootnodepath The node leading to this node (for breadcrumb).
* @return array
*/
protected function build_node($rootpath, $path, $name, $isdir, $rootnodepath) {
global $OUTPUT;

$relpath = trim($path, '/') . '/' . $name;
$abspath = $rootpath . $relpath;
$node = array(
'title' => $name,
'datecreated' => filectime($abspath),
'datemodified' => filemtime($abspath),
);

if ($isdir) {
$node['children'] = array();
$node['thumbnail'] = $OUTPUT->pix_url(file_folder_icon(90))->out(false);
$node['path'] = $this->build_node_path('browse', $relpath, $name, $rootnodepath);

} else {
$node['source'] = $relpath;
$node['size'] = filesize($abspath);
$node['thumbnail'] = $OUTPUT->pix_url(file_extension_icon($name, 90))->out(false);
$node['icon'] = $OUTPUT->pix_url(file_extension_icon($name, 24))->out(false);
$node['path'] = $relpath;

if (file_extension_in_typegroup($name, 'image') && ($imageinfo = @getimagesize($abspath))) {
// This means it is an image and we can return dimensions and try to generate thumbnail/icon.
$token = $node['datemodified'] . $node['size']; // To prevent caching by browser.
$node['realthumbnail'] = $this->get_thumbnail_url($path . '/' . $file, 'thumb', $token)->out(false);
$node['realicon'] = $this->get_thumbnail_url($path . '/' . $file, 'icon', $token)->out(false);
$node['realthumbnail'] = $this->get_thumbnail_url($relpath, 'thumb', $token)->out(false);
$node['realicon'] = $this->get_thumbnail_url($relpath, 'icon', $token)->out(false);
$node['image_width'] = $imageinfo[0];
$node['image_height'] = $imageinfo[1];
}
$list['list'][] = $node;
}
$list['list'] = array_filter($list['list'], array($this, 'filter'));
return $list;

return $node;
}

/**
* Build the path to a browsable node.
*
* @param string $mode The type of browse mode.
* @param string $realpath The path, or similar.
* @param string $display The way to display the node.
* @param string $root The path preceding this node.
* @return string
*/
protected function build_node_path($mode, $realpath = '', $display = '', $root = '') {
$path = $mode . ':' . base64_encode($realpath) . ':' . base64_encode($display);
if (!empty($root)) {
$path = $root . '|' . $path;
}
return $path;
}

/**
* Extract information from a node path.
*
* Note, this should not include preceding paths.
*
* @param string $path The path of the node.
* @return array Contains the mode, the relative path, and the display text.
*/
protected function explode_node_path($path) {
list($mode, $realpath, $display) = explode(':', $path);
return array(
$mode,
base64_decode($realpath),
base64_decode($display)
);
}

/**
* To check whether the user is logged in.
Expand Down

0 comments on commit 8ea783f

Please sign in to comment.