'use strict'; /* global angular */ /* global $ */ /* global asyncSeries */ angular.module('Application').controller('AppController', ['$scope', '$location', '$timeout', '$interval', 'Client', function ($scope, $location, $timeout, $interval, Client) { Client.onReady(function () { if (!Client.getUserInfo().admin) $location.path('/'); }); var appId = $location.path().slice('/app/'.length); $scope.app = null; $scope.ready = false; $scope.HOST_PORT_MIN = 1024; $scope.HOST_PORT_MAX = 65535; $scope.config = Client.getConfig(); $scope.user = Client.getUserInfo(); $scope.domains = []; $scope.groups = []; $scope.users = []; $scope.backupsEnabled = true; $scope.disableIndexingTemplate = '# Disable search engine indexing\n\nUser-agent: *\nDisallow: /'; $scope.display = { busy: false, error: {}, success: false, tags: '', label: '', icon: { data: null }, iconUrl: function () { if (!$scope.app) return ''; if ($scope.display.icon.data === '__original__') { // user clicked reset return $scope.app.iconUrl + '&original=true'; } else if ($scope.display.icon.data) { // user uploaded icon return $scope.display.icon.data; } else { // current icon return $scope.app.iconUrl; } }, resetCustomIcon: function () { $scope.display.icon.data = '__original__'; }, showCustomIconSelector: function () { $('#iconFileInput').click(); }, show: function () { var app = $scope.app; // translate for tag-input $scope.display.tags = app.tags ? app.tags.join(',') : ''; $scope.display.label = $scope.app.label || ''; $scope.display.icon = { data: null }; $('#iconFileInput').get(0).onchange = function (event) { var fr = new FileReader(); fr.onload = function () { $scope.$apply(function () { // var file = event.target.files[0]; $scope.display.icon.data = fr.result; }); }; fr.readAsDataURL(event.target.files[0]); }; }, submit: function () { $scope.display.busy = true; $scope.display.error = {}; // TODO break those apart Client.configureApp($scope.app.id, 'label', { label: $scope.display.label }, function (error) { if (error) return Client.error(error); var tags = $scope.display.tags.split(',').map(function (t) { return t.trim(); }).filter(function (t) { return !!t; }); Client.configureApp($scope.app.id, 'tags', { tags: tags }, function (error) { if (error) return Client.error(error); // skip if icon is unchanged if ($scope.display.icon.data === null) { $scope.display.busy = false; $scope.display.success = true; return; } var icon; if ($scope.display.icon.data === '__original__') { // user reset the icon icon = ''; } else if ($scope.display.icon.data) { // user loaded custom icon icon = $scope.display.icon.data.replace(/^data:image\/[a-z]+;base64,/, ''); } Client.configureApp($scope.app.id, 'icon', { icon: icon }, function (error) { if (error) return Client.error(error); $scope.display.busy = false; $scope.display.success = true; }); }); }); } }; $scope.location = { busy: false, error: {}, success: false, domain: null, location: '', alternateDomains: [], portBindings: {}, portBindingsEnabled: {}, portBindingsInfo: {}, addAlternateDomain: function (event) { event.preventDefault(); $scope.location.alternateDomains.push({ domain: $scope.domains[0], subdomain: '' }); }, delAlternateDomain: function (event, index) { event.preventDefault(); $scope.location.alternateDomains.splice(index, 1); }, show: function () { var app = $scope.app; $scope.location.location = app.location; $scope.location.domain = $scope.domains.filter(function (d) { return d.domain === app.domain; })[0]; $scope.location.portBindingsInfo = angular.extend({}, app.manifest.tcpPorts, app.manifest.udpPorts); // Portbinding map only for information $scope.location.alternateDomains = app.alternateDomains.map(function (a) { return { subdomain: a.subdomain, domain: $scope.domains.filter(function (d) { return d.domain === a.domain; })[0] };}); // fill the portBinding structures. There might be holes in the app.portBindings, which signalizes a disabled port for (var env in $scope.location.portBindingsInfo) { if (app.portBindings && app.portBindings[env]) { $scope.location.portBindings[env] = app.portBindings[env]; $scope.location.portBindingsEnabled[env] = true; } else { $scope.location.portBindings[env] = $scope.location.portBindingsInfo[env].defaultValue || 0; $scope.location.portBindingsEnabled[env] = false; } } }, submit: function () { $scope.location.busy = true; $scope.location.error = {}; // only use enabled ports from portBindings var portBindings = {}; for (var env in $scope.location.portBindings) { if ($scope.location.portBindingsEnabled[env]) { portBindings[env] = $scope.location.portBindings[env]; } } var data = { location: $scope.location.location, domain: $scope.location.domain.domain, portBindings: portBindings, alternateDomains: $scope.location.alternateDomains.map(function (a) { return { subdomain: a.subdomain, domain: a.domain.domain };}) }; Client.configureApp($scope.app.id, 'location', data, function (error) { if (error) return Client.error(error); $scope.location.success = true; $scope.location.busy = false; }); } }; $scope.access = { busy: false, error: {}, success: false, ftp: false, ssoAuth: false, accessRestrictionOption: 'any', accessRestriction: { users: [], groups: [] }, isAccessRestrictionValid: function () { var tmp = $scope.access.accessRestriction; return !!(tmp.users.length || tmp.groups.length); }, show: function () { var app = $scope.app; $scope.access.ftp = app.manifest.addons.localstorage && app.manifest.addons.localstorage.ftp; $scope.access.ssoAuth = (app.manifest.addons['ldap'] || app.manifest.addons['oauth']) && app.sso; $scope.access.accessRestrictionOption = app.accessRestriction ? 'groups' : 'any'; $scope.access.accessRestriction = { users: [], groups: [] }; if (app.accessRestriction) { var userSet = { }; app.accessRestriction.users.forEach(function (uid) { userSet[uid] = true; }); $scope.users.forEach(function (u) { if (userSet[u.id] === true) $scope.access.accessRestriction.users.push(u); }); var groupSet = { }; app.accessRestriction.groups.forEach(function (gid) { groupSet[gid] = true; }); $scope.groups.forEach(function (g) { if (groupSet[g.id] === true) $scope.access.accessRestriction.groups.push(g); }); } }, submit: function () { $scope.access.busy = true; $scope.access.error = {}; var accessRestriction = null; if ($scope.access.accessRestrictionOption === 'groups') { accessRestriction = { users: [], groups: [] }; accessRestriction.users = $scope.access.accessRestriction.users.map(function (u) { return u.id; }); accessRestriction.groups = $scope.access.accessRestriction.groups.map(function (g) { return g.id; }); } Client.configureApp($scope.app.id, 'access_restriction', { accessRestriction: accessRestriction }, function (error) { if (error) return Client.error(error); $scope.access.success = true; $scope.access.busy = false; }); } }; $scope.resources = { busy: false, error: {}, success: false, memoryLimit: 0, memoryTicks: [], dataDir: null, dataDirEnabled: false, show: function () { var app = $scope.app; $scope.resources.memoryLimit = app.memoryLimit || app.manifest.memoryLimit || (256 * 1024 * 1024); $scope.resources.dataDirEnabled = !!app.dataDir; $scope.resources.dataDir = app.dataDir; // create ticks starting from manifest memory limit. the memory limit here is currently split into ram+swap (and thus *2 below) // TODO: the *2 will overallocate since 4GB is max swap that cloudron itself allocates $scope.resources.memoryTicks = []; var npow2 = Math.pow(2, Math.ceil(Math.log($scope.config.memory)/Math.log(2))); for (var i = 256; i <= (npow2*2/1024/1024); i *= 2) { if (i >= (app.manifest.memoryLimit/1024/1024 || 0)) $scope.resources.memoryTicks.push(i * 1024 * 1024); } if (app.manifest.memoryLimit && $scope.resources.memoryTicks[0] !== app.manifest.memoryLimit) { $scope.resources.memoryTicks.unshift(app.manifest.memoryLimit); } }, submit: function () { $scope.resources.busy = true; $scope.resources.error = {}; var memoryLimit = $scope.resources.memoryLimit === $scope.resources.memoryTicks[0] ? 0 : $scope.resources.memoryLimit; Client.configureApp($scope.app.id, 'memory_limit', { memoryLimit: memoryLimit }, function (error) { if (error) return Client.error(error); $scope.resources.success = true; $scope.resources.busy = false; // TODO handle data dir once we show it }); } }; $scope.email = { busy: false, error: {}, success: false, mailboxNameEnabled: false, mailboxName: '', domain: '', show: function () { var app = $scope.app; $scope.email.mailboxNameEnabled = app.mailboxName && (app.mailboxName.match(/\.app$/) === null); $scope.email.mailboxName = app.mailboxName || ''; $scope.email.domain = $scope.domains.filter(function (d) { return d.domain === app.domain; })[0]; }, submit: function () { $scope.email.busy = true; } }; $scope.security = { busy: false, error: {}, success: false, robotsTxt: '', show: function () { var app = $scope.app; $scope.security.robotsTxt = app.robotsTxt; }, submit: function () { $scope.security.busy = true; $scope.security.error = {}; Client.configureApp($scope.app.id, 'robots_txt', { robotsTxt: $scope.security.robotsTxt }, function (error) { if (error) return Client.error(error); $scope.security.success = true; $scope.security.busy = false; }); } }; $scope.updates = { busy: false, error: {}, success: false, enableAutomaticUpdate: false, show: function () { var app = $scope.app; $scope.updates.enableAutomaticUpdate = app.enableAutomaticUpdate; }, submit: function () { $scope.updates.busy = true; $scope.updates.error = {}; Client.configureApp($scope.app.id, 'automatic_update', { enable: $scope.updates.enableAutomaticUpdate }, function (error) { if (error) return Client.error(error); $scope.updates.success = true; $scope.updates.busy = false; }); } }; $scope.backups = { busy: false, error: {}, success: false, enableBackup: false, show: function () { var app = $scope.app; $scope.backups.enableBackup = app.enableBackup; }, submit: function () { $scope.backups.busy = true; $scope.backups.error = {}; Client.configureApp($scope.app.id, 'automatic_backup', { enable: $scope.backups.enableBackup }, function (error) { if (error) return Client.error(error); $scope.backups.success = true; $scope.backups.busy = false; }); } }; function fetchUsers(callback) { Client.getUsers(function (error, users) { if (error) return callback(error); // ensure we have something to work with in the access restriction dropdowns users.forEach(function (user) { user.display = user.username || user.email; }); $scope.users = users; callback(); }); } function fetchGroups(callback) { Client.getGroups(function (error, groups) { if (error) return callback(error); $scope.groups = groups; callback(); }); } function getDomains(callback) { Client.getDomains(function (error, result) { if (error) return callback(error); $scope.domains = result; callback(); }); } function getBackupConfig(callback) { Client.getBackupConfig(function (error, backupConfig) { if (error) return callback(error); $scope.backupEnabled = backupConfig.provider !== 'noop'; callback(); }); } Client.onReady(function () { $scope.app = Client.getApp(appId, function (error, app) { if (error) return Client.error(error); $scope.app = app; asyncSeries([ fetchUsers, fetchGroups, getDomains, getBackupConfig ], function (error) { if (error) return Client.error(error); $scope.display.show(); $scope.location.show(); $scope.resources.show(); $scope.access.show(); $scope.email.show(); $scope.security.show(); $scope.backups.show(); $scope.updates.show(); $scope.ready = true; }); }); }); // setup all the dialog focus handling ['appConfigureModal', 'appUninstallModal', 'appUpdateModal', 'appRestoreModal', 'appInfoModal', 'appErrorModal'].forEach(function (id) { $('#' + id).on('shown.bs.modal', function () { $(this).find("[autofocus]:first").focus(); }); }); $('.modal-backdrop').remove(); }]);