Skip to content

Commit e64eb49

Browse files
OrangeJuicedDaneEveritt
authored andcommitted
Add multiple file/directory deletion in the filemanager (pterodactyl#544)
* Add deletion of multiple selected files * Adjust success/failure text to properly represent multiple files * Actually update the minimized versions with the new code * Use let instead of var and seperate items into seperate code tags * Deleting the selected items now supports the new endpoint * Replaced the select buttons with checkboxes * Selections is now handled by find all the selected checkboxes * Add a warning if no files/folders are selected when pressing delete * Add a select all files/folders checkbox * Move mass delete button into a mass actions dropdown * Move style to css file * Actually update the minimized files (again) * Mass actions button is now disabled by default * Clicking on a row selects the checkbox and enables the actions button * Fix clicking anything else but the row or checkbox triggering selection
1 parent 0def417 commit e64eb49

File tree

7 files changed

+203
-13
lines changed

7 files changed

+203
-13
lines changed

public/themes/pterodactyl/css/pterodactyl.css

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -295,3 +295,25 @@ input.form-autocomplete-stop[readonly] {
295295
background: white;
296296
box-shadow: none !important;
297297
}
298+
299+
.dropdown-massactions {
300+
min-width: 80px;
301+
}
302+
303+
.select-all-files {
304+
position: relative;
305+
bottom: 1px;
306+
margin-right: 7px !important;
307+
}
308+
309+
.select-file {
310+
position: relative;
311+
bottom: 1px;
312+
margin-right: 2px !important;
313+
}
314+
315+
.select-folder {
316+
position: relative;
317+
bottom: 1px;
318+
margin-right: 5px !important;
319+
}

public/themes/pterodactyl/js/frontend/files/filemanager.min.js

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

public/themes/pterodactyl/js/frontend/files/filemanager.min.js.map

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

public/themes/pterodactyl/js/frontend/files/src/actions.js

Lines changed: 127 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -292,12 +292,17 @@ class ActionsClass {
292292
showLoaderOnConfirm: true
293293
}, () => {
294294
$.ajax({
295-
type: 'DELETE',
296-
url: `${Pterodactyl.node.scheme}://${Pterodactyl.node.fqdn}:${Pterodactyl.node.daemonListen}/server/file/f/${delPath}${delName}`,
295+
type: 'POST',
297296
headers: {
298297
'X-Access-Token': Pterodactyl.server.daemonSecret,
299298
'X-Access-Server': Pterodactyl.server.uuid,
300-
}
299+
},
300+
contentType: 'application/json; charset=utf-8',
301+
url: `${Pterodactyl.node.scheme}://${Pterodactyl.node.fqdn}:${Pterodactyl.node.daemonListen}/server/file/delete`,
302+
timeout: 10000,
303+
data: JSON.stringify({
304+
items: [`${delPath}${delName}`]
305+
}),
301306
}).done(data => {
302307
nameBlock.parent().addClass('warning').delay(200).fadeOut();
303308
swal({
@@ -316,6 +321,125 @@ class ActionsClass {
316321
});
317322
}
318323

324+
toggleMassActions() {
325+
if ($('#file_listing input[type="checkbox"]:checked').length) {
326+
$('#mass_actions').removeClass('disabled');
327+
} else {
328+
$('#mass_actions').addClass('disabled');
329+
}
330+
}
331+
332+
toggleHighlight(event) {
333+
const parent = $(event.currentTarget);
334+
const item = $(event.currentTarget).find('input');
335+
336+
if($(item).is(':checked')) {
337+
$(item).prop('checked', false);
338+
parent.removeClass('warning').delay(200);
339+
} else {
340+
$(item).prop('checked', true);
341+
parent.addClass('warning').delay(200);
342+
}
343+
}
344+
345+
highlightAll(event) {
346+
let parent;
347+
const item = $(event.currentTarget).find('input');
348+
349+
if($(item).is(':checked')) {
350+
$('#file_listing input[type=checkbox]').prop('checked', false);
351+
$('#file_listing input[data-action="addSelection"]').each(function() {
352+
parent = $(this).closest('tr');
353+
parent.removeClass('warning').delay(200);
354+
});
355+
} else {
356+
$('#file_listing input[type=checkbox]').prop('checked', true);
357+
$('#file_listing input[data-action="addSelection"]').each(function() {
358+
parent = $(this).closest('tr');
359+
parent.addClass('warning').delay(200);
360+
});
361+
}
362+
}
363+
364+
deleteSelected() {
365+
let selectedItems = [];
366+
let selectedItemsElements = [];
367+
let parent;
368+
let nameBlock;
369+
let delLocation;
370+
371+
$('#file_listing input[data-action="addSelection"]:checked').each(function() {
372+
parent = $(this).closest('tr');
373+
nameBlock = $(parent).find('td[data-identifier="name"]');
374+
delLocation = decodeURIComponent(nameBlock.data('path')) + decodeURIComponent(nameBlock.data('name'));
375+
376+
selectedItems.push(delLocation);
377+
selectedItemsElements.push(parent);
378+
});
379+
380+
if (selectedItems.length != 0)
381+
{
382+
let formattedItems = "";
383+
$.each(selectedItems, function(key, value) {
384+
formattedItems += ("<code>" + value + "</code>, ");
385+
})
386+
387+
formattedItems = formattedItems.slice(0, -2);
388+
389+
swal({
390+
type: 'warning',
391+
title: '',
392+
text: 'Are you sure you want to delete:' + formattedItems + '? There is <strong>no</strong> reversing this action.',
393+
html: true,
394+
showCancelButton: true,
395+
showConfirmButton: true,
396+
closeOnConfirm: false,
397+
showLoaderOnConfirm: true
398+
}, () => {
399+
$.ajax({
400+
type: 'POST',
401+
headers: {
402+
'X-Access-Token': Pterodactyl.server.daemonSecret,
403+
'X-Access-Server': Pterodactyl.server.uuid,
404+
},
405+
contentType: 'application/json; charset=utf-8',
406+
url: `${Pterodactyl.node.scheme}://${Pterodactyl.node.fqdn}:${Pterodactyl.node.daemonListen}/server/file/delete`,
407+
timeout: 10000,
408+
data: JSON.stringify({
409+
items: selectedItems
410+
}),
411+
}).done(data => {
412+
$('#file_listing input:checked').each(function() {
413+
$(this).prop('checked', false);
414+
});
415+
416+
$.each(selectedItemsElements, function() {
417+
$(this).addClass('warning').delay(200).fadeOut();
418+
})
419+
420+
swal({
421+
type: 'success',
422+
title: 'Files Deleted'
423+
});
424+
}).fail(jqXHR => {
425+
console.error(jqXHR);
426+
swal({
427+
type: 'error',
428+
title: 'Whoops!',
429+
html: true,
430+
text: 'An error occured while attempting to delete these files. Please try again.',
431+
});
432+
});
433+
});
434+
} else {
435+
swal({
436+
type: 'warning',
437+
title: '',
438+
text: 'Please select files/folders to delete.',
439+
});
440+
}
441+
}
442+
319443
decompress() {
320444
const nameBlock = $(this.element).find('td[data-identifier="name"]');
321445
const compPath = decodeURIComponent(nameBlock.data('path'));

public/themes/pterodactyl/js/frontend/files/src/index.js

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,10 @@ class FileManager {
4545
ContextMenu.run();
4646
this.reloadFilesButton();
4747
this.addFolderButton();
48+
this.selectItem();
49+
this.selectAll();
50+
this.selectiveDeletion();
51+
this.selectRow();
4852
if (_.isFunction(next)) {
4953
return next();
5054
}
@@ -83,12 +87,42 @@ class FileManager {
8387
});
8488
}
8589

90+
selectItem() {
91+
$('[data-action="addSelection"]').on('click', event => {
92+
event.preventDefault();
93+
});
94+
}
95+
96+
selectAll() {
97+
$('[data-action="selectAll"]').on('click', event => {
98+
event.preventDefault();
99+
});
100+
}
101+
102+
selectiveDeletion() {
103+
$('[data-action="selective-deletion"]').on('mousedown', event => {
104+
new ActionsClass().deleteSelected();
105+
});
106+
}
107+
86108
addFolderButton() {
87109
$('[data-action="add-folder"]').unbind().on('click', () => {
88110
new ActionsClass().folder($('#file_listing').data('current-dir') || '/');
89111
})
90112
}
91113

114+
selectRow() {
115+
$('#file_listing tr').on('mousedown', event => {
116+
if($(event.target).is('th') || $(event.target).is('input[data-action="selectAll"]')) {
117+
new ActionsClass().highlightAll(event);
118+
} else if($(event.target).is('td') || $(event.target).is('input[data-action="addSelection"]')) {
119+
new ActionsClass().toggleHighlight(event);
120+
}
121+
122+
new ActionsClass().toggleMassActions();
123+
});
124+
}
125+
92126
decodeHash() {
93127
return decodeURIComponent(window.location.hash.substring(1));
94128
}

resources/lang/en/server.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -213,6 +213,8 @@
213213
'last_modified' => 'Last Modified',
214214
'add_new' => 'Add New File',
215215
'add_folder' => 'Add New Folder',
216+
'mass_actions' => 'Mass actions',
217+
'delete' => 'Delete',
216218
'edit' => [
217219
'header' => 'Edit File',
218220
'header_sub' => 'Make modifications to a file from the web.',

resources/themes/pterodactyl/server/files/list.blade.php

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,14 @@
2121
<div class="box-header with-border">
2222
<h3 class="box-title">/home/container{{ $directory['header'] }}</h3>
2323
<div class="box-tools pull-right">
24+
<div class="btn-group">
25+
<button type="button" id="mass_actions" class="btn btn-sm btn-info dropdown-toggle disabled" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
26+
@lang('server.files.mass_actions') <span class="caret"></span>
27+
</button>
28+
<ul class="dropdown-menu dropdown-massactions">
29+
<li><a href="#" id="selective-deletion" data-action="selective-deletion">@lang('server.files.delete') <i class="fa fa-fw fa-trash-o"></i></a></li>
30+
</ul>
31+
</div>
2432
<button class="btn btn-sm btn-success btn-icon" data-action="add-folder">
2533
<i class="fa fa-fw fa-folder-open-o"></i>
2634
</button>
@@ -38,13 +46,13 @@
3846
<table class="table table-hover" id="file_listing" data-current-dir="{{ $directory['header'] }}">
3947
<thead>
4048
<tr>
41-
<th style="width:2%;" class="middle text-center">
42-
<i class="fa fa-refresh muted muted-hover use-pointer" data-action="reload-files" style="font-size:14px;"></i>
49+
<th style="width:4%;" class="middle">
50+
<input type="checkbox" class="select-all-files" data-action="selectAll"><i class="fa fa-refresh muted muted-hover use-pointer" data-action="reload-files" style="font-size:14px;"></i>
4351
</th>
4452
<th style="width:55%">@lang('server.files.file_name')</th>
4553
<th style="width:15%" class="hidden-xs">@lang('server.files.size')</th>
4654
<th style="width:20%" class="hidden-xs">@lang('server.files.last_modified')</th>
47-
<th style="width:8%"></th>
55+
<th style="width:6%"></th>
4856
</tr>
4957
</thead>
5058
<tbody id="append_files_to">
@@ -70,7 +78,7 @@
7078
@endif
7179
@foreach ($folders as $folder)
7280
<tr data-type="folder">
73-
<td data-identifier="type" class="middle"><i class="fa fa-folder" style="margin-left: 0.859px;"></i></td>
81+
<td data-identifier="type" class="middle"><input type="checkbox" class="select-folder" data-action="addSelection"><i class="fa fa-folder" style="margin-left: 0.859px;"></i></td>
7482
<td data-identifier="name" data-name="{{ rawurlencode($folder['entry']) }}" data-path="@if($folder['directory'] !== ''){{ rawurlencode($folder['directory']) }}@endif/">
7583
<a href="/server/{{ $server->uuidShort }}/files" data-action="directory-view">{{ $folder['entry'] }}</a>
7684
</td>
@@ -85,12 +93,12 @@
8593
{{ $carbon->diffForHumans() }}
8694
@endif
8795
</td>
88-
<td><button class="btn btn-xxs btn-default disable-menu-hide" data-action="toggleMenu" style="padding:2px 6px 0px;"><i class="fa fa-ellipsis-h disable-menu-hide"></i></button></td>
96+
<td><button class="btn btn-xxs btn-default disable-menu-hide" data-action="toggleMenu" style="padding:2px 6px 0px;"><i class="fa fa-ellipsis-h disable-menu-hide"></i></td>
8997
</tr>
9098
@endforeach
9199
@foreach ($files as $file)
92100
<tr data-type="file" data-mime="{{ $file['mime'] }}">
93-
<td data-identifier="type" class="middle">
101+
<td data-identifier="type" class="middle"><input type="checkbox" class="select-file" data-action="addSelection">
94102
{{-- oh boy --}}
95103
@if(in_array($file['mime'], [
96104
'application/x-7z-compressed',
@@ -162,7 +170,7 @@
162170
{{ $carbon->diffForHumans() }}
163171
@endif
164172
</td>
165-
<td><button class="btn btn-xxs btn-default disable-menu-hide" data-action="toggleMenu" style="padding:2px 6px 0px;"><i class="fa fa-ellipsis-h disable-menu-hide"></i></button></td>
173+
<td><button class="btn btn-xxs btn-default disable-menu-hide" data-action="toggleMenu" style="padding:2px 6px 0px;"><i class="fa fa-ellipsis-h disable-menu-hide"></i></td>
166174
</tr>
167175
@endforeach
168176
</tbody>

0 commit comments

Comments
 (0)