Merge pull request #2149 from jaredevantabor/session

Session Fixes
This commit is contained in:
Jared Tabor
2016-06-03 13:25:30 -07:00
33 changed files with 356 additions and 393 deletions

View File

@@ -18,25 +18,26 @@ export default {
label: "ACTIVITY STREAM" label: "ACTIVITY STREAM"
}, },
resolve: { resolve: {
features: ['FeaturesService', 'ProcessErrors', '$state', function(FeaturesService, ProcessErrors, $state) { features: ['FeaturesService', 'ProcessErrors', '$state', '$rootScope',
FeaturesService.get() function(FeaturesService, ProcessErrors, $state, $rootScope) {
.then(function(features) { var features = FeaturesService.get();
if(features){
if(FeaturesService.featureEnabled('activity_streams')) { if(FeaturesService.featureEnabled('activity_streams')) {
// Good to go - pass the features along to the controller.
return features; return features;
} }
else { else {
// The activity stream feature isn't enabled. Take the user
// back to the dashboard
$state.go('dashboard'); $state.go('dashboard');
} }
}) }
.catch(function (response) { $rootScope.featuresConfigured.promise.then(function(features){
ProcessErrors(null, response.data, response.status, null, { if(features){
hdr: 'Error!', if(FeaturesService.featureEnabled('activity_streams')) {
msg: 'Failed to get feature info. GET returned status: ' + return features;
response.status }
}); else {
$state.go('dashboard');
}
}
}); });
}], }],
subTitle: subTitle:

View File

@@ -68,6 +68,7 @@ import './shared/directives';
import './shared/filters'; import './shared/filters';
import './shared/Socket'; import './shared/Socket';
import './shared/features/main'; import './shared/features/main';
import config from './shared/config/main';
import './login/authenticationServices/pendo/ng-pendo'; import './login/authenticationServices/pendo/ng-pendo';
import footer from './footer/main'; import footer from './footer/main';
import scheduler from './scheduler/main'; import scheduler from './scheduler/main';
@@ -109,6 +110,7 @@ var tower = angular.module('Tower', [
JobTemplates.name, JobTemplates.name,
portalMode.name, portalMode.name,
search.name, search.name,
config.name,
'ngToast', 'ngToast',
'templates', 'templates',
'Utilities', 'Utilities',
@@ -212,8 +214,10 @@ var tower = angular.module('Tower', [
timeout: 4000 timeout: 4000
}); });
}]) }])
.config(['$stateProvider', '$urlRouterProvider', '$breadcrumbProvider', '$urlMatcherFactoryProvider', .config(['$stateProvider', '$urlRouterProvider', '$breadcrumbProvider',
function ($stateProvider, $urlRouterProvider, $breadcrumbProvider, $urlMatcherFactoryProvider) { '$urlMatcherFactoryProvider',
function ($stateProvider, $urlRouterProvider, $breadcrumbProvider,
$urlMatcherFactoryProvider) {
$urlMatcherFactoryProvider.strictMode(false); $urlMatcherFactoryProvider.strictMode(false);
$breadcrumbProvider.setOptions({ $breadcrumbProvider.setOptions({
templateUrl: urlPrefix + 'partials/breadcrumb.html' templateUrl: urlPrefix + 'partials/breadcrumb.html'
@@ -241,10 +245,9 @@ var tower = angular.module('Tower', [
label: "DASHBOARD" label: "DASHBOARD"
}, },
resolve: { resolve: {
graphData: ['$q', 'jobStatusGraphData', 'FeaturesService', function($q, jobStatusGraphData, FeaturesService) { graphData: ['$q', 'jobStatusGraphData', function($q, jobStatusGraphData) {
return $q.all({ return $q.all({
jobStatus: jobStatusGraphData.get("month", "all"), jobStatus: jobStatusGraphData.get("month", "all"),
features: FeaturesService.get()
}); });
}] }]
} }
@@ -514,10 +517,16 @@ var tower = angular.module('Tower', [
}]); }]);
}]) }])
.run(['$q', '$compile', '$cookieStore', '$rootScope', '$log', 'CheckLicense', '$location', 'Authorization', 'LoadBasePaths', 'Timer', 'ClearScope', 'Socket', .run(['$q', '$compile', '$cookieStore', '$rootScope', '$log',
'LoadConfig', 'Store', 'ShowSocketHelp', 'pendoService', 'Prompt', 'Rest', 'Wait', 'ProcessErrors', '$state', 'GetBasePath', 'CheckLicense', '$location', 'Authorization', 'LoadBasePaths', 'Timer',
function ($q, $compile, $cookieStore, $rootScope, $log, CheckLicense, $location, Authorization, LoadBasePaths, Timer, ClearScope, Socket, 'ClearScope', 'Socket', 'LoadConfig', 'Store',
LoadConfig, Store, ShowSocketHelp, pendoService, Prompt, Rest, Wait, ProcessErrors, $state, GetBasePath) { 'ShowSocketHelp', 'pendoService', 'Prompt', 'Rest', 'Wait',
'ProcessErrors', '$state', 'GetBasePath', 'ConfigService',
'FeaturesService',
function ($q, $compile, $cookieStore, $rootScope, $log, CheckLicense,
$location, Authorization, LoadBasePaths, Timer, ClearScope, Socket,
LoadConfig, Store, ShowSocketHelp, pendoService, Prompt, Rest, Wait,
ProcessErrors, $state, GetBasePath, ConfigService, FeaturesService) {
var sock; var sock;
$rootScope.addPermission = function (scope) { $rootScope.addPermission = function (scope) {
$compile("<add-permissions class='AddPermissions'></add-permissions>")(scope); $compile("<add-permissions class='AddPermissions'></add-permissions>")(scope);
@@ -585,11 +594,11 @@ var tower = angular.module('Tower', [
Prompt({ Prompt({
hdr: `Remove role`, hdr: `Remove role`,
body: ` body: `
<div class="Prompt-bodyQuery"> <div class="Prompt-bodyQuery">
Confirm the removal of the ${roleType} Confirm the removal of the ${roleType}
<span class="Prompt-emphasis"> ${roleName} </span> <span class="Prompt-emphasis"> ${roleName} </span>
role associated with ${userName}. role associated with ${userName}.
</div> </div>
`, `,
action: action, action: action,
actionText: 'REMOVE' actionText: 'REMOVE'
@@ -615,11 +624,11 @@ var tower = angular.module('Tower', [
Prompt({ Prompt({
hdr: `Remove role`, hdr: `Remove role`,
body: ` body: `
<div class="Prompt-bodyQuery"> <div class="Prompt-bodyQuery">
Confirm the removal of the ${roleType} Confirm the removal of the ${roleType}
<span class="Prompt-emphasis"> ${roleName} </span> <span class="Prompt-emphasis"> ${roleName} </span>
role associated with the ${teamName} team. role associated with the ${teamName} team.
</div> </div>
`, `,
action: action, action: action,
actionText: 'REMOVE' actionText: 'REMOVE'
@@ -745,7 +754,7 @@ var tower = angular.module('Tower', [
control_socket.on("limit_reached", function(data) { control_socket.on("limit_reached", function(data) {
$log.debug(data.reason); $log.debug(data.reason);
$rootScope.sessionTimer.expireSession('session_limit'); $rootScope.sessionTimer.expireSession('session_limit');
$location.url('/login'); $state.go('signOut');
}); });
} }
openSocket(); openSocket();
@@ -760,9 +769,7 @@ var tower = angular.module('Tower', [
$rootScope.$on("$stateChangeStart", function (event, next, nextParams, prev) { $rootScope.$on("$stateChangeStart", function (event, next, nextParams, prev) {
if (next.name !== 'signOut'){
CheckLicense.notify();
}
$rootScope.$broadcast("closePermissionsModal"); $rootScope.$broadcast("closePermissionsModal");
$rootScope.$broadcast("closeUsersModal"); $rootScope.$broadcast("closeUsersModal");
// this line removes the query params attached to a route // this line removes the query params attached to a route
@@ -813,15 +820,15 @@ var tower = angular.module('Tower', [
if ($rootScope.current_user === undefined || $rootScope.current_user === null) { if ($rootScope.current_user === undefined || $rootScope.current_user === null) {
Authorization.restoreUserInfo(); //user must have hit browser refresh Authorization.restoreUserInfo(); //user must have hit browser refresh
} }
if (next && (next.name !== "signIn" && next.name !== "signOut" && next.name !== "license")) {
// if not headed to /login or /logout, then check the license
CheckLicense.test(event);
}
} }
activateTab(); activateTab();
}); });
$rootScope.$on('$stateChangeSuccess', function(event, toState, toParams, fromState) { $rootScope.$on('$stateChangeSuccess', function(event, toState, toParams, fromState) {
// catch license expiration notifications immediately after user logs in, redirect
if (fromState.name === 'signIn'){
CheckLicense.notify();
}
if(fromState.name === 'license' && toParams.hasOwnProperty('licenseMissing')){ if(fromState.name === 'license' && toParams.hasOwnProperty('licenseMissing')){
$rootScope.licenseMissing = toParams.licenseMissing; $rootScope.licenseMissing = toParams.licenseMissing;
@@ -865,11 +872,20 @@ var tower = angular.module('Tower', [
$rootScope.user_is_superuser = Authorization.getUserInfo('is_superuser'); $rootScope.user_is_superuser = Authorization.getUserInfo('is_superuser');
// state the user refreshes we want to open the socket, except if the user is on the login page, which should happen after the user logs in (see the AuthService module for that call to OpenSocket) // state the user refreshes we want to open the socket, except if the user is on the login page, which should happen after the user logs in (see the AuthService module for that call to OpenSocket)
if(!_.contains($location.$$url, '/login')){ if(!_.contains($location.$$url, '/login')){
Timer.init().then(function(timer){ ConfigService.getConfig().then(function(){
$rootScope.sessionTimer = timer; Timer.init().then(function(timer){
$rootScope.$emit('OpenSocket'); $rootScope.sessionTimer = timer;
pendoService.issuePendoIdentity(); $rootScope.$emit('OpenSocket');
CheckLicense.notify(); pendoService.issuePendoIdentity();
CheckLicense.test();
FeaturesService.get();
if($location.$$path === "/home" && $state.current && $state.current.name === ""){
$state.go('dashboard');
}
else if($location.$$path === "/portal" && $state.current && $state.current.name === ""){
$state.go('portalMode');
}
});
}); });
} }
} }
@@ -904,7 +920,11 @@ var tower = angular.module('Tower', [
// create a promise that will resolve state $AnsibleConfig is loaded // create a promise that will resolve state $AnsibleConfig is loaded
$rootScope.loginConfig = $q.defer(); $rootScope.loginConfig = $q.defer();
} }
if (!$rootScope.featuresConfigured) {
// create a promise that will resolve when features are loaded
$rootScope.featuresConfigured = $q.defer();
}
$rootScope.licenseMissing = true;
//the authorization controller redirects to the home page automatcially if there is no last path defined. in order to override //the authorization controller redirects to the home page automatcially if there is no last path defined. in order to override
// this, set the last path to /portal for instances where portal is visited for the first time. // this, set the last path to /portal for instances where portal is visited for the first time.
$rootScope.lastPath = ($location.path() === "/portal") ? 'portal' : undefined; $rootScope.lastPath = ($location.path() === "/portal") ? 'portal' : undefined;

View File

@@ -1,5 +1,6 @@
export default export default
[ 'templateUrl', '$state', 'FeaturesService', 'ProcessErrors', 'Store', 'Empty', '$log', function(templateUrl, $state, FeaturesService, ProcessErrors, Store, Empty, $log) { ['templateUrl', '$state', 'FeaturesService', 'ProcessErrors','$rootScope',
function(templateUrl, $state, FeaturesService, ProcessErrors, $rootScope) {
return { return {
restrict: 'E', restrict: 'E',
templateUrl: templateUrl('bread-crumb/bread-crumb'), templateUrl: templateUrl('bread-crumb/bread-crumb'),
@@ -12,49 +13,21 @@ export default
scope.toggleActivityStream = function() { scope.toggleActivityStream = function() {
// If the user is not already on the activity stream then they want to navigate to it var stateGoParams = {};
if(!scope.activityStreamActive) {
var stateGoParams = {};
if(streamConfig && streamConfig.activityStream) { if(streamConfig && streamConfig.activityStream) {
if(streamConfig.activityStreamTarget) { if(streamConfig.activityStreamTarget) {
stateGoParams.target = streamConfig.activityStreamTarget; stateGoParams.target = streamConfig.activityStreamTarget;
}
if(streamConfig.activityStreamId) {
stateGoParams.id = $state.params[streamConfig.activityStreamId];
}
} }
if(streamConfig.activityStreamId) {
$state.go('activityStream', stateGoParams); stateGoParams.id = $state.params[streamConfig.activityStreamId];
}
// The user is navigating away from the activity stream - take them back from whence they came
else {
// Pull the previous state out of local storage
var previousState = Store('previous_state');
if(previousState && !Empty(previousState.name)) {
$state.go(previousState.name, previousState.fromParams);
} }
else {
// If for some reason something went wrong (like local storage was wiped, etc) take the
// user back to the dashboard
$state.go('dashboard');
}
} }
$state.go('activityStream', stateGoParams);
}; };
scope.$on("$stateChangeSuccess", function updateActivityStreamButton(event, toState, toParams, fromState, fromParams) { scope.$on("$stateChangeStart", function updateActivityStreamButton(event, toState) {
if(fromState && !Empty(fromState.name)) {
// Go ahead and attach the from params to the state object so that it can all be stored together
fromState.fromParams = fromParams ? fromParams : {};
// Store the state that we're coming from in local storage to be accessed when navigating away from the
// activity stream
Store('previous_state', fromState);
}
streamConfig = (toState && toState.data) ? toState.data : {}; streamConfig = (toState && toState.data) ? toState.data : {};
@@ -65,27 +38,12 @@ export default
// point. We use the get() function call here just in case the features aren't available. // point. We use the get() function call here just in case the features aren't available.
// The get() function will only fire off the server call if the features aren't already // The get() function will only fire off the server call if the features aren't already
// attached to the $rootScope. // attached to the $rootScope.
var features = FeaturesService.get();
FeaturesService.get() if(features){
.then(function() {
scope.loadingLicense = false; scope.loadingLicense = false;
scope.activityStreamActive = (toState.name === 'activityStream') ? true : false; scope.activityStreamActive = (toState.name === 'activityStream') ? true : false;
scope.showActivityStreamButton = (FeaturesService.featureEnabled('activity_streams') || toState.name === 'activityStream') ? true : false; scope.showActivityStreamButton = (FeaturesService.featureEnabled('activity_streams') || toState.name ==='activityStream') ? true : false;
var licenseInfo = FeaturesService.getLicenseInfo(); }
scope.licenseType = licenseInfo ? licenseInfo.license_type : null;
if (!licenseInfo) {
console.warn("License info not loaded correctly"); // jshint ignore:line
$log.error("License info not loaded correctly");
}
})
.catch(function (response) {
ProcessErrors(null, response.data, response.status, null, {
hdr: 'Error!',
msg: 'Failed to get feature info. GET returned status: ' +
response.status
});
});
} }
else { else {
@@ -94,6 +52,15 @@ export default
} }
}); });
// scope.$on('featuresLoaded', function(){
$rootScope.featuresConfigured.promise.then(function(features){
// var features = FeaturesService.get();
if(features){
scope.loadingLicense = false;
scope.activityStreamActive = ($state.name === 'activityStream') ? true : false;
scope.showActivityStreamButton = (FeaturesService.featureEnabled('activity_streams') || $state.name ==='activityStream') ? true : false;
}
});
} }
}; };
}]; }];

View File

@@ -1,5 +1,5 @@
<div id="bread_crumb" class="BreadCrumb" ng-class="{'is-loggedOut' : !$root.current_user.username}"> <div id="bread_crumb" class="BreadCrumb" ng-class="{'is-loggedOut' : !$root.current_user.username}">
<div ng-if="!licenseMissing" ncy-breadcrumb></div> <div ng-hide="licenseMissing" ncy-breadcrumb></div>
<div class="BreadCrumb-menuLink" <div class="BreadCrumb-menuLink"
id="bread_crumb_activity_stream" id="bread_crumb_activity_stream"
aw-tool-tip="View Activity Stream" aw-tool-tip="View Activity Stream"
@@ -8,7 +8,7 @@
data-container="body" data-container="body"
ng-class="{'BreadCrumb-menuLinkActive' : activityStreamActive}" ng-class="{'BreadCrumb-menuLinkActive' : activityStreamActive}"
ng-if="showActivityStreamButton" ng-if="showActivityStreamButton"
ng-hide= "loadingLicense || licenseMissing || licenseType == 'basic'" ng-hide= "loadingLicense || licenseMissing"
ng-click="toggleActivityStream()"> ng-click="toggleActivityStream()">
<i class="BreadCrumb-menuLinkImage icon-activity-stream" <i class="BreadCrumb-menuLinkImage icon-activity-stream"
alt="Activity Stream"> alt="Activity Stream">

View File

@@ -22,9 +22,6 @@ var dashboardHostsList = {
label: "HOSTS" label: "HOSTS"
}, },
resolve: { resolve: {
features: ['FeaturesService', function(FeaturesService) {
return FeaturesService.get();
}],
hosts: ['Rest', 'GetBasePath', '$stateParams', function(Rest, GetBasePath, $stateParams){ hosts: ['Rest', 'GetBasePath', '$stateParams', function(Rest, GetBasePath, $stateParams){
var defaultUrl = GetBasePath('hosts') + '?page_size=10' + ($stateParams['active-failures'] ? '&has_active_failures=true' : '' ); var defaultUrl = GetBasePath('hosts') + '?page_size=10' + ($stateParams['active-failures'] ? '&has_active_failures=true' : '' );
Rest.setUrl(defaultUrl); Rest.setUrl(defaultUrl);

View File

@@ -316,7 +316,8 @@ export default
buttons: { //for now always generates <button> tags buttons: { //for now always generates <button> tags
add_survey: { add_survey: {
ngClick: 'addSurvey()', ngClick: 'addSurvey()',
ngShow: 'job_type.value !== "scan" && !survey_exists' ngShow: 'job_type.value !== "scan" && !survey_exists',
awFeature: 'surveys'
}, },
edit_survey: { edit_survey: {
ngClick: 'editSurvey()', ngClick: 'editSurvey()',

View File

@@ -16,7 +16,8 @@
export default export default
angular.module('ParseHelper', ['Utilities', 'AngularCodeMirrorModule']) angular.module('ParseHelper', ['Utilities', 'AngularCodeMirrorModule'])
.factory('ParseTypeChange', ['Alert', 'AngularCodeMirror', function (Alert, AngularCodeMirror) { .factory('ParseTypeChange', ['Alert', 'AngularCodeMirror', '$rootScope',
function (Alert, AngularCodeMirror, $rootScope) {
return function (params) { return function (params) {
var scope = params.scope, var scope = params.scope,
@@ -45,16 +46,18 @@ export default
function createField(onChange, onReady) { function createField(onChange, onReady) {
//hide the textarea and show a fresh CodeMirror with the current mode (json or yaml) //hide the textarea and show a fresh CodeMirror with the current mode (json or yaml)
scope.codeMirror = AngularCodeMirror(); $rootScope.loginConfig.promise.then(function () {
scope.codeMirror.addModes($AnsibleConfig.variable_edit_modes); scope.codeMirror = AngularCodeMirror();
scope.codeMirror.showTextArea({ scope.codeMirror.addModes($AnsibleConfig.variable_edit_modes);
scope: scope, scope.codeMirror.showTextArea({
model: fld, scope: scope,
element: field_id, model: fld,
lineNumbers: true, element: field_id,
mode: scope[pfld], lineNumbers: true,
onReady: onReady, mode: scope[pfld],
onChange: onChange onReady: onReady,
onChange: onChange
});
}); });
} }

View File

@@ -17,10 +17,5 @@ export default {
}, },
ncyBreadcrumb: { ncyBreadcrumb: {
label: "INVENTORY EDIT" label: "INVENTORY EDIT"
},
resolve: {
features: ['FeaturesService', function(FeaturesService) {
return FeaturesService.get();
}]
} }
}; };

View File

@@ -17,10 +17,5 @@ export default {
}, },
ncyBreadcrumb: { ncyBreadcrumb: {
label: "JOB TEMPLATES" label: "JOB TEMPLATES"
},
resolve: {
features: ['FeaturesService', function(FeaturesService) {
return FeaturesService.get();
}]
} }
}; };

View File

@@ -6,21 +6,15 @@
export default export default
['$state', '$rootScope', 'Rest', 'GetBasePath', 'ProcessErrors', '$q', ['$state', '$rootScope', 'Rest', 'GetBasePath', 'ProcessErrors', '$q',
function($state, $rootScope, Rest, GetBasePath, ProcessErrors, $q){ 'ConfigService',
function($state, $rootScope, Rest, GetBasePath, ProcessErrors, $q,
ConfigService){
return { return {
get: function() { get: function() {
var defaultUrl = GetBasePath('config'); var config = ConfigService.get();
Rest.setUrl(defaultUrl); return config.license_info;
return Rest.get()
.success(function(res){
$rootScope.license_tested = true;
return res;
})
.error(function(res, status){
ProcessErrors($rootScope, res, status, null, {hdr: 'Error!',
msg: 'Call to '+ defaultUrl + ' failed. Return status: '+ status});
});
}, },
post: function(license, eula){ post: function(license, eula){
var defaultUrl = GetBasePath('config'); var defaultUrl = GetBasePath('config');
Rest.setUrl(defaultUrl); Rest.setUrl(defaultUrl);
@@ -35,43 +29,51 @@ export default
msg: 'Call to '+ defaultUrl + ' failed. Return status: '+ status}); msg: 'Call to '+ defaultUrl + ' failed. Return status: '+ status});
}); });
}, },
// Checks current license validity
// Intended to for runtime or pre-state checks
// Returns false if invalid
valid: function(license) {
if (!license.valid_key){
return false;
}
else if (license.free_instances <= 0){
return false;
}
// notify if less than 15 days remaining
else if (license.time_remaining / 1000 / 60 / 60 / 24 > 15){
return false;
}
return true;
},
notify: function(){
var deferred = $q.defer(),
self = this;
if($rootScope.license_tested !== true){
this.get()
.then(function(res){
if(self.valid(res.data.license_info) === false) {
$rootScope.licenseMissing = true;
deferred.resolve();
$state.go('license');
}
else {
$rootScope.licenseMissing = false;
}
});
}
else{
deferred.resolve();
}
return deferred.promise; valid: function(license) {
if (!license.valid_key){
return false;
}
else if (license.free_instances <= 0){
return false;
}
// notify if less than 15 days remaining
else if (license.time_remaining / 1000 / 60 / 60 / 24 > 15){
return false;
}
return true;
},
test: function(event){
var //deferred = $q.defer(),
license = this.get();
if(license === null || !$rootScope.license_tested){
if(this.valid(license) === false) {
$rootScope.licenseMissing = true;
if(event){
event.preventDefault();
}
$state.go('license');
// deferred.reject();
}
else {
$rootScope.licenseMissing = false;
// deferred.resolve();
}
}
else if(this.valid(license) === false) {
$rootScope.licenseMissing = true;
$state.transitionTo('license');
if(event){
event.preventDefault();
}
// deferred.reject(license);
}
else {
$rootScope.licenseMissing = false;
// deferred.resolve(license);
}
return;// deferred.promise;
} }
}; };

View File

@@ -6,7 +6,6 @@
@import "awx/ui/client/src/shared/branding/colors.default.less"; @import "awx/ui/client/src/shared/branding/colors.default.less";
@import "awx/ui/client/src/shared/layouts/one-plus-two.less"; @import "awx/ui/client/src/shared/layouts/one-plus-two.less";
.License-container{ .License-container{
.OnePlusTwo-container; .OnePlusTwo-container;
} }

View File

@@ -7,8 +7,10 @@
export default export default
['Wait', '$state', '$scope', '$rootScope', '$location', 'GetBasePath', ['Wait', '$state', '$scope', '$rootScope', '$location', 'GetBasePath',
'Rest', 'ProcessErrors', 'CheckLicense', 'moment','$window', 'Rest', 'ProcessErrors', 'CheckLicense', 'moment','$window',
'ConfigService', 'FeaturesService', 'pendoService',
function( Wait, $state, $scope, $rootScope, $location, GetBasePath, Rest, function( Wait, $state, $scope, $rootScope, $location, GetBasePath, Rest,
ProcessErrors, CheckLicense, moment, $window){ ProcessErrors, CheckLicense, moment, $window, ConfigService,
FeaturesService, pendoService){
$scope.getKey = function(event){ $scope.getKey = function(event){
// Mimic HTML5 spec, show filename // Mimic HTML5 spec, show filename
$scope.fileName = event.target.files[0].name; $scope.fileName = event.target.files[0].name;
@@ -46,21 +48,27 @@ export default
CheckLicense.post($scope.newLicense.file, $scope.newLicense.eula) CheckLicense.post($scope.newLicense.file, $scope.newLicense.eula)
.success(function(){ .success(function(){
reset(); reset();
init(); ConfigService.delete();
if($rootScope.licenseMissing === true){ ConfigService.getConfig().then(function(){
$state.go('dashboard', { delete($rootScope.features);
licenseMissing: false FeaturesService.get();
}); pendoService.issuePendoIdentity();
} if($rootScope.licenseMissing === true){
else{ $state.go('dashboard', {
$scope.success = true; licenseMissing: false
$rootScope.licenseMissing = false; });
// for animation purposes }
var successTimeout = setTimeout(function(){ else{
$scope.success = false; init();
clearTimeout(successTimeout); $scope.success = true;
}, 4000); $rootScope.licenseMissing = false;
} // for animation purposes
var successTimeout = setTimeout(function(){
$scope.success = false;
clearTimeout(successTimeout);
}, 4000);
}
});
}); });
}; };
var calcDaysRemaining = function(seconds){ var calcDaysRemaining = function(seconds){
@@ -74,17 +82,15 @@ export default
var calcExpiresOn = function(days){ var calcExpiresOn = function(days){
// calculate the expiration date of the license // calculate the expiration date of the license
days = parseInt(days);
return moment().add(days, 'days').calendar(); return moment().add(days, 'days').calendar();
}; };
var init = function(){ var init = function(){
$scope.fileName = "No file selected."; $scope.fileName = "No file selected.";
$scope.title = $rootScope.licenseMissing ? "Tower License" : "License Management"; $scope.title = $rootScope.licenseMissing ? "Tower License" : "License Management";
Wait('start'); Wait('start');
CheckLicense.get() ConfigService.getConfig().then(function(config){
.then(function(res){ $scope.license = config;
$scope.license = res.data; $scope.license.version = config.version.split('-')[0];
$scope.license.version = res.data.version.split('-')[0];
$scope.time = {}; $scope.time = {};
$scope.time.remaining = calcDaysRemaining($scope.license.license_info.time_remaining); $scope.time.remaining = calcDaysRemaining($scope.license.license_info.time_remaining);
$scope.time.expiresOn = calcExpiresOn($scope.time.remaining); $scope.time.expiresOn = calcExpiresOn($scope.time.remaining);

View File

@@ -1,6 +1,6 @@
<div class="License-container" <div class="License-container"
ng-class="{'License-container--missing': licenseMissing}"> ng-class="{'License-container--missing': licenseMissing}">
<div class="License-details" ng-if="!licenseMissing"> <div class="License-details" ng-hide="licenseMissing">
<div class="Panel"> <div class="Panel">
<div class="License-titleText">Details</div> <div class="License-titleText">Details</div>
<div class="License-fields"> <div class="License-fields">
@@ -74,6 +74,7 @@
<div class="Panel"> <div class="Panel">
<div class="License-titleText">{{title}}</div> <div class="License-titleText">{{title}}</div>
<div class="License-body"> <div class="License-body">
<div class="License-helperText" ng-if="licenseMissing">Welcome to Ansible Tower! Please complete the steps below to acquire a license.</div>
<div class="AddPermissions-directions" ng-if="licenseMissing"> <div class="AddPermissions-directions" ng-if="licenseMissing">
<span class="AddPermissions-directionNumber"> <span class="AddPermissions-directionNumber">
1 1

View File

@@ -16,4 +16,4 @@ export default
.factory('CheckLicense', CheckLicense) .factory('CheckLicense', CheckLicense)
.run(['$stateExtender', function($stateExtender) { .run(['$stateExtender', function($stateExtender) {
$stateExtender.addState(route); $stateExtender.addState(route);
}]); }]);

View File

@@ -16,7 +16,9 @@
export default export default
['$http', '$rootScope', '$location', '$cookieStore', 'GetBasePath', 'Store', ['$http', '$rootScope', '$location', '$cookieStore', 'GetBasePath', 'Store',
function ($http, $rootScope, $location, $cookieStore, GetBasePath, Store) { '$injector',
function ($http, $rootScope, $location, $cookieStore, GetBasePath, Store,
$injector) {
return { return {
setToken: function (token, expires) { setToken: function (token, expires) {
// set the session cookie // set the session cookie
@@ -61,11 +63,12 @@ export default
// the following puts our primary scope up for garbage collection, which // the following puts our primary scope up for garbage collection, which
// should prevent content flash from the prior user. // should prevent content flash from the prior user.
var x, scope = angular.element(document.getElementById('main-view')).scope(); var x,
ConfigService = $injector.get('ConfigService'),
scope = angular.element(document.getElementById('main-view')).scope();
scope.$destroy(); scope.$destroy();
//$rootScope.$destroy(); //$rootScope.$destroy();
if($cookieStore.get('lastPath')==='/portal'){ if($cookieStore.get('lastPath')==='/portal'){
$cookieStore.put( 'lastPath', '/portal'); $cookieStore.put( 'lastPath', '/portal');
$rootScope.lastPath = '/portal'; $rootScope.lastPath = '/portal';
@@ -88,6 +91,7 @@ export default
if ($cookieStore.get('current_user')) { if ($cookieStore.get('current_user')) {
$rootScope.lastUser = $cookieStore.get('current_user').id; $rootScope.lastUser = $cookieStore.get('current_user').id;
} }
ConfigService.delete();
$cookieStore.remove('token_expires'); $cookieStore.remove('token_expires');
$cookieStore.remove('current_user'); $cookieStore.remove('current_user');
$cookieStore.remove('token'); $cookieStore.remove('token');
@@ -97,7 +101,8 @@ export default
$rootScope.current_user = {}; $rootScope.current_user = {};
$rootScope.license_tested = undefined; $rootScope.license_tested = undefined;
$rootScope.userLoggedIn = false; $rootScope.userLoggedIn = false;
$rootScope.sessionExpired = false; // $rootScope.sessionExpired = false;
$rootScope.licenseMissing = true;
$rootScope.token = null; $rootScope.token = null;
$rootScope.token_expires = null; $rootScope.token_expires = null;
$rootScope.login_username = null; $rootScope.login_username = null;
@@ -107,27 +112,6 @@ export default
} }
}, },
getLicense: function () {
//check in here first to see if license is already obtained, if we do have it, then rootScope.license
return $http({
method: 'GET',
url: GetBasePath('config'),
headers: {
'Authorization': 'Token ' + this.getToken()
}
});
},
setLicense: function (data) {
var license = data.license_info;
license.analytics_status = data.analytics_status;
license.version = data.version;
license.ansible_version = data.ansible_version;
license.tested = false;
Store('license', license);
$rootScope.features = Store('license').features;
},
licenseTested: function () { licenseTested: function () {
var license, result; var license, result;
if ($rootScope.license_tested !== undefined) { if ($rootScope.license_tested !== undefined) {

View File

@@ -7,9 +7,9 @@
export default export default
[ '$rootScope', '$pendolytics', 'Rest', 'GetBasePath', 'ProcessErrors', '$q', [ '$rootScope', '$pendolytics', 'Rest', 'GetBasePath', 'ProcessErrors', '$q',
'Store', '$log', 'ConfigService', '$log',
function ($rootScope, $pendolytics, Rest, GetBasePath, ProcessErrors, $q, function ($rootScope, $pendolytics, Rest, GetBasePath, ProcessErrors, $q,
Store, $log) { ConfigService, $log) {
return { return {
setPendoOptions: function (config) { setPendoOptions: function (config) {
var tower_version = config.version.split('-')[0], var tower_version = config.version.split('-')[0],
@@ -93,7 +93,7 @@ export default
}, },
getConfig: function () { getConfig: function () {
var config = Store('license'), var config = ConfigService.get(),
deferred = $q.defer(); deferred = $q.defer();
if(_.isEmpty(config)){ if(_.isEmpty(config)){
var url = GetBasePath('config'); var url = GetBasePath('config');

View File

@@ -23,9 +23,9 @@
*/ */
export default export default
['$rootScope', '$cookieStore', 'CreateDialog', 'Authorization', ['$rootScope', '$cookieStore', 'CreateDialog', 'Authorization',
'Store', '$interval', '$location', '$q', 'Store', '$interval', '$state', '$q',
function ($rootScope, $cookieStore, CreateDialog, Authorization, function ($rootScope, $cookieStore, CreateDialog, Authorization,
Store, $interval, $location, $q) { Store, $interval, $state, $q) {
return { return {
sessionTime: null, sessionTime: null,
@@ -62,7 +62,7 @@ export default
now = new Date().getTime()/1000, now = new Date().getTime()/1000,
diff = stime-now; diff = stime-now;
if(diff < 61){ if(diff < 60){
return diff; return diff;
} }
else { else {
@@ -157,13 +157,13 @@ export default
$('#idle-modal').dialog('close'); $('#idle-modal').dialog('close');
} }
that.expireSession('idle'); that.expireSession('idle');
$location.url('/login'); $state.go('signOut');
} }
if(Store('sessionTime') && if(Store('sessionTime') &&
Store('sessionTime')[$rootScope.current_user.id] && Store('sessionTime')[$rootScope.current_user.id] &&
Store('sessionTime')[$rootScope.current_user.id].loggedIn === false){ Store('sessionTime')[$rootScope.current_user.id].loggedIn === false){
that.expireSession(); that.expireSession();
$location.url('/login'); $state.go('signOut');
} }

View File

@@ -43,8 +43,8 @@
* - Start the expiration timer by calling the init() method of [js/shared/Timer.js](/static/docs/api/shared.function:Timer) * - Start the expiration timer by calling the init() method of [js/shared/Timer.js](/static/docs/api/shared.function:Timer)
* - Get user informaton by calling Authorization.getUser() - sends a GET request to /api/v1/me * - Get user informaton by calling Authorization.getUser() - sends a GET request to /api/v1/me
* - Store user information in the session cookie by calling Authorization.setUser(). * - Store user information in the session cookie by calling Authorization.setUser().
* - Get the Tower license by calling Authorization.getLicense() - sends a GET request to /api/vi/config * - Get the Tower license by calling ConfigService.getConfig() - sends a GET request to /api/vi/config
* - Stores the license object in local storage by calling Authorization.setLicense(). This adds the Tower version and a tested flag to the license object. The tested flag is initially set to false. * - Stores the license object in memory by calling CheckLicense.test(). This adds the Tower version and a tested flag to the license object. The tested flag is initially set to false. Additionally, the pendoService and FeaturesService are called to initiate the other startup services of Tower
* *
* Note that there is a session timer kept on the server side as well as the client side. Each time an API request is made, Tower (in app.js) calls * Note that there is a session timer kept on the server side as well as the client side. Each time an API request is made, Tower (in app.js) calls
* Timer.isExpired(). This verifies the UI does not think the session is expired, and if not, moves the expiration time into the future. The number of * Timer.isExpired(). This verifies the UI does not think the session is expired, and if not, moves the expiration time into the future. The number of
@@ -54,10 +54,13 @@
* This is usage information. * This is usage information.
*/ */
export default ['$log', '$cookieStore', '$compile', '$window', '$rootScope', '$location', 'Authorization', 'ToggleClass', 'Alert', 'Wait', export default ['$log', '$cookieStore', '$compile', '$window', '$rootScope',
'Timer', 'Empty', 'ClearScope', '$scope', 'pendoService', '$location', 'Authorization', 'ToggleClass', 'Alert', 'Wait', 'Timer',
function ($log, $cookieStore, $compile, $window, $rootScope, $location, Authorization, ToggleClass, Alert, Wait, 'Empty', 'ClearScope', '$scope', 'pendoService', 'ConfigService',
Timer, Empty, ClearScope, scope, pendoService) { 'CheckLicense', 'FeaturesService',
function ($log, $cookieStore, $compile, $window, $rootScope, $location,
Authorization, ToggleClass, Alert, Wait, Timer, Empty, ClearScope,
scope, pendoService, ConfigService, CheckLicense, FeaturesService) {
var lastPath, lastUser, sessionExpired, loginAgain; var lastPath, lastUser, sessionExpired, loginAgain;
@@ -110,10 +113,10 @@ export default ['$log', '$cookieStore', '$compile', '$window', '$rootScope', '$l
scope.removeAuthorizationGetLicense(); scope.removeAuthorizationGetLicense();
} }
scope.removeAuthorizationGetLicense = scope.$on('AuthorizationGetLicense', function() { scope.removeAuthorizationGetLicense = scope.$on('AuthorizationGetLicense', function() {
Authorization.getLicense() ConfigService.getConfig().then(function(){
.success(function (data) { CheckLicense.test();
Authorization.setLicense(data);
pendoService.issuePendoIdentity(); pendoService.issuePendoIdentity();
FeaturesService.get();
Wait("stop"); Wait("stop");
if (lastPath() && lastUser()) { if (lastPath() && lastUser()) {
// Go back to most recent navigation path // Go back to most recent navigation path
@@ -122,7 +125,7 @@ export default ['$log', '$cookieStore', '$compile', '$window', '$rootScope', '$l
$location.url('/home'); $location.url('/home');
} }
}) })
.error(function () { .catch(function () {
Wait('stop'); Wait('stop');
Alert('Error', 'Failed to access license information. GET returned status: ' + status, 'alert-danger', loginAgain); Alert('Error', 'Failed to access license information. GET returned status: ' + status, 'alert-danger', loginAgain);
}); });

View File

@@ -9,9 +9,9 @@
export default { export default {
name: 'signOut', name: 'signOut',
route: '/logout', route: '/logout',
controller: ['Authorization', '$location', function(Authorization, $location) { controller: ['Authorization', '$state', function(Authorization, $state) {
Authorization.logout(); Authorization.logout();
$location.path('/login'); $state.go('signIn');
}], }],
ncyBreadcrumb: { ncyBreadcrumb: {
skip: true skip: true

View File

@@ -18,7 +18,7 @@
} }
.MainMenu--licenseMissing{ .MainMenu--licenseMissing{
justify-content: flex-end; justify-content: space-between;
} }
.MainMenu-logo, .MainMenu-logo,
@@ -35,6 +35,11 @@
margin: 20px; margin: 20px;
} }
.MainMenu-logoImage--licenseMissing:hover{
cursor:default;
background-color: @default-bg!important;
}
.MainMenu-item { .MainMenu-item {
padding: 0 20px; padding: 0 20px;
} }
@@ -192,6 +197,23 @@
display: none; display: none;
} }
.MainMenu-item--licenseMissing{
justify-content: space-between!important;
display:flex!important;
align-items: center!important;
flex: initial!important;
padding-top:0px;
}
.MainMenu-item--licenseMissing:hover{
padding-top:20px;
border-bottom: 5px solid @menu-link-btm-hov;
}
.MainMenu-itemImage--licenseMissing{
font-size: 20px!important;
}
.MainMenu { .MainMenu {
flex-direction: row; flex-direction: row;
flex-wrap: wrap; flex-wrap: wrap;

View File

@@ -2,10 +2,10 @@
<!-- Menu logo item --> <!-- Menu logo item -->
<a id="main_menu_logo" <a id="main_menu_logo"
href="/#/" href="/#/"
class="MainMenu-logo" class="MainMenu-logo ng-cloak"
ng-if="!licenseMissing" ng-class="{'is-loggedOut' : !$root.current_user.username, 'MainMenu-logoImage--licenseMissing': licenseMissing}">
ng-class="{'is-loggedOut' : !$root.current_user.username}">
<img class="MainMenu-logoImage" <img class="MainMenu-logoImage"
ng-class="{'MainMenu-logoImage--licenseMissing': licenseMissing}"
ng-src="/static/assets/tower-logo-header.svg"> ng-src="/static/assets/tower-logo-header.svg">
</a> </a>
@@ -86,10 +86,10 @@
</span> </span>
<!-- Non-mobile menu items --> <!-- Non-mobile menu items -->
<a class="MainMenu-item MainMenu-item--notMobile MainMenu-item--left" <a class="MainMenu-item MainMenu-item--notMobile MainMenu-item--left ng-cloak"
id="main_menu_projects_link" id="main_menu_projects_link"
href="/#/projects" href="/#/projects"
ng-if="!licenseMissing" ng-hide="licenseMissing"
ng-class="{'is-currentRoute' : isCurrentState('projects'), 'is-loggedOut' : !$root.current_user.username}"> ng-class="{'is-currentRoute' : isCurrentState('projects'), 'is-loggedOut' : !$root.current_user.username}">
<span class="MainMenu-itemText"> <span class="MainMenu-itemText">
PROJECTS PROJECTS
@@ -98,7 +98,7 @@
<a class="MainMenu-item MainMenu-item--notMobile MainMenu-item--left" <a class="MainMenu-item MainMenu-item--notMobile MainMenu-item--left"
id="main_menu_inventories_link" id="main_menu_inventories_link"
href="/#/inventories" href="/#/inventories"
ng-if="!licenseMissing" ng-hide="licenseMissing"
ng-class="{'is-currentRoute' : isCurrentState('inventories'), 'is-loggedOut' : !$root.current_user.username}"> ng-class="{'is-currentRoute' : isCurrentState('inventories'), 'is-loggedOut' : !$root.current_user.username}">
<span class="MainMenu-itemText"> <span class="MainMenu-itemText">
INVENTORIES INVENTORIES
@@ -107,7 +107,7 @@
<a class="MainMenu-item MainMenu-item--notMobile MainMenu-item--left" <a class="MainMenu-item MainMenu-item--notMobile MainMenu-item--left"
id="main_menu_job_templates_link" id="main_menu_job_templates_link"
href="/#/job_templates" href="/#/job_templates"
ng-if="!licenseMissing" ng-hide="licenseMissing"
ng-class="{'is-currentRoute' : isCurrentState('jobTemplates'), 'is-loggedOut' : !$root.current_user.username}"> ng-class="{'is-currentRoute' : isCurrentState('jobTemplates'), 'is-loggedOut' : !$root.current_user.username}">
<span class="MainMenu-itemText"> <span class="MainMenu-itemText">
JOB TEMPLATES JOB TEMPLATES
@@ -116,7 +116,7 @@
<a class="MainMenu-item MainMenu-item--notMobile MainMenu-item--left MainMenu-item--lastLeft" <a class="MainMenu-item MainMenu-item--notMobile MainMenu-item--left MainMenu-item--lastLeft"
id="main_menu_jobs_link" id="main_menu_jobs_link"
href="/#/jobs" href="/#/jobs"
ng-if="!licenseMissing" ng-hide="licenseMissing"
ng-class="{'is-currentRoute' : isCurrentState('jobs'), 'is-loggedOut' : !$root.current_user.username}"> ng-class="{'is-currentRoute' : isCurrentState('jobs'), 'is-loggedOut' : !$root.current_user.username}">
<span class="MainMenu-itemText"> <span class="MainMenu-itemText">
JOBS JOBS
@@ -125,7 +125,7 @@
<a class="MainMenu-item MainMenu-item--notMobile MainMenu-item--user MainMenu-item--right" <a class="MainMenu-item MainMenu-item--notMobile MainMenu-item--user MainMenu-item--right"
id="main_menu_current_user_link" id="main_menu_current_user_link"
ng-href="/#/users/{{ $root.current_user.id }}" ng-href="/#/users/{{ $root.current_user.id }}"
ng-if="!licenseMissing" ng-hide="licenseMissing"
ng-class="{'is-currentRoute' : isCurrentState('users.edit'), 'is-loggedOut' : !$root.current_user.username}" ng-class="{'is-currentRoute' : isCurrentState('users.edit'), 'is-loggedOut' : !$root.current_user.username}"
aw-tool-tip="{{currentUserTip}}" aw-tool-tip="{{currentUserTip}}"
aw-tip-watch="currentUserTip" aw-tip-watch="currentUserTip"
@@ -142,7 +142,7 @@
<a class="MainMenu-item MainMenu-item--notMobile MainMenu-item--right" <a class="MainMenu-item MainMenu-item--notMobile MainMenu-item--right"
id="main_menu_setup_link" id="main_menu_setup_link"
ng-href="/#/setup" ng-href="/#/setup"
ng-if="!licenseMissing" ng-hide="licenseMissing"
ng-class="{'is-currentRoute' : isCurrentState('setup'), 'is-loggedOut' : !$root.current_user.username}" ng-class="{'is-currentRoute' : isCurrentState('setup'), 'is-loggedOut' : !$root.current_user.username}"
aw-tool-tip="Settings" aw-tool-tip="Settings"
data-placement="bottom" data-placement="bottom"
@@ -155,7 +155,7 @@
<a class="MainMenu-item MainMenu-item--notMobile MainMenu-item--right" <a class="MainMenu-item MainMenu-item--notMobile MainMenu-item--right"
id="main_menu_portal_link" id="main_menu_portal_link"
ng-href="/#/portal" ng-href="/#/portal"
ng-if="!licenseMissing" ng-hide="licenseMissing"
ng-class="{'is-currentRoute' : isCurrentState('portalMode'), 'is-loggedOut' : !$root.current_user.username}" ng-class="{'is-currentRoute' : isCurrentState('portalMode'), 'is-loggedOut' : !$root.current_user.username}"
aw-tool-tip="My View" aw-tool-tip="My View"
data-placement="bottom" data-placement="bottom"
@@ -168,7 +168,7 @@
<a class="MainMenu-item MainMenu-item--notMobile MainMenu-item--right" <a class="MainMenu-item MainMenu-item--notMobile MainMenu-item--right"
id="main_menu_docs_link" id="main_menu_docs_link"
ng-href="http://docs.ansible.com/ansible-tower/" ng-href="http://docs.ansible.com/ansible-tower/"
ng-if="!licenseMissing" ng-hide="licenseMissing"
ng-class="{'is-loggedOut' : !$root.current_user.username}" ng-class="{'is-loggedOut' : !$root.current_user.username}"
aw-tool-tip="View Documentation" aw-tool-tip="View Documentation"
data-placement="bottom" data-placement="bottom"
@@ -182,13 +182,16 @@
<a class="MainMenu-item MainMenu-item--notMobile MainMenu-item--right" <a class="MainMenu-item MainMenu-item--notMobile MainMenu-item--right"
id="main_menu_logout_link" id="main_menu_logout_link"
ng-href="/#/logout" ng-href="/#/logout"
ng-class="{'is-currentRoute' : isCurrentState('logout'), 'is-loggedOut' : !$root.current_user.username}" ng-class="{'is-currentRoute' : isCurrentState('logout'),
'is-loggedOut' : !$root.current_user.username,
'MainMenu-item--licenseMissing' : licenseMissing}"
aw-tool-tip="Log Out" aw-tool-tip="Log Out"
data-placement="bottom" data-placement="bottom"
data-trigger="hover" data-trigger="hover"
data-container="body" data-container="body"
data-tooltip_inner_class="tooltip-inner--logOut"> data-tooltip_inner_class="tooltip-inner--logOut">
<i class="MainMenu-itemImage fa fa-power-off" <i class="MainMenu-itemImage fa fa-power-off"
ng-class="{'MainMenu-itemImage--licenseMissing' : licenseMissing}"
alt="Log Out"> alt="Log Out">
</i> </i>
</a> </a>
@@ -213,7 +216,7 @@
<button <button
id="main_menu_mobile_toggle_button" id="main_menu_mobile_toggle_button"
class="MainMenu-toggle" class="MainMenu-toggle"
ng-if="!licenseMissing" ng-hide="licenseMissing"
ng-class="{'is-active': !isHiddenOnMobile, 'is-loggedOut' : !$root.current_user.username}" ng-class="{'is-active': !isHiddenOnMobile, 'is-loggedOut' : !$root.current_user.username}"
ng-click="toggleMenu()"> ng-click="toggleMenu()">
<i class="fa fa-bars MainMenu-toggleImage"></i> <i class="fa fa-bars MainMenu-toggleImage"></i>

View File

@@ -11,11 +11,6 @@ export default {
route: '/add', route: '/add',
templateUrl: templateUrl('notifications/add/add'), templateUrl: templateUrl('notifications/add/add'),
controller: 'notificationsAddController', controller: 'notificationsAddController',
resolve: {
features: ['FeaturesService', function(FeaturesService) {
return FeaturesService.get();
}]
},
ncyBreadcrumb: { ncyBreadcrumb: {
parent: 'notifications', parent: 'notifications',
label: 'Create Notification Template' label: 'Create Notification Template'

View File

@@ -15,10 +15,5 @@ export default {
ncyBreadcrumb: { ncyBreadcrumb: {
parent: "organizations", parent: "organizations",
label: "CREATE ORGANIZATION" label: "CREATE ORGANIZATION"
},
resolve: {
features: ['FeaturesService', function(FeaturesService) {
return FeaturesService.get();
}]
} }
}; };

View File

@@ -10,10 +10,5 @@ export default {
name: 'userPermissionsAdd', name: 'userPermissionsAdd',
route: '/users/:user_id/permissions/add', route: '/users/:user_id/permissions/add',
templateUrl: templateUrl('permissions/shared/user-permissions'), templateUrl: templateUrl('permissions/shared/user-permissions'),
controller: 'permissionsAddController', controller: 'permissionsAddController'
resolve: {
features: ['FeaturesService', function(FeaturesService) {
return FeaturesService.get();
}]
}
}; };

View File

@@ -10,10 +10,5 @@ export default {
name: 'userPermissionsEdit', name: 'userPermissionsEdit',
route: '/users/:user_id/permissions/:permission_id', route: '/users/:user_id/permissions/:permission_id',
templateUrl: templateUrl('permissions/shared/user-permissions'), templateUrl: templateUrl('permissions/shared/user-permissions'),
controller: 'permissionsEditController', controller: 'permissionsEditController'
resolve: {
features: ['FeaturesService', function(FeaturesService) {
return FeaturesService.get();
}]
}
}; };

View File

@@ -10,11 +10,6 @@ export default {
ncyBreadcrumb: { ncyBreadcrumb: {
label: "MY VIEW" label: "MY VIEW"
}, },
resolve: {
features: ['FeaturesService', function(FeaturesService) {
return FeaturesService.get();
}]
},
views: { views: {
// the empty parent ui-view // the empty parent ui-view
"" : { "" : {

View File

@@ -16,15 +16,17 @@
return { return {
response: function(config) { response: function(config) {
if(config.headers('auth-token-timeout') !== null){ if(config.headers('auth-token-timeout') !== null){
$AnsibleConfig.session_timeout = Number(config.headers('auth-token-timeout')); $rootScope.loginConfig.promise.then(function () {
$AnsibleConfig.session_timeout = Number(config.headers('auth-token-timeout'));
});
} }
return config; return config;
}, },
responseError: function(rejection){ responseError: function(rejection){
if(rejection && rejection.data && rejection.data.detail && rejection.data.detail === "Maximum per-user sessions reached"){ if(rejection && rejection.data && rejection.data.detail && rejection.data.detail === "Maximum per-user sessions reached"){
$rootScope.sessionTimer.expireSession('session_limit'); $rootScope.sessionTimer.expireSession('session_limit');
var location = $injector.get('$location'); var state = $injector.get('$state');
location.url('/login'); state.go('signOut');
return $q.reject(rejection); return $q.reject(rejection);
} }
return $q.reject(rejection); return $q.reject(rejection);

View File

@@ -0,0 +1,56 @@
/*************************************************
* Copyright (c) 2016 Ansible, Inc.
*
* All Rights Reserved
*************************************************/
export default
['GetBasePath', 'ProcessErrors', '$q', 'Rest', '$rootScope',
function (GetBasePath, ProcessErrors, $q, Rest, $rootScope) {
return {
get: function(){
return this.config;
},
set: function(config){
this.config = config;
},
delete: function(){
delete(this.config);
},
getConfig: function () {
var config = this.get(),
that = this,
deferred = $q.defer();
if(_.isEmpty(config)){
var url = GetBasePath('config');
Rest.setUrl(url);
var promise = Rest.get();
promise.then(function (response) {
var config = response.data;
that.set(config);
deferred.resolve(response.data);
});
promise.catch(function (response) {
ProcessErrors($rootScope, response.data, response.status, null, {
hdr: 'Error!',
msg: 'Failed to get config. GET returned status: ' +
response.status });
deferred.reject('Could not resolve pendo config.');
});
}
else if(config){
this.set(config);
deferred.resolve(config);
}
else {
deferred.reject('Config not found.');
}
return deferred.promise;
}
};
}
];

View File

@@ -0,0 +1,11 @@
/*************************************************
* Copyright (c) 2016 Ansible, Inc.
*
* All Rights Reserved
*************************************************/
import ConfigService from './config.service';
export default
angular.module('config', [])
.service('ConfigService', ConfigService);

View File

@@ -4,36 +4,27 @@
* All Rights Reserved * All Rights Reserved
*************************************************/ *************************************************/
export default ['$rootScope', 'Rest', 'GetBasePath', 'ProcessErrors', '$http', '$q', export default ['$rootScope', 'Rest', 'GetBasePath', 'ProcessErrors', '$http',
function ($rootScope, Rest, GetBasePath, ProcessErrors, $http, $q) { '$q', 'ConfigService',
var license_info; function ($rootScope, Rest, GetBasePath, ProcessErrors, $http, $q,
ConfigService) {
return { return {
getFeatures: function(){
var promise;
Rest.setUrl(GetBasePath('config'));
promise = Rest.get();
return promise.then(function (data) {
license_info = data.data.license_info;
$rootScope.features = data.data.license_info.features;
return $rootScope.features;
}).catch(function (response) {
ProcessErrors($rootScope, response.data, response.status, null, {
hdr: 'Error!',
msg: 'Failed to get license info. GET returned status: ' +
response.status
});
});
},
get: function(){ get: function(){
if(_.isEmpty($rootScope.features)){ if (_.isEmpty($rootScope.features)) {
return this.getFeatures(); var config = ConfigService.get();
} else { if(config){
// $q.when will ensure that the result is returned $rootScope.features = config.license_info.features;
// as a resovled promise. if($rootScope.featuresConfigured){
return $q.when($rootScope.features); $rootScope.featuresConfigured.resolve($rootScope.features);
}
return $rootScope.features;
}
}
else{
return $rootScope.features;
} }
}, },
featureEnabled: function(feature) { featureEnabled: function(feature) {
if($rootScope.features && $rootScope.features[feature] && $rootScope.features[feature] === true) { if($rootScope.features && $rootScope.features[feature] && $rootScope.features[feature] === true) {
return true; return true;
@@ -41,9 +32,6 @@ function ($rootScope, Rest, GetBasePath, ProcessErrors, $http, $q) {
else { else {
return false; return false;
} }
},
getLicenseInfo: function() {
return license_info;
} }
}; };
}]; }];

View File

@@ -1702,6 +1702,9 @@ angular.module('FormGenerator', [GeneratorHelpers.name, 'Utilities', listGenerat
if (button.ngClick) { if (button.ngClick) {
html += this.attr(button, 'ngClick'); html += this.attr(button, 'ngClick');
} }
if (button.awFeature) {
html += this.attr(button, 'awFeature');
}
if (button.ngDisabled) { if (button.ngDisabled) {
ngDisabled = (button.ngDisabled===true) ? this.form.name+"_form.$invalid" : button.ngDisabled; ngDisabled = (button.ngDisabled===true) ? this.form.name+"_form.$invalid" : button.ngDisabled;
if (btn !== 'reset') { if (btn !== 'reset') {

View File

@@ -1,29 +1,15 @@
export default function($stateProvider) { export default function($stateProvider) {
this.$get = function() { this.$get = function() {
return { return {
getResolves: function(state){
var resolve = state.resolve || {},
routes = ["signIn", "signOut"];
if(_.indexOf(routes, state.name)>-1){
return;
}
else{
resolve.features = ['FeaturesService', function(FeaturesService) {
return FeaturesService.get();
}];
return resolve;
}
},
addState: function(state) { addState: function(state) {
var route = state.route || state.url, var route = state.route || state.url;
resolve = this.getResolves(state);
$stateProvider.state(state.name, { $stateProvider.state(state.name, {
url: route, url: route,
controller: state.controller, controller: state.controller,
templateUrl: state.templateUrl, templateUrl: state.templateUrl,
resolve: resolve, resolve: state.resolve,
params: state.params, params: state.params,
data: state.data, data: state.data,
ncyBreadcrumb: state.ncyBreadcrumb, ncyBreadcrumb: state.ncyBreadcrumb,

View File

@@ -1,57 +0,0 @@
import '../support/node';
import features from 'shared/features/main';
import {describeModule} from '../support/describe-module';
//test that it returns features, as well as test that it is returned in rootScope
describeModule(features.name)
.testService('FeaturesService', function(test, restStub) {
var service;
test.withService(function(_service) {
service = _service;
});
it('returns list of features', function() {
var features = {},
result = {
data: {
license_info: {
features: features
}
}
};
var actual = service.get();
restStub.succeed(result);
restStub.flush();
return expect(actual).to.eventually.equal(features);
});
it('caches in rootScope', window.inject(['$rootScope',
function($rootScope){
var features = {},
result = {
data: {
license_info: {
features: features
}
}
};
var actual = service.get();
restStub.succeed(result);
restStub.flush();
return actual.then(function(){
expect($rootScope.features).to.equal(features);
});
}]));
});