Merge branch 'MDL-33671-master-3' of git://github.com/peterRd/moodle

This commit is contained in:
Andrew Nicols
2020-03-05 07:57:05 +08:00
13 changed files with 428 additions and 73 deletions
+4 -1
View File
@@ -113,13 +113,16 @@ class core_files_renderer extends plugin_renderer_base {
array('invalidjson', 'repository'), array('popupblockeddownload', 'repository'),
array('unknownoriginal', 'repository'), array('confirmdeletefolder', 'repository'),
array('confirmdeletefilewithhref', 'repository'), array('confirmrenamefolder', 'repository'),
array('confirmrenamefile', 'repository'), array('newfolder', 'repository'), array('edit', 'moodle')
array('confirmrenamefile', 'repository'), array('newfolder', 'repository'), array('edit', 'moodle'),
['nofilesselected', 'repository'], ['confirmdeleteselectedfile', 'repository'],
['selectall', 'moodle'], ['deselectall', 'moodle'], ['selectallornone', 'form'],
)
);
if ($this->page->requires->should_create_one_time_item_now('core_file_managertemplate')) {
$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
+4
View File
@@ -67,6 +67,7 @@ $string['configsyncfiletimeout'] = 'Timeout in seconds for synchronising the ext
$string['configsyncimagetimeout'] = 'Timeout in seconds for downloading an image file from external repository during synchronisation.';
$string['confirmdelete'] = 'Are you sure you want to delete the repository {$a}? If you choose "Continue and download", file references to external contents will be downloaded to Moodle. This could take a long time to process.';
$string['confirmdeletefile'] = 'Are you sure you want to delete this file?';
$string['confirmdeleteselectedfile'] = 'Are you sure you want to delete the selected {$a} file(s)?';
$string['confirmrenamefile'] = 'Are you sure you want to rename/move this file? There are {$a} alias/shortcut files that use this file as their source. If you proceed then those aliases will be converted to true copies.';
$string['confirmdeletefilewithhref'] = 'Are you sure you want to delete this file? There are {$a} alias/shortcut files that use this file as their source. If you proceed then those aliases will be converted to true copies.';
$string['confirmdeletefolder'] = 'Are you sure you want to delete this folder? All files and subfolders will be deleted.';
@@ -97,6 +98,8 @@ $string['displaytree'] = 'Display folder as file tree';
$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';
$string['editrepositoryinstance'] = 'Edit repository instance';
@@ -174,6 +177,7 @@ $string['newfoldername'] = 'New folder name';
$string['noenter'] = 'Nothing entered';
$string['nofilesattached'] = 'No files attached';
$string['nofilesavailable'] = 'No files available';
$string['nofilesselected'] = 'No files selected';
$string['nomorefiles'] = 'No more attachments allowed';
$string['nopathselected'] = 'No destination path select yet (double click tree node to select)';
$string['nopermissiontoaccess'] = 'No permission to access this repository.';
+9 -6
View File
@@ -221,20 +221,23 @@ class behat_field_manager {
}
// If the type is explictly set on the element pointed to by the label - use it.
if ($type = $fieldnode->getParent()->getAttribute('data-fieldtype')) {
if ($type == 'tags') {
$fieldtype = $fieldnode->getAttribute('data-fieldtype');
if ($fieldtype) {
if ($fieldtype == 'tags') {
return 'autocomplete';
}
return $type;
return $fieldtype;
}
if (!empty($fieldnode->find('xpath', '/ancestor::*[@data-passwordunmaskid]'))) {
return 'passwordunmask';
}
// We look for a parent node with 'felement' class.
if ($class = $fieldnode->getParent()->getAttribute('class')) {
// Fetch the parentnode only once.
$parentnode = $fieldnode->getParent();
// We look for a parent node with 'felement' class.
if ($class = $parentnode->getAttribute('class')) {
if (strstr($class, 'felement') != false) {
// Remove 'felement f' from class value.
return substr($class, 10);
@@ -246,7 +249,7 @@ class behat_field_manager {
}
}
return self::get_field_node_type($fieldnode->getParent(), $session);
return self::get_field_node_type($parentnode, $session);
}
/**
+66 -1
View File
@@ -269,10 +269,28 @@ M.form_filemanager.init = function(Y, options) {
is_disabled: function() {
return this.filemanager.ancestor('.fitem.disabled') != null;
},
getSelectedFiles: function() {
var markedFiles = this.filemanager.all('[data-togglegroup=file-selections]: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');
var button_addfile = this.filemanager.one('.fp-btn-add');
var buttonDeleteFile = this.filemanager.one('.fp-btn-delete');
// setup 'add file' button
button_addfile.on('click', this.show_filepicker, this);
@@ -378,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');
@@ -403,6 +423,43 @@ M.form_filemanager.init = function(Y, options) {
});
}, this);
buttonDeleteFile.on('click', function(e) {
e.preventDefault();
var dialogOptions = {};
var filenames = this.getSelectedFiles();
if (!filenames.length) {
this.print_msg(M.util.get_string('nofilesselected', 'repository'), 'error');
return;
}
dialogOptions.scope = this;
var params = {
selected: Y.JSON.stringify(filenames)
};
dialogOptions.header = M.util.get_string('confirm', 'moodle');
dialogOptions.message = M.util.get_string('confirmdeleteselectedfile', 'repository', filenames.length);
dialogOptions.callbackargs = [params];
dialogOptions.callback = function(params) {
this.request({
action: 'deleteselected',
scope: this,
params: params,
callback: function(id, obj, args) {
// Do something here
args.scope.filecount -= params.length;
if (obj && obj.length) {
args.scope.refresh(obj[0]);
}
if (typeof M.core_formchangechecker != 'undefined') {
M.core_formchangechecker.set_form_changed();
}
}
});
};
this.show_confirm_dialog(dialogOptions);
}, this);
this.filemanager.all('.fp-vb-icons,.fp-vb-tree,.fp-vb-details').
on('click', function(e) {
e.preventDefault();
@@ -569,10 +626,17 @@ M.form_filemanager.init = function(Y, options) {
this.viewmode = 1;
element_template = Y.Node.create(M.form_filemanager.templates.iconfilename);
}
if (this.viewmode == 1 || this.viewmode == 2) {
this.filemanager.one('.fp-btn-delete').addClass('d-none');
} else {
this.filemanager.one('.fp-btn-delete').removeClass('d-none');
}
var options = {
viewmode : this.viewmode,
appendonly : appendfiles != null,
filenode : element_template,
disablecheckboxes: false,
callbackcontext : this,
callback : function(e, node) {
if (e.preventDefault) { e.preventDefault(); }
@@ -783,6 +847,7 @@ M.form_filemanager.init = function(Y, options) {
node.one('.fp-dlg-butcancel').on('click', handle_cancel, this);
}
this.confirm_dlg.dlgopt = dialog_options;
this.confirm_dlg.set('headerContent', dialog_options.header);
this.confirm_dlg_node.one('.fp-dlg-text').setContent(dialog_options.message);
this.confirm_dlg.show();
},
@@ -45,10 +45,15 @@
</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>
<div class="fp-btn-delete">
<a role="button" title="{{#str}}deleteselected, repository{{/str}}" class="btn btn-secondary btn-sm" href="#">
{{#pix}}i/trash{{/pix}}
</a>
</div>
<span class="fp-img-downloading">
<span class="sr-only">{{#str}}loadinghelp{{/str}}</span>
{{#pix}}i/loading_small{{/pix}}
+46 -63
View File
@@ -41,7 +41,7 @@ $action = required_param('action', PARAM_ALPHA);
$draftid = required_param('itemid', PARAM_INT);
$filepath = optional_param('filepath', '/', PARAM_PATH);
$user_context = context_user::instance($USER->id);
$usercontext = context_user::instance($USER->id);
echo $OUTPUT->header(); // send headers
@@ -73,7 +73,7 @@ switch ($action) {
$newdirname = required_param('newdirname', PARAM_FILE);
$fs = get_file_storage();
$fs->create_directory($user_context->id, 'user', 'draft', $draftid, file_correct_filepath(file_correct_filepath($filepath).$newdirname));
$fs->create_directory($usercontext->id, 'user', 'draft', $draftid, file_correct_filepath(file_correct_filepath($filepath).$newdirname));
$return = new stdClass();
$return->filepath = $filepath;
echo json_encode($return);
@@ -82,31 +82,28 @@ switch ($action) {
case 'delete':
$filename = required_param('filename', PARAM_FILE);
$filepath = required_param('filepath', PARAM_PATH);
$selectedfile = (object)[
'filename' => $filename,
'filepath' => $filepath
];
$return = repository_delete_selected_files($usercontext, 'user', 'draft', $draftid, [$selectedfile]);
$fs = get_file_storage();
$filepath = file_correct_filepath($filepath);
$return = new stdClass();
if ($stored_file = $fs->get_file($user_context->id, 'user', 'draft', $draftid, $filepath, $filename)) {
$parent_path = $stored_file->get_parent_directory()->get_filepath();
if ($stored_file->is_directory()) {
$files = $fs->get_directory_files($user_context->id, 'user', 'draft', $draftid, $filepath, true);
foreach ($files as $file) {
$file->delete();
}
$stored_file->delete();
$return->filepath = $parent_path;
echo json_encode($return);
} else {
if($result = $stored_file->delete()) {
$return->filepath = $parent_path;
echo json_encode($return);
} else {
echo json_encode(false);
}
}
} else {
echo json_encode(false);
if ($return) {
$response = new stdClass();
$response->filepath = array_keys($return)[0];
echo json_encode($response);
die;
}
echo json_encode(false);
die;
case 'deleteselected':
$selected = required_param('selected', PARAM_RAW);
$return = [];
$selectedfiles = json_decode($selected);
$return = repository_delete_selected_files($usercontext, 'user', 'draft', $draftid, $selectedfiles);
echo (json_encode($return ? array_keys($return) : false));
die;
case 'setmainfile':
@@ -115,9 +112,9 @@ switch ($action) {
$filepath = file_correct_filepath($filepath);
// reset sort order
file_reset_sortorder($user_context->id, 'user', 'draft', $draftid);
file_reset_sortorder($usercontext->id, 'user', 'draft', $draftid);
// set main file
$return = file_set_sortorder($user_context->id, 'user', 'draft', $draftid, $filepath, $filename, 1);
$return = file_set_sortorder($usercontext->id, 'user', 'draft', $draftid, $filepath, $filename, 1);
echo json_encode($return);
die;
@@ -159,7 +156,7 @@ switch ($action) {
$zipper = get_file_packer('application/zip');
$fs = get_file_storage();
$file = $fs->get_file($user_context->id, 'user', 'draft', $draftid, $filepath, '.');
$file = $fs->get_file($usercontext->id, 'user', 'draft', $draftid, $filepath, '.');
$parent_path = $file->get_parent_directory()->get_filepath();
@@ -167,7 +164,7 @@ switch ($action) {
$filepath = array_pop($filepath);
$zipfile = repository::get_unused_filename($draftid, $parent_path, $filepath . '.zip');
if ($newfile = $zipper->archive_to_storage(array($filepath => $file), $user_context->id, 'user', 'draft', $draftid, $parent_path, $zipfile, $USER->id)) {
if ($newfile = $zipper->archive_to_storage([$filepath => $file], $usercontext->id, 'user', 'draft', $draftid, $parent_path, $zipfile, $USER->id)) {
$return = new stdClass();
$return->filepath = $parent_path;
echo json_encode($return);
@@ -175,36 +172,22 @@ switch ($action) {
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($user_context->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(array('/' => $stored_file), $user_context->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':
@@ -215,15 +198,15 @@ switch ($action) {
$fs = get_file_storage();
$file = $fs->get_file($user_context->id, 'user', 'draft', $draftid, $filepath, $filename);
$file = $fs->get_file($usercontext->id, 'user', 'draft', $draftid, $filepath, $filename);
// Find unused name for directory to extract the archive.
$temppath = $fs->get_unused_dirname($user_context->id, 'user', 'draft', $draftid, $filepath. pathinfo($filename, PATHINFO_FILENAME). '/');
$temppath = $fs->get_unused_dirname($usercontext->id, 'user', 'draft', $draftid, $filepath. pathinfo($filename, PATHINFO_FILENAME). '/');
$donotremovedirs = array();
$doremovedirs = array($temppath);
// Extract archive and move all files from $temppath to $filepath
if ($file->extract_to_storage($zipper, $user_context->id, 'user', 'draft', $draftid, $temppath, $USER->id) !== false) {
$extractedfiles = $fs->get_directory_files($user_context->id, 'user', 'draft', $draftid, $temppath, true);
if ($file->extract_to_storage($zipper, $usercontext->id, 'user', 'draft', $draftid, $temppath, $USER->id) !== false) {
$extractedfiles = $fs->get_directory_files($usercontext->id, 'user', 'draft', $draftid, $temppath, true);
$xtemppath = preg_quote($temppath, '|');
foreach ($extractedfiles as $file) {
$realpath = preg_replace('|^'.$xtemppath.'|', $filepath, $file->get_filepath());
@@ -231,7 +214,7 @@ switch ($action) {
// Set the source to the extracted file to indicate that it came from archive.
$file->set_source(serialize((object)array('source' => $filepath)));
}
if (!$fs->file_exists($user_context->id, 'user', 'draft', $draftid, $realpath, $file->get_filename())) {
if (!$fs->file_exists($usercontext->id, 'user', 'draft', $draftid, $realpath, $file->get_filename())) {
// File or directory did not exist, just move it.
$file->rename($realpath, $file->get_filename());
} else if (!$file->is_directory()) {
@@ -250,7 +233,7 @@ switch ($action) {
}
// Remove remaining temporary directories.
foreach (array_diff($doremovedirs, $donotremovedirs) as $filepath) {
if ($file = $fs->get_file($user_context->id, 'user', 'draft', $draftid, $filepath, '.')) {
if ($file = $fs->get_file($usercontext->id, 'user', 'draft', $draftid, $filepath, '.')) {
$file->delete();
}
}
@@ -261,7 +244,7 @@ switch ($action) {
$filepath = required_param('filepath', PARAM_PATH);
$fs = get_file_storage();
$file = $fs->get_file($user_context->id, 'user', 'draft', $draftid, $filepath, $filename);
$file = $fs->get_file($usercontext->id, 'user', 'draft', $draftid, $filepath, $filename);
if (!$file) {
echo json_encode(false);
} else {
@@ -275,7 +258,7 @@ switch ($action) {
$filepath = required_param('filepath', PARAM_PATH);
$fs = get_file_storage();
$file = $fs->get_file($user_context->id, 'user', 'draft', $draftid, $filepath, $filename);
$file = $fs->get_file($usercontext->id, 'user', 'draft', $draftid, $filepath, $filename);
if (!$file) {
echo json_encode(false);
} else {
+63 -1
View File
@@ -317,6 +317,36 @@ 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/>')
.setAttribute('type', 'checkbox')
.setAttribute('data-fieldtype', 'checkbox')
.setAttribute('data-fullname', o.data.fullname)
.setAttribute('data-action', 'toggle')
.setAttribute('data-toggle', 'slave')
.setAttribute('data-togglegroup', 'file-selections')
.setAttribute('data-toggle-selectall', M.util.get_string('selectall', 'moodle'))
.setAttribute('data-toggle-deselectall', M.util.get_string('deselectall', 'moodle'));
var checkboxLabel = Y.Node.create('<label>')
.setHTML("Select file '" + o.data.fullname + "'")
.addClass('sr-only')
.setAttrs({
for: checkbox.generateID(),
});
el.appendChild(checkbox);
el.appendChild(checkboxLabel);
return el.getContent();
};
/** sorting function for table view */
var sortFoldersFirst = function(a, b, desc) {
if (a.get('isfolder') && !b.get('isfolder')) {
@@ -340,6 +370,37 @@ YUI.add('moodle-core_filepicker', function(Y) {
{key: "mimetype", label: M.util.get_string('type', 'repository'), allowHTML: true,
sortable: true, sortFn: sortFoldersFirst}
];
// Generate a checkbox based on toggleall's specification
var div = Y.Node.create('<div/>');
var checkbox = Y.Node.create('<input/>')
.setAttribute('type', 'checkbox')
// .setAttribute('title', M.util.get_string('selectallornone', 'form'))
.setAttribute('data-action', 'toggle')
.setAttribute('data-toggle', 'master')
.setAttribute('data-togglegroup', 'file-selections');
var checkboxLabel = Y.Node.create('<label>')
.setHTML(M.util.get_string('selectallornone', 'form'))
.addClass('sr-only')
.setAttrs({
for: checkbox.generateID(),
});
div.appendChild(checkboxLabel);
div.appendChild(checkbox);
// Enable the selectable checkboxes
if (options.disablecheckboxes != undefined && !options.disablecheckboxes) {
cols.unshift({
key: "",
label: div.getContent(),
allowHTML: true,
formatter: formatCheckbox,
sortable: false
});
}
scope.tableview = new Y.DataTable({columns: cols, data: fileslist});
scope.tableview.delegate('click', function (e, tableview) {
var record = tableview.getRecord(e.currentTarget.get('id'));
@@ -350,7 +411,8 @@ YUI.add('moodle-core_filepicker', function(Y) {
}
Y.bind(callback, this)(e, record.getAttrs());
}
}, 'tr', options.callbackcontext, scope.tableview);
}, 'tr td:not(:first-child)', options.callbackcontext, scope.tableview);
if (options.rightclickcallback) {
scope.tableview.delegate('contextmenu', function (e, tableview) {
var record = tableview.getRecord(e.currentTarget.get('id'));
+98
View File
@@ -3209,3 +3209,101 @@ function initialise_filepicker($args) {
}
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_delete_selected_files($context, string $component, string $filearea, $itemid, array $files) {
$fs = get_file_storage();
$return = [];
foreach ($files as $selectedfile) {
$filename = clean_filename($selectedfile->filename);
$filepath = clean_param($selectedfile->filepath, PARAM_PATH);
$filepath = file_correct_filepath($filepath);
if ($storedfile = $fs->get_file($context->id, $component, $filearea, $itemid, $filepath, $filename)) {
$parentpath = $storedfile->get_parent_directory()->get_filepath();
if ($storedfile->is_directory()) {
$files = $fs->get_directory_files($context->id, $component, $filearea, $itemid, $filepath, true);
foreach ($files as $file) {
$file->delete();
}
$storedfile->delete();
$return[$parentpath] = "";
} else {
if ($result = $storedfile->delete()) {
$return[$parentpath] = "";
}
}
}
}
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;
}
@@ -19,3 +19,78 @@ Feature: Delete files and folders from the file manager
And I delete "Delete me" from "Files" filemanager
And I press "Save changes"
And I should not see "Delete me"
@javascript
Scenario: Delete a file and a folder using bulk functionality (individually)
Given I log in as "admin"
And I follow "Manage private files"
And I upload "lib/tests/fixtures/empty.txt" file to "Files" filemanager
And I create "Delete me later" folder in "Files" filemanager
And I press "Save changes"
And I follow "Manage private files"
And I click on "Display folder with file details" "link"
And I set the field "Select file 'empty.txt'" to "1"
When I click on "Delete selected" "link"
Then I should see "Are you sure you want to delete the selected 1 file(s)?"
When I click on "OK" "button" in the "Confirm" "dialogue"
Then I should not see "empty.txt"
But I should see "Delete me later"
When I press "Save changes"
And I follow "Manage private files"
Then I should not see "empty.txt"
But I should see "Delete me later"
And I set the field "Select file 'Delete me later'" to "1"
And I click on "Delete selected" "link"
And I click on "OK" "button" in the "Confirm" "dialogue"
Then I should not see "Delete me later"
When I press "Save changes"
Then I should not see "Delete me later"
@javascript
Scenario: Delete a file and a folder using bulk functionality (multiple)
Given I log in as "admin"
And I follow "Manage private files"
And I upload "lib/tests/fixtures/empty.txt" file to "Files" filemanager
And I create "Delete me" folder in "Files" filemanager
And I create "Do not delete me" folder in "Files" filemanager
And I press "Save changes"
And I follow "Manage private files"
And I click on "Display folder with file details" "link"
And I set the field "Select file 'empty.txt'" to "1"
And I set the field "Select file 'Delete me'" to "1"
When I click on "Delete selected" "link"
Then I should see "Are you sure you want to delete the selected 2 file(s)?"
When I click on "OK" "button" in the "Confirm" "dialogue"
Then I should not see "Delete me"
And I should not see "empty.txt"
But I should see "Do not delete me"
When I press "Save changes"
Then I should not see "Delete me" in the "Private files" "block"
And I should not see "empty.txt" in the "Private files" "block"
But I should see "Do not delete me" in the "Private files" "block"
@javascript
Scenario: Delete files using the select all checkbox
Given I log in as "admin"
And I follow "Manage private files"
And I upload "lib/tests/fixtures/empty.txt" file to "Files" filemanager
And I create "Delete me" folder in "Files" filemanager
And I create "Delete me too" folder in "Files" filemanager
And I press "Save changes"
And I follow "Manage private files"
And I click on "Display folder with file details" "link"
When I set the field "Select all/none" to "1"
Then the following fields match these values:
| Select file 'empty.txt' | 1 |
| Select file 'Delete me' | 1 |
| Select file 'Delete me too' | 1 |
When I click on "Delete selected" "link"
Then I should see "Are you sure you want to delete the selected 3 file(s)?"
When I click on "OK" "button" in the "Confirm" "dialogue"
Then I should not see "Delete me"
And I should not see "empty.txt"
And I should not see "Delete me too"
When I press "Save changes"
Then I should not see "Delete me" in the "Private files" "block"
And I should not see "empty.txt" in the "Private files" "block"
But I should not see "Delete me too" in the "Private files" "block"
+54
View File
@@ -154,6 +154,60 @@ class core_repositorylib_testcase extends advanced_testcase {
}
}
public function test_delete_selected_files() {
global $USER;
$this->resetAfterTest(true);
$this->setAdminUser();
$fs = get_file_storage();
$draftitemid = file_get_unused_draft_itemid();
$context = context_user::instance($USER->id);
$dummy = [
'contextid' => $context->id,
'component' => 'user',
'filearea' => 'draft',
'itemid' => $draftitemid,
'filepath' => '/',
'filename' => ''
];
// Create some files.
$existingfiles = [
'The Matrix.movie',
'Astalavista.txt',
'foobar',
];
$selectedfiles = [
'The Matrix.movie' => [],
'Astalavista.txt' => []
];
foreach ($existingfiles as $filename) {
$dummy['filename'] = $filename;
$file = $fs->create_file_from_string($dummy, 'Content of ' . $filename);
if (array_key_exists($filename, $selectedfiles)) {
$selectedfiles[$filename] = (object)[
'filename' => $filename,
'filepath' => $file->get_filepath()
];
}
}
// Get area files with default options.
$areafiles = $fs->get_area_files($context->id, 'user', 'draft', $draftitemid);
// Should be the 3 files we added plus the folder.
$this->assertEquals(4, count($areafiles));
repository_delete_selected_files($context, 'user', 'draft', $draftitemid, $selectedfiles);
$areafiles = $fs->get_area_files($context->id, 'user', 'draft', $draftitemid);
// Should be the 1 file left plus the folder.
$this->assertEquals(2, count($areafiles));
}
public function test_can_be_edited_by_user() {
$this->resetAfterTest(true);
+1
View File
@@ -553,6 +553,7 @@ a.ygtvspacer:hover {
.filemanager.fm-maxfiles .fp-btn-add,
.filemanager.fm-maxfiles .dndupload-message,
.filemanager.fm-noitems .fp-btn-download,
.filemanager.fm-noitems .fp-btn-delete,
.filemanager .fm-empty-container,
.filemanager.fm-noitems .filemanager-container .fp-content {
display: none;
+1
View File
@@ -13933,6 +13933,7 @@ a.ygtvspacer:hover {
.filemanager.fm-maxfiles .fp-btn-add,
.filemanager.fm-maxfiles .dndupload-message,
.filemanager.fm-noitems .fp-btn-download,
.filemanager.fm-noitems .fp-btn-delete,
.filemanager .fm-empty-container,
.filemanager.fm-noitems .filemanager-container .fp-content {
display: none; }
+1
View File
@@ -14148,6 +14148,7 @@ a.ygtvspacer:hover {
.filemanager.fm-maxfiles .fp-btn-add,
.filemanager.fm-maxfiles .dndupload-message,
.filemanager.fm-noitems .fp-btn-download,
.filemanager.fm-noitems .fp-btn-delete,
.filemanager .fm-empty-container,
.filemanager.fm-noitems .filemanager-container .fp-content {
display: none; }