mirror of
https://github.com/ansible/awx.git
synced 2026-03-13 23:17:32 -02:30
License
Changed license nagging to a modal dialog with license status aware text and a form for updating the license key. Form provides JSON validation via CodeMirror.
This commit is contained in:
@@ -511,7 +511,7 @@ angular.module('Tower', [
|
||||
if ($rootScope.current_user === undefined || $rootScope.current_user === null) {
|
||||
Authorization.restoreUserInfo(); //user must have hit browser refresh
|
||||
}
|
||||
CheckLicense();
|
||||
CheckLicense.test();
|
||||
}
|
||||
|
||||
activateTab();
|
||||
|
||||
37
awx/ui/static/js/forms/LicenseUpdate.js
Normal file
37
awx/ui/static/js/forms/LicenseUpdate.js
Normal file
@@ -0,0 +1,37 @@
|
||||
/*********************************************
|
||||
* Copyright (c) 2014 AnsibleWorks, Inc.
|
||||
*
|
||||
* License.js
|
||||
* Form definition for Organization model
|
||||
*
|
||||
*
|
||||
*/
|
||||
angular.module('LicenseUpdateFormDefinition', [])
|
||||
.value('LicenseUpdateForm', {
|
||||
|
||||
name: 'license',
|
||||
well: false,
|
||||
|
||||
fields: {
|
||||
license_json: {
|
||||
label: 'License Key:',
|
||||
type: 'textarea',
|
||||
addRequired: true,
|
||||
editRequird: true,
|
||||
rows: 10,
|
||||
'default': '---'
|
||||
}
|
||||
},
|
||||
|
||||
buttons: {
|
||||
form_submit: {
|
||||
label: "Submit",
|
||||
"class": "pull-right btn-primary",
|
||||
ngClick: "submitLicenseKey()",
|
||||
ngDisabled: true
|
||||
}
|
||||
},
|
||||
|
||||
related: { }
|
||||
|
||||
}); //LicenseUpdateForm
|
||||
@@ -9,7 +9,8 @@
|
||||
|
||||
'use strict';
|
||||
|
||||
angular.module('AccessHelper', ['RestServices', 'Utilities', 'ngCookies'])
|
||||
angular.module('AccessHelper', ['RestServices', 'Utilities', 'ngCookies', 'LicenseUpdateFormDefinition', 'FormGenerator', 'ParseHelper', 'ModalDialog', 'VariablesHelper'])
|
||||
|
||||
.factory('CheckAccess', ['$rootScope', 'Alert', 'Rest', 'GetBasePath', 'ProcessErrors',
|
||||
function ($rootScope, Alert, Rest, GetBasePath, ProcessErrors) {
|
||||
return function (params) {
|
||||
@@ -48,6 +49,199 @@ angular.module('AccessHelper', ['RestServices', 'Utilities', 'ngCookies'])
|
||||
}
|
||||
])
|
||||
|
||||
.factory('CheckLicense', ['$rootScope', '$compile', 'CreateDialog', 'Store', 'LicenseUpdateForm', 'GenerateForm', 'TextareaResize', 'ToJSON', 'GetBasePath', 'Rest', 'ProcessErrors', 'Alert',
|
||||
function($rootScope, $compile, CreateDialog, Store, LicenseUpdateForm, GenerateForm, TextareaResize, ToJSON, GetBasePath, Rest, ProcessErrors, Alert) {
|
||||
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;
|
||||
}
|
||||
},
|
||||
|
||||
getHTML: function(license) {
|
||||
var title, html, result = {};
|
||||
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. Please visit " +
|
||||
"<a href=\"http://ansible.com/license\" target=\"_blank\">http://ansible.com/license</a> to obtain a valid license key. " +
|
||||
"Copy and paste the key in the field below and click the Submit button.</p></div>";
|
||||
}
|
||||
else if (this.getRemainingDays(license.time_remaining) <= 0) {
|
||||
if (parseInt(license.grace_period_remaining,10) > 86400) {
|
||||
title = "License Expired";
|
||||
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 expired. You will no longer be able to run playbooks after " + this.getRemainingDays(license.grace_period_remaining) + " days</p>" +
|
||||
"<p>Please visit <a href=\"http://ansible.com/license\" target=\"_blank\">ansible.com/license</a> to purchse a valid license. " +
|
||||
"Copy and paste the new license key in the field below and click the Submit button.</p></div>";
|
||||
} else {
|
||||
title = "License Expired";
|
||||
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 expired, and the 30 day grace period has been exceeded. To continue using Tower to run playbooks and adding managed hosts a " +
|
||||
"valid license key is required.</p><p>Please visit <a href=\"ansible.com/license\" target=\"_blank\">http://ansible.com/license</a> to " +
|
||||
"purchse a license. Copy and paste the new license key in the field below and click the Submit button.</p>";
|
||||
}
|
||||
}
|
||||
else if (this.getRemainingDays(license.time_remaining) < 15) {
|
||||
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) + " remaining.</p>" +
|
||||
"<p>Extend your Ansible Tower license by visiting <a href=\"ansible.com/license\" target=\"_blank\">http://ansible.com/license</a>. " +
|
||||
"Copy and paste the new license key in the field below and click the Submit button.</p></div>";
|
||||
}
|
||||
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 additional hosts can be added.</p><p>To extend the Ansible Tower license please visit " +
|
||||
"<a href=\"http://ansible.com/license\" target=\"_blank\">ansible.com/license</a>. " +
|
||||
"Copy and paste the new license key in the field below and click the Submit button.</p>";
|
||||
}
|
||||
} else {
|
||||
// No license
|
||||
title = "License Required";
|
||||
html = "<div id=\"license-notification-body\"><div style=\"margin-top:5px; margin-bottom:25px;\"><p>Thank you for trying Ansible Tower. A <strong>FREE</strong> trial license is available for various infrastructure sizes, as well as free unlimited use for up to ten nodes.<p>" +
|
||||
"<p>Visit <a href=\"http://ansible.com/license\" target=\"_blank\">ansible.com/license</a> to obtain a free license key. Copy and paste the key in the field below and " +
|
||||
"click the Submit button.</p></div>";
|
||||
}
|
||||
html += GenerateForm.buildHTML(LicenseUpdateForm, { mode: 'edit', showButtons: true, breadCrumbs: false });
|
||||
html += "</div>";
|
||||
result.body = html;
|
||||
result.title = title;
|
||||
return result;
|
||||
},
|
||||
|
||||
test: function() {
|
||||
var license = Store('license'),
|
||||
notify = this.shouldNotify(license),
|
||||
html, buttons, 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
|
||||
}
|
||||
|
||||
if (!notify) {
|
||||
return true;
|
||||
}
|
||||
|
||||
scope = $rootScope.$new();
|
||||
html = this.getHTML(license);
|
||||
$('#license-modal-dialog').html(html.body);
|
||||
|
||||
scope.flashMessage = null;
|
||||
scope.parseType = 'json';
|
||||
scope.license_json = " ";
|
||||
|
||||
scope.removeLicenseDialogReady = scope.$on('LicenseDialogReady', function() {
|
||||
var e = angular.element(document.getElementById('license-modal-dialog'));
|
||||
$compile(e)(scope);
|
||||
$('#license-modal-dialog').dialog('open');
|
||||
});
|
||||
|
||||
scope.submitLicenseKey = function() {
|
||||
var url = GetBasePath('config'),
|
||||
json_data = ToJSON(scope.parseType, scope.license_json);
|
||||
if (typeof json_data === 'object' && Object.keys(json_data).length > 0) {
|
||||
Rest.setUrl(url);
|
||||
Rest.post(json_data)
|
||||
.success(function () {
|
||||
$('#license-modal-dialog').dialog('close');
|
||||
Alert('License Accepted', 'The Ansible Tower license was updated. To view or update license information in the future choose View License from the Account menu.','alert-info');
|
||||
})
|
||||
.error(function (data, status) {
|
||||
scope.license_json_api_error = "A valid license key in JSON format is required";
|
||||
ProcessErrors(scope, data, status, null, { hdr: 'Error!',
|
||||
msg: 'Failed to update license. POST returned: ' + status
|
||||
});
|
||||
});
|
||||
} else {
|
||||
scope.license_json_api_error = "A valid license key in JSON format is required";
|
||||
}
|
||||
};
|
||||
|
||||
buttons = [{
|
||||
label: "Cancel",
|
||||
onClick: function() {
|
||||
$('#license-modal-dialog').dialog('close');
|
||||
},
|
||||
"class": "btn btn-default",
|
||||
"id": "license-cancel-button"
|
||||
}];
|
||||
|
||||
CreateDialog({
|
||||
scope: scope,
|
||||
buttons: buttons,
|
||||
width: 700,
|
||||
height: 625,
|
||||
minWidth: 400,
|
||||
title: html.title,
|
||||
id: 'license-modal-dialog',
|
||||
clonseOnEscape: false,
|
||||
onClose: function() {
|
||||
if (scope.codeMirror) {
|
||||
scope.codeMirror.destroy();
|
||||
}
|
||||
$('#license-modal-dialog').empty();
|
||||
},
|
||||
onResizeStop: function() {
|
||||
TextareaResize({
|
||||
scope: scope,
|
||||
textareaId: 'license_license_json',
|
||||
modalId: 'license-modal-dialog',
|
||||
formId: 'license-notification-body',
|
||||
fld: 'license_json',
|
||||
bottom_margin: 30,
|
||||
parse: true,
|
||||
onChange: function() { scope.license_json_api_error = ''; }
|
||||
});
|
||||
},
|
||||
onOpen: function() {
|
||||
setTimeout(function() {
|
||||
TextareaResize({
|
||||
scope: scope,
|
||||
textareaId: 'license_license_json',
|
||||
modalId: 'license-modal-dialog',
|
||||
formId: 'license-notification-body',
|
||||
fld: 'license_json',
|
||||
bottom_margin: 30,
|
||||
parse: true,
|
||||
onChange: function() { scope.license_json_api_error = ''; }
|
||||
});
|
||||
$('#cm-license_json-container .CodeMirror textarea').focus();
|
||||
}, 300);
|
||||
},
|
||||
callback: 'LicenseDialogReady'
|
||||
});
|
||||
}
|
||||
};
|
||||
}]);
|
||||
|
||||
/*
|
||||
.factory('CheckLicense', ['$rootScope', 'Store', 'Alert', '$location', 'Authorization',
|
||||
function ($rootScope, Store, Alert, $location, Authorization) {
|
||||
return function () {
|
||||
@@ -103,4 +297,7 @@ angular.module('AccessHelper', ['RestServices', 'Utilities', 'ngCookies'])
|
||||
}
|
||||
};
|
||||
}
|
||||
]);
|
||||
]);
|
||||
*/
|
||||
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
angular.module('ParseHelper', ['Utilities', 'AngularCodeMirrorModule'])
|
||||
.factory('ParseTypeChange', ['Alert', 'AngularCodeMirror', function (Alert, AngularCodeMirror) {
|
||||
return function (params) {
|
||||
|
||||
|
||||
var scope = params.scope,
|
||||
field_id = params.field_id,
|
||||
fld = (params.variable) ? params.variable : 'variables',
|
||||
|
||||
@@ -8,11 +8,11 @@
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
'use strict';
|
||||
|
||||
angular.module('ModalDialog', ['Utilities', 'ParseHelper'])
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
* CreateDialog({
|
||||
@@ -30,7 +30,7 @@ angular.module('ModalDialog', ['Utilities', 'ParseHelper'])
|
||||
* callback: - String to pass to scope.$emit() after dialog is created, optional
|
||||
* })
|
||||
*
|
||||
* Note that the dialog will be created but not opened. It's up to the caller to open it. Use callback
|
||||
* Note that the dialog will be created but not opened. It's up to the caller to open it. Use callback
|
||||
* option to respond to dialog created event.
|
||||
*/
|
||||
.factory('CreateDialog', ['Empty', function(Empty) {
|
||||
@@ -65,12 +65,12 @@ angular.module('ModalDialog', ['Utilities', 'ParseHelper'])
|
||||
"id": "dialog-ok-button"
|
||||
}];
|
||||
}
|
||||
|
||||
|
||||
buttons = {};
|
||||
buttonSet.forEach( function(btn) {
|
||||
buttons[btn.label] = btn.onClick;
|
||||
});
|
||||
|
||||
|
||||
// Set modal dimensions based on viewport width
|
||||
ww = $(document).width();
|
||||
wh = $('body').height();
|
||||
@@ -90,7 +90,7 @@ angular.module('ModalDialog', ['Utilities', 'ParseHelper'])
|
||||
create: function () {
|
||||
// Fix the close button
|
||||
$('.ui-dialog[aria-describedby="' + id + '"]').find('.ui-dialog-titlebar button').empty().attr({'class': 'close'}).text('x');
|
||||
|
||||
|
||||
setTimeout(function() {
|
||||
// Make buttons bootstrapy
|
||||
$('.ui-dialog[aria-describedby="' + id + '"]').find('.ui-dialog-buttonset button').each(function () {
|
||||
@@ -161,26 +161,32 @@ angular.module('ModalDialog', ['Utilities', 'ParseHelper'])
|
||||
}])
|
||||
|
||||
/**
|
||||
* TextareaResize({
|
||||
* TextareaResize({
|
||||
* scope: - $scope associated with the textarea element
|
||||
* textareaId: - id attribute value of the textarea
|
||||
* modalId: - id attribute of the <div> element used to create the modal
|
||||
* formId: - id attribute of the textarea's parent form
|
||||
* parse: - if true, call ParseTypeChange and replace textarea with codemirror editor
|
||||
* fld: - optional, form field name
|
||||
* bottom_margin: - optional, integer value for additional margin to leave below the textarea
|
||||
* onChange; - optional, function to call when the textarea value changes
|
||||
* })
|
||||
*
|
||||
* Use to resize a textarea field contained on a modal. Has only been tested where the
|
||||
* Use to resize a textarea field contained on a modal. Has only been tested where the
|
||||
* form contains 1 textarea and the the textarea is at the bottom of the form/modal.
|
||||
*
|
||||
**/
|
||||
.factory('TextareaResize', ['ParseTypeChange', 'Wait', function(ParseTypeChange, Wait){
|
||||
return function(params) {
|
||||
|
||||
|
||||
var scope = params.scope,
|
||||
textareaId = params.textareaId,
|
||||
modalId = params.modalId,
|
||||
formId = params.formId,
|
||||
fld = params.fld,
|
||||
parse = (params.parse === undefined) ? true : params.parse,
|
||||
bottom_margin = (params.bottom_margin) ? params.bottom_margin : 0,
|
||||
onChange = params.onChange,
|
||||
textarea,
|
||||
formHeight, model, windowHeight, offset, rows;
|
||||
|
||||
@@ -188,7 +194,7 @@ angular.module('ModalDialog', ['Utilities', 'ParseHelper'])
|
||||
Wait('stop');
|
||||
}
|
||||
|
||||
// Attempt to create the largest textarea field that will fit on the window. Minimum
|
||||
// Attempt to create the largest textarea field that will fit on the window. Minimum
|
||||
// height is 6 rows, so on short windows you will see vertical scrolling
|
||||
textarea = $('#' + textareaId);
|
||||
if (scope.codeMirror) {
|
||||
@@ -199,16 +205,16 @@ angular.module('ModalDialog', ['Utilities', 'ParseHelper'])
|
||||
textarea.attr('rows', 1);
|
||||
formHeight = $('#' + formId).height();
|
||||
windowHeight = $('#' + modalId).height() - 20; //leave a margin of 20px
|
||||
offset = Math.floor(windowHeight - formHeight);
|
||||
offset = Math.floor(windowHeight - formHeight - bottom_margin);
|
||||
rows = Math.floor(offset / 20);
|
||||
rows = (rows < 6) ? 6 : rows;
|
||||
textarea.attr('rows', rows);
|
||||
while(rows > 6 && $('#' + formId).height() > $('#' + modalId).height()) {
|
||||
while(rows > 6 && ($('#' + formId).height() > $('#' + modalId).height() + bottom_margin)) {
|
||||
rows--;
|
||||
textarea.attr('rows', rows);
|
||||
}
|
||||
if (parse) {
|
||||
ParseTypeChange({ scope: scope, field_id: textareaId, onReady: waitStop });
|
||||
ParseTypeChange({ scope: scope, field_id: textareaId, onReady: waitStop, variable: fld, onChange: onChange });
|
||||
}
|
||||
};
|
||||
}]);
|
||||
@@ -51,7 +51,7 @@ angular.module('GeneratorHelpers', [])
|
||||
result = "ng-show=\"" + value + "\" ";
|
||||
break;
|
||||
case 'icon':
|
||||
// new method of constructing <i> icon tag. Replces Icon method.
|
||||
// new method of constructing <i> icon tag. Replaces Icon method.
|
||||
result = "<i class=\"fa fa-" + value;
|
||||
result += (obj.iconSize) ? " " + obj.iconSize : "";
|
||||
result += "\"></i>";
|
||||
@@ -127,6 +127,7 @@ angular.module('GeneratorHelpers', [])
|
||||
icon = 'fa-arrow-left';
|
||||
break;
|
||||
case 'save':
|
||||
case 'form_submit':
|
||||
icon = 'fa-check-square-o';
|
||||
break;
|
||||
case 'properties':
|
||||
|
||||
@@ -100,6 +100,7 @@
|
||||
<script src="{{ STATIC_URL }}js/forms/JobSummary.js"></script>
|
||||
<script src="{{ STATIC_URL }}js/forms/JobVarsPrompt.js"></script>
|
||||
<script src="{{ STATIC_URL }}js/forms/LicenseForm.js"></script>
|
||||
<script src="{{ STATIC_URL }}js/forms/LicenseUpdate.js"></script>
|
||||
<script src="{{ STATIC_URL }}js/forms/Source.js"></script>
|
||||
<script src="{{ STATIC_URL }}js/forms/LogViewerStatus.js"></script>
|
||||
<script src="{{ STATIC_URL }}js/forms/LogViewerOptions.js"></script>
|
||||
@@ -393,6 +394,7 @@
|
||||
</div><!-- modal -->
|
||||
|
||||
<div id="help-modal-dialog" style="display: none;"></div>
|
||||
<div id="license-modal-dialog" style="display: none;"></div>
|
||||
|
||||
</div><!-- container -->
|
||||
|
||||
|
||||
Reference in New Issue
Block a user