Skip to content

Commit e9f8751

Browse files
committed
More filemanager work, directory browsing working
1 parent ceef2ed commit e9f8751

File tree

4 files changed

+104
-37
lines changed

4 files changed

+104
-37
lines changed

app/Http/Controllers/Server/FileController.php

Lines changed: 1 addition & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -39,18 +39,6 @@ public function index(Request $request): JsonResponse
3939
$this->authorize('list-files', $server);
4040

4141
$requestDirectory = '/' . trim(urldecode($request->route()->parameter('directory', '/')), '/');
42-
$directory = [
43-
'header' => $requestDirectory !== '/' ? $requestDirectory : '',
44-
'first' => $requestDirectory !== '/',
45-
];
46-
47-
$goBack = explode('/', trim($requestDirectory, '/'));
48-
if (! empty(array_filter($goBack)) && count($goBack) >= 2) {
49-
array_pop($goBack);
50-
$directory['show'] = true;
51-
$directory['link'] = '/' . implode('/', $goBack);
52-
$directory['link_show'] = implode('/', $goBack) . '/';
53-
}
5442

5543
try {
5644
$contents = $this->fileRepository->setServer($server)->setToken(
@@ -63,7 +51,7 @@ public function index(Request $request): JsonResponse
6351
return JsonResponse::create([
6452
'contents' => $contents,
6553
'editable' => config('pterodactyl.files.editable'),
66-
'current_directory' => $directory,
54+
'current_directory' => $requestDirectory,
6755
]);
6856
}
6957
}

resources/assets/scripts/components/server/subpages/FileManager.vue

Lines changed: 86 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,17 @@
11
<template>
22
<div>
3+
<div class="filemanager-breadcrumbs">
4+
/<span class="px-1">home</span><!--
5+
-->/<router-link :to="{ name: 'server-files' }" class="px-1">container</router-link><!--
6+
--><span v-for="crumb in breadcrumbs" class="inline-block">
7+
<span v-if="crumb.path">
8+
/<router-link :to="{ name: 'server-files', params: { path: crumb.path } }" class="px-1">{{crumb.directoryName}}</router-link>
9+
</span>
10+
<span v-else>
11+
/<span class="px-1 font-semibold">{{crumb.directoryName}}</span>
12+
</span>
13+
</span>
14+
</div>
315
<div v-if="loading">
416
<div class="spinner spinner-xl blue"></div>
517
</div>
@@ -14,28 +26,40 @@
1426
<div class="flex-1 text-right">Modified</div>
1527
<div class="flex-none w-1/6">Actions</div>
1628
</div>
17-
<div class="row clickable" v-for="directory in directories" v-on:click="currentDirectory = directory.name">
18-
<div class="flex-none icon"><folder-icon/></div>
19-
<div class="flex-1">{{directory.name}}</div>
20-
<div class="flex-1 text-right text-grey-dark"></div>
21-
<div class="flex-1 text-right text-grey-dark">{{formatDate(directory.modified)}}</div>
22-
<div class="flex-none w-1/6"></div>
29+
<div v-if="!directories.length && !files.length">
30+
<p class="text-grey text-sm text-center p-6 pb-4">This directory is empty.</p>
2331
</div>
24-
<div class="row" v-for="file in files" :class="{ clickable: canEdit(file) }">
25-
<div class="flex-none icon">
26-
<file-text-icon v-if="!file.symlink"/>
27-
<link2-icon v-else/>
32+
<div v-else>
33+
<router-link class="row clickable"
34+
v-for="directory in directories"
35+
:to="{ name: 'server-files', params: { path: getClickablePath(directory.name).replace(/^\//, '') }}"
36+
:key="directory.name + directory.modified"
37+
>
38+
<div class="flex-none icon">
39+
<folder-icon/>
40+
</div>
41+
<div class="flex-1">{{directory.name}}</div>
42+
<div class="flex-1 text-right text-grey-dark"></div>
43+
<div class="flex-1 text-right text-grey-dark">{{formatDate(directory.modified)}}</div>
44+
<div class="flex-none w-1/6"></div>
45+
</router-link>
46+
<div class="row" v-for="file in files" :class="{ clickable: canEdit(file) }">
47+
<div class="flex-none icon">
48+
<file-text-icon v-if="!file.symlink"/>
49+
<link2-icon v-else/>
50+
</div>
51+
<div class="flex-1">{{file.name}}</div>
52+
<div class="flex-1 text-right text-grey-dark">{{readableSize(file.size)}}</div>
53+
<div class="flex-1 text-right text-grey-dark">{{formatDate(file.modified)}}</div>
54+
<div class="flex-none w-1/6"></div>
2855
</div>
29-
<div class="flex-1">{{file.name}}</div>
30-
<div class="flex-1 text-right text-grey-dark">{{readableSize(file.size)}}</div>
31-
<div class="flex-1 text-right text-grey-dark">{{formatDate(file.modified)}}</div>
32-
<div class="flex-none w-1/6"></div>
3356
</div>
3457
</div>
3558
</div>
3659
</template>
3760

3861
<script>
62+
import _ from 'lodash';
3963
import filter from 'lodash/filter';
4064
import isObject from 'lodash/isObject';
4165
import format from 'date-fns/format';
@@ -44,14 +68,43 @@
4468
4569
export default {
4670
name: 'file-manager-page',
47-
components: { FileTextIcon, FolderIcon, Link2Icon },
71+
components: {FileTextIcon, FolderIcon, Link2Icon},
4872
4973
computed: {
5074
...mapState('server', ['server', 'credentials']),
5175
...mapState('socket', ['connected']),
76+
77+
/**
78+
* Configure the breadcrumbs that display on the filemanager based on the directory that the
79+
* user is currently in.
80+
*/
81+
breadcrumbs: function () {
82+
const directories = this.currentDirectory.replace(/^\/|\/$/, '').split('/');
83+
if (directories.length < 1 || !directories[0]) {
84+
return [];
85+
}
86+
87+
return _.map(directories, function (value, key) {
88+
if (key === directories.length - 1) {
89+
return {directoryName: value};
90+
}
91+
92+
return {
93+
directoryName: value,
94+
path: directories.slice(0, key + 1).join('/'),
95+
};
96+
});
97+
}
5298
},
5399
54100
watch: {
101+
/**
102+
* When the route changes reload the directory.
103+
*/
104+
'$route': function (to) {
105+
this.currentDirectory = to.params.path || '/';
106+
},
107+
55108
/**
56109
* Watch the current directory setting and when it changes update the file listing.
57110
*/
@@ -72,7 +125,7 @@
72125
73126
data: function () {
74127
return {
75-
currentDirectory: '/',
128+
currentDirectory: this.$route.params.path || '/',
76129
loading: true,
77130
errorMessage: null,
78131
@@ -95,7 +148,7 @@
95148
96149
window.axios.get(this.route('server.files', {
97150
server: this.$route.params.id,
98-
directory: this.currentDirectory,
151+
directory: encodeURI(this.currentDirectory.replace(/^\/|\/$/, '')),
99152
}))
100153
.then((response) => {
101154
this.files = filter(response.data.contents, function (o) {
@@ -110,7 +163,12 @@
110163
this.errorMessage = null;
111164
})
112165
.catch(err => {
113-
console.error({ err });
166+
console.error({err});
167+
if (err.response.status === 404) {
168+
this.errorMessage = 'The directory you requested could not be located on the server.';
169+
return;
170+
}
171+
114172
if (err.response.data && isObject(err.response.data.errors)) {
115173
err.response.data.errors.forEach(error => {
116174
this.errorMessage = error.detail;
@@ -132,6 +190,15 @@
132190
return this.editableFiles.indexOf(file.mime) >= 0;
133191
},
134192
193+
/**
194+
* Return a formatted directory path that is used to switch to a nested directory.
195+
*
196+
* @return {String}
197+
*/
198+
getClickablePath (directory) {
199+
return `${this.currentDirectory.replace(/\/$/, '')}/${directory}`;
200+
},
201+
135202
/**
136203
* Return the human readable filesize for a given number of bytes. This
137204
* uses 1024 as the base, so the response is denoted accordingly.
@@ -152,7 +219,7 @@
152219
u++;
153220
} while (Math.abs(bytes) >= 1024 && u < units.length - 1);
154221
155-
return `${bytes.toFixed(1)} ${units[u]}`
222+
return `${bytes.toFixed(1)} ${units[u]}`;
156223
},
157224
158225
/**

resources/assets/scripts/router.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ const routes = [
4141
{ path: '/server/:id', component: Server,
4242
children: [
4343
{ name: 'server', path: '', component: ConsolePage },
44-
{ name: 'server-files', path: 'files', component: FileManagerPage },
44+
{ name: 'server-files', path: 'files/:path(.*)?', component: FileManagerPage },
4545
{ name: 'server-subusers', path: 'subusers', component: ServerSubusers },
4646
{ name: 'server-schedules', path: 'schedules', component: ServerSchedules },
4747
{ name: 'server-databases', path: 'databases', component: ServerDatabases },

resources/assets/styles/components/filemanager.css

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,21 @@
11
.filemanager {
2-
& > .header {
2+
& .header {
33
@apply .flex .text-sm .pb-4 .font-bold .border-b .border-grey-light .mb-3;
44

55
& > div {
66
@apply .pr-4;
77
}
88
}
99

10-
& > .row {
11-
@apply .flex .text-sm .py-3 .text-sm .border .border-transparent;
10+
& .row {
11+
@apply .flex .text-sm .py-3 .text-sm .border .border-transparent .text-black;
1212

1313
& > div {
1414
@apply .pr-4;
1515
}
1616

1717
&.clickable {
18-
@apply .rounded .cursor-pointer;
18+
@apply .rounded .cursor-pointer .no-underline;
1919

2020
&:hover {
2121
@apply .bg-grey-lightest .border-blue-light .text-blue-dark;
@@ -30,3 +30,15 @@
3030
}
3131
}
3232
}
33+
34+
.filemanager-breadcrumbs {
35+
@apply .px-4 .py-3 .mb-6 .rounded .border .bg-grey-lightest .text-grey-darker;
36+
37+
& a {
38+
@apply .no-underline .text-blue-light;
39+
40+
&:hover {
41+
@apply .text-blue-dark;
42+
}
43+
}
44+
}

0 commit comments

Comments
 (0)