mirror of
https://github.com/ansible/awx.git
synced 2026-01-21 14:38:00 -03:30
Merge pull request #1036 from leigh-johnson/LicenseMod
new License module, nuke old License module, resolves #1006, #1007, #1055, #1057
This commit is contained in:
commit
09da3c6f82
@ -99,7 +99,9 @@ a:focus {
|
||||
color: @blue-dark;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.btn{
|
||||
text-transform: uppercase;
|
||||
}
|
||||
/* Old style TB default button with grey background */
|
||||
.btn-grey {
|
||||
color: #333;
|
||||
|
||||
@ -60,7 +60,7 @@ body {
|
||||
}
|
||||
|
||||
#content-container {
|
||||
margin-top: 40px;
|
||||
padding-bottom: 40px;
|
||||
}
|
||||
|
||||
.group-breadcrumbs {
|
||||
|
||||
@ -1,11 +1,9 @@
|
||||
/*************************************************
|
||||
* Copyright (c) 2015 Ansible, Inc.
|
||||
* Copyright (c) 2016 Ansible, Inc.
|
||||
*
|
||||
* All Rights Reserved
|
||||
*************************************************/
|
||||
|
||||
|
||||
|
||||
var urlPrefix;
|
||||
|
||||
if ($basePath) {
|
||||
@ -34,6 +32,7 @@ import managementJobs from './management-jobs/main';
|
||||
import jobDetail from './job-detail/main';
|
||||
|
||||
// modules
|
||||
import license from './license/main';
|
||||
import setupMenu from './setup-menu/main';
|
||||
import mainMenu from './main-menu/main';
|
||||
import breadCrumb from './bread-crumb/main';
|
||||
@ -47,7 +46,6 @@ import activityStream from './activity-stream/main';
|
||||
import standardOut from './standard-out/main';
|
||||
import lookUpHelper from './lookup/main';
|
||||
import {JobTemplatesList, JobTemplatesAdd, JobTemplatesEdit} from './controllers/JobTemplates';
|
||||
import {LicenseController} from './controllers/License';
|
||||
import {ScheduleEditController} from './controllers/Schedules';
|
||||
import {ProjectsList, ProjectsAdd, ProjectsEdit} from './controllers/Projects';
|
||||
import {OrganizationsList, OrganizationsAdd, OrganizationsEdit} from './controllers/Organizations';
|
||||
@ -80,6 +78,7 @@ var tower = angular.module('Tower', [
|
||||
// 'ngAnimate',
|
||||
'ngSanitize',
|
||||
'ngCookies',
|
||||
license.name,
|
||||
RestServices.name,
|
||||
browserData.name,
|
||||
systemTracking.name,
|
||||
@ -100,7 +99,6 @@ var tower = angular.module('Tower', [
|
||||
standardOut.name,
|
||||
'templates',
|
||||
'Utilities',
|
||||
'LicenseHelper',
|
||||
'OrganizationFormDefinition',
|
||||
'UserFormDefinition',
|
||||
'FormGenerator',
|
||||
@ -859,21 +857,6 @@ var tower = angular.module('Tower', [
|
||||
}
|
||||
}).
|
||||
|
||||
state('license', {
|
||||
url: '/license',
|
||||
templateUrl: urlPrefix + 'partials/license.html',
|
||||
controller: LicenseController,
|
||||
ncyBreadcrumb: {
|
||||
parent: 'setup',
|
||||
label: 'LICENSE'
|
||||
},
|
||||
resolve: {
|
||||
features: ['FeaturesService', function(FeaturesService) {
|
||||
return FeaturesService.get();
|
||||
}]
|
||||
}
|
||||
}).
|
||||
|
||||
state('sockets', {
|
||||
url: '/sockets',
|
||||
templateUrl: urlPrefix + 'partials/sockets.html',
|
||||
@ -1043,7 +1026,6 @@ var tower = angular.module('Tower', [
|
||||
|
||||
|
||||
$rootScope.$on("$stateChangeStart", function (event, next, nextParams, prev) {
|
||||
|
||||
// this line removes the query params attached to a route
|
||||
if(prev && prev.$$route &&
|
||||
prev.$$route.name === 'systemTracking'){
|
||||
@ -1083,15 +1065,15 @@ var tower = angular.module('Tower', [
|
||||
if ($rootScope.current_user === undefined || $rootScope.current_user === null) {
|
||||
Authorization.restoreUserInfo(); //user must have hit browser refresh
|
||||
}
|
||||
if (next && next.$$route && (!/^\/(login|logout)/.test(next.$$route.originalPath))) {
|
||||
// if not headed to /login or /logout, then check the license
|
||||
CheckLicense.test();
|
||||
}
|
||||
}
|
||||
activateTab();
|
||||
});
|
||||
|
||||
$rootScope.$on('$stateChangeSuccess', function(event, toState, toParams, fromState, fromParams) {
|
||||
// catch license expiration notifications immediately after user logs in, redirect
|
||||
if (fromState.name == 'signIn'){
|
||||
CheckLicense.notify();
|
||||
}
|
||||
// broadcast event change if editing crud object
|
||||
if ($location.$$path && $location.$$path.split("/")[3] && $location.$$path.split("/")[3] === "schedules") {
|
||||
var list = $location.$$path.split("/")[3];
|
||||
|
||||
@ -1,189 +0,0 @@
|
||||
/************************************
|
||||
* Copyright (c) 2015 Ansible, Inc.
|
||||
*
|
||||
*
|
||||
* Organizations.js
|
||||
*
|
||||
* Controller functions for Organization model.
|
||||
*
|
||||
*/
|
||||
/**
|
||||
* @ngdoc function
|
||||
* @name controllers.function:Organizations
|
||||
* @description This controller's for the Organizations page
|
||||
*/
|
||||
|
||||
|
||||
export function LicenseController(ClearScope, $location, $rootScope, $compile, $filter, GenerateForm, Rest, Alert,
|
||||
GetBasePath, ProcessErrors, FormatDate, Prompt, Empty, LicenseForm, IsAdmin, CreateDialog, CheckLicense,
|
||||
TextareaResize, $scope, Wait) {
|
||||
|
||||
ClearScope();
|
||||
|
||||
$scope.getDefaultHTML = function(license_info) {
|
||||
var fld, html,
|
||||
self = this,
|
||||
generator = GenerateForm;
|
||||
|
||||
self.form = angular.copy(LicenseForm);
|
||||
|
||||
for (fld in self.form.fields) {
|
||||
if (fld !== 'time_remaining' && fld !== 'license_status' && fld !== 'tower_version') {
|
||||
if (Empty(license_info[fld])) {
|
||||
delete self.form.fields[fld];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!IsAdmin()) {
|
||||
delete self.form.fields.license_key;
|
||||
}
|
||||
|
||||
if (license_info.is_aws || Empty(license_info.license_date)) {
|
||||
delete self.form.fields.license_date;
|
||||
delete self.form.fields.time_remaining;
|
||||
}
|
||||
|
||||
html = generator.buildHTML(self.form, { mode: 'edit', showButtons: false });
|
||||
return html;
|
||||
};
|
||||
|
||||
$scope.loadDefaultScope = function(license_info, version) {
|
||||
var fld, dt, days, license,
|
||||
self = this;
|
||||
|
||||
for (fld in self.form.fields) {
|
||||
if (!Empty(license_info[fld])) {
|
||||
$scope[fld] = license_info[fld];
|
||||
}
|
||||
}
|
||||
|
||||
$scope.tower_version = version;
|
||||
|
||||
if ($scope.license_date) {
|
||||
dt = new Date(parseInt($scope.license_date, 10) * 1000); // expects license_date in seconds
|
||||
$scope.license_date = FormatDate(dt);
|
||||
$scope.time_remaining = parseInt($scope.time_remaining,10) * 1000;
|
||||
if ($scope.time_remaining < 0) {
|
||||
days = 0;
|
||||
} else {
|
||||
days = Math.floor($scope.time_remaining / 86400000);
|
||||
}
|
||||
$scope.time_remaining = (days!==1) ? $filter('number')(days, 0) + ' days' : $filter('number')(days, 0) + ' day'; // '1 day' and '0 days/2 days' or more
|
||||
}
|
||||
|
||||
if (parseInt($scope.free_instances) <= 0) {
|
||||
$scope.free_instances_class = 'field-failure';
|
||||
} else {
|
||||
$scope.free_instances_class = 'field-success';
|
||||
}
|
||||
|
||||
license = license_info;
|
||||
if (license.valid_key === undefined) {
|
||||
$scope.license_status = 'Missing License Key';
|
||||
$scope.status_color = 'license-invalid';
|
||||
} else if (!license.valid_key) {
|
||||
$scope.license_status = 'Invalid License Key';
|
||||
$scope.status_color = 'license-invalid';
|
||||
} else if (license.date_expired !== undefined && license.date_expired) {
|
||||
$scope.license_status = 'License Expired';
|
||||
$scope.status_color = 'license-expired';
|
||||
} else if (license.date_warning !== undefined && license.date_warning) {
|
||||
$scope.license_status = 'License Expiring Soon';
|
||||
$scope.status_color = 'license-warning';
|
||||
} else if (license.free_instances !== undefined && parseInt(license.free_instances) <= 0) {
|
||||
$scope.license_status = 'No Available Managed Hosts';
|
||||
$scope.status_color = 'license-invalid';
|
||||
} else {
|
||||
$scope.license_status = 'Valid License';
|
||||
$scope.status_color = 'license-valid';
|
||||
}
|
||||
};
|
||||
|
||||
$scope.setLicense = function(license_info, version) {
|
||||
this.license = license_info;
|
||||
this.version = version;
|
||||
};
|
||||
|
||||
$scope.getLicense = function(){
|
||||
return this.license;
|
||||
};
|
||||
|
||||
$scope.submitLicenseKey = function() {
|
||||
CheckLicense.postLicense($scope.license_json, $scope);
|
||||
};
|
||||
|
||||
if ($scope.removeLicenseDataReady) {
|
||||
$scope.removeLicenseDataReady();
|
||||
}
|
||||
$scope.removeLicenseDataReady = $scope.$on('LicenseDataReady', function(e, data) {
|
||||
var html, version, eula, h;
|
||||
version = data.version.replace(/-.*$/,'');
|
||||
$scope.setLicense(data.license_info, version);
|
||||
html = $scope.getDefaultHTML(data.license_info);
|
||||
$scope.loadDefaultScope(data.license_info, version);
|
||||
eula = (data.eula) ? data.eula : "" ;
|
||||
|
||||
e = angular.element(document.getElementById('license-modal-dialog'));
|
||||
e.empty().html(html);
|
||||
|
||||
$scope.parseType = 'json';
|
||||
$scope.license_json = JSON.stringify($scope.license, null, ' ');
|
||||
$scope.eula = eula;
|
||||
$scope.eula_agreement = false;
|
||||
|
||||
|
||||
h = CheckLicense.getHTML($scope.getLicense(),true).body;
|
||||
$('#license-modal-dialog #license_tabs').append("<li><a id=\"update_license_link\" ng-click=\"toggleTab($event, 'update_license_link', 'license_tabs')\" href=\"#update_license\" data-toggle=\"tab\">Update License</a></li>");
|
||||
$('#license-modal-dialog .tab-content').append("<div class=\"tab-pane\" id=\"update_license\"></div>");
|
||||
$('#license-modal-dialog #update_license').html(h);
|
||||
|
||||
if ($scope.license_status === 'Invalid License Key' || $scope.license_status === 'Missing License Key') {
|
||||
$('#license_tabs li:eq(1)').hide();
|
||||
$('#license_tabs li:eq(2) a').tab('show');
|
||||
}
|
||||
|
||||
$('#license_license_json').attr('ng-required' , 'true' );
|
||||
$('#license_eula_agreement_chbox').attr('ng-required' , 'true' );
|
||||
$('#license_form_submit_btn').attr('ng-disabled' , "license_form.$invalid" );
|
||||
e = angular.element(document.getElementById('license-modal-dialog'));
|
||||
$compile(e)($scope);
|
||||
|
||||
if (IsAdmin()) {
|
||||
setTimeout(function() {
|
||||
TextareaResize({
|
||||
scope: $scope,
|
||||
textareaId: 'license_license_json',
|
||||
modalId: 'license-modal-dialog',
|
||||
formId: 'license-notification-body',
|
||||
fld: 'license_json',
|
||||
parse: true,
|
||||
bottom_margin: 90,
|
||||
onChange: function() { $scope.license_json_api_error = ''; }
|
||||
});
|
||||
}, 300);
|
||||
}
|
||||
$('#license-ok-button').focus();
|
||||
$('#update_license_link').on('shown.bs.tab', function() {
|
||||
if (IsAdmin()) {
|
||||
TextareaResize({
|
||||
scope: $scope,
|
||||
textareaId: 'license_license_json',
|
||||
modalId: 'license-modal-dialog',
|
||||
formId: 'license-notification-body',
|
||||
fld: 'license_json',
|
||||
bottom_margin: 90,
|
||||
parse: true,
|
||||
onChange: function() { $scope.license_json_api_error = ''; }
|
||||
});
|
||||
}
|
||||
});
|
||||
Wait("stop");
|
||||
});
|
||||
CheckLicense.GetLicense('LicenseDataReady', $scope);
|
||||
|
||||
}
|
||||
|
||||
LicenseController.$inject = ['ClearScope', '$location', '$rootScope', '$compile', '$filter', 'GenerateForm', 'Rest', 'Alert',
|
||||
'GetBasePath', 'ProcessErrors', 'FormatDate', 'Prompt', 'Empty', 'LicenseForm', 'IsAdmin', 'CreateDialog',
|
||||
'CheckLicense', 'TextareaResize', '$scope', "Wait"];
|
||||
@ -6,7 +6,7 @@
|
||||
color: #848992;
|
||||
width: 100%;
|
||||
z-index: 1040;
|
||||
position: absolute;
|
||||
position: fixed;
|
||||
right: 0;
|
||||
left: 0;
|
||||
bottom: 0;
|
||||
|
||||
@ -19,7 +19,6 @@ import JobDetail from "./helpers/JobDetail";
|
||||
import JobSubmission from "./helpers/JobSubmission";
|
||||
import JobTemplates from "./helpers/JobTemplates";
|
||||
import Jobs from "./helpers/Jobs";
|
||||
import License from "./helpers/License";
|
||||
import LoadConfig from "./helpers/LoadConfig";
|
||||
import PaginationHelpers from "./helpers/PaginationHelpers";
|
||||
import Parse from "./helpers/Parse";
|
||||
@ -55,7 +54,6 @@ export
|
||||
JobSubmission,
|
||||
JobTemplates,
|
||||
Jobs,
|
||||
License,
|
||||
LoadConfig,
|
||||
PaginationHelpers,
|
||||
Parse,
|
||||
|
||||
@ -1,271 +0,0 @@
|
||||
/*************************************************
|
||||
* Copyright (c) 2015 Ansible, Inc.
|
||||
*
|
||||
* All Rights Reserved
|
||||
*************************************************/
|
||||
|
||||
/**
|
||||
* @ngdoc function
|
||||
* @name helpers.function:License
|
||||
* @description Routines for checking and reporting license status
|
||||
* CheckLicense.test() is called in app.js, in line 532, which is when the license is checked. The license information is
|
||||
* stored in local storage using 'Store()'.
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
import '../forms';
|
||||
|
||||
export default
|
||||
angular.module('LicenseHelper', ['RestServices', 'Utilities', 'LicenseUpdateFormDefinition',
|
||||
'FormGenerator', 'ParseHelper', 'ModalDialog', 'VariablesHelper', 'LicenseFormDefinition'])
|
||||
|
||||
|
||||
.factory('CheckLicense', ['$q', '$rootScope', '$compile', 'CreateDialog', 'Store',
|
||||
'LicenseUpdateForm', 'GenerateForm', 'TextareaResize', 'ToJSON', 'GetBasePath',
|
||||
'Rest', 'ProcessErrors', 'Alert', 'IsAdmin', '$location', 'pendoService',
|
||||
'Authorization', 'Wait',
|
||||
function($q, $rootScope, $compile, CreateDialog, Store, LicenseUpdateForm, GenerateForm,
|
||||
TextareaResize, ToJSON, GetBasePath, Rest, ProcessErrors, Alert, IsAdmin, $location,
|
||||
pendoService, Authorization, Wait) {
|
||||
return {
|
||||
getRemainingDays: function(time_remaining) {
|
||||
// assumes time_remaining will be in seconds
|
||||
var tr = parseInt(time_remaining, 10);
|
||||
return Math.floor(tr / 86400);
|
||||
},
|
||||
|
||||
shouldNotify: function(license) {
|
||||
if (license && typeof license === 'object' && Object.keys(license).length > 0) {
|
||||
// we have a license object
|
||||
if (!license.valid_key) {
|
||||
// missing valid key
|
||||
return true;
|
||||
}
|
||||
else if (license.free_instances <= 0) {
|
||||
// host count exceeded
|
||||
return true;
|
||||
}
|
||||
else if (this.getRemainingDays(license.time_remaining) < 15) {
|
||||
// below 15 days remaining on license
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
} else {
|
||||
// missing license object
|
||||
return true;
|
||||
}
|
||||
},
|
||||
|
||||
isAdmin: function() {
|
||||
return IsAdmin();
|
||||
},
|
||||
|
||||
getHTML: function(license, includeFormButton) {
|
||||
|
||||
var title, html,
|
||||
contact_us = "<a href=\"http://www.ansible.com/contact-us\" target=\"_black\">contact us <i class=\"fa fa-external-link\"></i></a>",
|
||||
renew = "<a href=\"http://www.ansible.com/renew\" target=\"_blank\">ansible.com/renew <i class=\"fa fa-external-link\"></i></a>",
|
||||
pricing = "<a href=\"http://www.ansible.com/pricing\" target=\"_blank\">ansible.com/pricing <i class=\"fa fa-external-link\"></i></a>",
|
||||
license_link = "<a href=\"http://www.ansible.com/license\" target=\"_blank\">click here</a>",
|
||||
result = {},
|
||||
license_is_valid=false;
|
||||
|
||||
if (license && typeof license === 'object' && Object.keys(license).length > 0 && license.valid_key !== undefined) {
|
||||
// we have a license
|
||||
if (!license.valid_key) {
|
||||
title = "Invalid License";
|
||||
html = "<div id=\"license-notification-body\"><div style=\"margin-top:5px; margin-bottom:25px;\"><p>The Ansible Tower license is invalid.</p>";
|
||||
}
|
||||
else if (this.getRemainingDays(license.time_remaining) <= 0) {
|
||||
title = "License Expired";
|
||||
html = "<div id=\"license-notification-body\"><div style=\"margin-top:5px; margin-bottom:25px;\">\n" +
|
||||
"<p>Thank you for using Ansible Tower. The Ansible Tower license has expired</p>";
|
||||
if (parseInt(license.grace_period_remaining,10) > 86400) {
|
||||
// trial licenses don't get a grace period
|
||||
if (license.trial) {
|
||||
html += "<p>Don't worry — your existing history and content has not been affected, but playbooks will no longer run and new hosts cannot be added. " +
|
||||
"If you are ready to upgrade, " + contact_us + " or visit " + pricing + " to see all of your license options. Thanks!</p>";
|
||||
} else {
|
||||
html += "<p>Don't worry — your existing history and content has not been affected, but in " + this.getRemainingDays(license.grace_period_remaining) + " days playbooks will no longer " +
|
||||
"run and new hosts cannot be added. If you are ready to upgrade, " + contact_us + " " +
|
||||
"or visit <a href=\"http://www.ansible.com/pricing\" target=\"_blank\">ansible.com/pricing <i class=\"fa fa-external-link\"></i></a> to see all of your license options. Thanks!</p>";
|
||||
}
|
||||
} else {
|
||||
html += "<p>Don’t worry — your existing history and content has not been affected, but playbooks will no longer run and new hosts cannot be added. If you are ready to renew or upgrade, contact us " +
|
||||
"at " + renew + ". Thanks!</p>";
|
||||
}
|
||||
}
|
||||
else if (this.getRemainingDays(license.time_remaining) < 15) {
|
||||
// Warning: license expiring in less than 15 days
|
||||
title = "License Warning";
|
||||
html = "<div id=\"license-notification-body\"><div style=\"margin-top:5px; margin-bottom:25px;\"><p>Thank you for using Ansible Tower. The Ansible Tower license " +
|
||||
"has " + this.getRemainingDays(license.time_remaining) + " days remaining.</p>";
|
||||
// trial licenses don't get a grace period
|
||||
if (license.trial) {
|
||||
html += "<p>After this license expires, playbooks will no longer run and hosts cannot be added. If you are ready to upgrade, " + contact_us + " or visit " + pricing + " to see all of your license options. Thanks!</p>";
|
||||
} else {
|
||||
html += "<p>After this license expires, playbooks will no longer run and hosts cannot be added. If you are ready to renew or upgrade, contact us at " + renew + ". Thanks!</p>";
|
||||
}
|
||||
|
||||
// If there is exactly one day remaining, change "days remaining"
|
||||
// to "day remaining".
|
||||
html = html.replace('has 1 days remaining', 'has 1 day remaining');
|
||||
}
|
||||
else if (license.free_instances <= 0) {
|
||||
title = "Host Count Exceeded";
|
||||
html = "<div id=\"license-notification-body\"><div style=\"margin-top:5px; margin-bottom:25px;\"><p>The Ansible Tower license has reached capacity for the number of managed hosts allowed. No new hosts can be added. Existing " +
|
||||
"playbooks can still be run against hosts already in inventory.</p>" +
|
||||
"<p>If you are ready to upgrade, contact us at " + renew + ". Thanks!</p>";
|
||||
|
||||
} else {
|
||||
// license is valid. the following text is displayed in the license viewer
|
||||
title = "Update License";
|
||||
html = "<div id=\"license-notification-body\"><div style=\"margin-top:5px; margin-bottom:25px;\"><p>The Ansible Tower license is valid.</p>" +
|
||||
"<p>If you are ready to upgrade, contact us at " + renew + ". Thanks!</p>";
|
||||
license_is_valid = true;
|
||||
}
|
||||
} else {
|
||||
// No license
|
||||
title = "Add Your License";
|
||||
html = "<div id=\"license-notification-body\"><div style=\"margin-top:5px; margin-bottom:25px;\"><p>Now that you’ve successfully installed or upgraded Ansible Tower, the next step is to add a license file. " +
|
||||
"If you don’t have a license file yet, " + license_link + " to see all of our free and paid license options.</p>" +
|
||||
"<p style=\"margin-top:15px; margin-bottom 15px; text-align:center;\"><a href=\"http://ansible.com/license\" target=\"_blank\" class=\"btn btn-danger free-button\">Get a Free Tower Trial License</a></p>";
|
||||
}
|
||||
|
||||
if (IsAdmin()) {
|
||||
html += "<p>Copy and paste the contents of your license in the field below, agree to the End User License Agreement, and click Submit.</p>";
|
||||
} else {
|
||||
html += "<p>A system administrator can install the new license by choosing View License on the Account Menu and clicking on the Update License tab.</p>";
|
||||
}
|
||||
|
||||
html += "</div>";
|
||||
|
||||
if (IsAdmin()) {
|
||||
html += GenerateForm.buildHTML(LicenseUpdateForm, { mode: 'edit', showButtons:((includeFormButton) ? true : false) });
|
||||
}
|
||||
|
||||
html += "</div>";
|
||||
|
||||
result.body = html;
|
||||
result.title = title;
|
||||
return result;
|
||||
},
|
||||
|
||||
postLicense: function(license_key, in_scope) {
|
||||
var url = GetBasePath('config'),
|
||||
self = this,
|
||||
json_data, scope;
|
||||
|
||||
scope = (in_scope) ? in_scope : self.scope;
|
||||
|
||||
json_data = ToJSON('json', license_key);
|
||||
json_data.eula_accepted = scope.eula_agreement;
|
||||
if (typeof json_data === 'object' && Object.keys(json_data).length > 0) {
|
||||
Rest.setUrl(url);
|
||||
Rest.post(json_data)
|
||||
.success(function (response) {
|
||||
response.license_info = response;
|
||||
Alert('License Accepted', 'The Ansible Tower license was updated. To review or update the license, choose View License from the Setup menu.','alert-info');
|
||||
$rootScope.features = undefined;
|
||||
|
||||
Authorization.getLicense()
|
||||
.success(function (data) {
|
||||
Authorization.setLicense(data);
|
||||
pendoService.issuePendoIdentity();
|
||||
Wait("stop");
|
||||
$location.path('/home');
|
||||
})
|
||||
.error(function () {
|
||||
Wait('stop');
|
||||
Alert('Error', 'Failed to access license information. GET returned status: ' + status, 'alert-danger',
|
||||
$location.path('/logout'));
|
||||
});
|
||||
|
||||
|
||||
|
||||
|
||||
})
|
||||
.catch(function (response) {
|
||||
scope.license_json_api_error = "A valid license key in JSON format is required";
|
||||
ProcessErrors(scope, response.data, response.status, null, { hdr: 'Error!',
|
||||
msg: 'Failed to update license. POST returned: ' + response.status
|
||||
});
|
||||
});
|
||||
} else {
|
||||
scope.license_json_api_error = "A valid license key in JSON format is required";
|
||||
}
|
||||
},
|
||||
|
||||
test: function() {
|
||||
var license = Store('license'),
|
||||
self = this,
|
||||
scope;
|
||||
|
||||
var getLicense = function() {
|
||||
var deferred = $q.defer();
|
||||
|
||||
if (license === null) {
|
||||
Rest.setUrl(GetBasePath('config'));
|
||||
return Rest.get()
|
||||
.then(function (data) {
|
||||
license = data.data.license_info;
|
||||
deferred.resolve();
|
||||
return deferred.promise;
|
||||
}, function () {
|
||||
deferred.resolve();
|
||||
return deferred.promise;
|
||||
});
|
||||
} else {
|
||||
deferred.resolve(license);
|
||||
return deferred.promise;
|
||||
}
|
||||
}
|
||||
|
||||
var promise = getLicense();
|
||||
promise.then(function() {
|
||||
self.scope = $rootScope.$new();
|
||||
scope = self.scope;
|
||||
|
||||
if (license && typeof license === 'object' && Object.keys(license).length > 0) {
|
||||
if (license.tested) {
|
||||
return true;
|
||||
}
|
||||
license.tested = true;
|
||||
Store('license',license); //update with tested flag
|
||||
}
|
||||
|
||||
// Don't do anything when the license is valid
|
||||
if (!self.shouldNotify(license)) {
|
||||
return true; // if the license is valid it would exit 'test' here, otherwise it moves on to making the modal for the license
|
||||
}
|
||||
|
||||
$location.path('/license');
|
||||
});
|
||||
},
|
||||
|
||||
GetLicense: function(callback, inScope) {
|
||||
// Retrieve license detail
|
||||
var self = this,
|
||||
scope = (inScope) ? inScope : self.scope,
|
||||
url = GetBasePath('config');
|
||||
Rest.setUrl(url);
|
||||
Rest.get()
|
||||
.success(function (data) {
|
||||
if (scope && callback) {
|
||||
scope.$emit(callback, data);
|
||||
}
|
||||
else if (scope) {
|
||||
scope.$emit('CheckLicenseReady', data);
|
||||
}
|
||||
})
|
||||
.error(function (data, status) {
|
||||
ProcessErrors($rootScope, data, status, null, { hdr: 'Error!',
|
||||
msg: 'Failed to retrieve license. GET status: ' + status
|
||||
});
|
||||
});
|
||||
}
|
||||
};
|
||||
}]);
|
||||
62
awx/ui/client/src/license/checkLicense.factory.js
Normal file
62
awx/ui/client/src/license/checkLicense.factory.js
Normal file
@ -0,0 +1,62 @@
|
||||
/*************************************************
|
||||
* Copyright (c) 2016 Ansible, Inc.
|
||||
*
|
||||
* All Rights Reserved
|
||||
*************************************************/
|
||||
|
||||
export default
|
||||
['$state', '$rootScope', 'Rest', 'GetBasePath', 'ProcessErrors', function($state, $rootScope, Rest, GetBasePath, ProcessErrors){
|
||||
return {
|
||||
get: function() {
|
||||
var defaultUrl = GetBasePath('config');
|
||||
Rest.setUrl(defaultUrl);
|
||||
return Rest.get()
|
||||
.success(function(res){
|
||||
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){
|
||||
var defaultUrl = GetBasePath('config');
|
||||
Rest.setUrl(defaultUrl);
|
||||
var data = license;
|
||||
data.eula_accepted = eula;
|
||||
return Rest.post(JSON.stringify(data))
|
||||
.success(function(res){
|
||||
return res
|
||||
})
|
||||
.error(function(res, status){
|
||||
ProcessErrors($rootScope, res, status, null, {hdr: 'Error!',
|
||||
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(){
|
||||
self = this;
|
||||
this.get()
|
||||
.then(function(res){
|
||||
self.valid(res.data.license_info) ? null : $state.go('license');
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
];
|
||||
16
awx/ui/client/src/license/fileOnChange.directive.js
Normal file
16
awx/ui/client/src/license/fileOnChange.directive.js
Normal file
@ -0,0 +1,16 @@
|
||||
/*************************************************
|
||||
* Copyright (c) 2016 Ansible, Inc.
|
||||
*
|
||||
* All Rights Reserved
|
||||
*************************************************/
|
||||
|
||||
export default
|
||||
[function(){
|
||||
return {
|
||||
restrict: 'A',
|
||||
link: function(scope, el, attrs){
|
||||
var onChange = scope.$eval(attrs.fileOnChange);
|
||||
el.bind('change', onChange);
|
||||
}
|
||||
}
|
||||
}];
|
||||
66
awx/ui/client/src/license/license.block.less
Normal file
66
awx/ui/client/src/license/license.block.less
Normal file
@ -0,0 +1,66 @@
|
||||
/*
|
||||
* Style conventions
|
||||
* .ModuleName-component-subComponent
|
||||
* Naming describes components of the view
|
||||
*/
|
||||
@import "awx/ui/client/src/shared/branding/colors.default.less";
|
||||
@import "awx/ui/client/src/shared/layouts/one-plus-two.less";
|
||||
|
||||
|
||||
.License-container{
|
||||
.OnePlusTwo-container;
|
||||
}
|
||||
.License-field--label{
|
||||
.OnePlusTwo-left--detailsLabel;
|
||||
}
|
||||
.License-management .CodeMirror-scroll{
|
||||
min-height: 140px;
|
||||
}
|
||||
.License-file textarea{
|
||||
display: block;
|
||||
width: 100%;
|
||||
}
|
||||
.License-eula textarea{
|
||||
width: 100%;
|
||||
height: 300px;
|
||||
}
|
||||
.License-field label{
|
||||
width: 155px;
|
||||
}
|
||||
.License-field--content{
|
||||
.OnePlusTwo-left--detailsContent;
|
||||
}
|
||||
.License-field{
|
||||
.OnePlusTwo-left--detailsRow;
|
||||
}
|
||||
.License-greenText{
|
||||
color: @submit-button-bg;
|
||||
}
|
||||
.License-redText{
|
||||
color: #d9534f;
|
||||
}
|
||||
.License-fields{
|
||||
.OnePlusTwo-left--details;
|
||||
}
|
||||
.License-details {
|
||||
.OnePlusTwo-left--panel(600px);
|
||||
}
|
||||
.License-titleText {
|
||||
.OnePlusTwo-panelHeader;
|
||||
}
|
||||
.License-management{
|
||||
.OnePlusTwo-right--panel(600px);
|
||||
}
|
||||
.License-submit--container{
|
||||
height: 33px;
|
||||
}
|
||||
.License-submit--success{
|
||||
line-height:33px;
|
||||
margin: 0 10px 0 0;
|
||||
}
|
||||
.License-file--container {
|
||||
margin: 20px 0 20px 0;
|
||||
input[type=file] {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
66
awx/ui/client/src/license/license.controller.js
Normal file
66
awx/ui/client/src/license/license.controller.js
Normal file
@ -0,0 +1,66 @@
|
||||
/*************************************************
|
||||
* Copyright (c) 2016 Ansible, Inc.
|
||||
*
|
||||
* All Rights Reserved
|
||||
*************************************************/
|
||||
|
||||
export default
|
||||
[ 'Wait', '$state', '$scope', '$location',
|
||||
'GetBasePath', 'Rest', 'ProcessErrors', 'CheckLicense', 'moment',
|
||||
function( Wait, $state, $scope, $location,
|
||||
GetBasePath, Rest, ProcessErrors, CheckLicense, moment){
|
||||
$scope.getKey = function(event){
|
||||
// Mimic HTML5 spec, show filename
|
||||
$scope.fileName = event.target.files[0].name;
|
||||
// Grab the key from the raw license file
|
||||
var raw = new FileReader();
|
||||
// readAsFoo runs async
|
||||
raw.onload = function(){
|
||||
$scope.newLicense.file = JSON.parse(raw.result);
|
||||
}
|
||||
raw.readAsText(event.target.files[0]);
|
||||
};
|
||||
// HTML5 spec doesn't provide a way to customize file input css
|
||||
// So we hide the default input, show our own, and simulate clicks to the hidden input
|
||||
$scope.fakeClick = function(){
|
||||
$('#License-file').click();
|
||||
}
|
||||
$scope.newLicense = {};
|
||||
$scope.submit = function(event){
|
||||
Wait('start');
|
||||
CheckLicense.post($scope.newLicense.file, $scope.newLicense.eula)
|
||||
.success(function(res){
|
||||
reset();
|
||||
init();
|
||||
$scope.success = true;
|
||||
});
|
||||
};
|
||||
var calcDaysRemaining = function(ms){
|
||||
// calculate the number of days remaining on the license
|
||||
var duration = moment.duration(ms);
|
||||
return duration.days()
|
||||
};
|
||||
|
||||
var calcExpiresOn = function(days){
|
||||
// calculate the expiration date of the license
|
||||
return moment().add(days, 'days').calendar()
|
||||
};
|
||||
var init = function(){
|
||||
$scope.fileName = "Please choose a file..."
|
||||
Wait('start');
|
||||
CheckLicense.get()
|
||||
.then(function(res){
|
||||
$scope.license = res.data;
|
||||
$scope.time = {};
|
||||
$scope.time.remaining = calcDaysRemaining($scope.license.license_info.time_remaining);
|
||||
$scope.time.expiresOn = calcExpiresOn($scope.time.remaining);
|
||||
$scope.valid = CheckLicense.valid($scope.license.license_info);
|
||||
Wait('stop');
|
||||
});
|
||||
};
|
||||
var reset = function(){
|
||||
document.getElementById('License-form').reset()
|
||||
};
|
||||
init();
|
||||
}
|
||||
];
|
||||
99
awx/ui/client/src/license/license.partial.html
Normal file
99
awx/ui/client/src/license/license.partial.html
Normal file
@ -0,0 +1,99 @@
|
||||
<div class="License-container">
|
||||
<div class="License-details">
|
||||
<div class="Panel">
|
||||
<div class="License-titleText">Details</div>
|
||||
<div class="License-fields">
|
||||
<div class="License-field">
|
||||
<div class="License-field--label">License</div>
|
||||
<div class="License-field--content">
|
||||
<span ng-show='valid'><i class="fa fa-circle License-greenText"></i> Valid</span>
|
||||
<span ng-show='invalid'><i class="fa fa-circle License-redText"></i> Invalid</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="License-field">
|
||||
<div class="License-field--label">Version</div>
|
||||
<div class="License-field--content">
|
||||
{{license.version}}
|
||||
</div>
|
||||
</div>
|
||||
<div class="License-field">
|
||||
<div class="License-field--label">License Type</div>
|
||||
<div class="License-field--content">
|
||||
{{license.license_info.license_type}}
|
||||
</div>
|
||||
</div>
|
||||
<div class="License-field">
|
||||
<div class="License-field--label">Subscription</div>
|
||||
<div class="License-field--content">
|
||||
{{license.license_info.subscription_name}}
|
||||
</div>
|
||||
</div>
|
||||
<div class="License-field">
|
||||
<div class="License-field--label">License Key</div>
|
||||
<div class="License-field--content">
|
||||
{{license.license_info.license_key}}
|
||||
</div>
|
||||
</div>
|
||||
<div class="License-field">
|
||||
<div class="License-field--label">Expires On</div>
|
||||
<div class="License-field--content">
|
||||
{{time.expiresOn}}
|
||||
</div>
|
||||
</div>
|
||||
<div class="License-field">
|
||||
<div class="License-field--label">Time Remaining</div>
|
||||
<div class="License-field--content">
|
||||
{{time.remaining}} Day
|
||||
</div>
|
||||
</div>
|
||||
<div class="License-field">
|
||||
<div class="License-field--label">Hosts Available</div>
|
||||
<div class="License-field--content">
|
||||
{{license.license_info.available_instances}}
|
||||
</div>
|
||||
</div>
|
||||
<div class="License-field">
|
||||
<div class="License-field--label">Hosts Used</div>
|
||||
<div class="License-field--content">
|
||||
{{license.license_info.current_instances}}
|
||||
</div>
|
||||
</div>
|
||||
<div class="License-field License-greenText">
|
||||
<div class="License-field--label">Hosts Remaining</div>
|
||||
<div class="License-field--content">
|
||||
{{license.license_info.free_instances}}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<p>If you are ready to upgrade, please contact us by clicking the button below</p>
|
||||
<a href="https://www.ansible.com/renew" target="_blank"><button class="btn btn-default">Upgrade</button></a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="License-management">
|
||||
<div class="Panel">
|
||||
<div class="License-titleText">License Management</div>
|
||||
<p>Choose your license file, agree to the End User License Agreement, and click submit.</p>
|
||||
<form id="License-form" name="license">
|
||||
<div class="input-group License-file--container">
|
||||
<span class="btn btn-default input-group-addon" ng-click="fakeClick()">Browse...</span>
|
||||
<input class="form-control" ng-disabled="true" placeholder="{{fileName}}" />
|
||||
<input id="License-file" class="form-control" type="file" file-on-change="getKey"/>
|
||||
</div>
|
||||
<div class="License-titleText prepend-asterisk"> End User License Agreement</div>
|
||||
<div class="form-group License-eula">
|
||||
<textarea class="form-control">{{license.eula}}
|
||||
</textarea>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<div class="checkbox">
|
||||
<div class="License-details--label"><input type="checkbox" ng-model="newLicense.eula" required> I agree to the End User License Agreement</div>
|
||||
<div class="License-submit--container pull-right">
|
||||
<span ng-hide="success == null || false" class="License-greenText License-submit--success pull-left">Save successful!</span>
|
||||
<button ng-click="submit()" class="btn btn-success pull-right" ng-disabled="newLicense.file.license_key == null || newLicense.eula == null">Submit</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
19
awx/ui/client/src/license/license.route.js
Normal file
19
awx/ui/client/src/license/license.route.js
Normal file
@ -0,0 +1,19 @@
|
||||
/*************************************************
|
||||
* Copyright (c) 2016 Ansible, Inc.
|
||||
*
|
||||
* All Rights Reserved
|
||||
*************************************************/
|
||||
|
||||
import {templateUrl} from '../shared/template-url/template-url.factory';
|
||||
|
||||
export default {
|
||||
name: 'license',
|
||||
route: '/license',
|
||||
templateUrl: templateUrl('license/license'),
|
||||
controller: 'licenseController',
|
||||
data: {},
|
||||
ncyBreadcrumb: {
|
||||
parent: 'setup',
|
||||
label: 'LICENSE'
|
||||
}
|
||||
}
|
||||
19
awx/ui/client/src/license/main.js
Normal file
19
awx/ui/client/src/license/main.js
Normal file
@ -0,0 +1,19 @@
|
||||
/*************************************************
|
||||
* Copyright (c) 2016 Ansible, Inc.
|
||||
*
|
||||
* All Rights Reserved
|
||||
*************************************************/
|
||||
|
||||
import route from './license.route';
|
||||
import controller from './license.controller';
|
||||
import CheckLicense from './checkLicense.factory';
|
||||
import fileOnChange from './fileOnChange.directive';
|
||||
|
||||
export default
|
||||
angular.module('license', [])
|
||||
.controller('licenseController', controller)
|
||||
.directive('fileOnChange', fileOnChange)
|
||||
.factory('CheckLicense', CheckLicense)
|
||||
.run(['$stateExtender', function($stateExtender) {
|
||||
$stateExtender.addState(route);
|
||||
}]);
|
||||
@ -1,4 +0,0 @@
|
||||
<div class="tab-pane" id="license-partial">
|
||||
<div ng-cloak id="htmlTemplate" class="Panel"></div>
|
||||
<div id="license-modal-dialog"></div>
|
||||
</div>
|
||||
79
awx/ui/client/src/shared/layouts/one-plus-two.less
Normal file
79
awx/ui/client/src/shared/layouts/one-plus-two.less
Normal file
@ -0,0 +1,79 @@
|
||||
/*
|
||||
* Large resolution: 1/3 + 2/3 width panels
|
||||
* Small resolution: 100% width panels, stacked
|
||||
* Options: static height, custom breakpoint
|
||||
*
|
||||
* Style conventions
|
||||
* .ModuleName-component--subComponent
|
||||
*/
|
||||
@import "awx/ui/client/src/shared/branding/colors.default.less";
|
||||
|
||||
|
||||
.OnePlusTwo-container(@height: 100%; @breakpoint: 900px){
|
||||
height: @height;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
@media screen and (max-width: @breakpoint){
|
||||
flex-direction: column;
|
||||
}
|
||||
}
|
||||
|
||||
.OnePlusTwo-left--panel(@height: 100%; @breakpoint: 900px) {
|
||||
flex: 0 0;
|
||||
height: @height;
|
||||
width: 100%;
|
||||
.Panel{
|
||||
height: 100%;
|
||||
}
|
||||
@media screen and (min-width: @breakpoint){
|
||||
max-width: 400px;
|
||||
}
|
||||
}
|
||||
|
||||
.OnePlusTwo-right--panel(@height: 100%; @breakpoint: 900px) {
|
||||
height: @height;
|
||||
flex: 1 0;
|
||||
margin-left: 20px;
|
||||
.Panel{
|
||||
height: 100%;
|
||||
}
|
||||
@media screen and (max-width: @breakpoint){
|
||||
flex-direction: column;
|
||||
margin-left: 0px;
|
||||
margin-top: 25px;
|
||||
}
|
||||
}
|
||||
|
||||
.OnePlusTwo-panelHeader {
|
||||
color: @default-interface-txt;
|
||||
font-size: 14px;
|
||||
font-weight: bold;
|
||||
margin-right: 10px;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.OnePlusTwo-left--details {
|
||||
margin-top: 25px;
|
||||
}
|
||||
|
||||
.OnePlusTwo-left--detailsRow {
|
||||
display: flex;
|
||||
:not(:last-child){
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
}
|
||||
|
||||
.OnePlusTwo-left--detailsLabel {
|
||||
width: 140px;
|
||||
display: inline-block;
|
||||
color: @default-interface-txt;
|
||||
text-transform: uppercase;
|
||||
font-weight: 400;
|
||||
}
|
||||
|
||||
.OnePlusTwo-left--detailsContent {
|
||||
display: inline-block;
|
||||
max-width: 220px;
|
||||
word-wrap: break-word;
|
||||
}
|
||||
|
||||
@ -41,7 +41,7 @@
|
||||
<main-menu></main-menu>
|
||||
<bread-crumb></bread-crumb>
|
||||
|
||||
<div class="container-fluid" id="#content-container">
|
||||
<div class="container-fluid" id="content-container">
|
||||
<div class="row">
|
||||
<div class="col-lg-12">
|
||||
<div ui-view id="main-view"></div>
|
||||
@ -221,8 +221,7 @@
|
||||
|
||||
<div class="overlay"></div>
|
||||
<div class="spinny"><i class="fa fa-cog fa-spin fa-2x"></i> <p>working...</p></div>
|
||||
|
||||
<!-- <div class="site-footer"></div> -->
|
||||
</div>
|
||||
<tower-footer></tower-footer>
|
||||
<script>
|
||||
// HACK: Need this to support global-dependent
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user