More filemanager work, directory browsing working
This commit is contained in:
parent
ceef2edf2e
commit
e9f8751c4c
|
@ -39,18 +39,6 @@ class FileController extends Controller
|
|||
$this->authorize('list-files', $server);
|
||||
|
||||
$requestDirectory = '/' . trim(urldecode($request->route()->parameter('directory', '/')), '/');
|
||||
$directory = [
|
||||
'header' => $requestDirectory !== '/' ? $requestDirectory : '',
|
||||
'first' => $requestDirectory !== '/',
|
||||
];
|
||||
|
||||
$goBack = explode('/', trim($requestDirectory, '/'));
|
||||
if (! empty(array_filter($goBack)) && count($goBack) >= 2) {
|
||||
array_pop($goBack);
|
||||
$directory['show'] = true;
|
||||
$directory['link'] = '/' . implode('/', $goBack);
|
||||
$directory['link_show'] = implode('/', $goBack) . '/';
|
||||
}
|
||||
|
||||
try {
|
||||
$contents = $this->fileRepository->setServer($server)->setToken(
|
||||
|
@ -63,7 +51,7 @@ class FileController extends Controller
|
|||
return JsonResponse::create([
|
||||
'contents' => $contents,
|
||||
'editable' => config('pterodactyl.files.editable'),
|
||||
'current_directory' => $directory,
|
||||
'current_directory' => $requestDirectory,
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,17 @@
|
|||
<template>
|
||||
<div>
|
||||
<div class="filemanager-breadcrumbs">
|
||||
/<span class="px-1">home</span><!--
|
||||
-->/<router-link :to="{ name: 'server-files' }" class="px-1">container</router-link><!--
|
||||
--><span v-for="crumb in breadcrumbs" class="inline-block">
|
||||
<span v-if="crumb.path">
|
||||
/<router-link :to="{ name: 'server-files', params: { path: crumb.path } }" class="px-1">{{crumb.directoryName}}</router-link>
|
||||
</span>
|
||||
<span v-else>
|
||||
/<span class="px-1 font-semibold">{{crumb.directoryName}}</span>
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
<div v-if="loading">
|
||||
<div class="spinner spinner-xl blue"></div>
|
||||
</div>
|
||||
|
@ -14,28 +26,40 @@
|
|||
<div class="flex-1 text-right">Modified</div>
|
||||
<div class="flex-none w-1/6">Actions</div>
|
||||
</div>
|
||||
<div class="row clickable" v-for="directory in directories" v-on:click="currentDirectory = directory.name">
|
||||
<div class="flex-none icon"><folder-icon/></div>
|
||||
<div class="flex-1">{{directory.name}}</div>
|
||||
<div class="flex-1 text-right text-grey-dark"></div>
|
||||
<div class="flex-1 text-right text-grey-dark">{{formatDate(directory.modified)}}</div>
|
||||
<div class="flex-none w-1/6"></div>
|
||||
<div v-if="!directories.length && !files.length">
|
||||
<p class="text-grey text-sm text-center p-6 pb-4">This directory is empty.</p>
|
||||
</div>
|
||||
<div class="row" v-for="file in files" :class="{ clickable: canEdit(file) }">
|
||||
<div class="flex-none icon">
|
||||
<file-text-icon v-if="!file.symlink"/>
|
||||
<link2-icon v-else/>
|
||||
<div v-else>
|
||||
<router-link class="row clickable"
|
||||
v-for="directory in directories"
|
||||
:to="{ name: 'server-files', params: { path: getClickablePath(directory.name).replace(/^\//, '') }}"
|
||||
:key="directory.name + directory.modified"
|
||||
>
|
||||
<div class="flex-none icon">
|
||||
<folder-icon/>
|
||||
</div>
|
||||
<div class="flex-1">{{directory.name}}</div>
|
||||
<div class="flex-1 text-right text-grey-dark"></div>
|
||||
<div class="flex-1 text-right text-grey-dark">{{formatDate(directory.modified)}}</div>
|
||||
<div class="flex-none w-1/6"></div>
|
||||
</router-link>
|
||||
<div class="row" v-for="file in files" :class="{ clickable: canEdit(file) }">
|
||||
<div class="flex-none icon">
|
||||
<file-text-icon v-if="!file.symlink"/>
|
||||
<link2-icon v-else/>
|
||||
</div>
|
||||
<div class="flex-1">{{file.name}}</div>
|
||||
<div class="flex-1 text-right text-grey-dark">{{readableSize(file.size)}}</div>
|
||||
<div class="flex-1 text-right text-grey-dark">{{formatDate(file.modified)}}</div>
|
||||
<div class="flex-none w-1/6"></div>
|
||||
</div>
|
||||
<div class="flex-1">{{file.name}}</div>
|
||||
<div class="flex-1 text-right text-grey-dark">{{readableSize(file.size)}}</div>
|
||||
<div class="flex-1 text-right text-grey-dark">{{formatDate(file.modified)}}</div>
|
||||
<div class="flex-none w-1/6"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import _ from 'lodash';
|
||||
import filter from 'lodash/filter';
|
||||
import isObject from 'lodash/isObject';
|
||||
import format from 'date-fns/format';
|
||||
|
@ -44,14 +68,43 @@
|
|||
|
||||
export default {
|
||||
name: 'file-manager-page',
|
||||
components: { FileTextIcon, FolderIcon, Link2Icon },
|
||||
components: {FileTextIcon, FolderIcon, Link2Icon},
|
||||
|
||||
computed: {
|
||||
...mapState('server', ['server', 'credentials']),
|
||||
...mapState('socket', ['connected']),
|
||||
|
||||
/**
|
||||
* Configure the breadcrumbs that display on the filemanager based on the directory that the
|
||||
* user is currently in.
|
||||
*/
|
||||
breadcrumbs: function () {
|
||||
const directories = this.currentDirectory.replace(/^\/|\/$/, '').split('/');
|
||||
if (directories.length < 1 || !directories[0]) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return _.map(directories, function (value, key) {
|
||||
if (key === directories.length - 1) {
|
||||
return {directoryName: value};
|
||||
}
|
||||
|
||||
return {
|
||||
directoryName: value,
|
||||
path: directories.slice(0, key + 1).join('/'),
|
||||
};
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
watch: {
|
||||
/**
|
||||
* When the route changes reload the directory.
|
||||
*/
|
||||
'$route': function (to) {
|
||||
this.currentDirectory = to.params.path || '/';
|
||||
},
|
||||
|
||||
/**
|
||||
* Watch the current directory setting and when it changes update the file listing.
|
||||
*/
|
||||
|
@ -72,7 +125,7 @@
|
|||
|
||||
data: function () {
|
||||
return {
|
||||
currentDirectory: '/',
|
||||
currentDirectory: this.$route.params.path || '/',
|
||||
loading: true,
|
||||
errorMessage: null,
|
||||
|
||||
|
@ -95,7 +148,7 @@
|
|||
|
||||
window.axios.get(this.route('server.files', {
|
||||
server: this.$route.params.id,
|
||||
directory: this.currentDirectory,
|
||||
directory: encodeURI(this.currentDirectory.replace(/^\/|\/$/, '')),
|
||||
}))
|
||||
.then((response) => {
|
||||
this.files = filter(response.data.contents, function (o) {
|
||||
|
@ -110,7 +163,12 @@
|
|||
this.errorMessage = null;
|
||||
})
|
||||
.catch(err => {
|
||||
console.error({ err });
|
||||
console.error({err});
|
||||
if (err.response.status === 404) {
|
||||
this.errorMessage = 'The directory you requested could not be located on the server.';
|
||||
return;
|
||||
}
|
||||
|
||||
if (err.response.data && isObject(err.response.data.errors)) {
|
||||
err.response.data.errors.forEach(error => {
|
||||
this.errorMessage = error.detail;
|
||||
|
@ -132,6 +190,15 @@
|
|||
return this.editableFiles.indexOf(file.mime) >= 0;
|
||||
},
|
||||
|
||||
/**
|
||||
* Return a formatted directory path that is used to switch to a nested directory.
|
||||
*
|
||||
* @return {String}
|
||||
*/
|
||||
getClickablePath (directory) {
|
||||
return `${this.currentDirectory.replace(/\/$/, '')}/${directory}`;
|
||||
},
|
||||
|
||||
/**
|
||||
* Return the human readable filesize for a given number of bytes. This
|
||||
* uses 1024 as the base, so the response is denoted accordingly.
|
||||
|
@ -152,7 +219,7 @@
|
|||
u++;
|
||||
} while (Math.abs(bytes) >= 1024 && u < units.length - 1);
|
||||
|
||||
return `${bytes.toFixed(1)} ${units[u]}`
|
||||
return `${bytes.toFixed(1)} ${units[u]}`;
|
||||
},
|
||||
|
||||
/**
|
||||
|
|
|
@ -41,7 +41,7 @@ const routes = [
|
|||
{ path: '/server/:id', component: Server,
|
||||
children: [
|
||||
{ name: 'server', path: '', component: ConsolePage },
|
||||
{ name: 'server-files', path: 'files', component: FileManagerPage },
|
||||
{ name: 'server-files', path: 'files/:path(.*)?', component: FileManagerPage },
|
||||
{ name: 'server-subusers', path: 'subusers', component: ServerSubusers },
|
||||
{ name: 'server-schedules', path: 'schedules', component: ServerSchedules },
|
||||
{ name: 'server-databases', path: 'databases', component: ServerDatabases },
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
.filemanager {
|
||||
& > .header {
|
||||
& .header {
|
||||
@apply .flex .text-sm .pb-4 .font-bold .border-b .border-grey-light .mb-3;
|
||||
|
||||
& > div {
|
||||
|
@ -7,15 +7,15 @@
|
|||
}
|
||||
}
|
||||
|
||||
& > .row {
|
||||
@apply .flex .text-sm .py-3 .text-sm .border .border-transparent;
|
||||
& .row {
|
||||
@apply .flex .text-sm .py-3 .text-sm .border .border-transparent .text-black;
|
||||
|
||||
& > div {
|
||||
@apply .pr-4;
|
||||
}
|
||||
|
||||
&.clickable {
|
||||
@apply .rounded .cursor-pointer;
|
||||
@apply .rounded .cursor-pointer .no-underline;
|
||||
|
||||
&:hover {
|
||||
@apply .bg-grey-lightest .border-blue-light .text-blue-dark;
|
||||
|
@ -30,3 +30,15 @@
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
.filemanager-breadcrumbs {
|
||||
@apply .px-4 .py-3 .mb-6 .rounded .border .bg-grey-lightest .text-grey-darker;
|
||||
|
||||
& a {
|
||||
@apply .no-underline .text-blue-light;
|
||||
|
||||
&:hover {
|
||||
@apply .text-blue-dark;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue