'use strict'; /* global angular:false */ /* global $:false */ 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.appstoreConfig = {}; $scope.installedApps = Client.getInstalledApps(); $scope.subscription = null; $scope.prettyProviderName = function (provider) { switch (provider) { case 'caas': return 'Managed Cloudron'; default: return provider; } }; $scope.update = { error: {}, // this is for the dialog busy: false, percent: 0, message: 'Downloading', errorMessage: '', // this shows inline taskId: '', show: function () { $scope.update.error.generic = null; $scope.update.busy = false; if (!$scope.config.update.box.sourceTarballUrl) { $('#setupSubscriptionModal').modal('show'); } else { $('#updateModal').modal('show'); } }, stopUpdate: function () { Client.stopTask($scope.update.taskId, function (error) { 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; } }); }, checkStatus: function () { Client.getLatestTaskByType('update', function (error, task) { if (error) return console.error(error); if (!task) return; $scope.update.taskId = task.id; $scope.update.updateStatus(); }); }, 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); if (!data.active) { $scope.update.busy = false; $scope.update.message = ''; $scope.update.percent = 100; // indicates that 'result' is valid $scope.update.errorMessage = data.errorMessage; if (!data.errorMessage) $scope.update.reloadIfNeeded(); // assume success return; } $scope.update.busy = true; $scope.update.percent = data.percent; $scope.update.message = data.message; window.setTimeout($scope.update.updateStatus, 500); }); }, startUpdate: function () { $scope.update.error.generic = null; $scope.update.busy = true; $scope.update.percent = 0; $scope.update.message = ''; $scope.update.errorMessage = ''; Client.update(function (error, taskId) { if (error) { $scope.update.error.generic = error.message; $scope.update.busy = false; return; } $('#updateModal').modal('hide'); $scope.update.taskId = taskId; $scope.update.updateStatus(); }); } }; $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 getUnstableAppsConfig() { Client.getUnstableAppsConfig(function (error, result) { if (error) return console.error(error); $scope.unstableApps.enabled = result; }); } function getSubscription() { AppStore.getSubscription($scope.appstoreConfig, function (error, result) { if (error) return console.error(error); if (!$scope.$parent) return; // user changed view. otherwise we get an error that $scope.$parent is null $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); }); } $('#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.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); }); } }; $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(); getUnstableAppsConfig(); $scope.update.checkStatus(); if (!$scope.config.managed) { 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(); }]);