From 5bdf63cce094e1e5cbf5bde056192934530e96ed Mon Sep 17 00:00:00 2001 From: Marina Glancy Date: Tue, 17 Apr 2012 14:20:20 +0800 Subject: [PATCH] MDL-14636: Support table view in Filepicker, provide more info about files - Added table view in Filepicker where for each file we show Name, Last modified, Size and Type. Sortable by column - Preprocess list of files returned from repositories to include formatted size, dates, type and also add filetype icon - Make sure that local repositories (coursefiles, filesystem, local, recent, user) return information about file size, datemodified, datecreated, author, license --- lang/en/repository.php | 1 + lib/outputrequirementslib.php | 3 +- repository/coursefiles/lib.php | 11 +- repository/filepicker.js | 124 +++++++++++++----- .../lang/en/repository_filesystem.php | 1 + repository/filesystem/lib.php | 7 +- repository/lib.php | 55 +++++++- repository/local/lib.php | 8 +- repository/recent/lib.php | 18 ++- repository/renderer.php | 19 +++ repository/repository_ajax.php | 4 +- repository/user/lib.php | 11 +- 12 files changed, 205 insertions(+), 57 deletions(-) diff --git a/lang/en/repository.php b/lang/en/repository.php index 2af26a974ea..ac8bebe2ede 100644 --- a/lang/en/repository.php +++ b/lang/en/repository.php @@ -179,6 +179,7 @@ $string['submit'] = 'Submit'; $string['sync'] = 'Sync'; $string['thumbview'] = 'View as icons'; $string['title'] = 'Choose a file...'; +$string['type'] = 'Type'; $string['typenotvisible'] = 'Type not visible'; $string['upload'] = 'Upload this file'; $string['uploading'] = 'Uploading...'; diff --git a/lib/outputrequirementslib.php b/lib/outputrequirementslib.php index f8f0167cff7..600f95608b2 100644 --- a/lib/outputrequirementslib.php +++ b/lib/outputrequirementslib.php @@ -439,7 +439,7 @@ class page_requirements_manager { case 'core_filepicker': $module = array('name' => 'core_filepicker', 'fullpath' => '/repository/filepicker.js', - 'requires' => array('base', 'node', 'node-event-simulate', 'json', 'async-queue', 'io-base', 'io-upload-iframe', 'io-form', 'yui2-menu', 'yui2-treeview', 'yui2-dragdrop', 'panel', 'cookie'), + 'requires' => array('base', 'node', 'node-event-simulate', 'json', 'async-queue', 'io-base', 'io-upload-iframe', 'io-form', 'yui2-menu', 'yui2-treeview', 'yui2-dragdrop', 'panel', 'cookie', 'datatable', 'datatable-sort'), // TODO check if those all are really required 'strings' => array(array('add', 'repository'), array('back', 'repository'), array('cancel', 'moodle'), array('close', 'repository'), array('cleancache', 'repository'), array('copying', 'repository'), array('date', 'repository'), array('downloadsucc', 'repository'), @@ -459,6 +459,7 @@ class page_requirements_manager { array('renameto', 'repository'), array('fileexists', 'repository'), array('fileexistsdialogheader', 'repository'), array('fileexistsdialog_editor', 'repository'), array('fileexistsdialog_filemanager', 'repository'), array('uselatestfile', 'repository') + array('lastmodified', 'moodle'), array('name', 'moodle'), array('type', 'repository') )); break; case 'core_comment': diff --git a/repository/coursefiles/lib.php b/repository/coursefiles/lib.php index 6cf0622c3dc..91f94ce32ef 100644 --- a/repository/coursefiles/lib.php +++ b/repository/coursefiles/lib.php @@ -105,8 +105,8 @@ class repository_coursefiles extends repository { $encodedpath = base64_encode(serialize($params)); $node = array( 'title' => $child->get_visible_name(), - 'size' => 0, - 'date' => '', + 'datemodified' => $child->get_timemodified(), + 'datecreated' => $child->get_timecreated(), 'path' => $encodedpath, 'children'=>array(), 'thumbnail' => $OUTPUT->pix_url('f/folder-32')->out(false) @@ -116,8 +116,11 @@ class repository_coursefiles extends repository { $encodedpath = base64_encode(serialize($child->get_params())); $node = array( 'title' => $child->get_visible_name(), - 'size' => 0, - 'date' => '', + 'size' => $child->get_filesize(), + 'author' => $child->get_author(), + 'license' => $child->get_license(), + 'datemodified' => $child->get_timemodified(), + 'datecreated' => $child->get_timecreated(), 'source'=> $encodedpath, 'thumbnail' => $OUTPUT->pix_url(file_extension_icon($child->get_visible_name(), 32))->out(false) ); diff --git a/repository/filepicker.js b/repository/filepicker.js index 657da8a2b62..895496dd62d 100644 --- a/repository/filepicker.js +++ b/repository/filepicker.js @@ -350,18 +350,18 @@ M.core_filepicker.init = function(Y, options) { build_tree: function(node, level) { var client_id = this.options.client_id; var dynload = this.active_repo.dynload; - var info = { - label:node.title, - filename:node.title, - source:node.source?node.source:'', - thumbnail:node.thumbnail, - path:node.path?node.path:'' - }; - if (node.dynamicLoadComplete) { - info.dynamicLoadComplete = true; + // prepare file name with icon + var el = Y.Node.create('
').setContent(M.core_filepicker.templates.listfilename); + el.one('.fp-filename').setContent(node.title); + if (node.icon && !node.children) { + el.one('.fp-icon').appendChild(Y.Node.create('').set('src', node.icon)); } - var tmpNode = new YAHOO.widget.TextNode(info, level, false); - tmpNode.repo_id = node.repo_id ? node.repo_id : this.active_repo.id; + // create node + var tmpNode = new YAHOO.widget.HTMLNode(el.getContent(), level, false); + if (node.dynamicLoadComplete) { + tmpNode.dynamicLoadComplete = true; + } + tmpNode.fileinfo = node; tmpNode.isLeaf = node.children ? false : true; if (!tmpNode.isLeaf) { if(node.expanded) { @@ -370,8 +370,7 @@ M.core_filepicker.init = function(Y, options) { if (dynload) { tmpNode.scope = this; } - tmpNode.client_id = client_id; - tmpNode.path = info.path; + tmpNode.path = node.path ? node.path : ''; for(var c in node.children) { this.build_tree(node.children[c], tmpNode); } @@ -385,10 +384,10 @@ M.core_filepicker.init = function(Y, options) { } else { this.viewbar_set_enabled(true); this.print_path(); - if (this.viewmode == 1) { - this.view_as_icons(); - } else if (this.viewmode == 2) { + if (this.viewmode == 2) { this.view_as_list(); + } else if (this.viewmode == 3) { + this.view_as_table(); } else { this.view_as_icons(); } @@ -446,7 +445,7 @@ M.core_filepicker.init = function(Y, options) { var dynload = scope.active_repo.dynload; var list = this.filelist; scope.viewmode = 2; - if (list && list.length==0 && (!this.filepath || !this.filepath.length)) { + if (!list || list.length==0 && (!this.filepath || !this.filepath.length)) { this.display_error(M.str.repository.nofilesavailable, 'nofilesavailable'); return; } @@ -485,7 +484,10 @@ M.core_filepicker.init = function(Y, options) { var root = scope.treeview.getRoot(); while (root && root.children && root.children.length) { root = root.children[0]; - if (!root.isLeaf && root.expanded && root.path != mytreeel.path) { + if (root.path == mytreeel.path) { + root.origpath = scope.filepath; + root.origlist = scope.filelist; + } else if (!root.isLeaf && root.expanded) { scope.treeview_dynload(root, null); } } @@ -498,18 +500,14 @@ M.core_filepicker.init = function(Y, options) { } scope.treeview.subscribe('clickEvent', function(e){ if(e.node.isLeaf){ - var fileinfo = {}; - fileinfo['title'] = e.node.data.filename; - fileinfo['source'] = e.node.data.source; - fileinfo['thumbnail'] = e.node.data.thumbnail; if (e.node.parent && e.node.parent.origpath) { // set the current path scope.filepath = e.node.parent.origpath; scope.filelist = e.node.parent.origlist; scope.print_path(); } - e.node.highlight(false) - scope.select_file(fileinfo); + e.node.highlight(false); + scope.select_file(e.node.fileinfo); } else { // save current path and filelist (in case we want to jump to other viewmode) scope.filepath = e.node.origpath; @@ -524,7 +522,7 @@ M.core_filepicker.init = function(Y, options) { var list = this.filelist; this.viewmode = 1; - if (list && list.length==0) { + if (!list || list.length==0) { this.display_error(M.str.repository.nofilesavailable, 'nofilesavailable'); return; } @@ -576,28 +574,84 @@ M.core_filepicker.init = function(Y, options) { element.on('click', function(e, p) { e.preventDefault(); if(dynload) { - var params = {'path':p.path}; - scope.list(params); + scope.list({'path':p.path}); }else{ + this.filepath = p.path; this.filelist = p.children; this.view_files(); } }, this, node); } else { - var fileinfo = {}; - fileinfo['title'] = list[k].title; - fileinfo['source'] = list[k].source; - fileinfo['thumbnail'] = list[k].thumbnail; - fileinfo['haslicense'] = list[k].haslicense?true:false; - fileinfo['hasauthor'] = list[k].hasauthor?true:false; element.on('click', function(e, args) { e.preventDefault(); this.select_file(args); - }, this, fileinfo); + }, this, list[k]); } count++; } }, + view_as_table: function() { + var list = this.filelist; + var client_id = this.options.client_id; + this.viewmode = 3; + + if (!list || list.length==0) { + this.display_error(M.str.repository.nofilesavailable, 'nofilesavailable'); + return; + } + var treeviewnode = Y.Node.create('
'). + setAttrs({'class':'fp-tableview', id:'tableview-'+client_id}); + this.fpnode.one('.fp-content').setContent('').appendChild(treeviewnode); + var formatValue = function (o){ + if (o.data[''+o.field+'_f_s']) { return o.data[''+o.field+'_f_s']; } + else if (o.data[''+o.field+'_f']) { return o.data[''+o.field+'_f']; } + else if (o.value) { return o.value; } + else { return ''; } + }; + var formatTitle = function(o) { + var el = Y.Node.create('
').setContent(M.core_filepicker.templates.listfilename); + el.one('.fp-filename').setContent(o.value); + el.one('.fp-icon').appendChild(Y.Node.create('').set('src', o.data['icon'])); + return el.getContent(); + } + + var cols = [ + {key: "title", label: M.str.moodle.name, sortable: true, formatter: formatTitle}, + {key: "datemodified", label: M.str.moodle.lastmodified, sortable: true, formatter: formatValue}, + {key: "size", label: M.str.repository.size, sortable: true, formatter: formatValue}, + {key: "type", label: M.str.repository.type, sortable: true} + ]; + var table = new Y.DataTable.Base({ + columnset: cols, + recordset: list + }); + // TODO allow sorting only if repository allows it, remember last sort order + table.plug(Y.Plugin.DataTableSort/*, { lastSortedBy: {key: "title", dir: "asc"} }*/); + + table.render('#tableview-'+client_id); + table.delegate('click', function (e) { + var rs = table.get('recordset'); + var id = e.currentTarget.get('id'), len = rs.size(), i; + for (i = 0; i < len; ++i) { + var record = rs.item(i); + if (record.get('id') == id) { + // we found the clicked record + var data = record.getValue(); + if (data.children) { + if (this.active_repo.dynload) { + this.list({'path':data.path}); + } else { + this.filepath = data.path; + this.filelist = data.children; + this.view_files(); + } + } else { + this.select_file(data); + } + } + } + }, 'tr', this); + }, select_file: function(args) { this.selectui.show(); var client_id = this.options.client_id; @@ -1391,7 +1445,7 @@ M.core_filepicker.init = function(Y, options) { this.viewbar_set_enabled(false); var repository_id = Y.Cookie.get('recentrepository'); this.viewmode = Y.Cookie.get('recentviewmode', Number); - if (this.viewmode != 1 && this.viewmode != 2 && this.viewmode != 3) { + if (this.viewmode != 2 && this.viewmode != 3) { this.viewmode = 1; } if (this.options.repositories[repository_id]) { diff --git a/repository/filesystem/lang/en/repository_filesystem.php b/repository/filesystem/lang/en/repository_filesystem.php index 16031379bb2..1a1de4a5262 100644 --- a/repository/filesystem/lang/en/repository_filesystem.php +++ b/repository/filesystem/lang/en/repository_filesystem.php @@ -30,6 +30,7 @@ $string['filesystem:view'] = 'View file system repository'; $string['information'] = 'These folders are within the {$a} directory.'; $string['invalidpath'] = 'Invalid root path'; $string['path'] = 'Select a subdirectory'; +$string['root'] = 'Root'; $string['nosubdir'] = 'You need to create at least one folder inside the {$a} directory so you can select it here.'; $string['pluginname_help'] = 'Create repository from local directory'; $string['pluginname'] = 'File system'; diff --git a/repository/filesystem/lib.php b/repository/filesystem/lib.php index 955b2785c0e..8dcfe52316b 100644 --- a/repository/filesystem/lib.php +++ b/repository/filesystem/lib.php @@ -70,7 +70,7 @@ class repository_filesystem extends repository { $list['list'] = array(); // process breacrumb trail $list['path'] = array( - array('name'=>'Root', 'path'=>'') + array('name'=>get_string('root', 'repository_filesystem'), 'path'=>'') ); $trail = ''; if (!empty($path)) { @@ -99,7 +99,8 @@ class repository_filesystem extends repository { 'title' => $file, 'source' => $path.'/'.$file, 'size' => filesize($this->root_path.$file), - 'date' => time(), + 'datecreated' => filectime($this->root_path.$file), + 'datemodified' => filemtime($this->root_path.$file), 'thumbnail' => $OUTPUT->pix_url(file_extension_icon($this->root_path.$file, 32))->out(false) ); } else { @@ -111,6 +112,8 @@ class repository_filesystem extends repository { $list['list'][] = array( 'title' => $file, 'children' => array(), + 'datecreated' => filectime($this->root_path.$file), + 'datemodified' => filemtime($this->root_path.$file), 'thumbnail' => $OUTPUT->pix_url('f/folder-32')->out(false), 'path' => $current_path ); diff --git a/repository/lib.php b/repository/lib.php index 85cfb10153f..b386394adae 100644 --- a/repository/lib.php +++ b/repository/lib.php @@ -1780,7 +1780,60 @@ abstract class repository { } /** - * Search files in repository. + * Prepares list of files before passing it to AJAX, makes sure data is in the correct + * format and stores formatted dates. + * + * @param array $listing result of get_listing() or search() + * @return array + */ + public static function prepare_listing($listing) { + global $OUTPUT; + if (!is_array($listing) || !isset($listing['list'])) { + return $listing; + } + $len = count($listing['list']); + for ($i=0; $i<$len; $i++) { + if (isset($listing['list'][$i]['size'])) { + $listing['list'][$i]['size'] = (int)$listing['list'][$i]['size']; + $listing['list'][$i]['size_f'] = display_size($listing['list'][$i]['size']); + } + foreach (array('date', 'datemodified', 'datecreated') as $key) { + if (!isset($listing['list'][$i][$key]) && isset($listing['list'][$i]['date'])) { + $listing['list'][$i][$key] = $listing['list'][$i]['date']; + } + if (isset($listing['list'][$i][$key])) { + // must be UNIX timestamp + $listing['list'][$i][$key] = (int)$listing['list'][$i][$key]; + if (!$listing['list'][$i][$key]) { + unset($listing['list'][$i][$key]); + } else { + $listing['list'][$i][$key.'_f'] = userdate($listing['list'][$i][$key], get_string('strftimedatetime', 'langconfig')); + $listing['list'][$i][$key.'_f_s'] = userdate($listing['list'][$i][$key], get_string('strftimedatetimeshort', 'langconfig')); + } + } + } + if (!isset($listing['list'][$i]['type']) && !array_key_exists('children', $listing['list'][$i]) && isset($listing['list'][$i]['title'])) { + $mimetype = mimeinfo('type', $listing['list'][$i]['title']); + if (get_string_manager()->string_exists($mimetype, 'mimetypes')) { + $mimetype = get_string($mimetype, 'mimetypes'); + } + $listing['list'][$i]['type'] = $mimetype; + } + if (!isset($listing['list'][$i]['icon']) && isset($listing['list'][$i]['title'])) { + if (array_key_exists('children', $listing['list'][$i])) { + $listing['list'][$i]['icon'] = $OUTPUT->pix_url('f/folder')->out(false); + } else { + $listing['list'][$i]['icon'] = $OUTPUT->pix_url('f/'.mimeinfo('icon', $listing['list'][$i]['title']))->out(false); + } + } + } + return $listing; + } + + /** + * Search files in repository + * When doing global search, $search_text will be used as + * keyword. * * @param string $search_text search key word * @param int $page page diff --git a/repository/local/lib.php b/repository/local/lib.php index f31317e184f..6a5ca884d47 100644 --- a/repository/local/lib.php +++ b/repository/local/lib.php @@ -208,13 +208,17 @@ class repository_local_file { $encodedpath = base64_encode(serialize($this->fileinfo->get_params())); $node = array( 'title' => $this->fileinfo->get_visible_name(), - 'size' => 0, - 'date' => ''); + 'datemodified' => $this->fileinfo->get_timemodified(), + 'datecreated' => $this->fileinfo->get_timecreated() + ); if ($this->isdir) { $node['path'] = $encodedpath; $node['thumbnail'] = $OUTPUT->pix_url('f/folder-32')->out(false); $node['children'] = array(); } else { + $node['size'] = $this->fileinfo->get_filesize(); + $node['author'] = $this->fileinfo->get_author(); + $node['license'] = $this->fileinfo->get_license(); $node['source'] = $encodedpath; $node['thumbnail'] = $OUTPUT->pix_url(file_extension_icon($node['title'], 32))->out(false); } diff --git a/repository/recent/lib.php b/repository/recent/lib.php index 05f739b1189..d1f5bbaca9d 100644 --- a/repository/recent/lib.php +++ b/repository/recent/lib.php @@ -115,14 +115,20 @@ class repository_recent extends repository { try { foreach ($files as $file) { - $params = base64_encode(serialize($file)); - // Check that file exists and accessible - $filesize = $this->get_file_size($params); - if (!empty($filesize)) { + // Check that file exists and accessible, retrieve size/date info + $browser = get_file_browser(); + $context = get_context_instance_by_id($file['contextid']); + $fileinfo = $browser->get_file_info($context, $file['component'], + $file['filearea'], $file['itemid'], $file['filepath'], $file['filename']); + if ($fileinfo) { + $params = base64_encode(serialize($file)); $node = array( 'title' => $file['filename'], - 'size' => $filesize, - 'date' => '', + 'size' => $fileinfo->get_filesize(), + 'datemodified' => $fileinfo->get_timemodified(), + 'datecreated' => $fileinfo->get_timecreated(), + 'author' => $fileinfo->get_author(), + 'license' => $fileinfo->get_license(), 'source'=> $params, 'thumbnail' => $OUTPUT->pix_url(file_extension_icon($file['filename'], 32))->out(false), ); diff --git a/repository/renderer.php b/repository/renderer.php index f44beba98f1..c023f7cc180 100644 --- a/repository/renderer.php +++ b/repository/renderer.php @@ -144,6 +144,25 @@ class core_repository_renderer extends plugin_renderer_base { return preg_replace('/\{\!\}/', '', $rv); } + /** + * Template for displaying file name in 'table view' and 'tree view' modes. + * + * content of the element with class 'fp-icon' will be replaced with an appropriate img; + * + * content of element with class 'fp-filename' will be replaced with filename supplied by + * repository; + * + * Note that tree view and table view are the YUI widgets and therefore there are no + * other templates. The widgets will be wrapped in
with class fp-treeview or + * fp-tableview (respectfully). + * + * @return string + */ + public function js_template_listfilename() { + $rv = ' '; + return preg_replace('/\{\!\}/', '', $rv); + } + /** * Template for window appearing to select a file. * diff --git a/repository/repository_ajax.php b/repository/repository_ajax.php index f581a13ab5f..56a482a2964 100644 --- a/repository/repository_ajax.php +++ b/repository/repository_ajax.php @@ -142,7 +142,7 @@ switch ($action) { case 'signin': case 'list': if ($repo->check_login()) { - $listing = $repo->get_listing($req_path, $page); + $listing = repository::prepare_listing($repo->get_listing($req_path, $page)); $listing['repo_id'] = $repo_id; echo json_encode($listing); break; @@ -166,7 +166,7 @@ switch ($action) { echo json_encode($search_form); break; case 'search': - $search_result = $repo->search($search_text, (int)$page); + $search_result = repository::prepare_listing($repo->search($search_text, (int)$page)); $search_result['repo_id'] = $repo_id; $search_result['issearchresult'] = true; echo json_encode($search_result); diff --git a/repository/user/lib.php b/repository/user/lib.php index 1f06ddd28ea..d0a07372bc7 100644 --- a/repository/user/lib.php +++ b/repository/user/lib.php @@ -96,8 +96,8 @@ class repository_user extends repository { $encodedpath = base64_encode(serialize($child->get_params())); $node = array( 'title' => $child->get_visible_name(), - 'size' => 0, - 'date' => '', + 'datemodified' => $child->get_timemodified(), + 'datecreated' => $child->get_timecreated(), 'path' => $encodedpath, 'children'=>array(), 'thumbnail' => $OUTPUT->pix_url('f/folder-32')->out(false) @@ -107,8 +107,11 @@ class repository_user extends repository { $encodedpath = base64_encode(serialize($child->get_params())); $node = array( 'title' => $child->get_visible_name(), - 'size' => 0, - 'date' => '', + 'size' => $child->get_filesize(), + 'datemodified' => $child->get_timemodified(), + 'datecreated' => $child->get_timecreated(), + 'author' => $child->get_author(), + 'license' => $child->get_license(), 'source'=> $encodedpath, 'thumbnail' => $OUTPUT->pix_url(file_extension_icon($child->get_visible_name(), 32))->out(false) );