Merge branch 'develop' into feature/service-changes

This commit is contained in:
Dane Everitt 2017-01-12 13:15:37 -05:00
commit 6bd9663f59
No known key found for this signature in database
GPG key ID: EEA66103B3D71F53
136 changed files with 2470 additions and 1737 deletions

View file

@ -287,48 +287,13 @@
Below is the configuration file for your daemon on this node. We recommend <strong>not</strong> simply copy and pasting the code below unless you know what you are doing. You should run the <code>auto-installer</code> or <code>auto-updater</code> to setup the daemon.
</div>
<div class="col-md-12">
<pre><code>{
"web": {
"host": "0.0.0.0",
"listen": {{ $node->daemonListen }},
"ssl": {
"enabled": {{ $node->scheme === 'https' ? 'true' : 'false' }},
"certificate": "/etc/letsencrypt/live/{{ $node->fqdn }}/fullchain.pem",
"key": "/etc/letsencrypt/live/{{ $node->fqdn }}/privkey.pem"
}
},
"docker": {
"socket": "/var/run/docker.sock",
"autoupdate_images": true
},
"sftp": {
"path": "{{ $node->daemonBase }}",
"port": {{ $node->daemonSFTP }},
"container": "ptdl-sftp"
},
"query": {
"kill_on_fail": true,
"fail_limit": 5
},
"logger": {
"path": "logs/",
"src": false,
"level": "info",
"period": "1d",
"count": 3
},
"remote": {
"base": "{{ config('app.url') }}",
"download": "{{ route('remote.download') }}",
"installed": "{{ route('remote.install') }}"
},
"uploads": {
"size_limit": {{ $node->upload_size }}
},
"keys": [
"{{ $node->daemonSecret }}"
]
}</code></pre>
<p>To simplify the configuration of nodes it is possible to fetch the config from the panel. A token is required for this process. The button below will generate a token and provide you with the commands necessary for automatic configuration of the node. Be aware that these tokens are only valid for 5 minutes.</p>
<p class="text-center">
<button type="button" id="configTokenBtn" class="btn btn-primary">Generate token</button>
</p>
</div>
<div class="col-md-12">
<pre><code>{{ $node->getConfigurationAsJson(true) }}</code></pre>
</div>
</div>
</div>
@ -536,6 +501,27 @@ $(document).ready(function () {
});
});
$('#configTokenBtn').on('click', function (event) {
$.getJSON('{{ route('admin.nodes.configuration-token', $node->id) }}')
.done(function (data) {
swal({
type: 'success',
title: 'Token created.',
text: 'Here is your token: <code>'+data.token+'</code><br />' +
'It will expire at <i>' + data.expires_at + '</i><br /><br />' +
'<p>To auto-configure your node run<br /><small><code>npm run configure -- --panel-url '+window.location.protocol+'//{{ config('app.url') }} --token '+data.token+'</code></small></p>',
html: true
})
})
.fail(function () {
swal({
title: 'Error',
text: 'Something went wrong creating your token.',
type: 'error'
});
})
})
$('.cloneElement').on('click', function (event) {
event.preventDefault();
var rnd = randomKey(10);

View file

@ -43,7 +43,7 @@
<label for="name" class="control-label">Server Name</label>
<div>
<input type="text" autocomplete="off" name="name" class="form-control" value="{{ old('name') }}" />
<p class="text-muted"><small><em>Character limits: <code>a-zA-Z0-9_-</code> and <code>[Space]</code> (max 35 characters)</em></small></p>
<p class="text-muted"><small><em>Character limits: <code>a-z A-Z 0-9 _ - .</code> and <code>[Space]</code> (max 200 characters).</em></small></p>
</div>
</div>
<div class="form-group col-md-6">
@ -171,7 +171,7 @@
</div>
<div class="row">
<div class="col-md-12">
<p class="text-muted"><small>If you do not want to limit CPU usage set the value to <code>0</code>. To determine a value, take the number <em>physical</em> cores and multiply it by 100. For example, on a quad core system <code>(4 * 100 = 400)</code> there is <code>400%</code> available. To limit a server to using half of a single core, you would set the value to <code>50</code>. To allow a server to use up to two physical cores, set the value to <code>200</code>. BlockIO should be a value between <code>10</code> and <code>1000</code>. Please see <a href="https://docs.docker.com/reference/run/#block-io-bandwidth-blkio-constraint" target="_blank">this documentation</a> for more information about it.</small><p>
<p class="text-muted"><small>If you do not want to limit CPU usage set the value to <code>0</code>. To determine a value, take the number <em>physical</em> cores and multiply it by 100. For example, on a quad core system <code>(4 * 100 = 400)</code> there is <code>400%</code> available. To limit a server to using half of a single core, you would set the value to <code>50</code>. To allow a server to use up to two physical cores, set the value to <code>200</code>. BlockIO should be a value between <code>10</code> and <code>1000</code>. Please see <a href="https://docs.docker.com/engine/reference/run/#/block-io-bandwidth-blkio-constraint" target="_blank">this documentation</a> for more information about it.</small><p>
</div>
</div>
</div>

View file

@ -208,14 +208,12 @@
<ul class="dropdown-menu">
<li><a href="/language/de">Deutsch</a></li>
<li><a href="/language/en">English</a></li>
<!-- <li><a href="/language/es">Espa&ntilde;ol</a></li>
<li><a href="/language/fr">Fran&ccedil;ais</a></li>
<li><a href="/language/it">Italiano</a></li>
<li><a href="/language/pl">Polski</a></li> -->
<li><a href="/language/et">Eesti</a></li>
<li><a href="/language/nl">Nederlands</a></li>
<li><a href="/language/nb">Norsk (Bokm&#xE5;l)</a></li>
<li><a href="/language/pt">Portugu&ecirc;s</a></li>
<!-- <li><a href="/language/ru">&#1088;&#1091;&#1089;&#1089;&#1082;&#1080;&#1081;</a></li>
<li><a href="/language/se">Svenska</a></li>
<li><a href="/language/zh">&#20013;&#22269;&#30340;的</a></li> -->
<li><a href="/language/ro">Rom&#xE2;n&#x103;</a></li>
<li><a href="/language/ru">&#1088;&#1091;&#1089;&#1089;&#1082;&#1080;&#1081;</a></li>
</ul>
</li>
@if (null !== Auth::user() && Auth::user()->root_admin == 1)

View file

@ -58,9 +58,13 @@
</div>
</div>
</div>
<script src="{{ route('server.js', [$server->uuidShort, 'filemanager', 'index.js']) }}"></script>
<script src="{{ route('server.js', [$server->uuidShort, 'filemanager', 'contextmenu.js']) }}"></script>
<script src="{{ route('server.js', [$server->uuidShort, 'filemanager', 'actions.js']) }}"></script>
@if(App::environment('production'))
{!! Theme::js('js/filemanager.min.js') !!}
@else
{!! Theme::js('js/files/index.js') !!}
{!! Theme::js('js/files/contextmenu.js') !!}
{!! Theme::js('js/files/actions.js') !!}
@endif
<script>
$(window).load(function () {
$('.server-files').addClass('active');

View file

@ -76,7 +76,7 @@
{{ $carbon->diffForHumans() }}
@endif
</td>
<td><button class="btn btn-xxs btn-default" data-action="toggleMenu" style="padding:0px 6px;"><i class="fa fa-ellipsis-h"></i></button></td>
<td><button class="btn btn-xxs btn-default" data-action="toggleMenu" style="padding:2px 6px 0px;"><i class="fa fa-ellipsis-h"></i></button></td>
</tr>
@endforeach
@foreach ($files as $file)
@ -153,7 +153,7 @@
{{ $carbon->diffForHumans() }}
@endif
</td>
<td><button class="btn btn-xxs btn-default" data-action="toggleMenu" style="padding:0px 6px;"><i class="fa fa-ellipsis-h"></i></button></td>
<td><button class="btn btn-xxs btn-default" data-action="toggleMenu" style="padding:2px 6px 0px;"><i class="fa fa-ellipsis-h"></i></button></td>
</tr>
@endforeach
</tbody>

View file

@ -149,7 +149,7 @@
</div>
</div>
@if($server->a_serviceFile === 'minecraft')
<script src="{{ route('server.js', [$server->uuidShort, 'minecraft', 'eula.js']) }}"></script>
{!! Theme::js('js/plugins/minecraft/eula.js') !!}
@endif
<script>
$(window).load(function () {
@ -202,19 +202,19 @@ $(window).load(function () {
return false;
}
});
const $consoleNotify = $('#consoleNotify');
$consoleNotify.on('click', function () {
terminal.scroll_to_bottom();
$consoleNotify.removeClass('hidden');
});
terminal.on('scroll', function() {
if (terminal.is_bottom()) {
$consoleNotify.addClass('hidden');
}
})
function terminalNotifyOutput() {
if (!terminal.is_bottom()) {
$consoleNotify.removeClass('hidden');

View file

@ -1,408 +0,0 @@
"use strict";
// Copyright (c) 2015 - 2016 Dane Everitt <dane@daneeveritt.com>
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// 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.
class ActionsClass {
constructor(element, menu) {
this.element = element;
this.menu = menu;
}
destroy() {
this.element = undefined;
}
@can('create-files', $server)
folder() {
const nameBlock = $(this.element).find('td[data-identifier="name"]');
const currentName = decodeURIComponent(nameBlock.attr('data-name'));
const currentPath = decodeURIComponent(nameBlock.data('path'));
let inputValue = `${currentPath}${currentName}/`;
if ($(this.element).data('type') === 'file') {
inputValue = currentPath;
}
swal({
type: 'input',
title: 'Create Folder',
text: 'Please enter the path and folder name below.',
showCancelButton: true,
showConfirmButton: true,
closeOnConfirm: false,
showLoaderOnConfirm: true,
inputValue: inputValue
}, (val) => {
$.ajax({
type: 'POST',
headers: {
'X-Access-Token': '{{ $server->daemonSecret }}',
'X-Access-Server': '{{ $server->uuid }}'
},
contentType: 'application/json; charset=utf-8',
url: '{{ $node->scheme }}://{{ $node->fqdn }}:{{ $node->daemonListen }}/server/file/folder',
timeout: 10000,
data: JSON.stringify({
path: val,
}),
}).done(data => {
swal.close();
Files.list();
}).fail(jqXHR => {
console.error(jqXHR);
var error = 'An error occured while trying to process this request.';
if (typeof jqXHR.responseJSON !== 'undefined' && typeof jqXHR.responseJSON.error !== 'undefined') {
error = jqXHR.responseJSON.error;
}
swal({
type: 'error',
title: '',
text: error,
});
});
});
}
@endcan
@can('move-files', $server)
move() {
const nameBlock = $(this.element).find('td[data-identifier="name"]');
const currentName = decodeURIComponent(nameBlock.attr('data-name'));
const currentPath = decodeURIComponent(nameBlock.data('path'));
swal({
type: 'input',
title: 'Move File',
text: 'Please enter the new path for the file below.',
showCancelButton: true,
showConfirmButton: true,
closeOnConfirm: false,
showLoaderOnConfirm: true,
inputValue: `${currentPath}${currentName}`,
}, (val) => {
$.ajax({
type: 'POST',
headers: {
'X-Access-Token': '{{ $server->daemonSecret }}',
'X-Access-Server': '{{ $server->uuid }}'
},
contentType: 'application/json; charset=utf-8',
url: '{{ $node->scheme }}://{{ $node->fqdn }}:{{ $node->daemonListen }}/server/file/move',
timeout: 10000,
data: JSON.stringify({
from: `${currentPath}${currentName}`,
to: `${val}`,
}),
}).done(data => {
nameBlock.parent().addClass('warning').delay(200).fadeOut();
swal.close();
}).fail(jqXHR => {
console.error(jqXHR);
var error = 'An error occured while trying to process this request.';
if (typeof jqXHR.responseJSON !== 'undefined' && typeof jqXHR.responseJSON.error !== 'undefined') {
error = jqXHR.responseJSON.error;
}
swal({
type: 'error',
title: '',
text: error,
});
});
});
}
rename() {
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');
inputField.focus();
inputField.on('blur keydown', e => {
// Save Field
if (
(e.type === 'keydown' && e.which === 27)
|| e.type === 'blur'
|| (e.type === 'keydown' && e.which === 13 && currentName === inputField.val())
) {
if (!_.isEmpty(currentLink)) {
nameBlock.html(currentLink);
} else {
nameBlock.html(currentName);
}
inputField.remove();
ContextMenu.unbind().run();
return;
}
if (e.type === 'keydown' && e.which !== 13) return;
inputLoader.show();
const currentPath = decodeURIComponent(nameBlock.data('path'));
$.ajax({
type: 'POST',
headers: {
'X-Access-Token': '{{ $server->daemonSecret }}',
'X-Access-Server': '{{ $server->uuid }}'
},
contentType: 'application/json; charset=utf-8',
url: '{{ $node->scheme }}://{{ $node->fqdn }}:{{ $node->daemonListen }}/server/file/rename',
timeout: 10000,
data: JSON.stringify({
from: `${currentPath}${currentName}`,
to: `${currentPath}${inputField.val()}`,
}),
}).done(data => {
nameBlock.attr('data-name', inputField.val());
if (!_.isEmpty(currentLink)) {
let newLink = currentLink.attr('href');
if (nameBlock.parent().data('type') !== 'folder') {
newLink = newLink.substr(0, newLink.lastIndexOf('/')) + '/' + inputField.val();
}
currentLink.attr('href', newLink);
nameBlock.html(
currentLink.html(inputField.val())
);
} else {
nameBlock.html(inputField.val());
}
inputField.remove();
}).fail(jqXHR => {
console.error(jqXHR);
var error = 'An error occured while trying to process this request.';
if (typeof jqXHR.responseJSON !== 'undefined' && typeof jqXHR.responseJSON.error !== 'undefined') {
error = jqXHR.responseJSON.error;
}
nameBlock.addClass('has-error').delay(2000).queue(() => {
nameBlock.removeClass('has-error').dequeue();
});
inputField.popover({
animation: true,
placement: 'top',
content: error,
title: 'Save Error'
}).popover('show');
}).always(() => {
inputLoader.remove();
ContextMenu.unbind().run();
});
});
}
@endcan
@can('copy-files', $server)
copy() {
const nameBlock = $(this.element).find('td[data-identifier="name"]');
const currentName = decodeURIComponent(nameBlock.attr('data-name'));
const currentPath = decodeURIComponent(nameBlock.data('path'));
swal({
type: 'input',
title: 'Copy File',
text: 'Please enter the new path for the copied file below.',
showCancelButton: true,
showConfirmButton: true,
closeOnConfirm: false,
showLoaderOnConfirm: true,
inputValue: `${currentPath}${currentName}`,
}, (val) => {
$.ajax({
type: 'POST',
headers: {
'X-Access-Token': '{{ $server->daemonSecret }}',
'X-Access-Server': '{{ $server->uuid }}'
},
contentType: 'application/json; charset=utf-8',
url: '{{ $node->scheme }}://{{ $node->fqdn }}:{{ $node->daemonListen }}/server/file/copy',
timeout: 10000,
data: JSON.stringify({
from: `${currentPath}${currentName}`,
to: `${val}`,
}),
}).done(data => {
swal({
type: 'success',
title: '',
text: 'File successfully copied.'
});
Files.list();
}).fail(jqXHR => {
console.error(jqXHR);
var error = 'An error occured while trying to process this request.';
if (typeof jqXHR.responseJSON !== 'undefined' && typeof jqXHR.responseJSON.error !== 'undefined') {
error = jqXHR.responseJSON.error;
}
swal({
type: 'error',
title: '',
text: error,
});
});
});
}
@endcan
@can('download-files', $server)
download() {
const nameBlock = $(this.element).find('td[data-identifier="name"]');
const fileName = decodeURIComponent(nameBlock.attr('data-name'));
const filePath = decodeURIComponent(nameBlock.data('path'));
window.location = `/server/{{ $server->uuidShort }}/files/download/${filePath}${fileName}`;
}
@endcan
@can('delete-files', $server)
delete() {
const nameBlock = $(this.element).find('td[data-identifier="name"]');
const delPath = decodeURIComponent(nameBlock.data('path'));
const delName = decodeURIComponent(nameBlock.data('name'));
swal({
type: 'warning',
title: '',
text: 'Are you sure you want to delete <code>' + delName + '</code>? There is <strong>no</strong> reversing this action.',
html: true,
showCancelButton: true,
showConfirmButton: true,
closeOnConfirm: false,
showLoaderOnConfirm: true
}, () => {
$.ajax({
type: 'DELETE',
url: `{{ $node->scheme }}://{{ $node->fqdn }}:{{ $node->daemonListen }}/server/file/f/${delPath}${delName}`,
headers: {
'X-Access-Token': '{{ $server->daemonSecret }}',
'X-Access-Server': '{{ $server->uuid }}'
}
}).done(data => {
nameBlock.parent().addClass('warning').delay(200).fadeOut();
swal({
type: 'success',
title: 'File Deleted'
});
}).fail(jqXHR => {
console.error(jqXHR);
swal({
type: 'error',
title: 'Whoops!',
html: true,
text: 'An error occured while attempting to delete this file. Please try again.',
});
});
});
}
@endcan
@can('decompress-files', $server)
decompress() {
const nameBlock = $(this.element).find('td[data-identifier="name"]');
const compPath = decodeURIComponent(nameBlock.data('path'));
const compName = decodeURIComponent(nameBlock.data('name'));
swal({
title: '<i class="fa fa-refresh fa-spin"></i> Decompressing...',
text: 'This might take a few seconds to complete.',
html: true,
allowOutsideClick: false,
allowEscapeKey: false,
showConfirmButton: false,
});
$.ajax({
type: 'POST',
url: `{{ $node->scheme }}://{{ $node->fqdn }}:{{ $node->daemonListen }}/server/file/decompress`,
headers: {
'X-Access-Token': '{{ $server->daemonSecret }}',
'X-Access-Server': '{{ $server->uuid }}'
},
contentType: 'application/json; charset=utf-8',
data: JSON.stringify({
files: `${compPath}${compName}`
})
}).done(data => {
swal.close();
Files.list(compPath);
}).fail(jqXHR => {
console.error(jqXHR);
var error = 'An error occured while trying to process this request.';
if (typeof jqXHR.responseJSON !== 'undefined' && typeof jqXHR.responseJSON.error !== 'undefined') {
error = jqXHR.responseJSON.error;
}
swal({
type: 'error',
title: 'Whoops!',
html: true,
text: error
});
});
}
@endcan
@can('compress-files', $server)
compress() {
const nameBlock = $(this.element).find('td[data-identifier="name"]');
const compPath = decodeURIComponent(nameBlock.data('path'));
const compName = decodeURIComponent(nameBlock.data('name'));
$.ajax({
type: 'POST',
url: `{{ $node->scheme }}://{{ $node->fqdn }}:{{ $node->daemonListen }}/server/file/compress`,
headers: {
'X-Access-Token': '{{ $server->daemonSecret }}',
'X-Access-Server': '{{ $server->uuid }}'
},
contentType: 'application/json; charset=utf-8',
data: JSON.stringify({
files: `${compPath}${compName}`,
to: compPath.toString()
})
}).done(data => {
Files.list(compPath, err => {
if (err) return;
const fileListing = $('#file_listing').find(`[data-name="${data.saved_as}"]`).parent();
fileListing.addClass('success pulsate').delay(3000).queue(() => {
fileListing.removeClass('success pulsate').dequeue();
});
});
}).fail(jqXHR => {
console.error(jqXHR);
var error = 'An error occured while trying to process this request.';
if (typeof jqXHR.responseJSON !== 'undefined' && typeof jqXHR.responseJSON.error !== 'undefined') {
error = jqXHR.responseJSON.error;
}
swal({
type: 'error',
title: 'Whoops!',
html: true,
text: error
});
});
}
@endcan
}

View file

@ -1,178 +0,0 @@
"use strict";
// Copyright (c) 2015 - 2016 Dane Everitt <dane@daneeveritt.com>
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// 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.
class ContextMenuClass {
constructor() {
this.activeLine = null;
}
run() {
this.directoryClick();
this.rightClick();
}
makeMenu(parent) {
$(document).find('#fileOptionMenu').remove();
if (!_.isNull(this.activeLine)) this.activeLine.removeClass('active');
let newFilePath = $('#headerTableRow').attr('data-currentDir');
if (parent.data('type') === 'folder') {
const nameBlock = parent.find('td[data-identifier="name"]');
const currentName = decodeURIComponent(nameBlock.attr('data-name'));
const currentPath = decodeURIComponent(nameBlock.data('path'));
newFilePath = `${currentPath}${currentName}`;
}
return '<ul id="fileOptionMenu" class="dropdown-menu" role="menu" style="display:none" > \
@can('move-files', $server)<li data-action="move"><a tabindex="-1" href="#"><i class="fa fa-fw fa-arrow-right"></i> Move</a></li>@endcan \
@can('copy-files', $server)<li data-action="copy"><a tabindex="-1" href="#"><i class="fa fa-fw fa-clone"></i> Copy</a></li>@endcan \
@can('move-files', $server)<li data-action="rename"><a tabindex="-1" href="#"><i class="fa fa-fw fa-pencil-square-o"></i> Rename</a></li>@endcan \
@can('compress-files', $server)<li data-action="compress" class="hidden"><a tabindex="-1" href="#"><i class="fa fa-fw fa-file-archive-o"></i> Compress</a></li>@endcan \
@can('decompress-files', $server)<li data-action="decompress" class="hidden"><a tabindex="-1" href="#"><i class="fa fa-fw fa-expand"></i> Decompress</a></li>@endcan \
<li class="divider"></li> \
@can('create-files', $server)<li data-action="file"><a href="/server/{{ $server->uuidShort }}/files/add/?dir=' + newFilePath + '" class="text-muted"><i class="fa fa-fw fa-plus"></i> New File</a></li> \
<li data-action="folder"><a tabindex="-1" href="#"><i class="fa fa-fw fa-folder"></i> New Folder</a></li>@endcan \
<li class="divider"></li> \
@can('download-files', $server)<li data-action="download" class="hidden"><a tabindex="-1" href="#"><i class="fa fa-fw fa-download"></i> Download</a></li>@endcan \
@can('delete-files', $server)<li data-action="delete" class="bg-danger"><a tabindex="-1" href="#"><i class="fa fa-fw fa-trash-o"></i> Delete</a></li>@endcan \
</ul>';
}
rightClick() {
$('[data-action="toggleMenu"]').on('mousedown', () => {
event.preventDefault();
this.showMenu(event);
});
$('#file_listing > tbody td').on('contextmenu', event => {
this.showMenu(event);
});
}
showMenu(event) {
const parent = $(event.target).closest('tr');
const menu = $(this.makeMenu(parent));
if (parent.data('type') === 'disabled') return;
event.preventDefault();
$(menu).appendTo('body');
$(menu).data('invokedOn', $(event.target)).show().css({
position: 'absolute',
left: event.pageX - 150,
top: event.pageY,
});
this.activeLine = parent;
this.activeLine.addClass('active');
@can('download-files', $server)
if (parent.data('type') === 'file') {
$(menu).find('li[data-action="download"]').removeClass('hidden');
}
@endcan
@can('compress-files', $server)
if (parent.data('type') === 'folder') {
$(menu).find('li[data-action="compress"]').removeClass('hidden');
}
@endcan
@can('decompress-files', $server)
if (_.without(['application/zip', 'application/gzip', 'application/x-gzip'], parent.data('mime')).length < 3) {
$(menu).find('li[data-action="decompress"]').removeClass('hidden');
}
@endcan
// Handle Events
const Actions = new ActionsClass(parent, menu);
@can('move-files', $server)
$(menu).find('li[data-action="move"]').unbind().on('click', e => {
e.preventDefault();
Actions.move();
});
@endcan
@can('copy-files', $server)
$(menu).find('li[data-action="copy"]').unbind().on('click', e => {
e.preventDefault();
Actions.copy();
});
@endcan
@can('move-files', $server)
$(menu).find('li[data-action="rename"]').unbind().on('click', e => {
e.preventDefault();
Actions.rename();
});
@endcan
@can('compress-files', $server)
$(menu).find('li[data-action="compress"]').unbind().on('click', e => {
e.preventDefault();
Actions.compress();
});
@endcan
@can('decompress-files', $server)
$(menu).find('li[data-action="decompress"]').unbind().on('click', e => {
e.preventDefault();
Actions.decompress();
});
@endcan
@can('create-files', $server)
$(menu).find('li[data-action="folder"]').unbind().on('click', e => {
e.preventDefault();
Actions.folder();
});
@endcan
@can('download-files', $server)
$(menu).find('li[data-action="download"]').unbind().on('click', e => {
e.preventDefault();
Actions.download();
});
@endcan
$(menu).find('li[data-action="delete"]').unbind().on('click', e => {
e.preventDefault();
Actions.delete();
});
$(window).on('click', () => {
$(menu).remove();
if(!_.isNull(this.activeLine)) this.activeLine.removeClass('active');
});
}
directoryClick() {
$('a[data-action="directory-view"]').on('click', function (event) {
event.preventDefault();
const path = $(this).parent().data('path') || '';
const name = $(this).parent().data('name') || '';
window.location.hash = encodeURIComponent(path + name);
Files.list();
});
}
}
window.ContextMenu = new ContextMenuClass;

View file

@ -1,100 +0,0 @@
"use strict";
// Copyright (c) 2015 - 2016 Dane Everitt <dane@daneeveritt.com>
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// 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.
class FileManager {
constructor() {
this.list(this.decodeHash());
}
list(path, next) {
if (_.isUndefined(path)) {
path = this.decodeHash();
}
this.loader(true);
$.ajax({
type: 'POST',
url: '{{ route('server.files.directory-list', $server->uuidShort) }}',
headers: {
'X-CSRF-Token': '{{ csrf_token() }}',
},
data: {
directory: path,
},
}).done(data => {
this.loader(false);
$('#load_files').slideUp().html(data).slideDown(100, () => {
ContextMenu.run();
this.reloadFilesButton();
if (_.isFunction(next)) {
return next();
}
});
$('#internal_alert').slideUp();
}).fail(jqXHR => {
this.loader(false);
if (_.isFunction(next)) {
return next(new Error('Failed to load file listing.'));
}
swal({
type: 'error',
title: 'File Error',
text: 'An error occured while attempting to process this request. Please try again.',
});
console.log(jqXHR);
});
}
loader(show) {
if ($('#load_files').height() < 5) return;
if (show === true){
var height = $('#load_files').height();
var width = $('.ajax_loading_box').width();
var center_height = (height / 2) - 30;
var center_width = (width / 2) - 30;
$('#position_me').css({
'top': center_height,
'left': center_width,
'font-size': '60px'
});
$('.ajax_loading_box').css('height', (height + 5)).show();
} else {
$('.ajax_loading_box').hide();
}
}
reloadFilesButton() {
$('i[data-action="reload-files"]').unbind().on('click', () => {
$('i[data-action="reload-files"]').addClass('fa-spin');
this.list();
});
}
decodeHash() {
return decodeURIComponent(window.location.hash.substring(1));
}
}
window.Files = new FileManager;

View file

@ -1,61 +0,0 @@
{{-- Copyright (c) 2015 - 2016 Dane Everitt <dane@daneeveritt.com> --}}
{{-- Permission is hereby granted, free of charge, to any person obtaining a copy --}}
{{-- of this software and associated documentation files (the "Software"), to deal --}}
{{-- in the Software without restriction, including without limitation the rights --}}
{{-- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell --}}
{{-- copies of the Software, and to permit persons to whom the Software is --}}
{{-- furnished to do so, subject to the following conditions: --}}
{{-- The above copyright notice and this permission notice shall be included in all --}}
{{-- copies or substantial portions of the Software. --}}
{{-- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR --}}
{{-- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, --}}
{{-- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE --}}
{{-- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER --}}
{{-- 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. --}}
$(window).load(function () {
socket.on('console', function (data) {
if (data.line.indexOf('You need to agree to the EULA in order to run the server') > -1) {
swal({
title: 'EULA Acceptance',
text: 'By pressing \'I Accept\' below you are indicating your agreement to the <a href="https://account.mojang.com/documents/minecraft_eula" target="_blank">Mojang EULA</a>.',
type: 'info',
html: true,
showCancelButton: true,
showConfirmButton: true,
cancelButtonText: 'I do not Accept',
confirmButtonText: 'I Accept',
closeOnConfirm: false,
showLoaderOnConfirm: true
}, function () {
$.ajax({
type: 'POST',
url: '{{ route('server.files.save', $server->uuidShort) }}',
headers: { 'X-CSRF-Token': '{{ csrf_token() }}' },
data: {
file: 'eula.txt',
contents: 'eula=true'
}
}).done(function (data) {
$('[data-attr="power"][data-action="start"]').trigger('click');
swal({
type: 'success',
title: '',
text: 'The EULA for this server has been accepted, restarting server now.',
});
}).fail(function (jqXHR) {
console.error(jqXHR);
swal({
title: 'Whoops!',
text: 'An error occured while attempting to set the EULA as accepted: ' . jqXHR.responseJSON.error,
type: 'error'
})
});
});
}
});
});