'use strict'; angular.module('Application').controller('SettingsController', ['$scope', '$location', '$rootScope', '$timeout', 'Client', 'AppStore', function ($scope, $location, $rootScope, $timeout, Client, AppStore) { Client.onReady(function () { if (!Client.getUserInfo().admin) $location.path('/'); }); $scope.client = Client; $scope.user = Client.getUserInfo(); $scope.config = Client.getConfig(); $scope.caasConfig = {}; $scope.appstoreConfig = {}; $scope.installedApps = Client.getInstalledApps(); $scope.currency = null; $scope.availableRegions = []; $scope.currentRegionSlug = null; $scope.availablePlans = []; $scope.currentPlan = null; $scope.subscription = null; $scope.prettyProviderName = function (provider) { switch (provider) { case 'caas': return 'Managed Cloudron'; default: return provider; } }; $scope.update = { error: {}, busy: false, show: function (form) { $scope.update.error.generic = null; $scope.update.busy = false; form.$setPristine(); form.$setUntouched(); if (!$scope.config.update.box.sourceTarballUrl) { $('#setupSubscriptionModal').modal('show'); } else { $('#updateModal').modal('show'); } }, submit: function () { $scope.update.error.generic = null; $scope.update.busy = true; Client.update(function (error) { if (error) { if (error.statusCode === 409) { $scope.update.error.generic = 'Please try again later. The Cloudron is creating a backup at the moment.'; } else { $scope.update.error.generic = error.message; console.error('Unable to update.', error); } $scope.update.busy = false; return; } window.location.href = '/update.html'; }); } }; $scope.planChange = { busy: false, error: {}, password: '', requestedPlan: null, showChangePlan: function () { $('#planChangeModal').modal('show'); }, planChangeReset: function () { $scope.planChange.error.password = null; $scope.planChange.password = ''; $scope.planChangeForm.$setPristine(); $scope.planChangeForm.$setUntouched(); }, doChangePlan: function () { $scope.planChange.busy = true; var options = { size: $scope.planChange.requestedPlan.slug, name: $scope.planChange.requestedPlan.name, price: $scope.planChange.requestedPlan.price, region: $scope.currentRegionSlug }; Client.changePlan(options, $scope.planChange.password, function (error) { $scope.planChange.busy = false; if (error) { if (error.statusCode === 403) { $scope.planChange.error.password = true; $scope.planChange.password = ''; $scope.planChangeForm.password.$setPristine(); $('#inputPlanChangePassword').focus(); } else { console.error('Unable to change plan.', error); } } else { $scope.planChange.planChangeReset(); $('#planChangeModal').modal('hide'); window.location.href = '/update.html'; } $scope.planChange.busy = false; }); } }; $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; Client.setAppAutoupdatePattern($scope.autoUpdate.pattern, function (error) { if (error) $scope.autoUpdate.error = error.message; else $scope.autoUpdate.currentPattern = $scope.autoUpdate.pattern; $scope.autoUpdate.busy = false; $scope.autoUpdate.success = true; }); } }; function getAutoupdatePattern() { Client.getAppAutoupdatePattern(function (error, result) { if (error) return console.error(error); $scope.autoUpdate.currentPattern = result.pattern; $scope.autoUpdate.pattern = result.pattern; }); } function getSubscription() { AppStore.getSubscription($scope.appstoreConfig, function (error, result) { if (error) return console.error(error); $scope.subscription = result; // also reload the subscription on the main controller $scope.$parent.fetchAppstoreProfileAndSubscription(function () {}); // check again to give more immediate feedback once a subscription was setup if (result.plan.id === 'free') $timeout(getSubscription, 10000); }); } function getPlans() { AppStore.getSizes(function (error, result) { if (error) return console.error(error); var found = false; var SIZE_SLUGS = [ '512mb', '1gb', '2gb', '4gb', '8gb', '16gb', '32gb', '48gb', '64gb' ]; result = result.filter(function (size) { // only show plans bigger than the current size if (found) return true; found = SIZE_SLUGS.indexOf(size.slug) > SIZE_SLUGS.indexOf($scope.caasConfig.plan.slug); return found; }); angular.copy(result, $scope.availablePlans); // prepend the current plan $scope.availablePlans.unshift($scope.caasConfig.plan); $scope.planChange.requestedPlan = $scope.availablePlans[0]; // need the reference to preselect AppStore.getRegions(function (error, result) { if (error) return console.error(error); angular.copy(result, $scope.availableRegions); $scope.currentRegionSlug = $scope.caasConfig.region; }); }); } $('#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]); }; $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(); if ($scope.config.provider === 'caas') { Client.getCaasConfig(function (error, caasConfig) { if (error) return console.error(error); $scope.caasConfig = caasConfig; getPlans(); $scope.currentPlan = caasConfig.plan; $scope.currency = caasConfig.currency === 'eur' ? '€' : '$'; }); } else { Client.getAppstoreConfig(function (error, appstoreConfig) { if (error) return console.error(error); if (!appstoreConfig.token) return; AppStore.getProfile(appstoreConfig.token, function (error, result) { if (error) return console.error(error); // assign late to avoid UI flicketing on update appstoreConfig.profile = result; $scope.appstoreConfig = appstoreConfig; getSubscription(); }); }); } }); // setup all the dialog focus handling ['planChangeModal', 'appstoreLoginModal', 'cloudronNameChangeModal'].forEach(function (id) { $('#' + id).on('shown.bs.modal', function () { $(this).find("[autofocus]:first").focus(); }); }); $('.modal-backdrop').remove(); }]);