More updates to file manager
Not doing individual commits for this, tons of changes for tons of different aspects across multiple files.
This commit is contained in:
parent
fe9c573533
commit
fb4d122a2a
12 changed files with 308 additions and 164 deletions
|
@ -8,6 +8,7 @@ This project follows [Semantic Versioning](http://semver.org) guidelines.
|
|||
### Added
|
||||
* Support for creating server without having to assign a node and allocation manually. Simply select the checkbox or pass `auto_deploy=true` to the API to auto-select a node and allocation given a location.
|
||||
* Support for setting IP Aliases through the panel on the node overview page. Also cleaned up allocation removal.
|
||||
* Support for renaming files through the panel's file mananger.
|
||||
|
||||
### Changed
|
||||
* Prevent clicking server start button until server is completely off, not just stopping.
|
||||
|
@ -15,6 +16,10 @@ This project follows [Semantic Versioning](http://semver.org) guidelines.
|
|||
* Trying to add a new node if no location exists redirects user to location management page and alerts them to add a location first.
|
||||
* `Server\AjaxController@postSetConnection` is now `Server\AjaxController@postSetPrimary` and accepts one post parameter of `allocation` rather than a combined `ip:port` value.
|
||||
* Port allocations on server view are now cleaner and should make more sense.
|
||||
* Improved File Manager
|
||||
* Rewritten Javascript to load, rename, and handle other file actions.
|
||||
* Uses Ace Editor for editing files rather than a non-formatted textarea
|
||||
* File actions that were previously icons to the right are now contained in a menu that appears when right-clicking a file or folder.
|
||||
|
||||
### Fixed
|
||||
* Team Fortress named 'Insurgency' in panel in database seeder. ([#96](https://github.com/Pterodactyl/Panel/issues/96), PR by [@MeltedLux](https://github.com/MeltedLux))
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
## Pterodactyl Panel
|
||||
Pterodactyl is the free game server management panel designed by users, for users. Featuring support for Vanilla Minecraft, Spigot, Source Dedicated Servers, BungeeCord, and many more. Pterodactyl is built on the `Laravel PHP Framework (v5.2)`.
|
||||
Pterodactyl is the free game server management panel designed by users, for users. Featuring support for Vanilla Minecraft, Spigot, Source Dedicated Servers, BungeeCord, and many more. Pterodactyl is built on the `Laravel PHP Framework (v5.3)`.
|
||||
|
||||
## Support & Documentation
|
||||
Support for using Pterodactyl can be found on our [wiki](https://github.com/Pterodactyl/Panel/wiki) or on our [Discord chat](https://discord.gg/0gYt8oU8QOkDhKLS).
|
||||
|
@ -28,6 +28,8 @@ SOFTWARE.
|
|||
```
|
||||
|
||||
### Credits
|
||||
Ace Editor - [license](https://github.com/ajaxorg/ace/blob/master/LICENSE) - [homepage](https://ace.c9.io)
|
||||
|
||||
Animate.css - [license](https://github.com/daneden/animate.css/blob/master/LICENSE) - [homepage](http://daneden.github.io/animate.css/)
|
||||
|
||||
Async.js - [license](https://github.com/caolan/async/blob/master/LICENSE) - [homepage](https://github.com/caolan/async/)
|
||||
|
@ -48,6 +50,8 @@ jQuery - [license](https://github.com/jquery/jquery/blob/master/LICENSE.txt) - [
|
|||
|
||||
jQuery Terminal - [license](https://github.com/jcubic/jquery.terminal/blob/master/LICENSE) - [homepage](http://terminal.jcubic.pl)
|
||||
|
||||
Lodash - [license](https://github.com/lodash/lodash/blob/master/LICENSE) - [homepage](https://lodash.com/)
|
||||
|
||||
MetricsGraphics.js - [license](https://github.com/mozilla/metrics-graphics/blob/master/LICENSE) - [homepage](http://metricsgraphicsjs.org/)
|
||||
|
||||
Socket.io - [license](https://github.com/socketio/socket.io/blob/master/LICENSE) - [homepage](http://socket.io)
|
||||
|
|
|
@ -137,7 +137,7 @@ class AjaxController extends Controller
|
|||
'server' => $server,
|
||||
'files' => $directoryContents->files,
|
||||
'folders' => $directoryContents->folders,
|
||||
'extensions' => Repositories\HelperRepository::editableFiles(),
|
||||
'editableMime' => Repositories\HelperRepository::editableFiles(),
|
||||
'directory' => $prevDir
|
||||
]);
|
||||
|
||||
|
|
|
@ -145,9 +145,9 @@ class ServerController extends Controller
|
|||
'server' => $server,
|
||||
'node' => Models\Node::find($server->node),
|
||||
'file' => $file,
|
||||
'contents' => $fileContent->content,
|
||||
'directory' => (in_array($fileInfo->dirname, ['.', './', '/'])) ? '/' : trim($fileInfo->dirname, '/') . '/',
|
||||
'extension' => $fileInfo->extension
|
||||
'stat' => $fileContent['stat'],
|
||||
'contents' => $fileContent['file']->content,
|
||||
'directory' => (in_array($fileInfo->dirname, ['.', './', '/'])) ? '/' : trim($fileInfo->dirname, '/') . '/'
|
||||
]);
|
||||
|
||||
}
|
||||
|
|
|
@ -85,7 +85,7 @@ class FileRepository
|
|||
* Get the contents of a requested file for the server.
|
||||
*
|
||||
* @param string $file
|
||||
* @return string
|
||||
* @return array
|
||||
*/
|
||||
public function returnFileContents($file)
|
||||
{
|
||||
|
@ -95,22 +95,39 @@ class FileRepository
|
|||
}
|
||||
|
||||
$file = (object) pathinfo($file);
|
||||
if (!in_array($file->extension, HelperRepository::editableFiles())) {
|
||||
throw new DisplayException('You do not have permission to edit this type of file.');
|
||||
}
|
||||
|
||||
$file->dirname = (in_array($file->dirname, ['.', './', '/'])) ? null : trim($file->dirname, '/') . '/';
|
||||
|
||||
$res = $this->client->request('GET', '/server/file/' . rawurlencode($file->dirname.$file->basename), [
|
||||
$res = $this->client->request('GET', '/server/files/stat/' . rawurlencode($file->dirname.$file->basename) , [
|
||||
'headers' => $this->headers
|
||||
]);
|
||||
|
||||
$stat = json_decode($res->getBody());
|
||||
if($res->getStatusCode() !== 200 || !isset($stat->size)) {
|
||||
throw new DisplayException('The daemon provided a non-200 error code on stat lookup: HTTP\\' . $res->getStatusCode());
|
||||
}
|
||||
|
||||
if (!in_array($stat->mime, HelperRepository::editableFiles())) {
|
||||
throw new DisplayException('You cannot edit that type of file (' . $stat->mime . ') through the panel.');
|
||||
}
|
||||
|
||||
if ($stat->size > 5000000) {
|
||||
throw new DisplayException('That file is too large to open in the browser, consider using a SFTP client.');
|
||||
}
|
||||
|
||||
$res = $this->client->request('GET', '/server/file/' . rawurlencode($file->dirname.$file->basename) , [
|
||||
'headers' => $this->headers
|
||||
]);
|
||||
|
||||
$json = json_decode($res->getBody());
|
||||
if($res->getStatusCode() !== 200 || !isset($json->content)) {
|
||||
throw new DisplayException('Scales provided a non-200 error code: HTTP\\' . $res->getStatusCode());
|
||||
throw new DisplayException('The daemon provided a non-200 error code: HTTP\\' . $res->getStatusCode());
|
||||
}
|
||||
|
||||
return $json;
|
||||
return [
|
||||
'file' => $json,
|
||||
'stat' => $stat
|
||||
];
|
||||
|
||||
}
|
||||
|
||||
|
@ -130,11 +147,24 @@ class FileRepository
|
|||
|
||||
$file = (object) pathinfo($file);
|
||||
|
||||
if(!in_array($file->extension, HelperRepository::editableFiles())) {
|
||||
throw new DisplayException('You do not have permission to edit this type of file.');
|
||||
$file->dirname = (in_array($file->dirname, ['.', './', '/'])) ? null : trim($file->dirname, '/') . '/';
|
||||
|
||||
$res = $this->client->request('GET', '/server/files/stat/' . rawurlencode($file->dirname.$file->basename) , [
|
||||
'headers' => $this->headers
|
||||
]);
|
||||
|
||||
$stat = json_decode($res->getBody());
|
||||
if($res->getStatusCode() !== 200 || !isset($stat->size)) {
|
||||
throw new DisplayException('The daemon provided a non-200 error code on stat lookup: HTTP\\' . $res->getStatusCode());
|
||||
}
|
||||
|
||||
$file->dirname = (in_array($file->dirname, ['.', './', '/'])) ? null : trim($file->dirname, '/') . '/';
|
||||
if (!in_array($stat->mime, HelperRepository::editableFiles())) {
|
||||
throw new DisplayException('You cannot edit that type of file (' . $stat->mime . ') through the panel.');
|
||||
}
|
||||
|
||||
if ($stat->size > 5000000) {
|
||||
throw new DisplayException('That file is too large to save in the browser, consider using a SFTP client.');
|
||||
}
|
||||
|
||||
$res = $this->client->request('POST', '/server/file/' . rawurlencode($file->dirname.$file->basename), [
|
||||
'headers' => $this->headers,
|
||||
|
|
|
@ -30,25 +30,20 @@ class HelperRepository {
|
|||
* @var array
|
||||
*/
|
||||
protected static $editable = [
|
||||
'txt',
|
||||
'yml',
|
||||
'yaml',
|
||||
'log',
|
||||
'conf',
|
||||
'config',
|
||||
'html',
|
||||
'json',
|
||||
'properties',
|
||||
'props',
|
||||
'cfg',
|
||||
'lang',
|
||||
'ini',
|
||||
'cmd',
|
||||
'sh',
|
||||
'lua',
|
||||
'0' // Supports BungeeCord Files
|
||||
'application/json',
|
||||
'application/javascript',
|
||||
'application/xml',
|
||||
'application/xhtml+xml',
|
||||
'text/xml',
|
||||
'text/css',
|
||||
'text/html',
|
||||
'text/plain',
|
||||
'text/x-perl',
|
||||
'text/x-shellscript',
|
||||
'inode/x-empty'
|
||||
];
|
||||
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
//
|
||||
|
|
|
@ -197,3 +197,41 @@ li.btn.btn-default.pill:active,li.btn.btn-default.pill:focus,li.btn.btn-default.
|
|||
.use-pointer {
|
||||
cursor: pointer !important;
|
||||
}
|
||||
|
||||
.dropdown-menu > li.bg-danger {
|
||||
background-color:#fdf7f7;
|
||||
color:#474a54;
|
||||
border-left: 4px solid #d9534f !important;
|
||||
}
|
||||
|
||||
.dropdown-menu > li.bg-info {
|
||||
background-color:#fcf8f2;
|
||||
color:#474a54;
|
||||
border-left: 4px solid #f0ad4e !important;
|
||||
}
|
||||
|
||||
.dropdown-menu > li.bg-success {
|
||||
background-color:#f4f8fa;
|
||||
color:#474a54;
|
||||
border-left: 4px solid #5bc0de !important;
|
||||
}
|
||||
|
||||
.dropdown-menu > li.bg-warning {
|
||||
background-color:#fdf7f7;
|
||||
color:#474a54;
|
||||
border-left: 4px solid #d9534f !important;
|
||||
}
|
||||
|
||||
.dropdown-menu > li.bg-default {
|
||||
border-left: 4px solid #bbbbbb !important;
|
||||
}
|
||||
|
||||
.dropdown-menu > li.bg-danger > a,
|
||||
.dropdown-menu > li.bg-info > a,
|
||||
.dropdown-menu > li.bg-success > a,
|
||||
.dropdown-menu > li.bg-warning > a,
|
||||
.dropdown-menu > li.bg-default > a {
|
||||
padding-left: 11px !important;
|
||||
}
|
||||
/*.bg-danger:active,.bg-danger:focus,.bg-danger:hover{color:#fff;background-color:#d32a0e;border-color:#b1240c}
|
||||
.bg-danger.disabled,.bg-danger.disabled:active,.bg-danger.disabled:focus,.bg-danger.disabled:hover,.bg-danger[disabled]{background-color:#f04124;border-color:#ea2f10}*/
|
||||
|
|
|
@ -26,65 +26,52 @@
|
|||
@section('content')
|
||||
<div class="col-md-12">
|
||||
<h3 class="nopad"><small>Editing File: /home/container/{{ $file }}</small></h3>
|
||||
<form method="post" id="editing_file">
|
||||
<div class="form-group">
|
||||
<div>
|
||||
@if (in_array($extension, ['yaml', 'yml']))
|
||||
<div class="alert alert-info">
|
||||
{!! trans('server.files.yaml_notice', [
|
||||
'dropdown' => '<select id="space_yaml">
|
||||
<option value="2">2</option>
|
||||
<option value="4" selected="selected">4</option>
|
||||
<option value="8">8</option>
|
||||
</select>'
|
||||
]) !!}
|
||||
</div>
|
||||
@endif
|
||||
<textarea name="file_contents" id="fileContent" style="border: 1px solid #dddddd;height:500px;" class="form-control console">{{ $contents }}</textarea>
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
<div id="editor" style="height:500px;">{{ $contents }}</div>
|
||||
</div>
|
||||
</div>
|
||||
@can('save-files', $server)
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
<hr />
|
||||
<input type="hidden" name="file" value="{{ $file }}" />
|
||||
<button class="btn btn-primary btn-sm" id="save_file" type="submit">{{ trans('strings.save') }}</button>
|
||||
<a href="/server/{{ $server->uuidShort }}/files?dir={{ rawurlencode($directory) }}" class="text-muted pull-right"><small>{{ trans('server.files.back') }}</small></a>
|
||||
</div>
|
||||
</div>
|
||||
@can('save-files', $server)
|
||||
<div class="form-group">
|
||||
<div>
|
||||
<input type="hidden" name="file" value="{{ $file }}" />
|
||||
{!! csrf_field() !!}
|
||||
<button class="btn btn-primary btn-sm" id="save_file" type="submit">{{ trans('strings.save') }}</button>
|
||||
<a href="/server/{{ $server->uuidShort }}/files?dir={{ rawurlencode($directory) }}" class="text-muted pull-right"><small>{{ trans('server.files.back') }}</small></a>
|
||||
</div>
|
||||
</div>
|
||||
@endcan
|
||||
</form>
|
||||
@endcan
|
||||
</div>
|
||||
{!! Theme::js('js/vendor/ace/ace.js') !!}
|
||||
{!! Theme::js('js/vendor/ace/ext-modelist.js') !!}
|
||||
<script>
|
||||
$(document).ready(function () {
|
||||
$('.server-files').addClass('active');
|
||||
$('textarea').keydown(function (e) {
|
||||
if (e.keyCode === 9) {
|
||||
const Editor = ace.edit('editor');
|
||||
const Modelist = ace.require('ace/ext/modelist')
|
||||
|
||||
var start = this.selectionStart;
|
||||
var end = this.selectionEnd;
|
||||
var value = $(this).val();
|
||||
var joinYML = '\t';
|
||||
var yamlSpaces = 1;
|
||||
Editor.setTheme('ace/theme/github');
|
||||
Editor.getSession().setMode(Modelist.getModeForPath('{{ $stat->name }}').mode);
|
||||
Editor.getSession().setUseWrapMode(true);
|
||||
Editor.setShowPrintMargin(false);
|
||||
|
||||
@if (in_array($extension, ['yaml', 'yml']))
|
||||
yamlSpaces = parseInt($("#space_yaml").val());
|
||||
joinYML = Array(yamlSpaces + 1).join(" ");
|
||||
@endif
|
||||
|
||||
$(this).val(value.substring(0, start) + joinYML + value.substring(end));
|
||||
this.selectionStart = this.selectionEnd = start + yamlSpaces;
|
||||
e.preventDefault();
|
||||
|
||||
}
|
||||
});
|
||||
@can('save-files', $server)
|
||||
$('#save_file').click(function (e) {
|
||||
Editor.commands.addCommand({
|
||||
name: 'save',
|
||||
bindKey: {win: 'Ctrl-S', mac: 'Command-S'},
|
||||
exec: function(editor) {
|
||||
save();
|
||||
},
|
||||
readOnly: false
|
||||
});
|
||||
|
||||
$('#save_file').on('click', function (e) {
|
||||
e.preventDefault();
|
||||
save();
|
||||
});
|
||||
|
||||
function save() {
|
||||
var fileName = $('input[name="file"]').val();
|
||||
var fileContents = $('#fileContent').val();
|
||||
|
||||
$('#save_file').append(' <i class="fa fa-spinner fa fa-spin"></i>').addClass('disabled');
|
||||
$.ajax({
|
||||
type: 'POST',
|
||||
|
@ -92,17 +79,24 @@ $(document).ready(function () {
|
|||
headers: { 'X-CSRF-Token': '{{ csrf_token() }}' },
|
||||
data: {
|
||||
file: fileName,
|
||||
contents: fileContents
|
||||
contents: Editor.getValue()
|
||||
}
|
||||
}).done(function (data) {
|
||||
$('#tpl_messages').html('<div class="alert alert-success">{{ trans('server.files.saved') }}</div>').show().delay(3000).slideUp();
|
||||
$.notify({
|
||||
message: '{{ trans('server.files.saved') }}'
|
||||
}, {
|
||||
type: 'success'
|
||||
});
|
||||
}).fail(function (jqXHR) {
|
||||
$('#tpl_messages').html('<div class="alert alert-danger">' + jqXHR.responseText + '</div>');
|
||||
$.notify({
|
||||
message: jqXHR.responseText
|
||||
}, {
|
||||
type: 'danger'
|
||||
});
|
||||
}).always(function () {
|
||||
$('#save_file').html('{{ trans('strings.save') }}').removeClass('disabled');
|
||||
});
|
||||
|
||||
});
|
||||
}
|
||||
@endcan
|
||||
});
|
||||
</script>
|
||||
|
|
|
@ -17,63 +17,109 @@
|
|||
{{-- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, --}}
|
||||
{{-- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE --}}
|
||||
{{-- SOFTWARE. --}}
|
||||
<h4 class="nopad">/home/container{{ $directory['header'] }} <small><a href="/server/{{ $server->uuidShort }}/files/add/@if($directory['header'] !== '')?dir={{ $directory['header'] }}@endif" class="text-muted"><i class="fa fa-plus" data-toggle="tooltip" data-placement="top" title="Add New File(s)"></i></a></small></h4>
|
||||
<table class="table table-striped table-bordered table-hover" id="file_listing">
|
||||
<table class="table table-hover" id="file_listing">
|
||||
<thead>
|
||||
<tr>
|
||||
<th style="width:2%;text-align:center;"></th>
|
||||
<th style="width:45%">File Name</th>
|
||||
<th style="width:15%">Size</th>
|
||||
<th style="width:20%">Last Modified</th>
|
||||
{{-- <th style="width:20%;text-align:center;">Options</th> --}}
|
||||
</tr>
|
||||
<tr>
|
||||
<th><i class="fa fa-folder-open"></i></th>
|
||||
<th colspan="3">
|
||||
<code>/home/container{{ $directory['header'] }}</code>
|
||||
<small>
|
||||
<a href="/server/{{ $server->uuidShort }}/files/add/@if($directory['header'] !== '')?dir={{ $directory['header'] }}@endif" class="text-muted">
|
||||
<i class="fa fa-plus" data-toggle="tooltip" data-placement="top" title="Add New File(s)"></i>
|
||||
</a>
|
||||
</small>
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@if (isset($directory['first']) && $directory['first'] === true)
|
||||
<tr data-type="disabled">
|
||||
<td><i class="fa fa-folder-open" style="margin-left: 0.859px;"></i></td>
|
||||
<td><i class="fa fa-folder" style="margin-left: 0.859px;"></i></td>
|
||||
<td><a href="/server/{{ $server->uuidShort }}/files" data-action="directory-view">←</a></a></td>
|
||||
<td></td>
|
||||
<td></td>
|
||||
{{-- <td></td> --}}
|
||||
</tr>
|
||||
@endif
|
||||
@if (isset($directory['show']) && $directory['show'] === true)
|
||||
<tr data-type="disabled">
|
||||
<td><i class="fa fa-folder-open" style="margin-left: 0.859px;"></i></td>
|
||||
<td><a href="/server/{{ $server->uuidShort }}/files?dir={{ rawurlencode($directory['link']) }}" data-action="directory-view">← {{ $directory['link_show'] }}</a></a></td>
|
||||
<td><i class="fa fa-folder" style="margin-left: 0.859px;"></i></td>
|
||||
<td data-name="{{ rawurlencode($directory['link']) }}">
|
||||
<a href="/server/{{ $server->uuidShort }}/files" data-action="directory-view">← {{ $directory['link_show'] }}</a>
|
||||
</td>
|
||||
<td></td>
|
||||
<td></td>
|
||||
{{-- <td></td> --}}
|
||||
</tr>
|
||||
@endif
|
||||
@foreach ($folders as $folder)
|
||||
<tr class="align-middle" data-type="folder" data-path="@if($folder['directory'] !== ''){{ rawurlencode($folder['directory']) }}/@endif{{ rawurlencode($folder['entry']) }}">
|
||||
<td data-identifier="type"><i class="fa fa-folder-open" style="margin-left: 0.859px;"></i></td>
|
||||
<td data-identifier="name" data-hash="@if($folder['directory'] !== ''){{ rawurlencode($folder['directory']) }}/@endif{{ rawurlencode($folder['entry']) }}">
|
||||
<tr class="align-middle" data-type="folder">
|
||||
<td data-identifier="type"><i class="fa fa-folder" style="margin-left: 0.859px;"></i></td>
|
||||
<td data-identifier="name" data-name="{{ rawurlencode($folder['entry']) }}" data-path="@if($folder['directory'] !== ''){{ rawurlencode($folder['directory']) }}@endif/">
|
||||
<a href="/server/{{ $server->uuidShort }}/files" data-action="directory-view">{{ $folder['entry'] }}</a>
|
||||
</td>
|
||||
<td data-identifier="size">{{ $folder['size'] }}</td>
|
||||
<td data-identifier="modified">{{ date('m/d/y H:i:s', $folder['date']) }}</td>
|
||||
{{-- <td class="text-center">
|
||||
<div class="row" style="text-align:center;">
|
||||
<div class="col-md-3 hidden-xs hidden-sm"></div>
|
||||
<div class="col-md-3 hidden-xs hidden-sm">
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
@can('delete-files', $server)
|
||||
<a href="@if($folder['directory'] !== ''){{ rawurlencode($folder['directory']) }}/@endif{{ rawurlencode($folder['entry']) }}" data-action="delete_file" data-name="{{ $folder['entry'] }}"><button class="btn btn-danger btn-xxs"><i class="fa fa-trash-o"></i></button></a>
|
||||
@endcan
|
||||
</div>
|
||||
</div>
|
||||
</td> --}}
|
||||
</tr>
|
||||
@endforeach
|
||||
@foreach ($files as $file)
|
||||
<tr class="align-middle" data-type="file" data-mime="{{ $file['mime'] }}">
|
||||
<td data-identifier="type"><i class="fa fa-file-text" style="margin-left: 2px;"></i></td>
|
||||
<td data-identifier="name" data-hash="@if($file['directory'] !== ''){{ rawurlencode($file['directory']) }}/@endif{{ rawurlencode($file['entry']) }}">
|
||||
@if(in_array($file['extension'], $extensions))
|
||||
<td data-identifier="type">
|
||||
{{-- oh boy --}}
|
||||
@if(in_array($file['mime'], [
|
||||
'application/x-7z-compressed',
|
||||
'application/zip',
|
||||
'application/x-compressed-zip',
|
||||
'application/x-tar',
|
||||
'application/x-gzip',
|
||||
'application/x-bzip',
|
||||
'application/x-bzip2',
|
||||
'application/java-archive'
|
||||
]))
|
||||
<i class="fa fa-file-archive-o" style="margin-left: 2px;"></i>
|
||||
@elseif(in_array($file['mime'], [
|
||||
'application/json',
|
||||
'application/javascript',
|
||||
'application/xml',
|
||||
'application/xhtml+xml',
|
||||
'text/xml',
|
||||
'text/css',
|
||||
'text/html',
|
||||
'text/x-perl',
|
||||
'text/x-shellscript'
|
||||
]))
|
||||
<i class="fa fa-file-code-o" style="margin-left: 2px;"></i>
|
||||
@elseif(starts_with($file['mime'], 'image'))
|
||||
<i class="fa fa-file-image-o" style="margin-left: 2px;"></i>
|
||||
@elseif(starts_with($file['mime'], 'video'))
|
||||
<i class="fa fa-file-video-o" style="margin-left: 2px;"></i>
|
||||
@elseif(starts_with($file['mime'], 'video'))
|
||||
<i class="fa fa-file-audio-o" style="margin-left: 2px;"></i>
|
||||
@elseif(starts_with($file['mime'], 'application/vnd.ms-powerpoint'))
|
||||
<i class="fa fa-file-powerpoint-o" style="margin-left: 2px;"></i>
|
||||
@elseif(in_array($file['mime'], [
|
||||
'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
|
||||
'application/vnd.openxmlformats-officedocument.wordprocessingml.template',
|
||||
'application/msword'
|
||||
]) || starts_with($file['mime'], 'application/vnd.ms-word'))
|
||||
<i class="fa fa-file-word-o" style="margin-left: 2px;"></i>
|
||||
@elseif(in_array($file['mime'], [
|
||||
'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
|
||||
'application/vnd.openxmlformats-officedocument.spreadsheetml.template',
|
||||
]) || starts_with($file['mime'], 'application/vnd.ms-excel'))
|
||||
<i class="fa fa-file-excel-o" style="margin-left: 2px;"></i>
|
||||
@elseif($file['mime'] === 'application/pdf')
|
||||
<i class="fa fa-file-pdf-o" style="margin-left: 2px;"></i>
|
||||
@else
|
||||
<i class="fa fa-file-text-o" style="margin-left: 2px;"></i>
|
||||
@endif
|
||||
</td>
|
||||
<td data-identifier="name" data-name="{{ rawurlencode($file['entry']) }}" data-path="@if($file['directory'] !== ''){{ rawurlencode($file['directory']) }}@endif/">
|
||||
@if(in_array($file['mime'], $editableMime))
|
||||
@can('edit-files', $server)
|
||||
<a href="/server/{{ $server->uuidShort }}/files/edit/@if($file['directory'] !== ''){{ rawurlencode($file['directory']) }}/@endif{{ rawurlencode($file['entry']) }}" class="edit_file">{{ $file['entry'] }}</a>
|
||||
@else
|
||||
|
@ -85,22 +131,6 @@
|
|||
</td>
|
||||
<td data-identifier="size">{{ $file['size'] }}</td>
|
||||
<td data-identifier="modified">{{ date('m/d/y H:i:s', $file['date']) }}</td>
|
||||
{{-- <td style="text-align:center;">
|
||||
<div class="row" style="text-align:center;">
|
||||
<div class="col-md-3 hidden-xs hidden-sm">
|
||||
</div>
|
||||
<div class="col-md-3 hidden-xs hidden-sm">
|
||||
@can('download-files', $server)
|
||||
<a href="/server/{{ $server->uuidShort }}/files/download/@if($file['directory'] !== ''){{ rawurlencode($file['directory']) }}/@endif{{ rawurlencode($file['entry']) }}"><span class="badge"><i class="fa fa-download"></i></span></a>
|
||||
@endcan
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
@can('delete-files', $server)
|
||||
<a href="@if($file['directory'] !== ''){{ rawurlencode($file['directory']) }}/@endif{{ rawurlencode($file['entry']) }}" data-action="delete_file" data-name="{{ $file['entry'] }}"><span class="badge label-danger"><i class="fa fa-trash-o"></i></span>
|
||||
@endcan
|
||||
</div>
|
||||
</div>
|
||||
</td> --}}
|
||||
</tr>
|
||||
@endforeach
|
||||
</tbody>
|
||||
|
|
|
@ -21,7 +21,7 @@
|
|||
// SOFTWARE.
|
||||
class FileActions {
|
||||
constructor() {
|
||||
//
|
||||
this.activeLine = null;
|
||||
}
|
||||
|
||||
run() {
|
||||
|
@ -31,53 +31,67 @@ class FileActions {
|
|||
|
||||
makeMenu() {
|
||||
$(document).find('#fileOptionMenu').remove();
|
||||
return $('<ul id="fileOptionMenu" class="dropdown-menu" role="menu" style="display:none" > \
|
||||
if (!_.isNull(this.activeLine)) this.activeLine.removeClass('active');
|
||||
return '<ul id="fileOptionMenu" class="dropdown-menu" role="menu" style="display:none" > \
|
||||
<li data-action="move"><a tabindex="-1" href="#"><i class="fa fa-arrow-right"></i> Move</a></li> \
|
||||
<li data-action="rename"><a tabindex="-1" href="#"><i class="fa fa-pencil-square-o"></i> Rename</a></li> \
|
||||
<li data-action="compress" class="hidden"><a tabindex="-1" href="#"><i class="fa fa-file-archive-o"></i> Compress</a></li> \
|
||||
<li data-action="decompress" class="hidden"><a tabindex="-1" href="#"><i class="fa fa-expand"></i> Decompress</a></li> \
|
||||
<li class="divider"></li> \
|
||||
<li data-action="download" class="hidden"><a tabindex="-1" href="#"><i class="fa fa-download"></i> Download</a></li> \
|
||||
<li data-action="delete"><a tabindex="-1" href="#"><i class="fa fa-trash-o"></i> Delete</a></li> \
|
||||
</ul>');
|
||||
<li data-action="download" class="hidden"><a tabindex="-1" href="/server/{{ $server->uuidShort }}/files/download/"><i class="fa fa-download"></i> Download</a></li> \
|
||||
<li data-action="delete" class="bg-danger"><a tabindex="-1" href="#"><i class="fa fa-trash-o"></i> Delete</a></li> \
|
||||
</ul>';
|
||||
}
|
||||
|
||||
rightClick() {
|
||||
$('#file_listing > tbody').on('contextmenu', event => {
|
||||
$('#file_listing > tbody td').on('contextmenu', event => {
|
||||
|
||||
const parent = $(event.target).parent();
|
||||
const menu = this.makeMenu();
|
||||
const menu = $(this.makeMenu());
|
||||
|
||||
if (parent.data('type') === 'disabled') return;
|
||||
event.preventDefault();
|
||||
|
||||
menu.appendTo('body');
|
||||
menu.data('invokedOn', $(event.target)).show().css({
|
||||
$(menu).appendTo('body');
|
||||
$(menu).data('invokedOn', $(event.target)).show().css({
|
||||
position: 'absolute',
|
||||
left: event.pageX,
|
||||
top: event.pageY,
|
||||
});
|
||||
|
||||
this.activeLine = parent;
|
||||
this.activeLine.addClass('active');
|
||||
|
||||
if (parent.data('type') === 'file') {
|
||||
menu.find('li[data-action="download"]').removeClass('hidden');
|
||||
$(menu).find('li[data-action="download"]').removeClass('hidden');
|
||||
}
|
||||
|
||||
if (parent.data('type') === 'folder') {
|
||||
$(menu).find('li[data-action="compress"]').removeClass('hidden');
|
||||
}
|
||||
|
||||
if (_.without(['application/zip', 'application/gzip', 'application/x-gzip'], parent.data('mime')).length < 3) {
|
||||
menu.find('li[data-action="decompress"]').removeClass('hidden');
|
||||
$(menu).find('li[data-action="decompress"]').removeClass('hidden');
|
||||
}
|
||||
|
||||
// Handle Events
|
||||
var Context = new ContextMenuActions(parent);
|
||||
menu.find('li[data-action="move"]').unbind().on('click', e => {
|
||||
var Context = new ContextMenuActions(parent, menu);
|
||||
$(menu).find('li[data-action="move"]').unbind().on('click', e => {
|
||||
Context.move();
|
||||
});
|
||||
|
||||
menu.find('li[data-action="rename"]').unbind().on('click', e => {
|
||||
$(menu).find('li[data-action="rename"]').unbind().on('click', e => {
|
||||
Context.rename();
|
||||
});
|
||||
|
||||
$(menu).find('li[data-action="download"]').unbind().on('click', e => {
|
||||
e.preventDefault();
|
||||
Context.download();
|
||||
});
|
||||
|
||||
$(window).on('click', () => {
|
||||
menu.remove();
|
||||
$(menu).remove();
|
||||
if(!_.isNull(this.activeLine)) this.activeLine.removeClass('active');
|
||||
});
|
||||
});
|
||||
}
|
||||
|
@ -85,7 +99,12 @@ class FileActions {
|
|||
directoryClick() {
|
||||
$('a[data-action="directory-view"]').on('click', function (event) {
|
||||
event.preventDefault();
|
||||
window.location.hash = encodeURIComponent($(this).parent().data('hash') || '');
|
||||
|
||||
const path = $(this).parent().data('path') || '';
|
||||
const name = $(this).parent().data('name') || '';
|
||||
|
||||
console.log('changing hash');
|
||||
window.location.hash = encodeURIComponent(path + name);
|
||||
Files.list();
|
||||
});
|
||||
}
|
||||
|
|
|
@ -20,8 +20,9 @@
|
|||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
class ContextMenuActions {
|
||||
constructor(element) {
|
||||
constructor(element, menu) {
|
||||
this.element = element;
|
||||
this.menu = menu;
|
||||
}
|
||||
|
||||
destroy() {
|
||||
|
@ -32,28 +33,46 @@ class ContextMenuActions {
|
|||
alert($(this.element).data('path'));
|
||||
}
|
||||
|
||||
download() {
|
||||
var baseURL = $(this.menu).find('li[data-action="download"] a').attr('href');
|
||||
var toURL = baseURL + $(this.element).find('td[data-identifier="name"]').data('name');
|
||||
|
||||
window.location = toURL;
|
||||
}
|
||||
|
||||
rename() {
|
||||
var desiredElement = $(this.element).find('td[data-identifier="name"]');
|
||||
var linkElement = desiredElement.find('a');
|
||||
var currentName = linkElement.html();
|
||||
var editField = `<input class="form-control input-sm" type="text" value="${currentName}" />`;
|
||||
desiredElement.find('a').remove();
|
||||
desiredElement.html(editField);
|
||||
const nameBlock = $(this.element).find('td[data-identifier="name"]');
|
||||
const currentLink = nameBlock.find('a');
|
||||
const currentName = decodeURIComponent(nameBlock.attr('data-name'));
|
||||
const attachEditor = `
|
||||
<input class="form-control input-sm" type="text" value="${currentName}" />
|
||||
<span class="input-loader"><i class="fa fa-refresh fa-spin fa-fw"></i></span>
|
||||
`;
|
||||
|
||||
nameBlock.html(attachEditor);
|
||||
const inputField = nameBlock.find('input');
|
||||
const inputLoader = nameBlock.find('.input-loader');
|
||||
|
||||
const inputField = desiredElement.find('input');
|
||||
inputField.focus();
|
||||
|
||||
inputField.on('blur keypress', e => {
|
||||
// Save Field
|
||||
if (e.type === 'blur' || (e.type === 'keypress' && e.which !== 13)) {
|
||||
// Escape Key Pressed, don't save.
|
||||
if (e.which === 27 || e.type === 'blur') {
|
||||
desiredElement.html(linkElement);
|
||||
if (!_.isEmpty(currentLink)) {
|
||||
nameBlock.html(currentLink);
|
||||
} else {
|
||||
nameBlock.html(currentName);
|
||||
}
|
||||
inputField.remove();
|
||||
Actions.run();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
inputLoader.show();
|
||||
const currentPath = decodeURIComponent(nameBlock.data('path'));
|
||||
|
||||
$.ajax({
|
||||
type: 'POST',
|
||||
headers: {
|
||||
|
@ -64,16 +83,24 @@ class ContextMenuActions {
|
|||
url: '{{ $node->scheme }}://{{ $node->fqdn }}:{{ $node->daemonListen }}/server/files/rename',
|
||||
timeout: 10000,
|
||||
data: JSON.stringify({
|
||||
from: currentName,
|
||||
to: inputField.val(),
|
||||
from: `${currentPath}${currentName}`,
|
||||
to: `${currentPath}${inputField.val()}`,
|
||||
}),
|
||||
}).done(data => {
|
||||
this.element.attr('data-path', inputField.val());
|
||||
desiredElement.attr('data-hash', inputField.val());
|
||||
desiredElement.html(linkElement.html(inputField.val()));
|
||||
nameBlock.attr('data-name', inputField.val());
|
||||
if (!_.isEmpty(currentLink)) {
|
||||
const newLink = currentLink.attr('href').substr(0, currentLink.attr('href').lastIndexOf('/')) + '/' + inputField.val();
|
||||
currentLink.attr('href', newLink);
|
||||
nameBlock.html(
|
||||
currentLink.html(inputField.val())
|
||||
);
|
||||
} else {
|
||||
nameBlock.html(inputField.val());
|
||||
}
|
||||
inputField.remove();
|
||||
Actions.run();
|
||||
}).fail(jqXHR => {
|
||||
nameBlock.addClass('has-error');
|
||||
inputLoader.remove();
|
||||
console.error(jqXHR);
|
||||
var error = 'An error occured while trying to process this request.';
|
||||
if (typeof jqXHR.responseJSON !== 'undefined' && typeof jqXHR.responseJSON.error !== 'undefined') {
|
||||
|
@ -84,6 +111,8 @@ class ContextMenuActions {
|
|||
title: '',
|
||||
text: error,
|
||||
});
|
||||
}).always(() => {
|
||||
inputLoader.remove();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
|
|
@ -44,11 +44,13 @@ class FileManager {
|
|||
directory: path,
|
||||
},
|
||||
}).done(data => {
|
||||
$('#load_files').slideUp().html(data).slideDown(() => {
|
||||
this.loader(false);
|
||||
$('#load_files').slideUp().html(data).slideDown(100, () => {
|
||||
Actions.run();
|
||||
});
|
||||
$('#internal_alert').slideUp();
|
||||
}).fail(jqXHR => {
|
||||
this.loader(false);
|
||||
swal({
|
||||
type: 'error',
|
||||
title: 'File Error',
|
||||
|
@ -56,9 +58,7 @@ class FileManager {
|
|||
});
|
||||
if (!isError) this.list('/', true);
|
||||
console.log(jqXHR);
|
||||
}).always(() => {
|
||||
this.loader(false);
|
||||
});
|
||||
})
|
||||
}
|
||||
|
||||
loader(show) {
|
||||
|
@ -76,9 +76,9 @@ class FileManager {
|
|||
'font-size': '60px'
|
||||
});
|
||||
|
||||
$('.ajax_loading_box').css('height', (height + 5)).fadeIn();
|
||||
$('.ajax_loading_box').css('height', (height + 5)).show();
|
||||
} else {
|
||||
$('.ajax_loading_box').fadeOut(100);
|
||||
$('.ajax_loading_box').hide();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue