2018-01-22 13:01:38 -08:00
|
|
|
'use strict';
|
|
|
|
|
|
2019-01-22 10:54:03 +01:00
|
|
|
/* global angular:false */
|
|
|
|
|
/* global $:false */
|
|
|
|
|
|
2019-05-04 21:57:42 -07:00
|
|
|
angular.module('Application').controller('SettingsController', ['$scope', '$location', '$rootScope', '$timeout', 'Client', function ($scope, $location, $rootScope, $timeout, Client) {
|
2018-08-03 10:09:04 -07:00
|
|
|
Client.onReady(function () { if (!Client.getUserInfo().admin) $location.path('/'); });
|
2018-01-22 13:01:38 -08:00
|
|
|
|
|
|
|
|
$scope.client = Client;
|
|
|
|
|
$scope.user = Client.getUserInfo();
|
|
|
|
|
$scope.config = Client.getConfig();
|
2018-03-30 15:12:34 +02:00
|
|
|
$scope.installedApps = Client.getInstalledApps();
|
2018-01-22 13:01:38 -08:00
|
|
|
|
2018-03-28 12:29:37 +02:00
|
|
|
$scope.subscription = null;
|
2019-05-06 11:39:45 +02:00
|
|
|
$scope.subscriptionBusy = true;
|
2018-01-22 13:01:38 -08:00
|
|
|
|
2018-03-09 00:32:34 -08:00
|
|
|
$scope.prettyProviderName = function (provider) {
|
|
|
|
|
switch (provider) {
|
2018-09-05 14:37:54 -07:00
|
|
|
case 'caas': return 'Managed Cloudron';
|
|
|
|
|
default: return provider;
|
2018-03-09 00:32:34 -08:00
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
2018-03-30 15:12:34 +02:00
|
|
|
$scope.update = {
|
2018-11-30 16:06:37 -08:00
|
|
|
error: {}, // this is for the dialog
|
2018-03-30 15:12:34 +02:00
|
|
|
busy: false,
|
2018-11-30 16:06:37 -08:00
|
|
|
percent: 0,
|
2018-12-10 16:50:40 +01:00
|
|
|
message: 'Downloading',
|
2018-11-30 16:06:37 -08:00
|
|
|
errorMessage: '', // this shows inline
|
2018-12-09 10:45:01 -08:00
|
|
|
taskId: '',
|
2018-03-30 15:12:34 +02:00
|
|
|
|
2018-11-30 16:06:37 -08:00
|
|
|
show: function () {
|
2018-03-30 15:12:34 +02:00
|
|
|
$scope.update.error.generic = null;
|
|
|
|
|
$scope.update.busy = false;
|
|
|
|
|
|
|
|
|
|
if (!$scope.config.update.box.sourceTarballUrl) {
|
|
|
|
|
$('#setupSubscriptionModal').modal('show');
|
|
|
|
|
} else {
|
|
|
|
|
$('#updateModal').modal('show');
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
|
2018-11-30 16:06:37 -08:00
|
|
|
stopUpdate: function () {
|
2019-01-24 15:55:21 -08:00
|
|
|
Client.stopTask($scope.update.taskId, function (error) {
|
2018-11-30 16:06:37 -08:00
|
|
|
if (error) {
|
|
|
|
|
if (error.statusCode === 409) {
|
|
|
|
|
$scope.update.errorMessage = 'No update is currently in progress';
|
|
|
|
|
} else {
|
|
|
|
|
console.error(error);
|
|
|
|
|
$scope.update.errorMessage = error.message;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$scope.update.busy = false;
|
|
|
|
|
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
},
|
|
|
|
|
|
2018-12-08 20:21:11 -08:00
|
|
|
checkStatus: function () {
|
2018-12-09 10:45:01 -08:00
|
|
|
Client.getLatestTaskByType('update', function (error, task) {
|
2018-12-08 20:21:11 -08:00
|
|
|
if (error) return console.error(error);
|
2019-01-06 14:52:36 -08:00
|
|
|
if (!task) return;
|
2018-12-08 20:21:11 -08:00
|
|
|
|
2018-12-09 10:45:01 -08:00
|
|
|
$scope.update.taskId = task.id;
|
|
|
|
|
$scope.update.updateStatus();
|
2018-12-08 20:21:11 -08:00
|
|
|
});
|
|
|
|
|
},
|
|
|
|
|
|
2018-12-09 10:45:01 -08:00
|
|
|
reloadIfNeeded: function () {
|
|
|
|
|
Client.getStatus(function (error, status) {
|
|
|
|
|
if (error) return $scope.error(error);
|
|
|
|
|
|
|
|
|
|
if (window.localStorage.version !== status.version) window.location.reload(true);
|
|
|
|
|
});
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
updateStatus: function () {
|
|
|
|
|
Client.getTask($scope.update.taskId, function (error, data) {
|
|
|
|
|
if (error) return window.setTimeout($scope.update.updateStatus, 5000);
|
2018-11-30 16:06:37 -08:00
|
|
|
|
|
|
|
|
if (!data.active) {
|
|
|
|
|
$scope.update.busy = false;
|
|
|
|
|
$scope.update.message = '';
|
|
|
|
|
$scope.update.percent = 100; // indicates that 'result' is valid
|
|
|
|
|
$scope.update.errorMessage = data.errorMessage;
|
|
|
|
|
|
2018-12-09 10:45:01 -08:00
|
|
|
if (!data.errorMessage) $scope.update.reloadIfNeeded(); // assume success
|
2018-11-30 16:06:37 -08:00
|
|
|
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$scope.update.busy = true;
|
|
|
|
|
$scope.update.percent = data.percent;
|
|
|
|
|
$scope.update.message = data.message;
|
|
|
|
|
|
2018-12-09 10:45:01 -08:00
|
|
|
window.setTimeout($scope.update.updateStatus, 500);
|
2018-11-30 16:06:37 -08:00
|
|
|
});
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
startUpdate: function () {
|
2018-03-30 15:12:34 +02:00
|
|
|
$scope.update.error.generic = null;
|
|
|
|
|
$scope.update.busy = true;
|
2018-11-30 16:06:37 -08:00
|
|
|
$scope.update.percent = 0;
|
|
|
|
|
$scope.update.message = '';
|
|
|
|
|
$scope.update.errorMessage = '';
|
2018-03-30 15:12:34 +02:00
|
|
|
|
2018-12-08 20:21:11 -08:00
|
|
|
Client.update(function (error, taskId) {
|
2018-03-30 15:12:34 +02:00
|
|
|
if (error) {
|
2018-12-04 14:12:35 -08:00
|
|
|
$scope.update.error.generic = error.message;
|
2018-03-30 15:12:34 +02:00
|
|
|
$scope.update.busy = false;
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2018-11-30 16:06:37 -08:00
|
|
|
$('#updateModal').modal('hide');
|
|
|
|
|
|
2018-12-09 10:45:01 -08:00
|
|
|
$scope.update.taskId = taskId;
|
|
|
|
|
$scope.update.updateStatus();
|
2018-03-30 15:12:34 +02:00
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
2018-01-22 13:01:38 -08:00
|
|
|
$scope.avatarChange = {
|
|
|
|
|
busy: false,
|
|
|
|
|
error: {},
|
|
|
|
|
avatar: null,
|
|
|
|
|
availableAvatars: [{
|
|
|
|
|
file: null,
|
|
|
|
|
data: null,
|
|
|
|
|
url: '/img/avatars/avatar_0.png',
|
|
|
|
|
}, {
|
|
|
|
|
file: null,
|
|
|
|
|
data: null,
|
|
|
|
|
url: '/img/avatars/rubber-duck.png'
|
|
|
|
|
}, {
|
|
|
|
|
file: null,
|
|
|
|
|
data: null,
|
|
|
|
|
url: '/img/avatars/carrot.png'
|
|
|
|
|
}, {
|
|
|
|
|
file: null,
|
|
|
|
|
data: null,
|
|
|
|
|
url: '/img/avatars/cup.png'
|
|
|
|
|
}, {
|
|
|
|
|
file: null,
|
|
|
|
|
data: null,
|
|
|
|
|
url: '/img/avatars/football.png'
|
|
|
|
|
}, {
|
|
|
|
|
file: null,
|
|
|
|
|
data: null,
|
|
|
|
|
url: '/img/avatars/owl.png'
|
|
|
|
|
}, {
|
|
|
|
|
file: null,
|
|
|
|
|
data: null,
|
|
|
|
|
url: '/img/avatars/space-rocket.png'
|
|
|
|
|
}, {
|
|
|
|
|
file: null,
|
|
|
|
|
data: null,
|
|
|
|
|
url: '/img/avatars/armchair.png'
|
|
|
|
|
}, {
|
|
|
|
|
file: null,
|
|
|
|
|
data: null,
|
|
|
|
|
url: '/img/avatars/cap.png'
|
|
|
|
|
}, {
|
|
|
|
|
file: null,
|
|
|
|
|
data: null,
|
|
|
|
|
url: '/img/avatars/pan.png'
|
|
|
|
|
}, {
|
|
|
|
|
file: null,
|
|
|
|
|
data: null,
|
|
|
|
|
url: '/img/avatars/meat.png'
|
|
|
|
|
}, {
|
|
|
|
|
file: null,
|
|
|
|
|
data: null,
|
|
|
|
|
url: '/img/avatars/umbrella.png'
|
|
|
|
|
}, {
|
|
|
|
|
file: null,
|
|
|
|
|
data: null,
|
|
|
|
|
url: '/img/avatars/jar.png'
|
|
|
|
|
}],
|
|
|
|
|
|
|
|
|
|
getBlobFromImg: function (img, callback) {
|
|
|
|
|
var size = 256;
|
|
|
|
|
|
|
|
|
|
var canvas = document.createElement('canvas');
|
|
|
|
|
canvas.width = size;
|
|
|
|
|
canvas.height = size;
|
|
|
|
|
|
|
|
|
|
var imageDimensionRatio = img.width / img.height;
|
|
|
|
|
var canvasDimensionRatio = canvas.width / canvas.height;
|
|
|
|
|
var renderableHeight, renderableWidth, xStart, yStart;
|
|
|
|
|
|
|
|
|
|
if (imageDimensionRatio > canvasDimensionRatio) {
|
|
|
|
|
renderableHeight = canvas.height;
|
|
|
|
|
renderableWidth = img.width * (renderableHeight / img.height);
|
|
|
|
|
xStart = (canvas.width - renderableWidth) / 2;
|
|
|
|
|
yStart = 0;
|
|
|
|
|
} else if (imageDimensionRatio < canvasDimensionRatio) {
|
|
|
|
|
renderableWidth = canvas.width;
|
|
|
|
|
renderableHeight = img.height * (renderableWidth / img.width);
|
|
|
|
|
xStart = 0;
|
|
|
|
|
yStart = (canvas.height - renderableHeight) / 2;
|
|
|
|
|
} else {
|
|
|
|
|
renderableHeight = canvas.height;
|
|
|
|
|
renderableWidth = canvas.width;
|
|
|
|
|
xStart = 0;
|
|
|
|
|
yStart = 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var ctx = canvas.getContext('2d');
|
|
|
|
|
ctx.clearRect(0, 0, canvas.width, canvas.height);
|
|
|
|
|
ctx.drawImage(img, xStart, yStart, renderableWidth, renderableHeight);
|
|
|
|
|
|
|
|
|
|
canvas.toBlob(callback);
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
doChangeAvatar: function () {
|
|
|
|
|
$scope.avatarChange.error.avatar = null;
|
|
|
|
|
$scope.avatarChange.busy = true;
|
|
|
|
|
|
|
|
|
|
var img = document.getElementById('previewAvatar');
|
|
|
|
|
$scope.avatarChange.avatar.file = $scope.avatarChange.getBlobFromImg(img, function (blob) {
|
|
|
|
|
Client.changeCloudronAvatar(blob, function (error) {
|
|
|
|
|
if (error) {
|
|
|
|
|
console.error('Unable to change cloudron avatar.', error);
|
|
|
|
|
} else {
|
|
|
|
|
Client.resetAvatar();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$('#avatarChangeModal').modal('hide');
|
|
|
|
|
$scope.avatarChange.avatarChangeReset();
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
setPreviewAvatar: function (avatar) {
|
|
|
|
|
$scope.avatarChange.avatar = avatar;
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
avatarChangeReset: function () {
|
|
|
|
|
$scope.avatarChange.error.avatar = null;
|
|
|
|
|
$scope.avatarChange.avatar = null;
|
|
|
|
|
$scope.avatarChange.busy = false;
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
showChangeAvatar: function () {
|
|
|
|
|
$scope.avatarChange.avatarChangeReset();
|
|
|
|
|
$('#avatarChangeModal').modal('show');
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
showCustomAvatarSelector: function () {
|
|
|
|
|
$('#avatarFileInput').click();
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
$scope.s3like = function (provider) {
|
|
|
|
|
return provider === 's3' || provider === 'minio' || provider === 's3-v4-compat' || provider === 'exoscale-sos' || provider === 'digitalocean-spaces';
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
$scope.autoUpdate = {
|
|
|
|
|
busy: false,
|
|
|
|
|
success: false,
|
|
|
|
|
error: '',
|
|
|
|
|
pattern: '',
|
|
|
|
|
currentPattern: '',
|
|
|
|
|
|
|
|
|
|
checkNow: function () {
|
|
|
|
|
$scope.autoUpdate.busy = true;
|
|
|
|
|
|
|
|
|
|
Client.checkForUpdates(function (error) {
|
|
|
|
|
if (error) $scope.autoUpdate.error = error.message;
|
|
|
|
|
|
|
|
|
|
$scope.autoUpdate.busy = false;
|
|
|
|
|
});
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
submit: function () {
|
|
|
|
|
if ($scope.autoUpdate.pattern === $scope.autoUpdate.currentPattern) return;
|
|
|
|
|
|
|
|
|
|
$scope.autoUpdate.error = '';
|
|
|
|
|
$scope.autoUpdate.busy = true;
|
|
|
|
|
$scope.autoUpdate.success = false;
|
|
|
|
|
|
2018-02-06 19:23:52 +01:00
|
|
|
Client.setAppAutoupdatePattern($scope.autoUpdate.pattern, function (error) {
|
2018-01-22 13:01:38 -08:00
|
|
|
if (error) $scope.autoUpdate.error = error.message;
|
|
|
|
|
else $scope.autoUpdate.currentPattern = $scope.autoUpdate.pattern;
|
|
|
|
|
|
|
|
|
|
$scope.autoUpdate.busy = false;
|
|
|
|
|
$scope.autoUpdate.success = true;
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
function getAutoupdatePattern() {
|
2018-02-06 19:23:52 +01:00
|
|
|
Client.getAppAutoupdatePattern(function (error, result) {
|
2018-01-22 13:01:38 -08:00
|
|
|
if (error) return console.error(error);
|
|
|
|
|
|
|
|
|
|
$scope.autoUpdate.currentPattern = result.pattern;
|
|
|
|
|
$scope.autoUpdate.pattern = result.pattern;
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
2019-04-27 22:38:09 +02:00
|
|
|
function getUnstableAppsConfig() {
|
|
|
|
|
Client.getUnstableAppsConfig(function (error, result) {
|
|
|
|
|
if (error) return console.error(error);
|
|
|
|
|
|
|
|
|
|
$scope.unstableApps.enabled = result;
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
2018-01-22 13:01:38 -08:00
|
|
|
function getSubscription() {
|
2019-05-06 11:39:45 +02:00
|
|
|
$scope.subscriptionBusy = true;
|
|
|
|
|
|
2019-05-04 21:26:50 -07:00
|
|
|
Client.getSubscription(function (error, result) {
|
2019-05-06 11:39:45 +02:00
|
|
|
$scope.subscriptionBusy = false;
|
|
|
|
|
|
|
|
|
|
if (error && error.statusCode === 412) return; // not yet registered
|
2018-01-22 13:01:38 -08:00
|
|
|
if (error) return console.error(error);
|
2019-01-06 15:09:02 -08:00
|
|
|
if (!$scope.$parent) return; // user changed view. otherwise we get an error that $scope.$parent is null
|
2018-01-22 13:01:38 -08:00
|
|
|
|
2018-03-28 12:29:37 +02:00
|
|
|
$scope.subscription = result;
|
2018-01-22 13:01:38 -08:00
|
|
|
|
2018-03-28 14:19:34 +02:00
|
|
|
// also reload the subscription on the main controller
|
2019-05-04 21:57:42 -07:00
|
|
|
$scope.$parent.updateSubscriptionStatus();
|
2018-03-28 14:19:34 +02:00
|
|
|
|
2018-01-22 13:01:38 -08:00
|
|
|
// check again to give more immediate feedback once a subscription was setup
|
|
|
|
|
if (result.plan.id === 'free') $timeout(getSubscription, 10000);
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$('#avatarFileInput').get(0).onchange = function (event) {
|
|
|
|
|
var fr = new FileReader();
|
|
|
|
|
fr.onload = function () {
|
|
|
|
|
$scope.$apply(function () {
|
|
|
|
|
var tmp = {
|
|
|
|
|
file: event.target.files[0],
|
|
|
|
|
data: fr.result,
|
|
|
|
|
url: null
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
$scope.avatarChange.availableAvatars.push(tmp);
|
|
|
|
|
$scope.avatarChange.setPreviewAvatar(tmp);
|
|
|
|
|
});
|
|
|
|
|
};
|
|
|
|
|
fr.readAsDataURL(event.target.files[0]);
|
|
|
|
|
};
|
|
|
|
|
|
2019-04-27 22:38:09 +02:00
|
|
|
$scope.unstableApps = {
|
|
|
|
|
busy: false,
|
|
|
|
|
success: false,
|
|
|
|
|
|
|
|
|
|
enabled: false,
|
|
|
|
|
|
|
|
|
|
submit: function () {
|
|
|
|
|
$scope.unstableApps.busy = true;
|
|
|
|
|
|
|
|
|
|
Client.setUnstableAppsConfig($scope.unstableApps.enabled, function (error) {
|
|
|
|
|
$scope.unstableApps.busy = false;
|
|
|
|
|
|
|
|
|
|
if (error) {
|
|
|
|
|
console.error('Unable to change unstable app listing.', error);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$scope.unstableApps.success = true;
|
|
|
|
|
$timeout(function () {
|
|
|
|
|
$scope.unstableApps.success = false;
|
|
|
|
|
}, 5000);
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
2018-01-22 13:01:38 -08:00
|
|
|
$scope.cloudronNameChange = {
|
|
|
|
|
busy: false,
|
|
|
|
|
error: {},
|
|
|
|
|
name: '',
|
|
|
|
|
|
|
|
|
|
reset: function () {
|
|
|
|
|
$scope.cloudronNameChange.busy = false;
|
|
|
|
|
$scope.cloudronNameChange.error.name = null;
|
|
|
|
|
$scope.cloudronNameChange.name = '';
|
|
|
|
|
|
|
|
|
|
$scope.cloudronNameChangeForm.$setUntouched();
|
|
|
|
|
$scope.cloudronNameChangeForm.$setPristine();
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
show: function () {
|
|
|
|
|
$scope.cloudronNameChange.reset();
|
|
|
|
|
$scope.cloudronNameChange.name = $scope.config.cloudronName;
|
|
|
|
|
$('#cloudronNameChangeModal').modal('show');
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
submit: function () {
|
|
|
|
|
$scope.cloudronNameChange.error.name = null;
|
|
|
|
|
$scope.cloudronNameChange.busy = true;
|
|
|
|
|
|
|
|
|
|
Client.changeCloudronName($scope.cloudronNameChange.name, function (error) {
|
|
|
|
|
$scope.cloudronNameChange.busy = false;
|
|
|
|
|
|
|
|
|
|
if (error) {
|
|
|
|
|
if (error.statusCode === 400) {
|
|
|
|
|
$scope.cloudronNameChange.error.name = 'Invalid name';
|
|
|
|
|
$scope.cloudronNameChange.name = '';
|
|
|
|
|
$('#inputCloudronName').focus();
|
|
|
|
|
$scope.cloudronNameChangeForm.password.$setPristine();
|
|
|
|
|
} else {
|
|
|
|
|
console.error('Unable to change name.', error);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$scope.cloudronNameChange.reset();
|
|
|
|
|
$('#cloudronNameChangeModal').modal('hide');
|
|
|
|
|
|
|
|
|
|
Client.refreshConfig();
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
Client.onReady(function () {
|
|
|
|
|
getAutoupdatePattern();
|
2019-04-27 22:38:09 +02:00
|
|
|
getUnstableAppsConfig();
|
2018-01-22 13:01:38 -08:00
|
|
|
|
2018-12-08 20:21:11 -08:00
|
|
|
$scope.update.checkStatus();
|
2018-12-04 14:05:55 -08:00
|
|
|
|
2019-05-07 10:11:54 -07:00
|
|
|
getSubscription();
|
2018-01-22 13:01:38 -08:00
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// setup all the dialog focus handling
|
2018-06-07 16:30:40 +02:00
|
|
|
['planChangeModal', 'appstoreLoginModal', 'cloudronNameChangeModal'].forEach(function (id) {
|
2018-01-22 13:01:38 -08:00
|
|
|
$('#' + id).on('shown.bs.modal', function () {
|
|
|
|
|
$(this).find("[autofocus]:first").focus();
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
$('.modal-backdrop').remove();
|
|
|
|
|
}]);
|