MDL-31901: Allow FileManager to re-use FilePicker JS code for view modes:

- when loading core_filepicker we extend the Node element with functions necessary to display files list in different view modes;
- repository/filepicker.js is changed to use those functions;
- FileManager now has three different view modes (icon view, tree view and table view), JS code is re-used from FilePicker;
- files in FileManager no longer have context menu, they have one popup Widget with different actions instead;
- Added more templates for JS code to FileManager, use similar CSS class names as FilePicker;
- lib/filelib.php : fixed double slashes in path, return more data about files;
- lib/form/filemanager.php : pass information to FileManager about available licenses and default author;
- repository/draftfiles_ajax.php : return information about directory tree for file moving UI in FileManager, return formatted information;
- repository/lib.php : function repository::prepare_listing() now can work also with draftarea files listing;
This commit is contained in:
Marina Glancy 2012-05-02 14:59:09 +08:00
parent d1d186917c
commit e709ddd29c
11 changed files with 1110 additions and 792 deletions

View File

@ -116,9 +116,7 @@ class core_files_renderer extends plugin_renderer_base {
if (empty($filemanagertemplateloaded)) {
$filemanagertemplateloaded = true;
$this->page->requires->js_init_call('M.form_filemanager.set_templates',
array(array(
'onefile' => '___fullname___ ___action___'
)), true, $module);
array($this->filemanager_js_templates()), true, $module);
}
$this->page->requires->js_init_call('M.form_filemanager.init', array($fm->options), true, $module);
@ -156,11 +154,21 @@ class core_files_renderer extends plugin_renderer_base {
* If browser supports Drag-and-drop, the body element will have class 'dndsupported',
* otherwise - 'dndnotsupported';
*
* Element with class 'fm-filelist' will be populated with files list;
* Element with class 'fm-breadcrumb' will be populated with the path or have class 'fm-empty' when empty;
* Element with class 'fm-btn-add' will hold onclick event for adding a file (opening filepicker);
* Element with class 'fm-btn-mkdir' will hold onclick event for adding new folder;
* Element with class 'fm-btn-download' will hold onclick event for download action;
* Element with class 'fp-content' will be populated with files list;
* Element with class 'fp-btn-add' will hold onclick event for adding a file (opening filepicker);
* Element with class 'fp-btn-mkdir' will hold onclick event for adding new folder;
* Element with class 'fp-btn-download' will hold onclick event for download action;
*
* Element with class 'fp-path-folder' is a template for one folder in path toolbar.
* It will hold mouse click event and will be assigned classes first/last/even/odd respectfully.
* Parent element will receive class 'empty' when there are no folders to be displayed;
* The content of subelement with class 'fp-path-folder-name' will be substituted with folder name;
*
* Element with class 'fp-viewbar' will have the class 'enabled' or 'disabled' when view mode
* can be changed or not;
* Inside element with class 'fp-viewbar' there are expected elements with classes
* 'fp-vb-icons', 'fp-vb-tree' and 'fp-vb-details'. They will handle onclick events to switch
* between the view modes, the last clicked element will have the class 'checked';
*
* @param form_filemanager $fm
* @return string
@ -181,30 +189,144 @@ class core_files_renderer extends plugin_renderer_base {
$strdndenabledinbox = get_string('dndenabled_inbox', 'moodle');
$loading = get_string('loading', 'repository');
$html .= <<<FMHTML
<div id="filemanager-{$client_id}" class="filemanager fm-loading">
<div class="filemanager-loading mdl-align">{$icon_progress}</div>
<div class="fm-breadcrumb"></div>
$html = '
<div id="filemanager-'.$client_id.'" class="filemanager fm-loading">
<div class="filemanager-loading mdl-align">'.$icon_progress.'</div>
<div class="fp-pathbar">
<span class="{!}fp-path-folder"><a class="{!}fp-path-folder-name"></a><span>/</span></span>
</div>
<div class="filemanager-toolbar">
<input type="button" class="fm-btn-add" value="{$straddfile}" />
<input type="button" class="fm-btn-mkdir" value="{$strmakedir}" />
<input type="button" class="fm-btn-download" value="{$strdownload}" />
{$restrictions}
<span class="dndupload-message"> - $strdndenabled </span>
<input type="button" class="{!}fp-btn-add" value="'.$straddfile.'" />
<input type="button" class="{!}fp-btn-mkdir" value="'.$strmakedir.'" />
<input type="button" class="{!}fp-btn-download" value="'.$strdownload.'" />
'.$restrictions.'
<span class="dndupload-message"> - '.$strdndenabled.' </span>
<div class="{!}fp-viewbar" style="float:none;">
<span class=""><button class="{!}fp-vb-icons">'.get_string('iconview', 'repository').'</button></span>
<span class=""><button class="{!}fp-vb-tree">'.get_string('listview', 'repository').'</button></span>
<span class=""><button class="{!}fp-vb-details">'.get_string('detailview', 'repository').'</button></span>
</div>
</div>
<div class="filemanager-container" >
<ul class="fm-filelist"></ul>
<div class="fm-empty-container mdl-align">{$strnofilesattached}
<span class="dndupload-message">{$strdndenabledinbox}</span>
<div class="fm-content-wrapper">
<ul class="fp-content"></ul>
<div class="fm-empty-container mdl-align">'.$strnofilesattached.'
<span class="dndupload-message">'.$strdndenabledinbox.'</span>
</div>
<div class="dndupload-target">'.$strdroptoupload.'</div>
<div class="dndupload-uploadinprogress">'.$icon_progress.'</div>
</div>
<div class="dndupload-target">{$strdroptoupload}</div>
<div class="dndupload-uploadinprogress">{$icon_progress}</div>
<div class="filemanager-updating">{$icon_progress}</div>
<div class="filemanager-updating">'.$icon_progress.'</div>
</div>
</div>
<div class="clearer"></div>
FMHTML;
return $html;
';
return preg_replace('/\{\!\}/', '', $html);
}
/**
* FileManager JS template for displaying one file in 'icon view' mode.
*
* @see fp_js_template_iconfilename()
* @return string
*/
private function fm_js_template_iconfilename() {
return $this->fp_js_template_iconfilename();
}
/**
* FileManager JS template for displaying file name in 'table view' and 'tree view' modes.
*
* @see fp_js_template_listfilename()
* @return string
*/
private function fm_js_template_listfilename() {
return $this->fp_js_template_listfilename();
}
/**
* FileManager JS template for window with file information/actions.
*
* All content must be enclosed in an element with class 'fp-select', CSS for this class
* must define width and height of the window;
*
* Thumbnail image will be added as content to the element with class 'fp-thumbnail';
*
* Inside the window the elements with the following classnames must be present:
* 'fp-saveas', 'fp-author', 'fp-license', 'fp-path'. Inside each of them must be
* one input element (or select in case of fp-license and fp-path). They may also have labels.
* The elements will be assign with class 'uneditable' and input/select element will become
* disabled if they are not applicable for the particular file;
*
* There may be present elements with classes 'fp-origpath', 'fp-datemodified', 'fp-datecreated',
* 'fp-size', 'fp-dimensions'. They will receive additional class 'fp-unknown' if information
* is unavailable. If there is information available, the content of embedded element
* with class 'fp-value' will be substituted with the value;
*
* Elements with classes 'fp-file-update', 'fp-file-download', 'fp-file-delete', 'fp-file-zip',
* 'fp-file-unzip', 'fp-file-setmain' and 'fp-file-cancel' will hold corresponding onclick
* events (there may be several elements with class 'fp-file-cancel');
*
* When confirm button is pressed and file is being selected, the top element receives
* additional class 'loading'. It is removed when response from server is received.
*
* When any of the input fields is changed, the top element receives class 'fp-changed';
* When current file can be set as main - top element receives class 'fp-cansetmain';
* When current file is folder/zip/file - top element receives respectfully class
* 'fp-folder'/'fp-zip'/'fp-file';
*
* @return string
*/
private function fm_js_template_fileselectlayout() {
$rv = '<div class="{!}fp-select">
<div class="fp-select-loading">
<img src="'.$this->pix_url('i/loading').'" />
<p>'.get_string('loading', 'repository').'</p>
</div>
<form>
<p class="{!}fp-thumbnail"></p>
<table width="100%">
<tr class="{!}fp-saveas"><td class="mdl-right"><label>'.get_string('name', 'moodle').'</label>:</td>
<td class="mdl-left"><input type="text"/></td></tr>
<tr class="{!}fp-author"><td class="mdl-right"><label>'.get_string('author', 'repository').'</label>:</td>
<td class="mdl-left"><input type="text" /></td></tr>
<tr class="{!}fp-license"><td class="mdl-right"><label>'.get_string('chooselicense', 'repository').'</label>:</td>
<td class="mdl-left"><select></select></td></tr>
<tr class="{!}fp-path"><td class="mdl-right"><label>'.get_string('path', 'moodle').'</label>:</td>
<td class="mdl-left"><select></select></td></tr>
<tr class="{!}fp-origpath"><td class="mdl-right"><label>'.get_string('originalpath', 'moodle').'</label>:</td>
<td class="mdl-left"><span class="fp-value"/></td></tr>
</table>
<p><button class="{!}fp-file-update" >'.get_string('update', 'moodle').'</button>
<button class="{!}fp-file-download" >'.get_string('download').'</button>
<button class="{!}fp-file-delete" >'.get_string('delete').'</button>
<button class="{!}fp-file-zip" >'.get_string('zip', 'editor').'</button>
<button class="{!}fp-file-unzip" >'.get_string('unzip').'</button>
<button class="{!}fp-file-setmain" >'.get_string('setmainfile', 'repository').'</button>
<button class="{!}fp-file-cancel" >'.get_string('cancel').'</button>
</p>
</form>
<div class="{!}fp-datemodified">'.get_string('lastmodified', 'moodle').': <span class="fp-value"/></div>
<div class="{!}fp-datecreated">'.get_string('datecreated', 'repository').': <span class="fp-value"/></div>
<div class="{!}fp-size">'.get_string('size', 'repository').': <span class="fp-value"/></div>
<div class="{!}fp-dimensions">'.get_string('dimensions', 'repository').': <span class="fp-value"/></div>
</div>';
return preg_replace('/\{\!\}/', '', $rv);
}
/**
* Returns all FileManager JavaScript templates as an array.
*
* @return array
*/
public function filemanager_js_templates() {
$class_methods = get_class_methods($this);
$templates = array();
foreach ($class_methods as $method_name) {
if (preg_match('/^fm_js_template_(.*)$/', $method_name, $matches))
$templates[$matches[1]] = $this->$method_name();
}
return $templates;
}
/**
@ -251,10 +373,10 @@ FMHTML;
*
* Element with class 'fp-paging' will contain page navigation (will be deprecated soon);
*
* Element with class 'fp-path-folder' will contain template for one folder in path toolbar.
* Element with class 'fp-path-folder' is a template for one folder in path toolbar.
* It will hold mouse click event and will be assigned classes first/last/even/odd respectfully.
* The content of element with class 'fp-path-folder-name' will be substituted with folder name;
* Parent element will receive class 'empty' when there are no folders to be displayed;
* The content of subelement with class 'fp-path-folder-name' will be substituted with folder name;
*
* Element with class 'fp-toolbar' will have class 'empty' if all 'Back', 'Search', 'Refresh',
* 'Logout', 'Manage' and 'Help' are unavailable for this repo;
@ -310,10 +432,7 @@ FMHTML;
}
/**
* FilePicker JS template for displaying list of files in 'icon view' mode.
*
* Element with class 'fp-file' is a template for displaying one file and indicates a place
* where files shall be output. It also will hold mouse events (click, over, out, etc.);
* FilePicker JS template for displaying one file in 'icon view' mode.
*
* the element with class 'fp-thumbnail' will be resized to the repository thumbnail size
* (both width and height, unless min-width and/or min-height is set in CSS) and the content of
@ -323,15 +442,17 @@ FMHTML;
* (unless min-width is set in css) and the content of an element will be replaced with filename
* supplied by repository;
*
* top element(s) will have class fp-folder if the element is a folder;
*
* List of files will have parent <div> element with class 'fp-iconview'
*
* @return string
*/
private function fp_js_template_iconview() {
$rv = '<div class="fp-iconview">
<div class="{!}fp-file">
private function fp_js_template_iconfilename() {
$rv = '<div class="fp-file">
<div class="{!}fp-thumbnail"></div>
<div class="{!}fp-filename"></div>
</div>
</div>';
</div>';
return preg_replace('/\{\!\}/', '', $rv);
}
@ -343,6 +464,8 @@ FMHTML;
* content of element with class 'fp-filename' will be replaced with filename supplied by
* repository;
*
* top element(s) will have class fp-folder if the element is a folder;
*
* Note that tree view and table view are the YUI widgets and therefore there are no
* other templates. The widgets will be wrapped in <div> with class fp-treeview or
* fp-tableview (respectfully).
@ -350,7 +473,7 @@ FMHTML;
* @return string
*/
private function fp_js_template_listfilename() {
$rv = '<span class="{!}fp-icon"></span> <span class="{!}fp-filename"></span>';
$rv = '<span><span class="{!}fp-icon"></span> <span class="{!}fp-filename"></span></span>';
return preg_replace('/\{\!\}/', '', $rv);
}
@ -379,7 +502,7 @@ FMHTML;
}
/**
* Template for window appearing to select a file.
* FilePicker JS template for window appearing to select a file.
*
* All content must be enclosed in an element with class 'fp-select', CSS for this class
* must define width and height of the window;
@ -393,9 +516,9 @@ FMHTML;
* disabled if they are not applicable for the particular file;
*
* There may be present elements with classes 'fp-datemodified', 'fp-datecreated', 'fp-size',
* 'fp-license', 'fp-author'. They will receive additional class 'fp-unknown' if information
* is unavailable. If there is information available, the content of embedded element
* with class 'fp-value' will be substituted with the value;
* 'fp-license', 'fp-author', 'fp-dimensions'. They will receive additional class 'fp-unknown'
* if information is unavailable. If there is information available, the content of embedded
* element with class 'fp-value' will be substituted with the value;
*
* Elements with classes 'fp-select-confirm' and 'fp-select-cancel' will hold corresponding
* onclick events;

View File

@ -1242,6 +1242,7 @@ $string['olduserdirectory'] = 'This is the OLD users directory, and is no longer
$string['opentoguests'] = 'Guest access';
$string['optional'] = 'optional';
$string['order'] = 'Order';
$string['originalpath'] = 'Original path';
$string['orphanedactivities'] = 'Orphaned activities';
$string['other'] = 'Other';
$string['outline'] = 'Outline';

View File

@ -566,13 +566,13 @@ function file_get_drafarea_files($draftitemid, $filepath = '/') {
$data->path[] = array('name'=>get_string('files'), 'path'=>'/');
// will be used to build breadcrumb
$trail = '';
$trail = '/';
if ($filepath !== '/') {
$filepath = file_correct_filepath($filepath);
$parts = explode('/', $filepath);
foreach ($parts as $part) {
if ($part != '' && $part != null) {
$trail .= ('/'.$part.'/');
$trail .= ($part.'/');
$data->path[] = array('name'=>$part, 'path'=>$trail);
}
}
@ -587,11 +587,16 @@ function file_get_drafarea_files($draftitemid, $filepath = '/') {
$item->filepath = $file->get_filepath();
$item->fullname = trim($item->filename, '/');
$filesize = $file->get_filesize();
$item->size = $filesize ? $filesize : null;
$item->filesize = $filesize ? display_size($filesize) : '';
$icon = mimeinfo_from_type('icon', $file->get_mimetype());
$item->icon = $OUTPUT->pix_url('f/' . $icon)->out();
$item->sortorder = $file->get_sortorder();
$item->author = $file->get_author();
$item->license = $file->get_license();
$item->datemodified = $file->get_timemodified();
$item->datecreated = $file->get_timecreated();
if ($icon == 'zip') {
$item->type = 'zip';
@ -605,9 +610,11 @@ function file_get_drafarea_files($draftitemid, $filepath = '/') {
$item->type = 'folder';
$foldername = explode('/', trim($item->filepath, '/'));
$item->fullname = trim(array_pop($foldername), '/');
$item->thumbnail = $OUTPUT->pix_url('f/folder-32')->out(false);
} else {
// do NOT use file browser here!
$item->url = moodle_url::make_draftfile_url($draftitemid, $item->filepath, $item->filename)->out();
$item->thumbnail = $OUTPUT->pix_url(file_extension_icon($item->filename, 32))->out(false);
}
$list[] = $item;
}

View File

@ -80,6 +80,7 @@ M.form_filemanager.init = function(Y, options) {
} else {
this.filecount = 0;
}
// prepare filemanager for drag-and-drop upload
this.filemanager = Y.one('#filemanager-'+options.client_id);
if (this.filemanager.hasClass('filemanager-container') || !this.filemanager.one('.filemanager-container')) {
this.dndcontainer = this.filemanager;
@ -89,7 +90,31 @@ M.form_filemanager.init = function(Y, options) {
this.dndcontainer.generateID();
}
}
// save template for one path element and location of path bar
if (this.filemanager.one('.fp-path-folder')) {
this.pathnode = this.filemanager.one('.fp-path-folder');
this.pathbar = this.pathnode.get('parentNode');
this.pathbar.removeChild(this.pathnode);
}
// initialize 'select file' panel
var fpselectnode = Y.Node.create(M.form_filemanager.templates.fileselectlayout);
this.filemanager.appendChild(fpselectnode);
this.selectui = new Y.Panel({
srcNode : fpselectnode,
zIndex : 600000,
centered : true,
modal : true,
close : true,
render : true
});
this.selectui.hide();
this.setup_select_file();
// setup buttons onclick events
this.setup_buttons();
// display files
this.viewmode = 1; // TODO take from cookies?
this.filemanager.all('.fp-vb-icons,.fp-vb-tree,.fp-vb-details').removeClass('checked')
this.filemanager.all('.fp-vb-icons').addClass('checked')
this.refresh(this.currentpath); // MDL-31113 get latest list from server
},
@ -120,7 +145,18 @@ M.form_filemanager.init = function(Y, options) {
alert('IO FATAL');
return;
}
var data = Y.JSON.parse(o.responseText);
var data = null;
try {
data = Y.JSON.parse(o.responseText);
} catch(e) {
// TODO display error
//scope.print_msg(M.str.repository.invalidjson, 'error');
//scope.display_error(M.str.repository.invalidjson+'<pre>'+stripHTML(o.responseText)+'</pre>', 'invalidjson')
return;
}
if (data && data.tree && scope.set_current_tree) {
scope.set_current_tree(data.tree);
}
args.callback(id,data,p);
}
},
@ -177,9 +213,9 @@ M.form_filemanager.init = function(Y, options) {
}, true);
},
setup_buttons: function() {
var button_download = this.filemanager.one('.fm-btn-download');
var button_create = this.filemanager.one('.fm-btn-mkdir');
var button_addfile = this.filemanager.one('.fm-btn-add');
var button_download = this.filemanager.one('.fp-btn-download');
var button_create = this.filemanager.one('.fp-btn-mkdir');
var button_addfile = this.filemanager.one('.fp-btn-add');
// setup 'add file' button
// if maxfiles == -1, the no limit
@ -263,484 +299,428 @@ M.form_filemanager.init = function(Y, options) {
}
});
}, this);
},
render: function() {
var options = this.options;
var path = this.options.path;
var list = this.options.list;
var breadcrumb = this.filemanager.one('.fm-breadcrumb');
// empty breadcrumb
breadcrumb.set('innerHTML', '').addClass('fm-empty');
// build breadcrumb
if (path && path.length) {
breadcrumb.removeClass('fm-empty');
var count = 0;
for(var p in path) {
var arrow = '';
if (count==0) {
arrow = Y.Node.create('<span>'+M.str.moodle.path + ': </span>');
} else {
arrow = Y.Node.create('<span> ▶ </span>');
this.filemanager.all('.fp-vb-icons,.fp-vb-tree,.fp-vb-details').
on('click', function(e) {
e.preventDefault();
var viewbar = this.filemanager.one('.fp-viewbar')
if (!viewbar || !viewbar.hasClass('disabled')) {
this.filemanager.all('.fp-vb-icons,.fp-vb-tree,.fp-vb-details').removeClass('checked')
if (e.currentTarget.hasClass('fp-vb-tree')) {
this.viewmode = 2;
} else if (e.currentTarget.hasClass('fp-vb-details')) {
this.viewmode = 3;
} else {
this.viewmode = 1;
}
e.currentTarget.addClass('checked')
this.render();
//Y.Cookie.set('recentviewmode', this.viewmode);
}
count++;
var pathid = 'fm-path-node-'+this.client_id;
pathid += ('-'+count);
var crumb = Y.Node.create('<a href="###" id="'+pathid+'">'+path[p].name+'</a>');
breadcrumb.appendChild(arrow);
breadcrumb.appendChild(crumb);
var args = {};
args.requestpath = path[p].path;
args.client_id = this.client_id;
Y.one('#'+pathid).on('click', function(e, args) {
var scope = this;
var params = {};
params['filepath'] = args.requestpath;
this.currentpath = args.requestpath;
this.request({
action: 'list',
scope: scope,
params: params,
callback: function(id, obj, args) {
scope.filecount = obj.filecount;
scope.check_buttons();
scope.options = obj;
scope.render(obj);
}
}, true);
}, this, args);
}, this);
},
print_path: function() {
var p = this.options.path;
this.pathbar.setContent('').addClass('empty');
if (p && p.length!=0 && this.viewmode != 2) {
for(var i = 0; i < p.length; i++) {
var el = this.pathnode.cloneNode(true);
this.pathbar.appendChild(el);
if (i == 0) {el.addClass('first');}
if (i == p.length-1) {el.addClass('last');}
if (i%2) {el.addClass('even');} else {el.addClass('odd');}
el.one('.fp-path-folder-name').setContent(p[i].name).
on('click', function(e, path) {
e.preventDefault();
var scope = this;
this.currentpath = path;
this.request({
action: 'list',
scope: scope,
params: {filepath:path},
callback: function(id, obj, args) {
scope.filecount = obj.filecount;
scope.check_buttons();
scope.options = obj;
scope.render(obj);
}
}, true);
}, this, p[i].path);
}
this.pathbar.removeClass('empty');
}
},
get_filepath: function(obj) {
if (obj.path && obj.path.length) {
return obj.path[obj.path.length-1].path;
}
return '';
},
treeview_dynload: function(node, cb) {
var retrieved_children = {};
if (node.children) {
for (var i in node.children) {
retrieved_children[node.children[i].path] = node.children[i];
}
}
var listhtml = '';
// folder list items
var folder_ids = [];
var folder_data = {};
// normal file list items
var file_ids = [];
var file_data = {};
// archives list items
var zip_ids = [];
var zip_data = {};
var html_ids = [];
var html_data = {};
file_data.itemid = folder_data.itemid = zip_data.itemid = options.itemid;
file_data.client_id = folder_data.client_id = zip_data.client_id = this.client_id;
var foldername_ids = [];
this.request({
action:'list',
params: {filepath:node.path?node.path:''},
scope:this,
callback: function(id, obj, args) {
var list = obj.list;
var scope = args.scope;
// check that user did not leave the view mode before recieving this response
if (!(scope.viewmode == 2 && node && node.getChildrenEl())) {
return;
}
if (cb != null) { // (in manual mode do not update current path)
scope.options = obj;
}
node.highlight(false);
node.origlist = obj.list?obj.list:null;
node.origpath = obj.path?obj.path:null;
node.children = [];
for(k in list) {
if (list[k].type == 'folder' && retrieved_children[list[k].filepath]) {
// if this child is a folder and has already been retrieved
node.children[node.children.length] = retrieved_children[list[k].filepath];
} else {
// append new file to the list
scope.view_files([list[k]]);
}
}
if (cb == null) {
node.refresh();
} else {
// invoke callback requested by TreeView component
cb();
}
//scope.content_scrolled();
}
}, false);
},
view_files: function(appendfiles) {
this.filemanager.removeClass('fm-updating').removeClass('fm-noitems');
if (!list || list.length == 0) {
if ((appendfiles == null) && (!this.options.list || this.options.list.length == 0)) {
this.filemanager.addClass('fm-noitems');
return;
}
var count = 0;
for(var i in list) {
count++;
// the li html element
var htmlid = 'fileitem-'+this.client_id+'-'+count;
// link to file
var fileid = 'filename-'+this.client_id+'-'+count;
// file menu
var action = 'action-' +this.client_id+'-'+count;
var html = M.form_filemanager.templates.onefile;
html_ids.push('#'+htmlid);
html_data[htmlid] = action;
list[i].htmlid = htmlid;
list[i].fileid = fileid;
list[i].action = action;
var url = "###";
switch (list[i].type) {
case 'folder':
// click folder name
foldername_ids.push('#'+fileid);
// click folder menu
folder_ids.push('#'+action);
folder_data[action] = list[i];
folder_data[fileid] = list[i];
break;
case 'file':
file_ids.push('#'+action);
// click file name
file_ids.push('#'+fileid);
file_data[action] = list[i];
file_data[fileid] = list[i];
if (list[i].url) {
url = list[i].url;
}
break;
case 'zip':
zip_ids.push('#'+action);
zip_ids.push('#'+fileid);
zip_data[action] = list[i];
zip_data[fileid] = list[i];
if (list[i].url) {
url = list[i].url;
}
break;
var list = (appendfiles != null) ? appendfiles : this.options.list;
var element_template;
if (this.viewmode == 2 || this.viewmode == 3) {
element_template = Y.Node.create(M.form_filemanager.templates.listfilename);
} else {
this.viewmode = 1;
element_template = Y.Node.create(M.form_filemanager.templates.iconfilename);
}
var options = {
viewmode : this.viewmode,
appendonly : appendfiles != null,
filenode : element_template,
callbackcontext : this,
callback : function(e, node) {
e.preventDefault();
if (node.type == 'folder') {
this.refresh(node.filepath);
} else {
this.select_file(node);
}
},
rightclickcallback : function(e, node) {
this.select_file(node);
e.preventDefault();
}
var fullname = list[i].fullname;
};
if (this.viewmode == 2) {
options.dynload = true;
options.filepath = this.options.path;
options.treeview_dynload = this.treeview_dynload;
options.callback = function(e, node) {
if (node.type != 'folder') {
if (e.node.parent && e.node.parent.origpath) {
// set the current path
this.options.path = e.node.parent.origpath;
this.options.list = e.node.parent.origlist;
this.print_path();
}
this.select_file(node);
} else {
// save current path and filelist (in case we want to jump to other viewmode)
this.options.path = e.node.origpath;
this.options.list = e.node.origlist;
this.print_path();
//this.content_scrolled();
}
};
}
if (!this.lazyloading) {this.lazyloading={};}
this.filemanager.one('.fp-content').fp_display_filelist(options, list, this.lazyloading);
},
populate_licenses_select: function(node) {
if (!node) {return;}
node.setContent('');
var licenses = this.options.licenses;
for (var i in licenses) {
var option = Y.Node.create('<option/>').
set('value', licenses[i].shortname).
setContent(licenses[i].fullname);
node.appendChild(option)
}
},
set_current_tree: function(tree) {
var appendfilepaths = function(list, node) {
if (!node || !node.children || !node.children.length) {return;}
for (var i in node.children) {
list[list.length] = node.children[i].filepath;
appendfilepaths(list, node.children[i]);
}
}
var list = ['/'];
appendfilepaths(list, tree);
var selectnode = this.filemanager.one('.fp-select');
node = selectnode.one('.fp-path select');
node.setContent('');
for (var i in list) {
node.appendChild(Y.Node.create('<option/>').
set('value', list[i]).setContent(list[i]))
}
},
update_file: function() {
var selectnode = this.filemanager.one('.fp-select');
var fileinfo = this.selectui.fileinfo;
if (list[i].sortorder == 1) {
html = html.replace('___fullname___', '<strong><a title="'+fullname+'" href="'+url+'" id="'+fileid+'"><img src="'+list[i].icon+'" /> ' + fullname + '</a></strong>');
var newfilename = selectnode.one('.fp-saveas input').get('value');
var filenamechanged = (newfilename && newfilename != fileinfo.fullname);
var pathselect = selectnode.one('.fp-path select'),
pathindex = pathselect.get('selectedIndex'),
targetpath = pathselect.get("options").item(pathindex).get('value');
var filepathchanged = (targetpath != this.get_parent_folder_name(fileinfo));
if (filenamechanged && filepathchanged) {
alert('Sorry, simultaneous changing of name and path is not supported yet'); // TODO
return;
}
if (!filenamechanged && !filepathchanged) {
// no changes
this.selectui.hide();
}
selectnode.addClass('loading');
// RENAME
if (filenamechanged) {
var action = '';
var params = {};
if (fileinfo.type == 'folder') {
params['filepath'] = fileinfo.filepath;
params['filename'] = '.';
params['newdirname'] = newfilename;
action = 'renamedir';
} else {
html = html.replace('___fullname___', '<a title="'+fullname+'" href="'+url+'" id="'+fileid+'"><img src="'+list[i].icon+'" /> ' + fullname + '</a>');
params['filepath'] = fileinfo.filepath;
params['filename'] = fileinfo.fullname;
params['newfilename'] = newfilename;
action = 'rename';
}
html = html.replace('___action___', '<span class="fm-menuicon" id="'+action+'"><img alt="▶" src="'+M.util.image_url('i/menu')+'" /></span>');
html = '<li id="'+htmlid+'">'+html+'</li>';
listhtml += html;
}
this.filemanager.one('.fm-filelist').set('innerHTML', listhtml);
// click normal file menu
Y.on('click', this.create_filemenu, file_ids, this, file_data);
Y.on('contextmenu', this.create_filemenu, file_ids, this, file_data);
// click folder menu
Y.on('click', this.create_foldermenu, folder_ids, this, folder_data);
Y.on('contextmenu', this.create_foldermenu, folder_ids, this, folder_data);
Y.on('contextmenu', this.create_foldermenu, foldername_ids, this, folder_data);
// click archievs menu
Y.on('click', this.create_zipmenu, zip_ids, this, zip_data);
Y.on('contextmenu', this.create_zipmenu, zip_ids, this, zip_data);
// click folder name
Y.on('click', this.enter_folder, foldername_ids, this, folder_data);
},
enter_folder: function(e, data) {
var node = e.currentTarget;
var file = data[node.get('id')];
e.preventDefault();
this.refresh(file.filepath);
},
create_filemenu: function(e, data) {
e.preventDefault();
var options = this.options;
var node = e.currentTarget;
var file = data[node.get('id')];
var scope = this;
var menuitems = [
{text: M.str.moodle.download, onclick:{fn:open_file_in_new_window, obj:file, scope:this}}
];
function setmainfile(type, ev, obj) {
var file = obj[node.get('id')];
//Y.one(mainid).set('value', file.filepath+file.filename);
var params = {};
params['filepath'] = file.filepath;
params['filename'] = file.filename;
this.request({
action: 'setmainfile',
scope: scope,
action: action,
scope: this,
params: params,
callback: function(id, obj, args) {
scope.refresh(scope.currentpath);
args.scope.selectui.hide();
if (obj == false) {
alert(M.str.repository.fileexists); // TODO!
} else {
args.scope.refresh(obj.filepath);
if (typeof M.core_formchangechecker != 'undefined') {
M.core_formchangechecker.set_form_changed();
}
}
}
});
}
function open_file_in_new_window(type, ev, obj) {
// We open in a new window rather than changing the current windows URL as we don't
// want to navigate away from the page
window.open(obj.url, 'fm-download-file');
}
if (this.enablemainfile && (file.sortorder != 1)) {
var mainid = '#id_'+this.enablemainfile;
var menu = {text: M.str.repository.setmainfile, onclick:{fn: setmainfile, obj:data, scope:this}};
menuitems.push(menu);
}
this.create_menu(e, 'filemenu', menuitems, file, data);
},
create_foldermenu: function(e, data) {
e.preventDefault();
var scope = this;
var node = e.currentTarget;
var fileinfo = data[node.get('id')];
// an extra menu item for folder to zip it
function archive_folder(type,ev,obj) {
var params = {};
params['filepath'] = fileinfo.filepath;
params['filename'] = '.';
this.request({
action: 'zip',
scope: scope,
params: params,
callback: function(id, obj, args) {
scope.refresh(obj.filepath);
}
});
}
var menuitems = [
{text: M.str.editor.zip, onclick: {fn: archive_folder, obj: data, scope: this}},
];
this.create_menu(e, 'foldermenu', menuitems, fileinfo, data);
},
create_zipmenu: function(e, data) {
e.preventDefault();
var scope = this;
var node = e.currentTarget;
var fileinfo = data[node.get('id')];
function unzip(type, ev, obj) {
// MOVE
if (filepathchanged) {
var params = {};
if (fileinfo.type == 'folder') {
action = 'movedir';
} else {
action = 'movefile';
}
params['filepath'] = fileinfo.filepath;
params['filename'] = fileinfo.fullname;
params['newfilepath'] = targetpath;
this.request({
action: 'unzip',
scope: scope,
action: action,
scope: this,
params: params,
callback: function(id, obj, args) {
scope.refresh(obj.filepath);
args.scope.selectui.hide();
args.scope.refresh((obj && obj.filepath) ? obj.filepath : '/');
if (typeof M.core_formchangechecker != 'undefined') {
M.core_formchangechecker.set_form_changed();
}
}
});
}
var menuitems = [
{text: M.str.moodle.download, url:fileinfo.url},
{text: M.str.moodle.unzip, onclick: {fn: unzip, obj: data, scope: this}}
];
function setmainfile(type, ev, obj) {
var file = obj[node.get('id')];
//Y.one(mainid).set('value', file.filepath+file.filename);
var params = {};
params['filepath'] = file.filepath;
params['filename'] = file.filename;
this.request({
action: 'setmainfile',
scope: scope,
params: params,
callback: function(id, obj, args) {
scope.refresh(scope.currentpath);
}
});
}
if (this.enablemainfile && (fileinfo.sortorder != 1)) {
var mainid = '#id_'+this.enablemainfile;
var menu = {text: M.str.repository.setmainfile, onclick:{fn: setmainfile, obj:data, scope:this}};
menuitems.push(menu);
}
this.create_menu(e, 'zipmenu', menuitems, fileinfo, data);
},
create_menu: function(ev, menuid, menuitems, fileinfo, options) {
var position = [ev.pageX, ev.pageY];
var scope = this;
function remove(type, ev, obj) {
setup_select_file: function() {
var selectnode = this.filemanager.one('.fp-select');
// bind labels with corresponding inputs
selectnode.all('.fp-saveas,.fp-path,.fp-author,.fp-license').each(function (node) {
node.all('label').set('for', node.one('input,select').generateID());
});
this.populate_licenses_select(selectnode.one('.fp-license select'));
// register event on clicking buttons
selectnode.one('.fp-file-update').on('click', function(e) {
e.preventDefault();
this.update_file();
}, this);
selectnode.one('.fp-file-download').on('click', function(e) {
e.preventDefault();
window.open(this.selectui.fileinfo.url, 'fm-download-file');
}, this);
selectnode.one('.fp-file-delete').on('click', function(e) {
e.preventDefault();
var dialog_options = {};
var params = {};
dialog_options.message = M.str.repository.confirmdeletefile;
dialog_options.scope = this;
var filename = '';
var filepath = '';
if (fileinfo.type == 'folder') {
if (this.selectui.fileinfo.type == 'folder') {
params.filename = '.';
params.filepath = fileinfo.filepath;
params.filepath = this.selectui.fileinfo.filepath;
} else {
params.filename = fileinfo.fullname;
params.filename = this.selectui.fileinfo.fullname;
}
dialog_options.callbackargs = [params];
dialog_options.callback = function(params) {
//selectnode.addClass('loading');
this.request({
action: 'delete',
scope: this,
params: params,
callback: function(id, obj, args) {
scope.filecount--;
scope.refresh(obj.filepath);
//args.scope.selectui.hide();
args.scope.filecount--;
args.scope.refresh(obj.filepath);
if (typeof M.core_formchangechecker != 'undefined') {
M.core_formchangechecker.set_form_changed();
}
}
});
};
M.util.show_confirm_dialog(ev, dialog_options);
}
function rename (type, ev, obj) {
var scope = this;
var perform = function(e) {
var newfilename = Y.one('#fm-rename-input').get('value');
if (!newfilename) {
return;
this.selectui.hide(); // TODO remove this after confirm dialog is replaced with YUI3
M.util.show_confirm_dialog(e, dialog_options);
}, this);
selectnode.one('.fp-file-zip').on('click', function(e) {
e.preventDefault();
var params = {};
var fileinfo = this.selectui.fileinfo;
params['filepath'] = fileinfo.filepath;
params['filename'] = '.';
selectnode.addClass('loading');
this.request({
action: 'zip',
scope: this,
params: params,
callback: function(id, obj, args) {
args.scope.selectui.hide();
args.scope.refresh(obj.filepath);
}
var action = '';
var params = {};
if (fileinfo.type == 'folder') {
params['filepath'] = fileinfo.filepath;
params['filename'] = '.';
params['newdirname'] = newfilename;
action = 'renamedir';
} else {
params['filepath'] = fileinfo.filepath;
params['filename'] = fileinfo.fullname;
params['newfilename'] = newfilename;
action = 'rename';
}
scope.request({
action: action,
scope: scope,
params: params,
callback: function(id, obj, args) {
if (obj == false) {
alert(M.str.repository.fileexists);
} else {
scope.refresh(obj.filepath);
if (typeof M.core_formchangechecker != 'undefined') {
M.core_formchangechecker.set_form_changed();
}
}
Y.one('#fm-rename-input').set('value', '');
scope.rename_dialog.hide();
}
});
};
var dialog = Y.one('#fm-rename-dlg');
if (!dialog) {
dialog = Y.Node.create('<div id="fm-rename-dlg"><div class="hd">'+M.str.repository.enternewname+'</div><div class="bd"><input type="text" id="fm-rename-input" /></div></div>');
Y.one(document.body).appendChild(dialog);
this.rename_dialog = new YAHOO.widget.Dialog("fm-rename-dlg", {
width: "300px",
fixedcenter: true,
visible: true,
constraintoviewport : true
});
}
var buttons = [ { text:M.str.moodle.rename, handler:perform, isDefault:true},
{ text:M.str.moodle.cancel, handler:function(){this.cancel();}}];
this.rename_dialog.cfg.queueProperty('buttons', buttons);
this.rename_dialog.render();
this.rename_dialog.show();
//var k1 = new YAHOO.util.KeyListener(scope, {keys:13}, {fn:function(){perform();}, correctScope: true});
//k1.enable();
Y.one('#fm-rename-input').set('value', fileinfo.fullname);
}
function move(type, ev, obj) {
var scope = this;
var itemid = this.options.itemid;
// setup move file dialog
var dialog = null;
if (!Y.one('#fm-move-dlg')) {
dialog = Y.Node.create('<div id="fm-move-dlg"></div>');
Y.one(document.body).appendChild(dialog);
} else {
dialog = Y.one('#fm-move-dlg');
}
dialog.set('innerHTML', '<div class="hd">'+M.str.repository.moving+'</div><div class="bd"><div id="fm-move-div">'+M.str.repository.nopathselected+'</div><div id="fm-tree"></div></div>');
this.movefile_dialog = new YAHOO.widget.Dialog("fm-move-dlg", {
width : "600px",
fixedcenter : true,
visible : false,
constraintoviewport : true
});
var treeview = new YAHOO.widget.TreeView("fm-tree");
var dialog = this.movefile_dialog;
function _move(e) {
if (!treeview.targetpath) {
return;
}
var params = {};
if (fileinfo.type == 'folder') {
action = 'movedir';
} else {
action = 'movefile';
}
params['filepath'] = fileinfo.filepath;
params['filename'] = fileinfo.fullname;
params['newfilepath'] = treeview.targetpath;
scope.request({
action: action,
scope: scope,
params: params,
callback: function(id, obj, args) {
var p = '/';
if (obj) {
p = obj.filepath;
}
dialog.cancel();
scope.refresh(p);
if (typeof M.core_formchangechecker != 'undefined') {
M.core_formchangechecker.set_form_changed();
}
}
});
}
var buttons = [ { text:M.str.moodle.move, handler:_move, isDefault:true },
{ text:M.str.moodle.cancel, handler:function(){this.cancel();}}];
this.movefile_dialog.cfg.queueProperty("buttons", buttons);
this.movefile_dialog.render();
treeview.subscribe("dblClickEvent", function(e) {
// update destidatoin folder
this.targetpath = e.node.data.path;
var title = Y.one('#fm-move-div');
title.set('innerHTML', '<strong>"' + this.targetpath + '"</strong> has been selected.');
});
function loadDataForNode(node, onCompleteCallback) {
var params = {};
params['filepath'] = node.data.path;
var obj = {
action: 'dir',
scope: scope,
params: params,
callback: function(id, obj, args) {
data = obj.children;
if (data.length == 0) {
// so it is empty
} else {
for (var i in data) {
var textnode = {label: data[i].fullname, path: data[i].filepath, itemid: this.itemid};
var tmpNode = new YAHOO.widget.TextNode(textnode, node, false);
}
}
this.oncomplete();
}
};
obj.oncomplete = onCompleteCallback;
scope.request(obj);
}
this.movefile_dialog.subscribe('show', function(){
var rootNode = treeview.getRoot();
treeview.setDynamicLoad(loadDataForNode);
treeview.removeChildren(rootNode);
var textnode = {label: M.str.moodle.files, path: '/'};
var tmpNode = new YAHOO.widget.TextNode(textnode, rootNode, true);
treeview.draw();
}, this, true);
this.movefile_dialog.show();
}, this);
selectnode.one('.fp-file-unzip').on('click', function(e) {
e.preventDefault();
var params = {};
var fileinfo = this.selectui.fileinfo;
params['filepath'] = fileinfo.filepath;
params['filename'] = fileinfo.fullname;
selectnode.addClass('loading');
this.request({
action: 'unzip',
scope: this,
params: params,
callback: function(id, obj, args) {
args.scope.selectui.hide();
args.scope.refresh(obj.filepath);
}
});
}, this);
selectnode.one('.fp-file-setmain').on('click', function(e) {
e.preventDefault();
var params = {};
var fileinfo = this.selectui.fileinfo;
params['filepath'] = fileinfo.filepath;
params['filename'] = fileinfo.fullname;
selectnode.addClass('loading');
this.request({
action: 'setmainfile',
scope: this,
params: params,
callback: function(id, obj, args) {
args.scope.selectui.hide();
args.scope.refresh(obj.filepath);
}
});
}, this);
selectnode.all('.fp-file-cancel').on('click', function(e) {
e.preventDefault();
// TODO if changed asked to confirm, the same with close button
this.selectui.hide();
}, this);
},
get_parent_folder_name: function(node) {
if (node.type != 'folder' || node.filepath.length < node.fullname.length+1) { return node.filepath; }
var basedir = node.filepath.substr(0, node.filepath.length - node.fullname.length - 1);
var lastdir = node.filepath.substr(node.filepath.length - node.fullname.length - 2);
if (lastdir == '/' + node.fullname + '/') { return basedir; }
return node.filepath;
},
select_file: function(node) {
var selectnode = this.filemanager.one('.fp-select');
selectnode.removeClass('loading').removeClass('fp-folder').
removeClass('fp-file').removeClass('fp-zip').removeClass('fp-cansetmain');
if (node.type == 'folder' || node.type == 'zip') {selectnode.addClass('fp-'+node.type);}
else {selectnode.addClass('fp-file');}
if (this.enablemainfile && (node.sortorder != 1)) {
selectnode.addClass('fp-cansetmain');
}
var shared_items = [
{text: M.str.moodle.rename+'...', onclick: {fn: rename, obj: options, scope: this}},
{text: M.str.moodle.move+'...', onclick: {fn: move, obj: options, scope: this}}
];
// delete is reserve word in Javascript
shared_items.push({text: M.str.moodle['delete']+'...', onclick: {fn: remove, obj: options, scope: this}});
var menu = new YAHOO.widget.Menu(menuid, {xy:position, clicktohide:true});
menu.clearContent();
menu.addItems(menuitems);
menu.addItems(shared_items);
menu.render(document.body);
menu.subscribe('hide', function(){
this.fireEvent('destroy');
this.selectui.fileinfo = node;
selectnode.one('.fp-saveas input').set('value', node.fullname);
var foldername = this.get_parent_folder_name(node);
selectnode.all('.fp-origpath .fp-value').setContent(foldername);
selectnode.all('.fp-author input').set('value', node.author);
selectnode.all('.fp-license select option[selected]').set('selected', false);
selectnode.all('.fp-license select option[value='+node.license+']').set('selected', true);
selectnode.all('.fp-path select option[selected]').set('selected', false);
selectnode.all('.fp-path select option').each(function(el){
if (el.get('value') == foldername) {el.set('selected', true);}
});
menu.show();
selectnode.all('.fp-author input').set('disabled','disabled'); //TODO
selectnode.all('.fp-license select').set('disabled','disabled'); //TODO
// display static information about a file (when known)
var attrs = ['datemodified','datecreated','size','dimensions'];
for (var i in attrs) {
if (selectnode.one('.fp-'+attrs[i])) {
var value = (node[attrs[i]+'_f']) ? node[attrs[i]+'_f'] : (node[attrs[i]] ? node[attrs[i]] : '');
selectnode.one('.fp-'+attrs[i]).addClassIf('fp-unknown', ''+value == '')
.one('.fp-value').setContent(value);
}
}
// display thumbnail
var imgnode = Y.Node.create('<img/>').
set('src', node.realthumbnail ? node.realthumbnail : node.thumbnail).
setStyle('maxHeight', ''+(node.thumbnail_height ? node.thumbnail_height : 90)+'px').
setStyle('maxWidth', ''+(node.thumbnail_width ? node.thumbnail_width : 90)+'px');
selectnode.one('.fp-thumbnail').setContent('').appendChild(imgnode);
// show panel
this.selectui.show();
},
render: function() {
this.print_path();
this.view_files();
}
});

View File

@ -290,7 +290,10 @@ class form_filemanager implements renderable {
* client_id=>uniqid(),
* acepted_types=>'*',
* return_types=>FILE_INTERNAL,
* context=>$PAGE->context
* context=>$PAGE->context,
* author=>fullname($USER),
* licenses=>array build from $CFG->licenses,
* defaultlicense=>$CFG->sitedefaultlicense
*/
public function __construct(stdClass $options) {
global $CFG, $USER, $PAGE;
@ -303,8 +306,22 @@ class form_filemanager implements renderable {
'client_id'=>uniqid(),
'accepted_types'=>'*',
'return_types'=>FILE_INTERNAL,
'context'=>$PAGE->context
'context'=>$PAGE->context,
'author'=>fullname($USER),
'licenses'=>array()
);
if (!empty($CFG->licenses)) {
$array = explode(',', $CFG->licenses);
foreach ($array as $license) {
$l = new stdClass();
$l->shortname = $license;
$l->fullname = get_string($license, 'license');
$defaults['licenses'][] = $l;
}
}
if (!empty($CFG->sitedefaultlicense)) {
$defaults['defaultlicense'] = $CFG->sitedefaultlicense;
}
foreach ($defaults as $key=>$value) {
if (empty($options->$key)) {
$options->$key = $value;

View File

@ -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', 'datatable', 'datatable-sort', 'resize-plugin', 'dd-plugin', 'resize-constrain'),
'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', 'resize-plugin', 'dd-plugin', 'moodle-core_filepicker'),
// 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'),

View File

@ -29,6 +29,7 @@ define('AJAX_SCRIPT', true);
require('../config.php');
require_once($CFG->libdir.'/filelib.php');
require_once($CFG->libdir.'/adminlib.php');
require_once($CFG->dirroot.'/repository/lib.php');
$PAGE->set_context(get_system_context());
require_login();
if (isguestuser()) {
@ -58,10 +59,12 @@ switch ($action) {
case 'list':
$filepath = optional_param('filepath', '/', PARAM_PATH);
$data = file_get_drafarea_files($draftid, $filepath);
$data = repository::prepare_listing(file_get_drafarea_files($draftid, $filepath));
$info = file_get_draft_area_info($draftid);
$data->filecount = $info['filecount'];
$data->filesize = $info['filesize'];
$data->tree = new stdClass();
file_get_drafarea_folders($draftid, '/', $data->tree);
echo json_encode($data);
die;
@ -73,6 +76,8 @@ switch ($action) {
$fs->create_directory($user_context->id, 'user', 'draft', $draftid, file_correct_filepath(file_correct_filepath($filepath).$newdirname));
$return = new stdClass();
$return->filepath = $filepath;
$return->tree = new stdClass();
file_get_drafarea_folders($draftid, '/', $return->tree);
echo json_encode($return);
die;
@ -190,6 +195,8 @@ switch ($action) {
} else {
$return->filepath = $newfilepath;
}
$return->tree = new stdClass();
file_get_drafarea_folders($draftid, '/', $return->tree);
echo json_encode($return);
die;
@ -277,6 +284,8 @@ switch ($action) {
if ($newfile = $file->extract_to_storage($zipper, $user_context->id, 'user', 'draft', $draftid, $filepath, $USER->id)) {
$return = new stdClass();
$return->filepath = $filepath;
$return->tree = new stdClass();
file_get_drafarea_folders($draftid, '/', $return->tree);
echo json_encode($return);
} else {
echo json_encode(false);

View File

@ -42,6 +42,332 @@
* this.logindata, cached login form
*/
YUI.add('moodle-core_filepicker', function(Y) {
/** help function to extract width/height style as a number, not as a string */
Y.Node.prototype.getStylePx = function(attr) {
var style = this.getStyle(attr);
if (''+style == '0' || ''+style == '0px') {
return 0;
}
var matches = style.match(/^([\d\.]+)px$/)
if (matches && parseFloat(matches[1])) {
return parseFloat(matches[1]);
}
return null;
}
/** if condition is met, the class is added to the node, otherwise - removed */
Y.Node.prototype.addClassIf = function(className, condition) {
if (condition) {
this.addClass(className);
} else {
this.removeClass(className);
}
return this;
}
/** sets the width(height) of the node considering existing minWidth(minHeight) */
Y.Node.prototype.setStyleAdv = function(stylename, value) {
var stylenameCap = stylename.substr(0,1).toUpperCase() + stylename.substr(1, stylename.length-1).toLowerCase();
this.setStyle(stylename, '' + Math.max(value, this.getStylePx('min'+stylenameCap)) + 'px')
return this;
}
/**
* Displays a list of files (used by filepicker, filemanager) inside the Node
*
* @param array options
* viewmode : 1 - icons, 2 - tree, 3 - table
* appendonly : whether fileslist need to be appended instead of replacing the existing content
* filenode : Node element that contains template for displaying one file
* callback : On click callback. The element of the fileslist array will be passed as argument
* rightclickcallback : On right click callback (optional).
* callbackcontext : context where callbacks are executed
* sortable : whether content may be sortable (in table mode)
* dynload : allow dynamic load for tree view
* filepath : for pre-building of tree view - the path to the current directory in filepicker format
* treeview_dynload : callback to ...
* @param array fileslist array of files to show, each array element may have attributes:
* title or fullname : file name
* shorttitle (optional) : display file name
* thumbnail : url of image
* icon : url of icon image
* thumbnail_width : width of thumbnail, default 90
* thumbnail_height : height of thumbnail, default 90
* thumbnail_alt : TODO not needed!
* description or thumbnail_title : alt text
* @param array lazyloading : reference to the array with lazy loading images
*/
Y.Node.prototype.fp_display_filelist = function(options, fileslist, lazyloading) {
var viewmodeclassnames = {1:'fp-iconview', 2:'fp-treeview', 3:'fp-tableview'};
var classname = viewmodeclassnames[options.viewmode];
var scope = this;
/** return whether file is a folder (different attributes in FileManager and FilePicker) */
var file_is_folder = function(node) {
if (node.children) {return true;}
if (node.type && node.type == 'folder') {return true;}
return false;
};
/** return the name of the file (different attributes in FileManager and FilePicker) */
var file_get_filename = function(node) {
return node.title ? node.title : node.fullname;
}
/** return display name of the file (different attributes in FileManager and FilePicker) */
var file_get_displayname = function(node) {
return node.shorttitle ? node.shorttitle : file_get_filename(node);
}
/** return file description (different attributes in FileManager and FilePicker) */
var file_get_description = function(node) {
return node.description ? node.description : (node.thumbnail_title ? node.thumbnail_title : file_get_filename(node));
}
/** help funciton for tree view */
var build_tree = function(node, level) {
// prepare file name with icon
var el = Y.Node.create('<div/>');
el.appendChild(options.filenode.cloneNode(true));
el.one('.fp-filename').setContent(file_get_displayname(node));
// TODO add tooltip with node.title or node.thumbnail_title
if (file_is_folder(node)) {
el.get('children').addClass('fp-folder');
}
if (node.icon) {
el.one('.fp-icon').appendChild(Y.Node.create('<img/>').set('src', node.icon));
if (node.realicon) {
lazyloading[el.one('.fp-icon img').generateID()] = node.realicon;
}
}
// create node
var tmpNode = new YAHOO.widget.HTMLNode(el.getContent(), level, false);
if (node.dynamicLoadComplete) {
tmpNode.dynamicLoadComplete = true;
}
tmpNode.fileinfo = node;
tmpNode.isLeaf = !file_is_folder(node);
if (!tmpNode.isLeaf) {
if(node.expanded) {
tmpNode.expand();
}
tmpNode.path = node.path ? node.path : (node.filepath ? node.filepath : '');
for(var c in node.children) {
build_tree(node.children[c], tmpNode);
}
}
};
/** initialize tree view */
var initialize_tree_view = function() {
var parentid = scope.one('.'+classname).get('id');
scope.treeview = new YAHOO.widget.TreeView(parentid);
if (options.dynload) {
scope.treeview.setDynamicLoad(Y.bind(options.treeview_dynload, options.callbackcontext), 1);
}
scope.treeview.singleNodeHighlight = true;
if (options.filepath && options.filepath.length) {
// we just jumped from icon/details view, we need to show all parents
// we extract as much information as possible from filepath and filelist
// and send additional requests to retrieve siblings for parent folders
var mytree = {};
var mytreeel = null;
for (var i in options.filepath) {
if (mytreeel == null) {
mytreeel = mytree;
} else {
mytreeel.children = [{}];
mytreeel = mytreeel.children[0];
}
var pathelement = options.filepath[i];
mytreeel.path = pathelement.path;
mytreeel.title = pathelement.name;
mytreeel.dynamicLoadComplete = true; // we will call it manually
mytreeel.expanded = true;
}
mytreeel.children = fileslist;
build_tree(mytree, scope.treeview.getRoot());
// manually call dynload for parent elements in the tree so we can load other siblings
if (options.dynload) {
var root = scope.treeview.getRoot();
while (root && root.children && root.children.length) {
root = root.children[0];
if (root.path == mytreeel.path) {
root.origpath = options.filepath;
root.origlist = fileslist;
} else if (!root.isLeaf && root.expanded) {
Y.bind(options.treeview_dynload, options.callbackcontext)(root, null);
}
}
}
} else {
// there is no path information, just display all elements as a list, without hierarchy
for(k in fileslist) {
build_tree(fileslist[k], scope.treeview.getRoot());
}
}
scope.treeview.subscribe('clickEvent', function(e){
e.node.highlight(false);
Y.bind(options.callback, options.callbackcontext)(e, e.node.fileinfo);
});
if (options.rightclickcallback) {
scope.treeview.subscribe('dblClickEvent', function(e){ // TODO right click!
e.node.highlight(false);
if (e.node.path != '/') {
Y.bind(options.rightclickcallback, options.callbackcontext)(e, e.node.fileinfo);
}
});
}
scope.treeview.draw();
};
/** formatting function for table view */
var formatValue = function (o){
if (o.data[''+o.column.key+'_f_s']) {return o.data[''+o.column.key+'_f_s'];}
else if (o.data[''+o.column.key+'_f']) {return o.data[''+o.column.key+'_f'];}
else if (o.value) {return o.value;}
else {return '';}
};
/** formatting function for table view */
var formatTitle = function(o) {
var el = Y.Node.create('<div/>');
el.appendChild(options.filenode.cloneNode(true)); // TODO not node but string!
if (o.data['isfolder']) {
el.get('children').addClass('fp-folder');
}
el.one('.fp-filename').setContent(o.value);
if (o.data['icon']) {
el.one('.fp-icon').appendChild(Y.Node.create('<img/>').set('src', o.data['icon']));
if (o.data['realicon']) {
lazyloading[el.one('.fp-icon img').generateID()] = o.data['realicon'];
}
}
// TODO add tooltip with o.data['title'] (o.value) or o.data['thumbnail_title']
return el.getContent();
}
/** sorting function for table view */
var sortFoldersFirst = function(a, b, desc) {
if (a.get('isfolder') && !b.get('isfolder')) {return -1;}
if (!a.get('isfolder') && b.get('isfolder')) {return 1;}
var aa = a.get(this.key), bb = b.get(this.key), dir = desc?-1:1;
return (aa > bb) ? dir : ((aa < bb) ? -dir : 0);
}
/** initialize table view */
var initialize_table_view = function() {
var parentid = scope.one('.'+classname).get('id');
var cols = [
{key: "displayname", label: M.str.moodle.name, allowHTML: true, formatter: formatTitle,
sortable: true, sortFn: sortFoldersFirst},
{key: "datemodified", label: M.str.moodle.lastmodified, allowHTML: true, formatter: formatValue,
sortable: true, sortFn: sortFoldersFirst},
{key: "size", label: M.str.repository.size, allowHTML: true, formatter: formatValue,
sortable: true, sortFn: sortFoldersFirst},
{key: "mimetype", label: M.str.repository.type, allowHTML: true,
sortable: true, sortFn: sortFoldersFirst}
];
scope.tableview = new Y.DataTable({columns: cols});
scope.tableview.render('#'+parentid);
scope.tableview.delegate('click', function (e, tableview) {
var record = tableview.getRecord(e.currentTarget.get('id'));
if (record) { Y.bind(options.callback, this)(e, record.getAttrs()); }
}, 'tr', options.callbackcontext, scope.tableview);
if (options.rightclickcallback) {
scope.tableview.delegate('contextmenu', function (e, tableview) {
var record = tableview.getRecord(e.currentTarget.get('id'));
if (record) { Y.bind(options.rightclickcallback, this)(e, record.getAttrs()); }
}, 'tr', options.callbackcontext, scope.tableview);
}
}
/** append items in table view mode */
var append_files_table = function() {
for (var k in fileslist) {
// to speed up sorting and formatting
fileslist[k].displayname = file_get_displayname(fileslist[k]);
fileslist[k].isfolder = file_is_folder(fileslist[k]);
}
scope.tableview.addRows(fileslist);
scope.tableview.sortable = options.sortable ? true : false;
};
/** append items in tree view mode */
var append_files_tree = function() {
if (options.appendonly) {
var parentnode = scope.treeview.getRoot();
if (scope.treeview.getHighlightedNode()) {
parentnode = scope.treeview.getHighlightedNode();
if (parentnode.isLeaf) {parentnode = parentnode.parent;}
}
for (var k in fileslist) {
build_tree(fileslist[k], parentnode);
}
scope.treeview.draw();
} else {
// otherwise files were already added in initialize_tree_view()
}
}
/** append items in icon view mode */
var append_files_icons = function() {
parent = scope.one('.'+classname);
for (var k in fileslist) {
var node = fileslist[k];
var element = options.filenode.cloneNode(true);
parent.appendChild(element);
if (file_is_folder(node)) {
element.addClass('fp-folder');
}
var filenamediv = element.one('.fp-filename');
filenamediv.setContent(file_get_displayname(node));
var imgdiv = element.one('.fp-thumbnail'), width, height, src;
if (node.thumbnail) {
width = node.thumbnail_width ? node.thumbnail_width : 90;
height = node.thumbnail_height ? node.thumbnail_height : 90;
src = node.thumbnail;
} else {
width = 16;
height = 16;
src = node.icon;
}
filenamediv.setStyleAdv('width', width);
imgdiv.setStyleAdv('width', width).setStyleAdv('height', height);
var img = Y.Node.create('<img/>').setAttrs({
src: src,
title: file_get_description(node),
alt: node.thumbnail_alt ? node.thumbnail_alt : file_get_filename(node)}).
setStyle('maxWidth', ''+width+'px').
setStyle('maxHeight', ''+height+'px');
if (node.realthumbnail) {
lazyloading[img.generateID()] = node.realthumbnail;
}
imgdiv.appendChild(img);
element.on('click', options.callback, options.callbackcontext, node);
if (options.rightclickcallback) {
element.on('contextmenu', options.rightclickcallback, options.callbackcontext, node);
}
}
}
// initialize files view
if (!options.appendonly) {
var parent = Y.Node.create('<div/>').addClass(classname);
this.setContent('').appendChild(parent);
parent.generateID();
if (options.viewmode == 2) {
initialize_tree_view();
} else if (options.viewmode == 3) {
initialize_table_view();
} else {
// nothing to initialize for icon view
}
}
// append files to the list
if (options.viewmode == 2) {
append_files_tree();
} else if (options.viewmode == 3) {
append_files_table();
} else {
append_files_icons();
}
}
}, '@VERSION@', {
requires:['base','node'] // TODO TreeView, Table, etc.
});
M.core_filepicker = M.core_filepicker || {};
/**
@ -75,36 +401,6 @@ M.core_filepicker.set_templates = function(Y, templates) {
* Add new file picker to current instances
*/
M.core_filepicker.init = function(Y, options) {
/** help function to extract width/height style as a number, not as a string */
Y.Node.prototype.getStylePx = function(attr) {
var style = this.getStyle(attr);
if (''+style == '0' || ''+style == '0px') {
return 0;
}
var matches = style.match(/^([\d\.]+)px$/)
if (matches && parseFloat(matches[1])) {
return parseFloat(matches[1]);
}
return null;
}
/** if condition is met, the class is added to the node, otherwise - removed */
Y.Node.prototype.addClassIf = function(className, condition) {
if (condition) {
this.addClass(className);
} else {
this.removeClass(className);
}
return this;
}
/** sets the width(height) of the node considering existing minWidth(minHeight) */
Y.Node.prototype.setStyleAdv = function(stylename, value) {
var stylenameCap = stylename.substr(0,1).toUpperCase() + stylename.substr(1, stylename.length-1).toLowerCase();
this.setStyle(stylename, '' + Math.max(value, this.getStylePx('min'+stylenameCap)) + 'px')
return this;
}
var FilePickerHelper = function(options) {
FilePickerHelper.superclass.constructor.apply(this, arguments);
};
@ -378,41 +674,15 @@ M.core_filepicker.init = function(Y, options) {
this.fpnode.one('.fp-msg .fp-msg-text').setContent(msg);
this.msg_dlg.show();
},
build_tree: function(node, level) {
var dynload = this.active_repo.dynload;
// prepare file name with icon
var el = Y.Node.create('<div/>').setContent(M.core_filepicker.templates.listfilename);
el.one('.fp-filename').setContent(node.shorttitle ? node.shorttitle : node.title);
// TODO add tooltip with node.title or node.thumbnail_title
if (node.icon && !node.children) {
el.one('.fp-icon').appendChild(Y.Node.create('<img/>').set('src', node.icon));
if (node.realicon) {
this.lazyloading[el.one('.fp-icon img').generateID()] = node.realicon;
}
}
// 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) {
tmpNode.expand();
}
if (dynload) {
tmpNode.scope = this;
}
tmpNode.path = node.path ? node.path : '';
for(var c in node.children) {
this.build_tree(node.children[c], tmpNode);
}
}
},
view_files: function(appenditems) {
this.viewbar_set_enabled(true);
this.print_path();
/*if ((appenditems == null) && (!this.filelist || !this.filelist.length) && !this.active_repo.hasmorepages) {
// TODO do it via classes and adjust for each view mode!
// If there are no items and no next page, just display status message and quit
this.display_error(M.str.repository.nofilesavailable, 'nofilesavailable');
return;
}*/
if (this.viewmode == 2) {
this.view_as_list(appenditems);
} else if (this.viewmode == 3) {
@ -472,23 +742,22 @@ M.core_filepicker.init = function(Y, options) {
}, this), 200)
},
treeview_dynload: function(node, cb) {
var scope = node.scope;
var client_id = scope.options.client_id;
var repository_id = scope.active_repo.id;
var retrieved_children = {};
if (node.children) {
for (var i in node.children) {
retrieved_children[node.children[i].path] = node.children[i];
}
}
scope.request({
this.request({
action:'list',
client_id: client_id,
repository_id: repository_id,
client_id: this.options.client_id,
repository_id: this.active_repo.id,
path:node.path?node.path:'',
page:node.page?args.page:'',
scope:this,
callback: function(id, obj, args) {
var list = obj.list;
var scope = args.scope;
// check that user did not leave the view mode before recieving this response
if (!(scope.active_repo.id == obj.repo_id && scope.viewmode == 2 && node && node.getChildrenEl())) {
return;
@ -496,8 +765,8 @@ M.core_filepicker.init = function(Y, options) {
if (cb != null) { // (in manual mode do not update current path)
scope.viewbar_set_enabled(true);
scope.parse_repository_options(obj);
node.highlight(false);
}
node.highlight(false);
node.origlist = obj.list?obj.list:null;
node.origpath = obj.path?obj.path:null;
node.children = [];
@ -506,13 +775,14 @@ M.core_filepicker.init = function(Y, options) {
// if this child is a folder and has already been retrieved
node.children[node.children.length] = retrieved_children[list[k].path];
} else {
scope.build_tree(list[k], node);
// append new file to the list
scope.view_as_list([list[k]]);
}
}
if (cb == null) {
node.refresh();
} else {
// invoke callback requested by TreeView
// invoke callback requested by TreeView component
cb();
}
scope.content_scrolled();
@ -523,245 +793,108 @@ M.core_filepicker.init = function(Y, options) {
* appends those items to the end of the list. Otherwise (default behaviour)
* clears the contents and displays the items from this.filelist */
view_as_list: function(appenditems) {
var scope = this;
var client_id = scope.options.client_id;
var dynload = scope.active_repo.dynload;
var list = this.filelist;
scope.viewmode = 2;
if (appenditems) {
var parentnode = scope.treeview.getRoot();
if (scope.treeview.getHighlightedNode()) {
parentnode = scope.treeview.getHighlightedNode();
if (parentnode.isLeaf) {parentnode = parentnode.parent;}
}
for (var k in appenditems) {
scope.build_tree(appenditems[k], parentnode);
}
scope.treeview.draw();
return;
}
if (!list || list.length==0 && (!this.filepath || !this.filepath.length)) {
var list = (appenditems != null) ? appenditems : this.filelist;
this.viewmode = 2;
if (!this.filelist || this.filelist.length==0 && (!this.filepath || !this.filepath.length)) {
this.display_error(M.str.repository.nofilesavailable, 'nofilesavailable');
return;
}
var treeviewnode = Y.Node.create('<div/>').
setAttrs({'class':'fp-treeview', id:'treeview-'+client_id});
this.fpnode.one('.fp-content').setContent('').appendChild(treeviewnode);
scope.treeview = new YAHOO.widget.TreeView('treeview-'+client_id);
if (dynload) {
scope.treeview.setDynamicLoad(scope.treeview_dynload, 1);
}
scope.treeview.singleNodeHighlight = true;
if (scope.filepath && scope.filepath.length) {
// we just jumped from icon/details view, we need to show all parents
// we extract as much information as possible from filepath and filelist
// and send additional requests to retrieve siblings for parent folders
var mytree = {};
var mytreeel = null;
for (var i in scope.filepath) {
if (mytreeel == null) {
mytreeel = mytree;
} else {
mytreeel.children = [{}];
mytreeel = mytreeel.children[0];
}
var parent = scope.filepath[i];
mytreeel.path = parent.path;
mytreeel.title = parent.name;
mytreeel.dynamicLoadComplete = true; // we will call it manually
mytreeel.expanded = true;
}
mytreeel.children = scope.filelist
scope.build_tree(mytree, scope.treeview.getRoot());
// manually call dynload for parent elements in the tree so we can load other siblings
if (dynload) {
var root = scope.treeview.getRoot();
while (root && root.children && root.children.length) {
root = root.children[0];
if (root.path == mytreeel.path) {
root.origpath = scope.filepath;
root.origlist = scope.filelist;
} else if (!root.isLeaf && root.expanded) {
scope.treeview_dynload(root, null);
var element_template = Y.Node.create(M.core_filepicker.templates.listfilename);
var options = {
viewmode : this.viewmode,
appendonly : (appenditems != null),
filenode : element_template,
callbackcontext : this,
callback : function(e, node) {
if (!node.children) {
if (e.node.parent && e.node.parent.origpath) {
// set the current path
this.filepath = e.node.parent.origpath;
this.filelist = e.node.parent.origlist;
this.print_path();
}
this.select_file(node);
} else {
// save current path and filelist (in case we want to jump to other viewmode)
this.filepath = e.node.origpath;
this.filelist = e.node.origlist;
this.print_path();
this.content_scrolled();
}
}
} else {
// there is no path information, just display all elements as a list, without hierarchy
for(k in list) {
scope.build_tree(list[k], scope.treeview.getRoot());
}
}
scope.treeview.subscribe('clickEvent', function(e){
e.node.highlight(false);
if(e.node.isLeaf){
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();
}
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;
scope.filelist = e.node.origlist;
scope.print_path();
scope.content_scrolled();
}
});
scope.treeview.draw();
},
dynload : this.active_repo.dynload,
filepath : this.filepath,
treeview_dynload : this.treeview_dynload
};
this.fpnode.one('.fp-content').fp_display_filelist(options, list, this.lazyloading);
},
/** displays list of files in icon view mode. If param appenditems is specified,
* appends those items to the end of the list. Otherwise (default behaviour)
* clears the contents and displays the items from this.filelist */
view_as_icons: function(appenditems) {
var scope = this;
this.viewmode = 1;
var list = this.filelist, container, element_template;
if (!appenditems || !this.filelist || !this.filelist.length) {
if (!list || list.length==0) {
this.display_error(M.str.repository.nofilesavailable, 'nofilesavailable');
return;
}
this.fpnode.one('.fp-content').setContent(M.core_filepicker.templates.iconview);
element_template = this.fpnode.one('.fp-content').one('.fp-file');
container = element_template.get('parentNode');
container.removeChild(element_template);
} else {
list = appenditems;
element_template = Y.Node.create(M.core_filepicker.templates.iconview).one('.fp-file');
container = this.fpnode.one('.fp-content').one('.fp-file').get('parentNode')
var list = (appenditems != null) ? appenditems : this.filelist;
var element_template = Y.Node.create(M.core_filepicker.templates.iconfilename);
if ((appenditems == null) && (!this.filelist || !this.filelist.length)) {
this.display_error(M.str.repository.nofilesavailable, 'nofilesavailable');
return;
}
var count = 0;
for(var k in list) {
var node = list[k];
var element = element_template.cloneNode(true);
container.appendChild(element);
var filename = node.shorttitle ? node.shorttitle : node.title;
var filenamediv = element.one('.fp-filename');
filenamediv.setContent(filename);
var imgdiv = element.one('.fp-thumbnail');
var width = node.thumbnail_width ? node.thumbnail_width : 90;
var height = node.thumbnail_height ? node.thumbnail_height : 90;
filenamediv.setStyleAdv('width', width);
imgdiv.setStyleAdv('width', width).setStyleAdv('height', height);
var img = Y.Node.create('<img/>').setAttrs({src:node.thumbnail,title:node.title});
if(node.thumbnail_alt) {
img.set('alt', node.thumbnail_alt);
}
if(node.thumbnail_title) {
img.set('title', node.thumbnail_title);
}
img.setStyle('maxWidth', ''+width+'px').setStyle('maxHeight', ''+height+'px');
if (node.realthumbnail) {
this.lazyloading[img.generateID()] = node.realthumbnail;
}
imgdiv.appendChild(img)
var dynload = this.active_repo.dynload;
if(node.children) {
element.on('click', function(e, p) {
e.preventDefault();
if(dynload) {
scope.list({'path':p.path});
}else{
this.filepath = p.path;
this.filelist = p.children;
var options = {
viewmode : this.viewmode,
appendonly : (appenditems != null),
filenode : element_template,
callbackcontext : this,
callback : function(e, node) {
e.preventDefault();
if(node.children) {
if (this.active_repo.dynload) {
this.list({'path':node.path});
} else {
this.filepath = node.path;
this.filelist = node.children;
this.view_files();
}
}, this, node);
} else {
element.on('click', function(e, args) {
e.preventDefault();
this.select_file(args);
}, this, list[k]);
} else {
this.select_file(node);
}
}
count++;
}
};
this.fpnode.one('.fp-content').fp_display_filelist(options, list, this.lazyloading);
},
/** displays list of files in table view mode. If param appenditems is specified,
* appends those items to the end of the list. Otherwise (default behaviour)
* clears the contents and displays the items from this.filelist */
view_as_table: function(appenditems) {
var list = this.filelist, scope = this;
var client_id = this.options.client_id;
this.viewmode = 3;
if (appenditems != null) {
this.tableview.addRows(appenditems);
this.tableview.sortable = !this.active_repo.hasmorepages;
return;
}
if (!list || list.length==0) {
var list = (appenditems != null) ? appenditems : this.filelist;
if (!appenditems && (!this.filelist || this.filelist.length==0) && !this.active_repo.hasmorepages) {
this.display_error(M.str.repository.nofilesavailable, 'nofilesavailable');
return;
}
var treeviewnode = Y.Node.create('<div/>').
setAttrs({'class':'fp-tableview', id:'tableview-'+client_id});
this.fpnode.one('.fp-content').setContent('').appendChild(treeviewnode);
var formatValue = function (o){
if (o.data[''+o.column.key+'_f_s']) { return o.data[''+o.column.key+'_f_s']; }
else if (o.data[''+o.column.key+'_f']) { return o.data[''+o.column.key+'_f']; }
else if (o.value) { return o.value; }
else { return ''; }
};
var formatTitle = function(o) {
var el = Y.Node.create('<div/>').setContent(M.core_filepicker.templates.listfilename);
el.one('.fp-filename').setContent(o.data['shorttitle'] ? o.data['shorttitle'] : o.value);
el.one('.fp-icon').appendChild(Y.Node.create('<img/>').set('src', o.data['icon']));
if (o.data['realicon']) {
scope.lazyloading[el.one('.fp-icon img').generateID()] = o.data['realicon'];
}
// TODO add tooltip with o.data['title'] (o.value) or o.data['thumbnail_title']
return el.getContent();
}
var sortFoldersFirst = function(a, b, desc) {
if (a.get('children') && !b.get('children')) {return -1;}
if (!a.get('children') && b.get('children')) {return 1;}
var aa = a.get(this.key), bb = b.get(this.key), dir = desc?-1:1;
if (this.key == 'title' && a.get('shorttitle')) {aa=a.get('shorttitle');}
if (this.key == 'title' && b.get('shorttitle')) {bb=b.get('shorttitle');}
return (aa > bb) ? dir : ((aa < bb) ? -dir : 0);
}
var cols = [
{key: "title", label: M.str.moodle.name, allowHTML: true, formatter: formatTitle,
sortable: true, sortFn: sortFoldersFirst},
{key: "datemodified", label: M.str.moodle.lastmodified, allowHTML: true, formatter: formatValue,
sortable: true, sortFn: sortFoldersFirst},
{key: "size", label: M.str.repository.size, allowHTML: true, formatter: formatValue,
sortable: true, sortFn: sortFoldersFirst},
{key: "type", label: M.str.repository.type, allowHTML: true,
sortable: true, sortFn: sortFoldersFirst}
];
this.tableview = new Y.DataTable({
columns: cols,
data: list,
sortable: !this.active_repo.hasmorepages // allow sorting only if there are no more pages to load
});
this.tableview.render('#tableview-'+client_id);
this.tableview.delegate('click', function (e) {
var record = this.tableview.getRecord(e.currentTarget.get('id'));
if (record) {
var data = record.getAttrs();
if (data.children) {
var element_template = Y.Node.create(M.core_filepicker.templates.listfilename);
var options = {
viewmode : this.viewmode,
appendonly : (appenditems != null),
filenode : element_template,
callbackcontext : this,
sortable : !this.active_repo.hasmorepages,
callback : function(e, node) {
e.preventDefault();
if (node.children) {
if (this.active_repo.dynload) {
this.list({'path':data.path});
this.list({'path':node.path});
} else {
this.filepath = data.path;
this.filelist = data.children;
this.filepath = node.path;
this.filelist = node.children;
this.view_files();
}
} else {
this.select_file(data);
this.select_file(node);
}
}
}, 'tr', this);
};
this.fpnode.one('.fp-content').fp_display_filelist(options, list, this.lazyloading);
},
/** If more than one page available, requests and displays the files from the next page */
request_next_page: function() {

View File

@ -1783,17 +1783,27 @@ abstract class 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()
* @param array|stdClass $listing result of get_listing() or search() or file_get_drafarea_files()
* @return array
*/
public static function prepare_listing($listing) {
global $OUTPUT;
if (!is_array($listing) || !isset($listing['list'])) {
if (is_array($listing) && isset($listing['list'])) {
$files = &$listing['list'];
} else if (is_object($listing) && isset($listing->list)) {
$files = &$listing->list;
} else {
return $listing;
}
$len = count($listing['list']);
$len = count($files);
for ($i=0; $i<$len; $i++) {
$file = & $listing['list'][$i];
if (is_object($files[$i])) {
$file = (array)$files[$i];
$converttoobject = true;
} else {
$file = & $files[$i];
$converttoobject = false;
}
if (isset($file['size'])) {
$file['size'] = (int)$file['size'];
$file['size_f'] = display_size($file['size']);
@ -1820,20 +1830,31 @@ abstract class repository {
}
}
}
if (!isset($file['type']) && !array_key_exists('children', $file) && isset($file['title'])) {
$mimetype = mimeinfo('type', $file['title']);
$isfolder = (array_key_exists('children', $file) || (isset($file['type']) && $file['type'] == 'folder'));
$filename = null;
if (isset($file['title'])) {
$filename = $file['title'];
}
else if (isset($file['fullname'])) {
$filename = $file['fullname'];
}
if (!isset($file['mimetype']) && !$isfolder && $filename) {
$mimetype = mimeinfo('type', $filename);
if (get_string_manager()->string_exists($mimetype, 'mimetypes')) {
$mimetype = get_string($mimetype, 'mimetypes');
}
$file['type'] = $mimetype;
$file['mimetype'] = $mimetype;
}
if (!isset($file['icon']) && isset($file['title'])) {
if (array_key_exists('children', $file)) {
if (!isset($file['icon'])) {
if ($isfolder) {
$file['icon'] = $OUTPUT->pix_url('f/folder')->out(false);
} else {
$file['icon'] = $OUTPUT->pix_url('f/'.mimeinfo('icon', $file['title']))->out(false);
} else if ($filename) {
$file['icon'] = $OUTPUT->pix_url('f/'.mimeinfo('icon', $filename))->out(false);
}
}
if ($converttoobject) {
$files[$i] = (object)$file;
}
}
return $listing;
}

View File

@ -221,6 +221,7 @@ class repository_local_file {
$node['license'] = $this->fileinfo->get_license();
$node['source'] = $encodedpath;
$node['thumbnail'] = $OUTPUT->pix_url(file_extension_icon($node['title'], 32))->out(false);
$node['realthumbnail'] = $this->fileinfo->get_url();
}
return $node;
}

View File

@ -2,40 +2,47 @@
* File picker
*/
/* first or middle sibling, no children */
.file-picker .ygtvtn {background: url([[pix:moodle|y/tn]]) 0 0 no-repeat;width:17px;height:22px;}
.file-picker .ygtvtn, .filemanager .ygtvtn {background: url([[pix:moodle|y/tn]]) 0 0 no-repeat;width:17px;height:22px;}
/* first or middle sibling, collapsable */
.file-picker .ygtvtm {background: url([[pix:moodle|y/tm]]) 0 0 no-repeat;width:34px;height:22px;cursor:pointer;}
.file-picker .ygtvtm, .filemanager .ygtvtm {background: url([[pix:moodle|y/tm]]) 0 0 no-repeat;width:34px;height:22px;cursor:pointer;}
/* first or middle sibling, collapsable, hover */
.file-picker .ygtvtmh {background: url([[pix:moodle|y/tmh]]) 0 0 no-repeat;width:34px;height:22px;cursor:pointer;}
.file-picker .ygtvtmh, .filemanager .ygtvtmh {background: url([[pix:moodle|y/tmh]]) 0 0 no-repeat;width:34px;height:22px;cursor:pointer;}
/* first or middle sibling, expandable */
.file-picker .ygtvtp {background: url([[pix:moodle|y/tp]]) 0 0 no-repeat;width:34px;height:22px;cursor:pointer;}
.file-picker .ygtvtp, .filemanager .ygtvtp {background: url([[pix:moodle|y/tp]]) 0 0 no-repeat;width:34px;height:22px;cursor:pointer;}
/* first or middle sibling, expandable, hover */
.file-picker .ygtvtph {background: url([[pix:moodle|y/tph]]) 0 0 no-repeat;width:34px;height:22px;cursor:pointer;}
.file-picker .ygtvtph, .filemanager .ygtvtph {background: url([[pix:moodle|y/tph]]) 0 0 no-repeat;width:34px;height:22px;cursor:pointer;}
/* last sibling, no children */
.file-picker .ygtvln {background: url([[pix:moodle|y/ln]]) 0 0 no-repeat;width:17px;height:22px;}
.file-picker .ygtvln, .filemanager .ygtvln {background: url([[pix:moodle|y/ln]]) 0 0 no-repeat;width:17px;height:22px;}
/* Last sibling, collapsable */
.file-picker .ygtvlm {background: url([[pix:moodle|y/lm]]) 0 0 no-repeat;width:34px;height:22px;cursor:pointer;}
.file-picker .ygtvlm, .filemanager .ygtvlm {background: url([[pix:moodle|y/lm]]) 0 0 no-repeat;width:34px;height:22px;cursor:pointer;}
/* Last sibling, collapsable, hover */
.file-picker .ygtvlmh {background: url([[pix:moodle|y/lmh]]) 0 0 no-repeat;width:34px;height:22px;cursor:pointer;}
.file-picker .ygtvlmh, .filemanager .ygtvlmh {background: url([[pix:moodle|y/lmh]]) 0 0 no-repeat;width:34px;height:22px;cursor:pointer;}
/* Last sibling, expandable */
.file-picker .ygtvlp {background: url([[pix:moodle|y/lp]]) 0 0 no-repeat;width:34px;height:22px;cursor:pointer;}
.file-picker .ygtvlp, .filemanager .ygtvlp {background: url([[pix:moodle|y/lp]]) 0 0 no-repeat;width:34px;height:22px;cursor:pointer;}
/* Last sibling, expandable, hover */
.file-picker .ygtvlph {background: url([[pix:moodle|y/lph]]) 0 0 no-repeat;width:34px;height:22px;cursor:pointer;}
.file-picker .ygtvlph, .filemanager .ygtvlph {background: url([[pix:moodle|y/lph]]) 0 0 no-repeat;width:34px;height:22px;cursor:pointer;}
/* Loading icon */
.file-picker .ygtvloading {background: url([[pix:moodle|y/loading]]) 0 0 no-repeat;width:16px;height:22px;}
.file-picker .ygtvloading, .filemanager .ygtvloading {background: url([[pix:moodle|y/loading]]) 0 0 no-repeat;width:16px;height:22px;}
/* the style for the empty cells that are used for rendering the depth* of the node */
.file-picker .ygtvdepthcell {background: url([[pix:moodle|y/vline]]) 0 0 no-repeat;width:17px;height:22px;}
.file-picker .ygtvblankdepthcell {width:17px;height:22px;}
.file-picker .ygtvdepthcell, .filemanager .ygtvdepthcell {background: url([[pix:moodle|y/vline]]) 0 0 no-repeat;width:17px;height:22px;}
.file-picker .ygtvblankdepthcell, .filemanager .ygtvblankdepthcell {width:17px;height:22px;}
/* the style of the div around each node */
.file-picker .ygtvitem table{margin-bottom:0;}
.file-picker .ygtvitem td {border:none;padding:0;}
.file-picker .ygtvitem table, .filemanager .ygtvitem table{margin-bottom:0;}
.file-picker .ygtvitem td, .filemanager .ygtvitem td {border:none;padding:0;}
/* the style of the div around each node's collection of children */
* html .file-picker .ygtvchildren {height:1%;}
* html .file-picker .ygtvchildren, * html .filemanager .ygtvchildren {height:1%;}
/* the style of the text label in ygTextNode */
.file-picker .ygtvlabel,
.file-picker .ygtvlabel:link,
.file-picker .ygtvlabel:visited,
.file-picker .ygtvlabel:hover {margin-left:2px;text-decoration: none;}
.file-picker .ygtvlabel:hover,
.filemanager .ygtvlabel,
.filemanager .ygtvlabel:link,
.filemanager .ygtvlabel:visited,
.filemanager .ygtvlabel:hover {margin-left:2px;text-decoration: none;}
.file-picker .fp-treeview .fp-folder .fp-icon,
.filemanager .fp-treeview .fp-folder .fp-icon {display:none;}
.file-picker {font-size:12px;}
.file-picker.fp-generallayout {width:700px;height:480px;}
.file-picker strong {background:#FFFFCC;}
@ -89,8 +96,8 @@
.filemanager-toolbar a {border: 1px solid #AACCEE;background: #F4FAFF;color: black;padding: 3px;}
.filemanager-toolbar a:hover {background: #FFFFFF;}
.filemanager-toolbar .helplink a {border: 0px; background: transparent;}
.fm-breadcrumb {margin:0;}
.filemanager .fm-breadcrumb.fm-empty {display:none;}
.fp-pathbar {margin:0;}
.filemanager .fp-pathbar.empty {display:none;}
.filemanager-container {padding: 5px;margin: 6px 0;background: #E9F4FF;border: #AACCEE 1px solid;position: relative;}
.filemanager-container ul{margin:0;padding:0;}
.filemanager-container ul li{white-space:nowrap;list-style-type:none;}
@ -106,27 +113,46 @@
.filemanager-container,
.filepicker-filelist {overflow:hidden;}
.filemanager.fm-loading .fm-breadcrumb,
.filemanager.fm-loading .fp-pathbar,
.filemanager.fm-loading .filemanager-toolbar,
.filemanager.fm-loading .filemanager-updating,
.filemanager.fm-loading .filemanager-container {display:none;}
.filemanager.fm-loaded .filemanager-loading {display:none;}
.filemanager.fm-nofiles .fm-btn-download {display:none;}
.filemanager.fm-nofiles .fp-btn-download {display:none;}
.filemanager.fm-nomkdir .fm-btn-mkdir {display:none;}
.filemanager.fm-nomkdir .fp-btn-mkdir {display:none;}
.filemanager.fm-maxfiles .fm-btn-add {display:none;}
.filemanager.fm-maxfiles .fp-btn-add {display:none;}
.filemanager.fm-maxfiles .dndupload-message {display:none;}
.filemanager .fm-empty-container {display:none;}
.filemanager.fm-noitems .fm-empty-container {display:block;}
.filemanager.fm-noitems .filemanager-container .fm-filelist {display:none;}
.filemanager.fm-noitems .filemanager-container .fp-content {display:none;}
.filemanager .filemanager-updating {display:none;text-align:center;}
.filemanager.fm-updating .filemanager-updating {display:block;}
.filemanager.fm-updating .filemanager-container {display:none;}
.filemanager.fm-updating .fm-content-wrapper {display:none;}
.filemanager .fp-iconview .fp-file {float:left;text-align:center;}
.filemanager .fp-iconview .fp-file div {overflow: hidden;}
.filemanager .fp-iconview .fp-file .fp-filename {height:48px;text-align:center;min-width:50px;}
.filemanager .fp-select .fp-select-loading {display:none;}
.filemanager .fp-select.loading .fp-select-loading {display:block;}
.filemanager .fp-select.loading form {display:none;}
/* disable unavailable actions: */
/*.filemanager .fp-select.fp-zip .fp-license,*/
.filemanager .fp-select.fp-folder .fp-license,
/*.filemanager .fp-select.fp-zip .fp-author,*/
.filemanager .fp-select.fp-folder .fp-author,
.filemanager .fp-select.fp-file .fp-file-unzip,
.filemanager .fp-select.fp-folder .fp-file-unzip,
.filemanager .fp-select.fp-file .fp-file-zip,
.filemanager .fp-select.fp-zip .fp-file-zip {display:none;}
.filemanager .fp-select .fp-file-setmain {display:none;}
.filemanager .fp-select.fp-cansetmain .fp-file-setmain {display:inline-block;}
.filemanager .fp-select.fp-folder .fp-file-download {display:none;} /* to be implemented */
/*
* Drag and drop support