Skip to content

Commit

Permalink
MDL-33671 core: Add ability to download selected items
Browse files Browse the repository at this point in the history
Utilise the new checkbox-toggleall functionality
  • Loading branch information
Peter committed Mar 2, 2020
1 parent 5c567c1 commit a216d86
Show file tree
Hide file tree
Showing 8 changed files with 121 additions and 54 deletions.
1 change: 1 addition & 0 deletions files/renderer.php
Expand Up @@ -121,6 +121,7 @@ public function render_form_filemanager($fm) {
$this->page->requires->js_init_call('M.form_filemanager.set_templates',
array($this->filemanager_js_templates()), true, $module);
}
$this->page->requires->js_call_amd('core/checkbox-toggleall', 'init');
$this->page->requires->js_init_call('M.form_filemanager.init', array($fm->options), true, $module);

// non javascript file manager
Expand Down
1 change: 1 addition & 0 deletions lang/en/repository.php
Expand Up @@ -98,6 +98,7 @@
$string['download'] = 'Download';
$string['downloadallfiles'] = 'Download all files';
$string['downloadfolder'] = 'Download all';
$string['downloadselected'] = 'Download selected files';
$string['deleteselected'] = 'Delete selected';
$string['downloadsucc'] = 'The file has been downloaded successfully';
$string['draftareanofiles'] = 'Cannot be downloaded because there is no files attached';
Expand Down
37 changes: 22 additions & 15 deletions lib/form/filemanager.js
Expand Up @@ -269,6 +269,23 @@ M.form_filemanager.init = function(Y, options) {
is_disabled: function() {
return this.filemanager.ancestor('.fitem.disabled') != null;
},
getSelectedFiles: function() {
var markedFiles = this.filemanager.all('.mark-for-selection:checked');
var filenames = [];
markedFiles.each(function(item) {
var fileinfo = this.options.list.find(function(element) {
return item.getData().fullname == element.fullname;
});
if (fileinfo && fileinfo != undefined) {
filenames.push({
filepath: fileinfo.filepath,
filename: fileinfo.filename
});
}
}, this);

return filenames;
},
setup_buttons: function() {
var button_download = this.filemanager.one('.fp-btn-download');
var button_create = this.filemanager.one('.fp-btn-mkdir');
Expand Down Expand Up @@ -379,11 +396,13 @@ M.form_filemanager.init = function(Y, options) {
return;
}
image_downloading.setStyle('display', 'inline');
var filenames = this.getSelectedFiles();

// perform downloaddir ajax request
this.request({
action: 'downloaddir',
action: 'downloadselected',
scope: scope,
params: {selected: Y.JSON.stringify(filenames)},
callback: function(id, obj, args) {
var image_downloading = scope.filemanager.one('.fp-img-downloading');
image_downloading.setStyle('display', 'none');
Expand All @@ -407,19 +426,7 @@ M.form_filemanager.init = function(Y, options) {
buttonDeleteFile.on('click', function(e) {
e.preventDefault();
var dialogOptions = {};
var markedForDeletion = this.filemanager.all('.mark-for-deletion:checked');
var filenames = [];
markedForDeletion.each(function(item) {
var fileinfo = this.options.list.find(function(element) {
return item.getData().fullname == element.fullname;
});
if (fileinfo && fileinfo != undefined) {
filenames.push({
filepath: fileinfo.filepath,
filename: fileinfo.filename
});
}
}, this);
var filenames = this.getSelectedFiles();

if (!filenames.length) {
this.print_msg(M.util.get_string('nofilesselected', 'repository'), 'error');
Expand Down Expand Up @@ -628,7 +635,7 @@ M.form_filemanager.init = function(Y, options) {
viewmode : this.viewmode,
appendonly : appendfiles != null,
filenode : element_template,
disablecheckboxes : false,
disablecheckboxes: false,
callbackcontext : this,
callback : function(e, node) {
if (e.preventDefault) { e.preventDefault(); }
Expand Down
2 changes: 1 addition & 1 deletion lib/templates/filemanager_page_generallayout.mustache
Expand Up @@ -45,7 +45,7 @@
</a>
</div>
<div class="fp-btn-download">
<a role="button" title="{{#str}}downloadfolder, repository{{/str}}" class="btn btn-secondary btn-sm" href="#">
<a role="button" title="{{#str}}downloadselected, repository{{/str}}" class="btn btn-secondary btn-sm" href="#">
{{#pix}}a/download_all{{/pix}}
</a>
</div>
Expand Down
38 changes: 12 additions & 26 deletions repository/draftfiles_ajax.php
Expand Up @@ -172,36 +172,22 @@
echo json_encode(false);
}
die;
case 'downloadselected':
$selected = required_param('selected', PARAM_RAW);
$selectedfiles = json_decode($selected);
$return = repository_download_selected_files($usercontext, 'user', 'draft', $draftid, $selectedfiles);
echo (json_encode($return));
die;

case 'downloaddir':
$filepath = required_param('filepath', PARAM_PATH);

$zipper = get_file_packer('application/zip');
$fs = get_file_storage();
$area = file_get_draft_area_info($draftid, $filepath);
if ($area['filecount'] == 0 && $area['foldercount'] == 0) {
echo json_encode(false);
die;
}

$stored_file = $fs->get_file($usercontext->id, 'user', 'draft', $draftid, $filepath, '.');
if ($filepath === '/') {
$filename = get_string('files').'.zip';
} else {
$filename = explode('/', trim($filepath, '/'));
$filename = array_pop($filename) . '.zip';
}

// archive compressed file to an unused draft area
$newdraftitemid = file_get_unused_draft_itemid();
if ($newfile = $zipper->archive_to_storage(['/' => $stored_file], $usercontext->id, 'user', 'draft', $newdraftitemid, '/', $filename, $USER->id)) {
$return = new stdClass();
$return->fileurl = moodle_url::make_draftfile_url($newdraftitemid, '/', $filename)->out();
$return->filepath = $filepath;
echo json_encode($return);
} else {
echo json_encode(false);
}
$selectedfile = (object)[
'filename' => '',
'filepath' => $filepath
];
$return = repository_download_selected_files($usercontext, 'user', 'draft', $draftid, [$selectedfile]);
echo json_encode($return);
die;

case 'unzip':
Expand Down
36 changes: 25 additions & 11 deletions repository/filepicker.js
Expand Up @@ -317,12 +317,23 @@ YUI.add('moodle-core_filepicker', function(Y) {
// TODO add tooltip with o.data['title'] (o.value) or o.data['thumbnail_title']
return el.getContent();
}

/**
* Generate slave checkboxes based on toggleall's specification
* @param {object} o An object reprsenting the record for the current row.
* @return {html} The checkbox html
*/
var formatCheckbox = function(o) {
var el = Y.Node.create('<div/>');
var checkbox = Y.Node.create('<input/>');
checkbox.setAttribute('type', 'checkbox')
.setAttribute('class', 'mark-for-deletion')
.setAttribute('data-fullname', o.data.fullname);
.setAttribute('class', 'mark-for-selection')
.setAttribute('data-fullname', o.data.fullname)
.setAttribute('data-action', 'toggle')
.setAttribute('data-toggle', 'slave')
.setAttribute('data-togglegroup', 'file-selections')
.setAttribute('data-toggle-selectall', 'Select all')
.setAttribute('data-toggle-deselectall', 'Deselectall');

el.appendChild(checkbox);
return el.getContent();
Expand Down Expand Up @@ -351,10 +362,20 @@ YUI.add('moodle-core_filepicker', function(Y) {
sortable: true, sortFn: sortFoldersFirst}
];

// Generate a checkbox based on toggleall's specification
var checkbox = Y.Node.create('<input/>');
var div = Y.Node.create('<div/>');
checkbox.setAttribute('type', 'checkbox')
.setAttribute('class', 'mark-for-selection')
.setAttribute('data-action', 'toggle')
.setAttribute('data-toggle', 'master')
.setAttribute('data-togglegroup', 'file-selections');
div.appendChild(checkbox);

// Enable the selectable checkboxes
if (options.disablecheckboxes != undefined && !options.disablecheckboxes) {
cols.unshift({
key: "", label: "<input type='checkbox' id='select-all'/>",
key: "", label: div.getContent(),
allowHTML: true, formatter: formatCheckbox,
sortable: false
});
Expand All @@ -370,14 +391,7 @@ YUI.add('moodle-core_filepicker', function(Y) {
Y.bind(callback, this)(e, record.getAttrs());
}
}, 'tr td:not(:first-child)', options.callbackcontext, scope.tableview);
scope.tableview.delegate('change', function(e) {
e.preventDefault();
if (e.target.get('checked')) {
e.container.all('.mark-for-deletion').setAttribute('checked', true);
} else {
e.container.all('.mark-for-deletion').removeAttribute('checked');
}
}, '#select-all', options.callbackcontext, scope.tableview);

if (options.rightclickcallback) {
scope.tableview.delegate('contextmenu', function (e, tableview) {
var record = tableview.getRecord(e.currentTarget.get('id'));
Expand Down
58 changes: 58 additions & 0 deletions repository/lib.php
Expand Up @@ -3249,3 +3249,61 @@ function repository_delete_selected_files($context, string $component, string $f

return $return;
}

/**
* Convenience function to handle deletion of files.
*
* @param object $context The context where the delete is called
* @param string $component component
* @param string $filearea filearea
* @param int $itemid the item id
* @param array $files Array of files object with each item having filename/filepath as values
* @return array $return Array of strings matching up to the parent directory of the deleted files
* @throws coding_exception
*/
function repository_download_selected_files($context, string $component, string $filearea, $itemid, array $files) {
global $USER;
$return = false;

$zipper = get_file_packer('application/zip');
$fs = get_file_storage();
// Archive compressed file to an unused draft area.
$newdraftitemid = file_get_unused_draft_itemid();
$filestoarchive = [];

foreach ($files as $selectedfile) {
$filename = clean_filename($selectedfile->filename); // Default to '.' for root.
$filepath = clean_param($selectedfile->filepath, PARAM_PATH); // Default to '/' for downloadall.
$filepath = file_correct_filepath($filepath);
$area = file_get_draft_area_info($itemid, $filepath);
if ($area['filecount'] == 0 && $area['foldercount'] == 0) {
continue;
}

$storedfile = $fs->get_file($context->id, $component, $filearea, $itemid, $filepath, $filename);
// If it is empty we are downloading a directory.
$archivefile = $storedfile->get_filename();
if (!$filename || $filename == '.' ) {
$archivefile = $filepath;
}

$filestoarchive[$archivefile] = $storedfile;
}
$zippedfile = get_string('files') . '.zip';
if ($newfile =
$zipper->archive_to_storage(
$filestoarchive,
$context->id,
$component,
$filearea,
$newdraftitemid,
"/",
$zippedfile, $USER->id)
) {
$return = new stdClass();
$return->fileurl = moodle_url::make_draftfile_url($newdraftitemid, '/', $zippedfile)->out();
$return->filepath = $filepath;
}

return $return;
}
2 changes: 1 addition & 1 deletion repository/tests/behat/behat_filepicker.php
Expand Up @@ -185,7 +185,7 @@ public function i_delete_file_from_filemanager($name, $filemanagerelement) {
*/
public function i_mark_for_deletion_from_filemanager($name) {
$name = behat_context_helper::escape($name);
$okbutton = $this->find('css', "input.mark-for-deletion[data-fullname=$name]");
$okbutton = $this->find('css', "input.mark-for-selection[data-fullname=$name]");
$okbutton->click();
}

Expand Down

0 comments on commit a216d86

Please sign in to comment.