Files
cloudron-box/src/filemanager.html
T

348 lines
20 KiB
HTML
Raw Normal View History

2020-07-08 17:30:37 +02:00
<!DOCTYPE html>
<html ng-app="Application">
2020-07-08 17:30:37 +02:00
<head>
2020-11-09 23:49:22 +01:00
<meta charset="utf-8" />
<meta name="viewport" content="user-scalable=no, initial-scale=1, maximum-scale=1, minimum-scale=1, width=device-width, height=device-height" />
2020-07-08 17:30:37 +02:00
2020-11-09 23:49:22 +01:00
<title> FileManager </title>
2020-07-08 17:30:37 +02:00
2020-11-09 23:49:22 +01:00
<link id="favicon" href="/api/v1/cloudron/avatar" rel="icon" type="image/png">
<link rel="apple-touch-icon" href="/api/v1/cloudron/avatar">
<link rel="icon" href="/api/v1/cloudron/avatar">
2020-07-08 17:30:37 +02:00
2020-11-09 23:49:22 +01:00
<!-- CSS -->
<link type="text/css" rel="stylesheet" href="/3rdparty/angular-ui-notification.css?<%= revision %>"/>
<link type="text/css" rel="stylesheet" href="/theme.css?<%= revision %>">
2020-07-08 17:30:37 +02:00
2020-11-09 23:49:22 +01:00
<!-- Fontawesome -->
<link type="text/css" rel="stylesheet" href="/3rdparty/fontawesome/css/all.css?<%= revision %>"/>
2020-07-08 17:30:37 +02:00
2020-11-09 23:49:22 +01:00
<!-- jQuery-->
<script type="text/javascript" src="/3rdparty/js/jquery.min.js?<%= revision %>"></script>
2020-07-08 17:30:37 +02:00
2020-11-09 23:49:22 +01:00
<!-- async -->
<script type="text/javascript" src="/3rdparty/js/async-3.2.0.min.js?<%= revision %>"></script>
2020-07-08 17:30:37 +02:00
2020-11-09 23:49:22 +01:00
<!-- Showdown (markdown converter) -->
<script type="text/javascript" src="/3rdparty/js/showdown-1.9.1.min.js?<%= revision %>"></script>
2020-07-10 15:27:44 +02:00
2020-11-09 23:49:22 +01:00
<!-- Bootstrap Core JavaScript -->
<script type="text/javascript" src="/3rdparty/js/bootstrap.min.js?<%= revision %>"></script>
2020-07-08 17:30:37 +02:00
2020-11-09 23:49:22 +01:00
<!-- Angularjs scripts -->
<script type="text/javascript" src="/3rdparty/js/angular.min.js?<%= revision %>"></script>
<script type="text/javascript" src="/3rdparty/js/angular-loader.min.js?<%= revision %>"></script>
<script type="text/javascript" src="/3rdparty/js/angular-cookies.min.js?<%= revision %>"></script>
<script type="text/javascript" src="/3rdparty/js/angular-animate.min.js?<%= revision %>"></script>
<script type="text/javascript" src="/3rdparty/js/angular-base64.min.js?<%= revision %>"></script>
<script type="text/javascript" src="/3rdparty/js/angular-md5.min.js?<%= revision %>"></script>
<script type="text/javascript" src="/3rdparty/js/angular-sanitize.min.js?<%= revision %>"></script>
<script type="text/javascript" src="/3rdparty/js/angular-ui-notification.js?<%= revision %>"></script>
2020-07-08 17:30:37 +02:00
2020-11-09 23:49:22 +01:00
<!-- Angular directives for bootstrap https://angular-ui.github.io/bootstrap/ -->
<script type="text/javascript" src="/3rdparty/js/ui-bootstrap-tpls-1.3.3.min.js?<%= revision %>"></script>
2020-07-08 17:30:37 +02:00
2020-11-09 23:49:22 +01:00
<!-- Angular translate https://angular-translate.github.io/ -->
<script type="text/javascript" src="/3rdparty/js/angular-translate.min.js?<%= revision %>"></script>
<script type="text/javascript" src="/3rdparty/js/angular-translate-loader-static-files.min.js?<%= revision %>"></script>
<script type="text/javascript" src="/3rdparty/js/angular-translate-storage-cookie.min.js?<%= revision %>"></script>
<script type="text/javascript" src="/3rdparty/js/angular-translate-storage-local.min.js?<%= revision %>"></script>
2020-11-09 23:49:22 +01:00
<!-- colors -->
<script type="text/javascript" src="/3rdparty/js/colors.js?<%= revision %>"></script>
2020-07-08 17:30:37 +02:00
2020-11-09 23:49:22 +01:00
<!-- moment -->
<script type="text/javascript" src="/3rdparty/js/moment.min.js?<%= revision %>"></script>
2020-07-08 17:30:37 +02:00
2020-11-09 23:49:22 +01:00
<!-- https://github.com/data-uri/mimer -->
<script type="text/javascript" src="/3rdparty/js/mimer.min.js?<%= revision %>"></script>
2020-07-13 23:35:49 +02:00
<!-- https://github.com/Templarian/ui.bootstrap.contextMenu -->
<script type="text/javascript" src="/3rdparty/js/contextMenu.js?<%= revision %>"></script>
2020-07-14 23:49:14 +02:00
2020-11-09 23:49:22 +01:00
<!-- WARNING this adds an AMD loader! Make sure script tag includes like mimer are above -->
<!-- monaco-editor -->
<script type="text/javascript" src="/3rdparty/vs/loader.js?<%= revision %>"></script>
2020-11-09 23:49:22 +01:00
<!-- Main Application -->
<script type="text/javascript" src="/js/filemanager.js?<%= revision %>"></script>
2020-07-08 17:30:37 +02:00
</head>
<body class="filemanager" ng-drop="drop($event)" ng-dragover="dragEnter($event)" ng-dragleave="dragExit($event)" ng-controller="FileManagerController">
2020-07-08 17:30:37 +02:00
2020-11-10 00:59:03 +01:00
<a class="offline-banner animateMe" ng-show="client.offline" ng-cloak href="https://docs.cloudron.io/troubleshooting/" target="_blank"><i class="fa fa-circle-notch fa-spin"></i> {{ 'main.offline' | tr }}</a>
2020-07-08 17:30:37 +02:00
2021-09-27 11:38:28 -07:00
<div class="restart-banner animateMe" ng-show="restartBusy" ng-cloak><i class="fa fa-circle-notch fa-spin"></i> {{ 'filemanager.status.restartingApp' | tr}}</div>
2022-08-25 19:38:02 +02:00
<!-- Modal delete entries -->
<div class="modal fade" id="entriesDeleteModal" tabindex="-1" role="dialog">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-body">
<p class="text-bold text-danger" ng-show="deleteEntries.error">{{ deleteEntries.error }}</p>
<h4 ng-hide="deleteEntries.error">{{ 'filemanager.removeDialog.reallyDelete' | tr }}</h4>
2022-08-25 19:38:02 +02:00
<ul>
<li ng-repeat="entry in deleteEntries.entries">{{ entry.fileName }}</li>
</ul>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">{{ 'main.dialog.no' | tr }}</button>
<button type="button" class="btn btn-danger" ng-click="deleteEntries.submit()" ng-hide="deleteEntries.error" ng-disabled="deleteEntries.busy"><i class="fa fa-circle-notch fa-spin" ng-show="deleteEntries.busy"></i> {{ 'main.dialog.yes' | tr }}</button>
</div>
</div>
</div>
</div>
<!-- Modal rename entry -->
<div class="modal fade" id="renameEntryModal" tabindex="-1" role="dialog">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h4 class="modal-title">{{ 'filemanager.renameDialog.title' | tr:{ fileName: renameEntry.entry.fileName } }}</h4>
</div>
<div class="modal-body">
<form role="form" name="renameEntryForm" ng-submit="renameEntry.submit()" autocomplete="off">
<div class="form-group" ng-class="{ 'has-error': (renameEntryForm.newName.$dirty && renameEntryForm.newName.$invalid) }">
<label class="control-label">{{ 'filemanager.renameDialog.newName' | tr }}</label>
<div class="control-label" ng-show="renameEntry.error">{{ renameEntry.error }}</div>
<input type="text" class="form-control" id="inputNewName" name="newName" ng-model="renameEntry.newName" required autofocus>
</div>
<input class="ng-hide" type="submit" ng-disabled="renameEntryForm.$invalid || renameEntry.busy"/>
</form>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">{{ 'main.dialog.close' | tr }}</button>
<button type="button" class="btn btn-primary" ng-click="renameEntry.submit()" ng-hide="renameEntry.error" ng-disabled="renameEntry.busy"><i class="fa fa-circle-notch fa-spin" ng-show="renameEntry.busy"></i> {{ 'filemanager.renameDialog.rename' | tr }}</button>
</div>
</div>
</div>
</div>
<!-- Modal extract -->
<div class="modal fade" id="extractModal" tabindex="-1" role="dialog">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h4 class="modal-title">{{ 'filemanager.extractDialog.title' | tr:{ fileName: extractStatus.fileName } }}</h4>
</div>
<div class="modal-body">
<div ng-show="extractStatus.error">
<p class="text-danger">{{ extractStatus.error }}</p>
</div>
<div class="progress progress-striped active" ng-hide="extractStatus.error">
<div class="progress-bar" role="progressbar" style="width: 100%">
</div>
</div>
<p class="no-wrap" ng-hide="extractStatus.error">{{ extractStatus.fileName }}</p>
</div>
<div class="modal-footer" style="text-align: left;">
<small ng-hide="extractStatus.error">{{ 'filemanager.extractDialog.closeWarning' | tr }}</small>
<button class="btn btn-primary pull-right" ng-show="extractStatus.error" data-dismiss="modal">{{ 'main.dialog.close' | tr }}</button>
</div>
</div>
</div>
</div>
<!-- Modal chown entry -->
<div class="modal fade" id="chownEntriesModal" tabindex="-1" role="dialog">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h4 class="modal-title">{{ 'filemanager.chownDialog.title' | tr }}</h4>
</div>
<div class="modal-body">
<form role="form" name="chownEntryForm" ng-submit="chownEntries.submit()" autocomplete="off">
<div class="form-group" ng-class="{ 'has-error': (chownEntryForm.newOwner.$dirty && chownEntries.error) }">
<label class="control-label">{{ 'filemanager.chownDialog.newOwner' | tr }}</label>
<div class="control-label" for="inputNewOwner" ng-show="chownEntries.error">{{ chownEntries.error }}</div>
<select class="form-control" id="inputNewOwner" name="newOwner" ng-model="chownEntries.newOwner" ng-options="a.value as a.name for a in OWNERS" ng-disabled="chownEntries.busy"></select>
</div>
<div class="form-group" ng-show="chownEntries.showRecursiveOption">
<input type="checkbox" id="inputNewOwnerRecursive" ng-model="chownEntries.recursive">
<label class="control-label" for="inputNewOwnerRecursive">{{ 'filemanager.chownDialog.recursiveCheckbox' | tr }}</label>
</div>
<input class="ng-hide" type="submit" ng-disabled="chownEntryForm.$invalid || chownEntries.busy"/>
</form>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">{{ 'main.dialog.close' | tr }}</button>
<button type="button" class="btn btn-primary" ng-click="chownEntries.submit()" ng-hide="chownEntries.error" ng-disabled="chownEntries.busy"><i class="fa fa-circle-notch fa-spin" ng-show="chownEntries.busy"></i> {{ 'filemanager.chownDialog.change' | tr }}</button>
</div>
</div>
</div>
</div>
<!-- Modal new file -->
<div class="modal fade" id="newFileModal" tabindex="-1" role="dialog">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h4 class="modal-title">{{ 'filemanager.newFileDialog.title' | tr }}</h4>
</div>
<div class="modal-body">
<form role="form" name="newFileForm" ng-submit="newFile.submit()" autocomplete="off">
<div class="form-group" ng-class="{ 'has-error': newFile.error || (newFileForm.fileName.$dirty && newFileForm.fileName.$invalid) }">
<input type="text" class="form-control" id="inputFileName" name="fileName" ng-model="newFile.name" required autofocus>
<div class="control-label" ng-show="newFile.error === 'exists'">{{ 'filemanager.newFile.errorAlreadyExists' | tr }}</div>
</div>
<input class="ng-hide" type="submit" ng-disabled="newFileForm.$invalid || newFile.busy"/>
</form>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">{{ 'main.dialog.close' | tr }}</button>
<button type="button" class="btn btn-primary" ng-click="newFile.submit()" ng-disabled="newFile.busy"><i class="fa fa-circle-notch fa-spin" ng-show="newFile.busy"></i> {{ 'filemanager.newFileDialog.create' | tr }}</button>
</div>
</div>
</div>
</div>
<!-- Modal new directory -->
<div class="modal fade" id="newFolderModal" tabindex="-1" role="dialog">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h4 class="modal-title">{{ 'filemanager.newDirectoryDialog.title' | tr }}</h4>
</div>
<div class="modal-body">
<form role="form" name="newFolderForm" ng-submit="newFolder.submit()" autocomplete="off">
<div class="form-group" ng-class="{ 'has-error': newFolder.error || (newFolderForm.directoryName.$dirty && newFolderForm.directoryName.$invalid) }">
<input type="text" class="form-control" id="inputDirectoryName" name="directoryName" ng-model="newFolder.name" required autofocus>
<div class="control-label" ng-show="newFolder.error === 'exists'">{{ 'filemanager.newDirectory.errorAlreadyExists' | tr }}</div>
</div>
<input class="ng-hide" type="submit" ng-disabled="newDirectoryForm.$invalid || newFolder.busy"/>
</form>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">{{ 'main.dialog.close' | tr }}</button>
<button type="button" class="btn btn-primary" ng-click="newFolder.submit()" ng-disabled="newFolder.busy"><i class="fa fa-circle-notch fa-spin" ng-show="newFolder.busy"></i> {{ 'filemanager.newDirectoryDialog.create' | tr }}</button>
</div>
</div>
</div>
</div>
2022-08-25 19:38:02 +02:00
<!-- Modal editor close -->
<div class="modal fade" id="textEditorCloseModal" tabindex="-1" role="dialog">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h4 class="modal-title">{{ 'filemanager.textEditorCloseDialog.title' | tr }}</h4>
</div>
<div class="modal-body">
<p class="text-bold text-danger">{{ 'filemanager.textEditorCloseDialog.details' | tr }}</p>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" ng-click="textEditor.onClose()">{{ 'filemanager.textEditorCloseDialog.dontSave' | tr }}</button>
<button type="button" class="btn btn-default" data-dismiss="modal">{{ 'main.dialog.cancel' | tr }}</button>
<button type="button" class="btn btn-success" ng-click="textEditor.saveAndClose()"><i class="fa fa-circle-notch fa-spin" ng-show="textEditor.busy"></i> {{ 'main.dialog.save' | tr }}</button>
</div>
</div>
</div>
</div>
2020-07-13 18:30:29 +02:00
2020-07-10 19:10:29 +02:00
<!-- Modal upload -->
<div class="modal fade" id="uploadModal" tabindex="-1" role="dialog">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
2020-11-12 23:13:52 +01:00
<h4 class="modal-title">{{ 'filemanager.uploadingDialog.title' | tr:{ countDone: uploadStatus.countDone, count: uploadStatus.count } }}</h4>
2020-07-10 19:10:29 +02:00
</div>
<div class="modal-body">
2020-10-08 16:05:24 +02:00
<div ng-show="uploadStatus.error">
2020-11-10 00:59:03 +01:00
<p class="text-danger" ng-show="uploadStatus.error === 'exists'">{{ 'filemanager.uploadingDialog.errorAlreadyExists' | tr }}</p>
<p class="text-danger" ng-show="uploadStatus.error === 'generic'">{{ 'filemanager.uploadingDialog.errorFailed' | tr }}</p>
2020-10-08 16:05:24 +02:00
</div>
2020-12-08 12:01:47 +01:00
<span><b>{{ uploadStatus.sizeDone | prettyByteSize }}</b> (total {{ uploadStatus.size | prettyByteSize }})</span>
2020-10-08 16:05:24 +02:00
<div class="progress progress-striped active" ng-hide="uploadStatus.error">
2020-07-10 19:10:29 +02:00
<div class="progress-bar progress-bar-success" role="progressbar" style="width: {{ uploadStatus.percentDone || 0 }}%"></div>
</div>
2020-10-08 16:05:24 +02:00
<p class="no-wrap" ng-hide="uploadStatus.error">{{ uploadStatus.fileName }}</p>
2020-07-10 19:10:29 +02:00
</div>
<div class="modal-footer" style="text-align: left;">
2020-11-10 00:59:03 +01:00
<small ng-hide="uploadStatus.error">{{ 'filemanager.uploadingDialog.closeWarning' | tr }}</small>
<button class="btn btn-default pull-right" ng-show="uploadStatus.error" data-dismiss="modal">{{ 'main.dialog.close' | tr }}</button>
<button class="btn btn-primary pull-right" ng-show="uploadStatus.error === 'generic'" ng-click="retryUpload(false)">{{ 'filemanager.uploadingDialog.retry' | tr }}</button>
<button class="btn btn-danger pull-right" ng-show="uploadStatus.error === 'exists'" ng-click="retryUpload(true)">{{ 'filemanager.uploadingDialog.overwrite' | tr }}</button>
2020-07-10 19:10:29 +02:00
</div>
</div>
</div>
</div>
2022-08-25 10:26:22 +02:00
<div class="animateMe ng-hide layout-root" ng-show="initialized">
<div class="row" ng-hide="title">
<div class="col-md-12 text-center">
<h3>{{ 'filemanager.notFound' | tr }}</h3>
</div>
</div>
2020-07-08 17:30:37 +02:00
<input type="file" id="uploadFileInput" style="display: none" multiple/>
<input type="file" id="uploadFolderInput" style="display: none" multiple webkitdirectory directory/>
2020-07-10 15:01:56 +02:00
<div class="container card" ng-show="title" style="max-width: unset;">
<h4 class="text-left">
{{ title }}
2020-07-10 15:27:44 +02:00
2022-08-24 12:47:00 +02:00
<div class="pull-right">
<div class="btn-group" ng-show="volumes.length">
2022-08-24 12:47:00 +02:00
<button type="button" class="btn btn-sm btn-default dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false"><i class="fas fa-folder"></i> <span class="caret"></span></button>
<ul class="dropdown-menu dropdown-menu-right">
<li ng-repeat="volume in volumes"><a class="hand" ng-href="{{ '/filemanager.html?type=volume&id=' + volume.id }}" target="_blank"><i class="fas fa-folder fa-fw"></i> {{ volume.name }}</a></li>
</ul>
2020-07-14 14:03:40 +02:00
</div>
2022-08-25 12:23:38 +02:00
<button type="button" class="btn btn-sm btn-default" ng-class="{ 'active': splitView }" ng-click="toggleSplitView()"><i class="fas fa-columns"></i></button>
2022-08-24 12:47:00 +02:00
<div class="btn-group">
2022-08-25 12:23:38 +02:00
<button type="button" class="btn btn-sm btn-default" ng-show="backendType === 'app'" ng-click="onRestartApp()" uib-tooltip="{{ 'filemanager.toolbar.restartApp' | tr }}" tooltip-placement="bottom" tooltip-append-to-body="true"><i class="fas fa-sync-alt"></i></button>
<button type="button" class="btn btn-sm btn-default" ng-show="backendType === 'mail'" ng-click="onRestartMail()" uib-tooltip="{{ 'filemanager.toolbar.restartApp' | tr }}" tooltip-placement="bottom" tooltip-append-to-body="true"><i class="fas fa-sync-alt"></i></button>
<a type="button" class="btn btn-sm btn-default" ng-href="/logs.html?{{ backendType === 'app' ? 'appId=' + backendId : 'id=mail' }}" target="_blank" uib-tooltip="{{ 'filemanager.toolbar.openLogs' | tr }}" tooltip-placement="bottom" tooltip-append-to-body="true"><i class="fas fa-align-left"></i></a>
<a type="button" class="btn btn-sm btn-default" ng-show="backendType === 'app'" ng-href="{{ '/terminal.html?id=' + backendId }}" target="_blank" uib-tooltip="{{ 'filemanager.toolbar.openTerminal' | tr }}" tooltip-placement="bottom" tooltip-append-to-body="true"><i class="fa fa-terminal"></i></a>
2022-08-24 12:47:00 +02:00
</div>
2020-07-14 14:03:40 +02:00
</div>
</h4>
2020-07-14 14:03:40 +02:00
<div class="file-trees">
2022-08-25 19:38:02 +02:00
<filetree ng-class="{ 'two-pane': splitView }"
on-upload-folder="onUploadFolder(cwd)"
on-upload-file="onUploadFile(cwd)"
on-new-file="newFile.show(cwd)"
on-new-folder="newFolder.show(cwd)"
2022-08-25 19:38:02 +02:00
on-delete-entries="deleteEntries.show(cwd, entries)"
on-rename-entry="renameEntry.show(cwd, entry)"
on-extract-entry="extractEntry(cwd, entry)"
on-chown-entries="chownEntries.show(cwd, entries)"
2022-08-25 19:38:02 +02:00
backend-type="backendType" backend-id="backendId" view="VIEW.LEFT"
ng-click="setActiveView(VIEW.LEFT)"></filetree>
<filetree ng-show="splitView" class="two-pane"
2022-08-25 19:38:02 +02:00
on-upload-folder="onUploadFolder(cwd)"
on-upload-file="onUploadFile(cwd)"
on-new-file="newFile.show(cwd)"
on-new-folder="newFolder.show(cwd)"
2022-08-25 19:38:02 +02:00
on-delete-entries="deleteEntries.show(cwd, entries)"
on-rename-entry="renameEntry.show(cwd, entry)"
on-extract-entry="extractEntry(cwd, entry)"
on-chown-entries="chownEntries.show(cwd, entries)"
2022-08-25 19:38:02 +02:00
backend-type="backendType" backend-id="backendId" view="VIEW.RIGHT"
ng-click="setActiveView(VIEW.RIGHT)"></filetree>
2020-07-08 17:30:37 +02:00
</div>
2020-07-08 17:30:37 +02:00
</div>
</div>
2020-07-10 15:27:44 +02:00
<div ng-show="textEditor.visible" class="text-editor">
<div>
<div class="toolbar">
<div><span>{{ textEditor.entry.fileName }}</span></div>
<button type="button" class="btn btn-primary" ng-click="textEditor.maybeClose()">{{ 'main.dialog.close' | tr }}</button>
<button type="button" class="btn btn-success" ng-click="textEditor.save()" ng-disabled="textEditor.busy"><i class="fa fa-circle-notch fa-spin" ng-show="textEditor.busy"></i> {{ 'main.dialog.save' | tr }}</button>
</div>
</div>
<div id="textEditorContainer" style="flex-grow: 2; border: 0px solid black"></div>
2020-07-08 17:30:37 +02:00
</div>
<footer class="text-center ng-cloak">
<span class="text-muted" ng-bind-html="status.footer | markdown2html"></span>
</footer>
</div>
2020-07-08 17:30:37 +02:00
</body>
</html>