diff --git a/src/components/filetree.js b/src/components/filetree.js
index 819cdbd27..13f4fd3f7 100644
--- a/src/components/filetree.js
+++ b/src/components/filetree.js
@@ -1,19 +1,24 @@
'use strict';
/* global angular */
-/* global sanitize */
+/* global sanitize, isModalVisible */
angular.module('Application').component('filetree', {
bindings: {
backendId: '<',
backendType: '<',
- view: '<'
+ view: '<',
+ onUploadFile: '&',
+ onUploadFolder: '&',
+ onDeleteEntries: '&'
},
templateUrl: 'components/filetree.html?<%= revision %>',
controller: [ '$scope', '$translate', '$timeout', 'Client', FileTreeController ]
});
function FileTreeController($scope, $translate, $timeout, Client) {
+ var ctrl = this;
+
$scope.backendId = this.backendId;
$scope.backendType = this.backendType;
$scope.view = this.view;
@@ -30,6 +35,9 @@ function FileTreeController($scope, $translate, $timeout, Client) {
$scope.dropToBody = false;
$scope.applicationLink = '';
+ // register so parent can call child
+ $scope.$parent.registerChild($scope);
+
$scope.owners = [
{ name: 'cloudron', value: 1000 },
{ name: 'www-data', value: 33 },
@@ -305,6 +313,10 @@ function FileTreeController($scope, $translate, $timeout, Client) {
};
$scope.refresh = function () {
+ $scope.$parent.refresh();
+ };
+
+ $scope.onRefresh = function () {
$scope.selected = [];
Client.filesGet($scope.backendId, $scope.backendType, $scope.cwd, 'data', function (error, result) {
@@ -495,101 +507,9 @@ function FileTreeController($scope, $translate, $timeout, Client) {
$scope.selected = $scope.entries.slice();
};
- $scope.uploadStatus = {
- error: null,
- busy: false,
- fileName: '',
- count: 0,
- countDone: 0,
- size: 0,
- done: 0,
- percentDone: 0,
- files: [],
- targetFolder: ''
- };
-
- function uploadFiles(files, targetFolder, overwrite) {
- if (!files || !files.length) return;
-
- targetFolder = targetFolder || $scope.cwd;
- overwrite = !!overwrite;
-
- // prevent it from getting closed
- $('#uploadModal').modal({
- backdrop: 'static',
- keyboard: false
- });
-
- $scope.uploadStatus.files = files;
- $scope.uploadStatus.targetFolder = targetFolder;
- $scope.uploadStatus.error = null;
- $scope.uploadStatus.busy = true;
- $scope.uploadStatus.count = files.length;
- $scope.uploadStatus.countDone = 0;
- $scope.uploadStatus.size = 0;
- $scope.uploadStatus.sizeDone = 0;
- $scope.uploadStatus.done = 0;
- $scope.uploadStatus.percentDone = 0;
-
- for (var i = 0; i < files.length; ++i) {
- $scope.uploadStatus.size += files[i].size;
- }
-
- async.eachSeries(files, function (file, callback) {
- var filePath = sanitize(targetFolder + '/' + (file.webkitRelativePath || file.name));
-
- $scope.uploadStatus.fileName = file.name;
-
- Client.filesUpload($scope.backendId, $scope.backendType, filePath, file, overwrite, function (loaded) {
- $scope.uploadStatus.percentDone = ($scope.uploadStatus.done+loaded) * 100 / $scope.uploadStatus.size;
- $scope.uploadStatus.sizeDone = loaded;
- }, function (error) {
- if (error) return callback(error);
-
- $scope.uploadStatus.done += file.size;
- $scope.uploadStatus.percentDone = $scope.uploadStatus.done * 100 / $scope.uploadStatus.size;
- $scope.uploadStatus.countDone++;
-
- callback();
- });
- }, function (error) {
- $scope.uploadStatus.busy = false;
-
- if (error && error.statusCode === 409) {
- $scope.uploadStatus.error = 'exists';
- return;
- } else if (error) {
- console.error(error);
- $scope.uploadStatus.error = 'generic';
- return;
- }
-
- $('#uploadModal').modal('hide');
-
- $scope.uploadStatus.fileName = '';
- $scope.uploadStatus.count = 0;
- $scope.uploadStatus.size = 0;
- $scope.uploadStatus.sizeDone = 0;
- $scope.uploadStatus.done = 0;
- $scope.uploadStatus.percentDone = 100;
- $scope.uploadStatus.files = [];
- $scope.uploadStatus.targetFolder = '';
-
- $scope.refresh();
- });
- }
-
- $scope.retryUpload = function (overwrite) {
- uploadFiles($scope.uploadStatus.files, $scope.uploadStatus.targetFolder, !!overwrite);
- };
-
- // file upload
- $('#uploadFileInput').on('change', function (e) { uploadFiles(e.target.files || [], $scope.cwd, false); });
- $scope.onUploadFile = function () { $('#uploadFileInput').click(); };
-
- // folder upload
- $('#uploadFolderInput').on('change', function (e ) { uploadFiles(e.target.files || [], $scope.cwd, false); });
- $scope.onUploadFolder = function () { $('#uploadFolderInput').click(); };
+ // just events to the parent controller
+ $scope.onUploadFile = function () { ctrl.onUploadFile({ cwd: $scope.cwd }); };
+ $scope.onUploadFolder = function () { ctrl.onUploadFolder({ cwd: $scope.cwd }); };
$scope.restartBusy = false;
$scope.onRestartApp = function () {
@@ -799,35 +719,6 @@ function FileTreeController($scope, $translate, $timeout, Client) {
}
};
- $scope.entryRemove = {
- busy: false,
- error: null,
-
- show: function () {
- $scope.entryRemove.error = null;
-
- $('#entryRemoveModal-' + $scope.$id).modal('show');
- },
-
- submit: function () {
- $scope.entryRemove.busy = true;
-
- async.eachLimit($scope.selected, 5, function (entry, callback) {
- var filePath = sanitize($scope.cwd + '/' + entry.fileName);
-
- Client.filesRemove($scope.backendId, $scope.backendType, filePath, callback);
- }, function (error) {
- $scope.entryRemove.busy = false;
- if (error) return Client.error(error);
-
- $scope.refresh();
-
- $('#entryRemoveModal-' + $scope.$id).modal('hide');
- });
-
- }
- };
-
$translate(['filemanager.list.menu.edit', 'filemanager.list.menu.cut', 'filemanager.list.menu.copy', 'filemanager.list.menu.paste', 'filemanager.list.menu.rename', 'filemanager.list.menu.chown', 'filemanager.list.menu.extract', 'filemanager.list.menu.download', 'filemanager.list.menu.delete' ]).then(function (tr) {
$scope.menuOptions = [
{
@@ -865,7 +756,7 @@ function FileTreeController($scope, $translate, $timeout, Client) {
}, {
text: tr['filemanager.list.menu.delete'],
hasTopDivider: true,
- click: function ($itemScope, $event, entry) { $scope.entryRemove.show(); }
+ click: function ($itemScope, $event, entry) { ctrl.onDeleteEntries({ cwd: $scope.cwd, entries: $scope.selected }); }
}
];
});
@@ -891,27 +782,6 @@ function FileTreeController($scope, $translate, $timeout, Client) {
];
});
- $('.file-list').on('scroll', function (event) {
- if (event.target.scrollTop > 10) event.target.classList.add('top-scroll-indicator');
- else event.target.classList.remove('top-scroll-indicator');
- });
-
- // setup all the dialog focus handling
- ['newFileModal', 'newDirectoryModal', 'renameEntryModal'].forEach(function (id) {
- $('#' + id).on('shown.bs.modal', function () {
- $(this).find('[autofocus]:first').focus();
- });
- });
-
- // selects filename (without extension)
- ['renameEntryModal'].forEach(function (id) {
- $('#' + id).on('shown.bs.modal', function () {
- var elem = $(this).find('[autofocus]:first');
- var text = elem.val();
- elem[0].setSelectionRange(0, text.indexOf('.'));
- });
- });
-
function scrollInView(element) {
if (!element) return;
@@ -970,6 +840,30 @@ function FileTreeController($scope, $translate, $timeout, Client) {
openPath('.');
+ // DOM handlers, wait for elements to exist
+ setTimeout(function () {
+ $('.file-list').on('scroll', function (event) {
+ if (event.target.scrollTop > 10) event.target.classList.add('top-scroll-indicator');
+ else event.target.classList.remove('top-scroll-indicator');
+ });
+
+ // setup all the dialog focus handling
+ ['newFileModal', 'newDirectoryModal', 'renameEntryModal'].forEach(function (id) {
+ $('#' + id + '-' + $scope.$id).on('shown.bs.modal', function () {
+ $(this).find('[autofocus]:first').focus();
+ });
+ });
+
+ // selects filename (without extension)
+ ['renameEntryModal'].forEach(function (id) {
+ $('#' + id + '-' + $scope.$id).on('shown.bs.modal', function () {
+ var elem = $(this).find('[autofocus]:first');
+ var text = elem.val();
+ elem[0].setSelectionRange(0, text.indexOf('.'));
+ });
+ });
+ }, 0);
+
// handle save shortcuts
window.addEventListener('keydown', function (event) {
if ($scope.$parent.activeView !== $scope.view || $scope.$parent.viewerOpen || isModalVisible()) return;
diff --git a/src/filemanager.html b/src/filemanager.html
index fb2f29200..40b20be31 100644
--- a/src/filemanager.html
+++ b/src/filemanager.html
@@ -75,6 +75,44 @@
{{ 'filemanager.status.restartingApp' | tr}}
+
+
+
+
+
+
{{ deleteEntries.error }}
+
+
{{ 'filemanager.removeDialog.reallyDelete' | tr:{ fileName: deleteEntries.entries[0].fileName } }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
{{ 'filemanager.textEditorCloseDialog.details' | tr }}
+
+
+
+
+
@@ -104,25 +142,6 @@
@@ -155,9 +174,19 @@
-
+
-
+
diff --git a/src/js/filemanager.js b/src/js/filemanager.js
index 3be90e081..4474692ee 100644
--- a/src/js/filemanager.js
+++ b/src/js/filemanager.js
@@ -82,6 +82,7 @@ var VIEW = {
app.controller('FileManagerController', ['$scope', '$translate', '$timeout', 'Client', function ($scope, $translate, $timeout, Client) {
var search = decodeURIComponent(window.location.search).slice(1).split('&').map(function (item) { return item.split('='); }).reduce(function (o, k) { o[k[0]] = k[1]; return o; }, {});
+ // expose enums
$scope.VIEW = VIEW;
$scope.initialized = false;
@@ -91,24 +92,189 @@ app.controller('FileManagerController', ['$scope', '$translate', '$timeout', 'Cl
$scope.backendId = search.id;
$scope.backendType = search.type;
$scope.volumes = [];
- $scope.splitView = false;
+ $scope.splitView = !!window.localStorage.splitView;
$scope.activeView = VIEW.LEFT;
$scope.viewerOpen = false;
+
+ // add a hook for children to refresh both tree views
+
+ $scope.children = [];
+ $scope.registerChild = function (child) { $scope.children.push(child); };
+ $scope.refresh = function () {
+ $scope.children.forEach(function (child) {
+ child.onRefresh();
+ });
+ };
+
+ // handle uploads
+
+ $scope.uploadStatus = {
+ error: null,
+ busy: false,
+ fileName: '',
+ count: 0,
+ countDone: 0,
+ size: 0,
+ done: 0,
+ percentDone: 0,
+ files: [],
+ targetFolder: ''
+ };
+
+ function uploadFiles(files, targetFolder, overwrite) {
+ if (!files || !files.length) return;
+
+ targetFolder = targetFolder || $scope.cwd;
+ overwrite = !!overwrite;
+
+ // prevent it from getting closed
+ $('#uploadModal').modal({
+ backdrop: 'static',
+ keyboard: false
+ });
+
+ $scope.uploadStatus.files = files;
+ $scope.uploadStatus.targetFolder = targetFolder;
+ $scope.uploadStatus.error = null;
+ $scope.uploadStatus.busy = true;
+ $scope.uploadStatus.count = files.length;
+ $scope.uploadStatus.countDone = 0;
+ $scope.uploadStatus.size = 0;
+ $scope.uploadStatus.sizeDone = 0;
+ $scope.uploadStatus.done = 0;
+ $scope.uploadStatus.percentDone = 0;
+
+ for (var i = 0; i < files.length; ++i) {
+ $scope.uploadStatus.size += files[i].size;
+ }
+
+ async.eachSeries(files, function (file, callback) {
+ var filePath = sanitize(targetFolder + '/' + (file.webkitRelativePath || file.name));
+
+ $scope.uploadStatus.fileName = file.name;
+
+ Client.filesUpload($scope.backendId, $scope.backendType, filePath, file, overwrite, function (loaded) {
+ $scope.uploadStatus.percentDone = ($scope.uploadStatus.done+loaded) * 100 / $scope.uploadStatus.size;
+ $scope.uploadStatus.sizeDone = loaded;
+ }, function (error) {
+ if (error) return callback(error);
+
+ $scope.uploadStatus.done += file.size;
+ $scope.uploadStatus.percentDone = $scope.uploadStatus.done * 100 / $scope.uploadStatus.size;
+ $scope.uploadStatus.countDone++;
+
+ callback();
+ });
+ }, function (error) {
+ $scope.uploadStatus.busy = false;
+
+ if (error && error.statusCode === 409) {
+ $scope.uploadStatus.error = 'exists';
+ return;
+ } else if (error) {
+ console.error(error);
+ $scope.uploadStatus.error = 'generic';
+ return;
+ }
+
+ $('#uploadModal').modal('hide');
+
+ $scope.uploadStatus.fileName = '';
+ $scope.uploadStatus.count = 0;
+ $scope.uploadStatus.size = 0;
+ $scope.uploadStatus.sizeDone = 0;
+ $scope.uploadStatus.done = 0;
+ $scope.uploadStatus.percentDone = 100;
+ $scope.uploadStatus.files = [];
+ $scope.uploadStatus.targetFolder = '';
+
+ $scope.refresh();
+ });
+ }
+
+ $scope.retryUpload = function (overwrite) {
+ uploadFiles($scope.uploadStatus.files, $scope.uploadStatus.targetFolder, !!overwrite);
+ };
+
+
+ // file and folder upload hooks, stashing $scope.uploadCwd for now
+
+ $scope.uploadCwd = '';
+ $('#uploadFileInput').on('change', function (e ) {
+ uploadFiles(e.target.files || [], $scope.uploadCwd, false);
+ });
+ $scope.onUploadFile = function (cwd) {
+ $scope.uploadCwd = cwd;
+ $('#uploadFileInput').click();
+ };
+
+ $('#uploadFolderInput').on('change', function (e ) {
+ uploadFiles(e.target.files || [], $scope.uploadCwd, false);
+ });
+ $scope.onUploadFolder = function (cwd) {
+ $scope.uploadCwd = cwd;
+ $('#uploadFolderInput').click();
+ };
+
+
+ // handle delete
+
+ $scope.deleteEntries = {
+ busy: false,
+ error: null,
+ cwd: '',
+ entries: [],
+
+ show: function (cwd, entries) {
+ $scope.deleteEntries.error = null;
+ $scope.deleteEntries.cwd = cwd;
+ $scope.deleteEntries.entries = entries;
+
+ $('#entriesDeleteModal').modal('show');
+ },
+
+ submit: function () {
+ $scope.deleteEntries.busy = true;
+
+ async.eachLimit($scope.deleteEntries.entries, 5, function (entry, callback) {
+ var filePath = sanitize($scope.deleteEntries.cwd + '/' + entry.fileName);
+
+ Client.filesRemove($scope.backendId, $scope.backendType, filePath, callback);
+ }, function (error) {
+ $scope.deleteEntries.busy = false;
+ if (error) return Client.error(error);
+
+ $scope.refresh();
+
+ $('#entriesDeleteModal').modal('hide');
+ });
+
+ }
+ };
+
+
+ // split view handling
+
$scope.toggleSplitView = function () {
$scope.splitView = !$scope.splitView;
- if (!$scope.splitView) $scope.activeView = VIEW.LEFT;
+ if (!$scope.splitView) {
+ $scope.activeView = VIEW.LEFT;
+ delete window.localStorage.splitView;
+ } else {
+ window.localStorage.splitView = true;
+ }
};
$scope.setActiveView = function (view) {
$scope.activeView = view;
};
- // for monaco editor
+
+ // monaco text editor
+
var LANGUAGES = [];
- require(['vs/editor/editor.main'], function() {
- LANGUAGES = monaco.languages.getLanguages();
- });
+ require(['vs/editor/editor.main'], function() { LANGUAGES = monaco.languages.getLanguages(); });
function getLanguage(filename) {
var ext = '.' + filename.split('.').pop();
@@ -207,6 +373,9 @@ app.controller('FileManagerController', ['$scope', '$translate', '$timeout', 'Cl
},
};
+
+ // init code
+
function fetchVolumesInfo(mounts) {
$scope.volumes = [];
@@ -299,7 +468,9 @@ app.controller('FileManagerController', ['$scope', '$translate', '$timeout', 'Cl
init();
- // handle save shortcuts
+
+ // toplevel key input handling
+
window.addEventListener('keydown', function (event) {
if((navigator.platform.match('Mac') ? event.metaKey : event.ctrlKey) && event.key === 's') {
if ($scope.view !== 'textEditor') return;