386 lines
15 KiB
JavaScript
386 lines
15 KiB
JavaScript
'use strict';
|
|
|
|
/* global angular:false */
|
|
/* global $:false */
|
|
/* global APP_TYPES */
|
|
/* global onAppClick */
|
|
/* global localStorage, document, FileReader */
|
|
|
|
angular.module('Application').controller('AppsController', ['$scope', '$translate', '$interval', '$location', 'Client', function ($scope, $translate, $interval, $location, Client) {
|
|
var ALL_DOMAINS_DOMAIN = { _alldomains: true, domain: 'All Domains' }; // dummy record for the single select filter
|
|
var GROUP_ACCESS_UNSET = { _unset: true, name: 'Select Group' }; // dummy record for the single select filter
|
|
|
|
$scope.installedApps = Client.getInstalledApps();
|
|
$scope.tags = Client.getAppTags();
|
|
$scope.states = [
|
|
{ state: '', label: 'All States' },
|
|
{ state: 'running', label: 'Running' },
|
|
{ state: 'stopped', label: 'Stopped' },
|
|
{ state: 'update_available', label: 'Update Available' },
|
|
{ state: 'not_responding', label: 'Not Responding' }
|
|
];
|
|
$scope.selectedState = $scope.states[0];
|
|
$scope.selectedTags = [];
|
|
$scope.selectedGroup = GROUP_ACCESS_UNSET;
|
|
$scope.selectedDomain = ALL_DOMAINS_DOMAIN;
|
|
$scope.filterDomains = [ ALL_DOMAINS_DOMAIN ];
|
|
$scope.config = Client.getConfig();
|
|
$scope.user = Client.getUserInfo();
|
|
$scope.domains = [];
|
|
$scope.appSearch = '';
|
|
$scope.groups = [ GROUP_ACCESS_UNSET ];
|
|
$scope.APP_TYPES = APP_TYPES;
|
|
$scope.showFilter = false;
|
|
$scope.filterActive = false;
|
|
|
|
$scope.VIEWS = {
|
|
GRID: 'grid',
|
|
LIST: 'list'
|
|
};
|
|
$scope.view = $scope.VIEWS.GRID;
|
|
|
|
$scope.orderBy = 'location'; // or app, status, sso
|
|
$scope.orderByReverse = false;
|
|
|
|
$scope.allUsers = [];
|
|
$scope.allGroups = [];
|
|
|
|
$translate(['apps.stateFilterHeader', 'apps.domainsFilterHeader', 'apps.groupsFilterHeader', 'app.states.running', 'app.states.stopped', 'app.states.notResponding', 'app.states.updateAvailable']).then(function (tr) {
|
|
if (tr['apps.domainsFilterHeader']) ALL_DOMAINS_DOMAIN.domain = tr['apps.domainsFilterHeader'];
|
|
if (tr['apps.groupsFilterHeader']) GROUP_ACCESS_UNSET.name = tr['apps.groupsFilterHeader'];
|
|
if (tr['apps.stateFilterHeader']) $scope.states[0].label = tr['apps.stateFilterHeader'];
|
|
if (tr['app.states.running']) $scope.states[1].label = tr['app.states.running'];
|
|
if (tr['app.states.stopped']) $scope.states[2].label = tr['app.states.stopped'];
|
|
if (tr['app.states.notResponding']) $scope.states[4].label = tr['app.states.notResponding'];
|
|
if (tr['app.states.updateAvailable']) $scope.states[3].label = tr['app.states.updateAvailable'];
|
|
});
|
|
|
|
$scope.pendingChecklistItems = function (app) {
|
|
if (!app.checklist) return 0;
|
|
return Object.keys(app.checklist).filter(function (key) { return !app.checklist[key].acknowledged; }).length;
|
|
};
|
|
|
|
$scope.setOrderBy = function (by) {
|
|
if (by === $scope.orderBy) {
|
|
$scope.orderByReverse = !$scope.orderByReverse;
|
|
} else {
|
|
$scope.orderBy = by;
|
|
$scope.orderByReverse = false;
|
|
}
|
|
|
|
localStorage.appsOrderBy = by;
|
|
if ($scope.orderByReverse) localStorage.appsOrderByReverse = true;
|
|
else localStorage.removeItem('appsOrderByReverse');
|
|
};
|
|
|
|
// for sorting/grouping
|
|
$scope.orderByFilter = function (item) {
|
|
if ($scope.orderBy === 'app') return item.manifest.title || 'App Link';
|
|
if ($scope.orderBy === 'status') return item.installationState + '-' + item.runState;
|
|
if ($scope.orderBy === 'sso') return item.sso;
|
|
return item.label || item.fqdn;
|
|
};
|
|
|
|
$scope.setView = function (view) {
|
|
if (view !== $scope.VIEWS.LIST && view !== $scope.VIEWS.GRID) return;
|
|
|
|
$scope.view = view;
|
|
localStorage.appsView = view;
|
|
};
|
|
|
|
$scope.toggleView = function () {
|
|
$scope.view = $scope.view === $scope.VIEWS.GRID ? $scope.VIEWS.LIST : $scope.VIEWS.GRID;
|
|
localStorage.appsView = $scope.view;
|
|
};
|
|
|
|
$scope.toggleFilter = function () {
|
|
$scope.showFilter = !$scope.showFilter;
|
|
|
|
if ($scope.showFilter) localStorage.appsShowFilter = true;
|
|
else localStorage.removeItem('appsShowFilter');
|
|
|
|
// clear on hide
|
|
if (!$scope.showFilter) {
|
|
$scope.selectedState = $scope.states[0];
|
|
$scope.selectedTags = [];
|
|
$scope.selectedGroup = GROUP_ACCESS_UNSET;
|
|
$scope.selectedDomain = ALL_DOMAINS_DOMAIN;
|
|
}
|
|
};
|
|
|
|
$scope.$watch('selectedTags', function (newVal, oldVal) {
|
|
if (newVal === oldVal) return;
|
|
|
|
localStorage.selectedTags = newVal.join(',');
|
|
});
|
|
|
|
$scope.$watch('selectedState', function (newVal, oldVal) {
|
|
if (newVal === oldVal) return;
|
|
|
|
if (newVal === $scope.states[0]) localStorage.removeItem('selectedState');
|
|
else localStorage.selectedState = newVal.state;
|
|
});
|
|
|
|
$scope.$watch('selectedGroup', function (newVal, oldVal) {
|
|
if (newVal === oldVal) return;
|
|
|
|
if (newVal === GROUP_ACCESS_UNSET) localStorage.removeItem('selectedGroup');
|
|
else localStorage.selectedGroup = newVal.id;
|
|
});
|
|
|
|
$scope.$watch('selectedDomain', function (newVal, oldVal) {
|
|
if (newVal === oldVal) return;
|
|
|
|
if (newVal._alldomains) localStorage.removeItem('selectedDomain');
|
|
else localStorage.selectedDomain = newVal.domain;
|
|
});
|
|
|
|
$scope.onAppClick = function (app, $event) { onAppClick(app, $event, $scope.isOperator(app), $scope); };
|
|
|
|
$scope.appPostInstallConfirm = {
|
|
app: {},
|
|
message: '',
|
|
|
|
show: function (app) {
|
|
$scope.appPostInstallConfirm.app = app;
|
|
$scope.appPostInstallConfirm.message = app.manifest.postInstallMessage;
|
|
|
|
$('#appsPostInstallConfirmModal').modal('show');
|
|
|
|
return false; // prevent propagation and default
|
|
},
|
|
|
|
submit: function () {
|
|
$scope.appPostInstallConfirm.app.pendingPostInstallConfirmation = false;
|
|
delete localStorage['confirmPostInstall_' + $scope.appPostInstallConfirm.app.id];
|
|
|
|
$('#appsPostInstallConfirmModal').modal('hide');
|
|
}
|
|
};
|
|
|
|
$scope.applinksEdit = {
|
|
error: {},
|
|
busyEdit: false,
|
|
busyRemove: false,
|
|
applink: {},
|
|
id: '',
|
|
upstreamUri: '',
|
|
label: '',
|
|
tags: '',
|
|
accessRestrictionOption: '',
|
|
accessRestriction: { users: [], groups: [] },
|
|
icon: { data: null },
|
|
|
|
iconUrl: function () {
|
|
if ($scope.applinksEdit.icon.data === '__original__') { // user clicked reset
|
|
// https://png-pixel.com/ white pixel placeholder
|
|
return 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mP8/x8AAwMCAO+ip1sAAAAASUVORK5CYII=';
|
|
} else if ($scope.applinksEdit.icon.data) { // user uploaded icon
|
|
return $scope.applinksEdit.icon.data;
|
|
} else { // current icon
|
|
return $scope.applinksEdit.applink.iconUrl;
|
|
}
|
|
},
|
|
|
|
resetCustomIcon: function () {
|
|
$scope.applinksEdit.icon.data = '__original__';
|
|
},
|
|
|
|
showCustomIconSelector: function () {
|
|
$('#applinksEditIconFileInput').click();
|
|
},
|
|
|
|
isAccessRestrictionValid: function () {
|
|
return !!($scope.applinksEdit.accessRestriction.users.length || $scope.applinksEdit.accessRestriction.groups.length);
|
|
},
|
|
|
|
show: function (applink) {
|
|
$scope.applinksEdit.error = {};
|
|
$scope.applinksEdit.busyEdit = false;
|
|
$scope.applinksEdit.busyRemove = false;
|
|
$scope.applinksEdit.applink = applink;
|
|
$scope.applinksEdit.id = applink.id;
|
|
$scope.applinksEdit.upstreamUri = applink.upstreamUri;
|
|
$scope.applinksEdit.label = applink.label;
|
|
$scope.applinksEdit.accessRestrictionOption = applink.accessRestriction ? 'groups' : 'any';
|
|
$scope.applinksEdit.accessRestriction = { users: [], groups: [] };
|
|
$scope.applinksEdit.icon = { data: null };
|
|
|
|
var userSet, groupSet;
|
|
if (applink.accessRestriction) {
|
|
userSet = {};
|
|
applink.accessRestriction.users.forEach(function (uid) { userSet[uid] = true; });
|
|
$scope.allUsers.forEach(function (u) { if (userSet[u.id] === true) $scope.applinksEdit.accessRestriction.users.push(u); });
|
|
|
|
groupSet = {};
|
|
if (applink.accessRestriction.groups) applink.accessRestriction.groups.forEach(function (gid) { groupSet[gid] = true; });
|
|
$scope.allGroups.forEach(function (g) { if (groupSet[g.id] === true) $scope.applinksEdit.accessRestriction.groups.push(g); });
|
|
}
|
|
|
|
// translate for tag-input
|
|
$scope.applinksEdit.tags = applink.tags ? applink.tags.join(' ') : '';
|
|
|
|
$scope.applinksEditForm.$setUntouched();
|
|
$scope.applinksEditForm.$setPristine();
|
|
|
|
$('#applinksEditModal').modal('show');
|
|
|
|
return false; // prevent propagation and default
|
|
},
|
|
|
|
submit: function () {
|
|
$scope.applinksEdit.busyEdit = true;
|
|
$scope.applinksEdit.error = {};
|
|
|
|
var accessRestriction = null;
|
|
if ($scope.applinksEdit.accessRestrictionOption === 'groups') {
|
|
accessRestriction = { users: [], groups: [] };
|
|
accessRestriction.users = $scope.applinksEdit.accessRestriction.users.map(function (u) { return u.id; });
|
|
accessRestriction.groups = $scope.applinksEdit.accessRestriction.groups.map(function (g) { return g.id; });
|
|
}
|
|
|
|
var data = {
|
|
upstreamUri: $scope.applinksEdit.upstreamUri,
|
|
label: $scope.applinksEdit.label,
|
|
accessRestriction: accessRestriction,
|
|
tags: $scope.applinksEdit.tags.split(' ').map(function (t) { return t.trim(); }).filter(function (t) { return !!t; })
|
|
};
|
|
|
|
if ($scope.applinksEdit.icon.data === '__original__') { // user reset the icon
|
|
data.icon = '';
|
|
} else if ($scope.applinksEdit.icon.data) { // user loaded custom icon
|
|
data.icon = $scope.applinksEdit.icon.data.replace(/^data:image\/[a-z]+;base64,/, '');
|
|
}
|
|
|
|
Client.updateApplink($scope.applinksEdit.id, data, function (error) {
|
|
$scope.applinksEdit.busyEdit = false;
|
|
|
|
if (error && error.statusCode === 400 && error.message.includes('upstreamUri')) {
|
|
$scope.applinksEdit.error.upstreamUri = error.message;
|
|
$scope.applinksEditForm.$setUntouched();
|
|
$scope.applinksEditForm.$setPristine();
|
|
return;
|
|
}
|
|
if (error) return console.error('Failed to update applink', error);
|
|
|
|
Client.refreshInstalledApps();
|
|
|
|
$('#applinksEditModal').modal('hide');
|
|
});
|
|
},
|
|
|
|
remove: function () {
|
|
$scope.applinksEdit.busyRemove = true;
|
|
|
|
Client.removeApplink($scope.applinksEdit.id, function (error) {
|
|
$scope.applinksEdit.busyRemove = false;
|
|
|
|
if (error) return console.error('Failed to remove applink', error);
|
|
|
|
Client.refreshInstalledApps();
|
|
|
|
$('#applinksEditModal').modal('hide');
|
|
});
|
|
}
|
|
};
|
|
|
|
$scope.showAppConfigure = function (app, view) {
|
|
$location.path('/app/' + app.id + '/' + view);
|
|
};
|
|
|
|
$scope.isOperator = function (app) {
|
|
return app.accessLevel === 'operator' || app.accessLevel === 'admin';
|
|
};
|
|
|
|
Client.onReady(function () {
|
|
setTimeout(function () { $('#appSearch').focus(); }, 1);
|
|
|
|
// refresh the new list immediately when switching from another view (appstore)
|
|
Client.refreshInstalledApps(function () {
|
|
var refreshAppsTimer = $interval(Client.refreshInstalledApps.bind(Client, function () {}), 5000);
|
|
$scope.$on('$destroy', function () {
|
|
$interval.cancel(refreshAppsTimer);
|
|
});
|
|
});
|
|
|
|
$scope.setView(localStorage.appsView);
|
|
|
|
$scope.orderBy = localStorage.appsOrderBy || 'location';
|
|
$scope.orderByReverse = !!localStorage.appsOrderByReverse;
|
|
$scope.showFilter = !!localStorage.appsShowFilter;
|
|
|
|
if (!$scope.user.isAtLeastAdmin) return;
|
|
|
|
// load local settings and apply tag filter
|
|
if (localStorage.selectedTags) {
|
|
if (!$scope.tags.length) localStorage.removeItem('selectedTags');
|
|
else $scope.selectedTags = localStorage.selectedTags.split(',');
|
|
}
|
|
|
|
if (localStorage.selectedState) $scope.selectedState = $scope.states.find(function (s) { return s.state === localStorage.selectedState; }) || $scope.states[0];
|
|
|
|
Client.getGroups(function (error, result) {
|
|
if (error) Client.error(error);
|
|
|
|
$scope.groups = [ GROUP_ACCESS_UNSET ].concat(result);
|
|
$scope.allGroups = result;
|
|
|
|
if (localStorage.selectedGroup) $scope.selectedGroup = $scope.groups.find(function (g) { return g.id === localStorage.selectedGroup; }) || GROUP_ACCESS_UNSET;
|
|
});
|
|
|
|
Client.getDomains(function (error, result) {
|
|
if (error) Client.error(error);
|
|
|
|
$scope.domains = result;
|
|
$scope.filterDomains = [ALL_DOMAINS_DOMAIN].concat(result);
|
|
|
|
if (localStorage.selectedDomain) $scope.selectedDomain = $scope.filterDomains.find(function (d) { return d.domain === localStorage.selectedDomain; }) || ALL_DOMAINS_DOMAIN;
|
|
});
|
|
|
|
Client.getAllUsers(function (error, users) {
|
|
if (error) Client.error(error);
|
|
|
|
$scope.allUsers = users;
|
|
});
|
|
});
|
|
|
|
$('#applinksEditIconFileInput').get(0).onchange = function (event) {
|
|
var fr = new FileReader();
|
|
fr.onload = function () {
|
|
$scope.$apply(function () {
|
|
// var file = event.target.files[0];
|
|
$scope.applinksEdit.icon.data = fr.result;
|
|
});
|
|
};
|
|
fr.readAsDataURL(event.target.files[0]);
|
|
};
|
|
|
|
// setup all the dialog focus handling
|
|
['applinksAddModal', 'applinksEditModal'].forEach(function (id) {
|
|
$('#' + id).on('shown.bs.modal', function () {
|
|
$(this).find('autofocus]:first').focus();
|
|
});
|
|
});
|
|
|
|
$('.collapse').on('shown.bs.collapse', function(){
|
|
$(this).parent().find('.fa-angle-right').removeClass('fa-angle-right').addClass('fa-angle-down');
|
|
}).on('hidden.bs.collapse', function(){
|
|
$(this).parent().find('.fa-angle-down').removeClass('fa-angle-down').addClass('fa-angle-right');
|
|
});
|
|
|
|
$('.modal-backdrop').remove();
|
|
|
|
function keyboardHandler(event) {
|
|
if (event.key === '/') {
|
|
document.getElementById('appSearch').focus();
|
|
event.preventDefault();
|
|
}
|
|
}
|
|
|
|
document.addEventListener('keydown', keyboardHandler);
|
|
|
|
$scope.$on('$destroy', function () {
|
|
document.removeEventListener('keydown', keyboardHandler);
|
|
});
|
|
}]);
|