From 91df59611d9fba877de52655a29346fc17c4deb5 Mon Sep 17 00:00:00 2001 From: Chris Houseknecht Date: Tue, 5 Aug 2014 17:13:12 -0400 Subject: [PATCH] License 30 day trial modifiations. Refactored Access helper and created a new License helper. Removed ansible/License module. Created a new license viewer that allows admin user to update the license key. Nag message at login now differentiates between admin and non-admin user. --- awx/ui/static/js/app.js | 18 +- awx/ui/static/js/forms/LicenseForm.js | 11 +- awx/ui/static/js/helpers/Access.js | 313 ++------------ awx/ui/static/js/helpers/License.js | 566 ++++++++++++++++++++++++++ awx/ui/static/js/helpers/Variables.js | 14 +- awx/ui/static/less/ansible-ui.less | 10 +- awx/ui/static/lib/ansible/license.js | 126 ------ awx/ui/templates/ui/index.html | 2 +- 8 files changed, 633 insertions(+), 427 deletions(-) create mode 100644 awx/ui/static/js/helpers/License.js delete mode 100644 awx/ui/static/lib/ansible/license.js diff --git a/awx/ui/static/js/app.js b/awx/ui/static/js/app.js index 3957621aca..798fd7605a 100644 --- a/awx/ui/static/js/app.js +++ b/awx/ui/static/js/app.js @@ -25,6 +25,7 @@ angular.module('Tower', [ 'RestServices', 'AuthService', 'Utilities', + 'LicenseHelper', 'OrganizationFormDefinition', 'UserFormDefinition', 'FormGenerator', @@ -79,7 +80,6 @@ angular.module('Tower', [ 'md5Helper', 'AccessHelper', 'SelectionHelper', - 'License', 'HostGroupsFormDefinition', 'DashboardCountsWidget', 'JobStatusGraphWidget', @@ -420,10 +420,10 @@ angular.module('Tower', [ }]); }]) - .run(['$compile', '$cookieStore', '$rootScope', '$log', 'CheckLicense', '$location', 'Authorization', 'LoadBasePaths', 'ViewLicense', - 'Timer', 'ClearScope', 'HideStream', 'Socket', 'LoadConfig', 'Store', 'ShowSocketHelp', - function ($compile, $cookieStore, $rootScope, $log, CheckLicense, $location, Authorization, LoadBasePaths, ViewLicense, - Timer, ClearScope, HideStream, Socket, LoadConfig, Store, ShowSocketHelp) { + .run(['$compile', '$cookieStore', '$rootScope', '$log', 'CheckLicense', '$location', 'Authorization', 'LoadBasePaths', 'Timer', 'ClearScope', 'HideStream', 'Socket', + 'LoadConfig', 'Store', 'ShowSocketHelp', 'LicenseViewer', + function ($compile, $cookieStore, $rootScope, $log, CheckLicense, $location, Authorization, LoadBasePaths, Timer, ClearScope, HideStream, Socket, + LoadConfig, Store, ShowSocketHelp, LicenseViewer) { var e, html, sock, checkCount = 0; @@ -522,9 +522,11 @@ angular.module('Tower', [ if ($rootScope.current_user === undefined || $rootScope.current_user === null) { Authorization.restoreUserInfo(); //user must have hit browser refresh } - CheckLicense.test(); + if (!/^\/(login|logout)/.test(next.$$route.originalPath)) { + // if not headed to /login or /logout, then check the license + CheckLicense.test(); + } } - activateTab(); }); @@ -545,7 +547,7 @@ angular.module('Tower', [ }; $rootScope.viewLicense = function () { - ViewLicense(); + LicenseViewer.showViewer(); }; $rootScope.toggleTab = function(e, tab, tabs) { e.preventDefault(); diff --git a/awx/ui/static/js/forms/LicenseForm.js b/awx/ui/static/js/forms/LicenseForm.js index f13cefda11..fcea0fa4ee 100644 --- a/awx/ui/static/js/forms/LicenseForm.js +++ b/awx/ui/static/js/forms/LicenseForm.js @@ -25,13 +25,18 @@ angular.module('LicenseFormDefinition', []) fields: { license_status: { type: 'custom', - control: "
" + - "{{ license_status }}
", + control: "
{{ license_status }}
", + readonly: true, + tab: 'license' + }, + tower_version: { + label: 'Tower Version', + type: 'text', readonly: true, tab: 'license' }, license_key: { - label: 'Key', + label: 'License Key', type: 'textarea', 'class': 'modal-input-xlarge', readonly: true, diff --git a/awx/ui/static/js/helpers/Access.js b/awx/ui/static/js/helpers/Access.js index a6f20107dc..6e7aed58a0 100644 --- a/awx/ui/static/js/helpers/Access.js +++ b/awx/ui/static/js/helpers/Access.js @@ -3,301 +3,50 @@ * * helpers/Access.js * - * Routines for checking user access and license state + * Routines for checking user access * */ 'use strict'; -angular.module('AccessHelper', ['RestServices', 'Utilities', 'ngCookies', 'LicenseUpdateFormDefinition', 'FormGenerator', 'ParseHelper', 'ModalDialog', 'VariablesHelper']) +angular.module('AccessHelper', ['RestServices', 'Utilities']) - .factory('CheckAccess', ['$rootScope', 'Alert', 'Rest', 'GetBasePath', 'ProcessErrors', - function ($rootScope, Alert, Rest, GetBasePath, ProcessErrors) { - return function (params) { - // set PermissionAddAllowed to true or false based on user access. admins and org admins are granted - // accesss. - var me = $rootScope.current_user, - scope = params.scope; + .factory('CheckAccess', ['$rootScope', 'Alert', 'Rest', 'GetBasePath', 'ProcessErrors', function ($rootScope, Alert, Rest, GetBasePath, ProcessErrors) { + return function (params) { + // set PermissionAddAllowed to true or false based on user access. admins and org admins are granted + // accesss. + var me = $rootScope.current_user, + scope = params.scope; - if (me.is_superuser) { - scope.PermissionAddAllowed = true; - } else { - if (me.related.admin_of_organizations) { - Rest.setUrl(me.related.admin_of_organizations); - Rest.get() - .success(function (data) { - if (data.results.length > 0) { - scope.PermissionAddAllowed = true; - } else { - scope.PermissionAddAllowed = false; - } - }) - .error(function (data, status) { - ProcessErrors(scope, data, status, null, { - hdr: 'Error!', - msg: 'Call to ' + me.related.admin_of_organizations + - ' failed. DELETE returned status: ' + status - }); - }); - } - } - //if (!access) { - // Alert('Access Denied', 'You do not have access to this function. Please contact your system administrator.'); - //} - //return access; - }; - } - ]) - -.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; + if (me.is_superuser) { + scope.PermissionAddAllowed = true; } 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 = "

The Ansible Tower license is invalid. Please visit " + - "http://ansible.com/license to obtain a valid license key. " + - "Copy and paste the key in the field below and click the Submit button.

"; - } - else if (this.getRemainingDays(license.time_remaining) <= 0) { - if (parseInt(license.grace_period_remaining,10) > 86400) { - title = "License Expired"; - html = "

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

" + - "

Please visit ansible.com/license to purchse a valid license. " + - "Copy and paste the new license key in the field below and click the Submit button.

"; - } else { - title = "License Expired"; - html = "

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.

Please visit http://ansible.com/license to " + - "purchse a license. Copy and paste the new license key in the field below and click the Submit button.

"; - } - } - else if (this.getRemainingDays(license.time_remaining) < 15) { - html = "

Thank you for using Ansible Tower. The Ansible Tower license " + - "has " + this.getRemainingDays(license.time_remaining) + " remaining.

" + - "

Extend your Ansible Tower license by visiting http://ansible.com/license. " + - "Copy and paste the new license key in the field below and click the Submit button.

"; - } - else if (license.free_instances <= 0) { - title = "Host Count Exceeded"; - html = "

The Ansible Tower license has reached capacity for the number of " + - "managed hosts allowed. No additional hosts can be added.

To extend the Ansible Tower license please visit " + - "ansible.com/license. " + - "Copy and paste the new license key in the field below and click the Submit button.

"; - } - } else { - // No license - title = "License Required"; - html = "

Thank you for trying Ansible Tower. A FREE trial license is available for various infrastructure sizes, as well as free unlimited use for up to ten nodes.

" + - "

Visit ansible.com/license to obtain a free license key. Copy and paste the key in the field below and " + - "click the Submit button.

"; - } - html += GenerateForm.buildHTML(LicenseUpdateForm, { mode: 'edit', showButtons: true, breadCrumbs: false }); - html += "
"; - 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'); + if (me.related.admin_of_organizations) { + Rest.setUrl(me.related.admin_of_organizations); + Rest.get() + .success(function (data) { + if (data.results.length > 0) { + scope.PermissionAddAllowed = true; + } else { + scope.PermissionAddAllowed = false; + } }) .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 + ProcessErrors(scope, data, status, null, { + hdr: 'Error!', + msg: 'Call to ' + me.related.admin_of_organizations + + ' failed. DELETE returned status: ' + 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 () { - // Check license status and alert the user, if needed - var status = 'success', - hdr, msg, - license = Store('license'), - purchase_msg = '

To purchase a license or extend an existing license ' + - 'visit the Ansible online store, ' + - 'or visit support.ansible.com for assistance.

'; - - if (license && !Authorization.licenseTested()) { - // This is our first time evaluating the license - license.tested = true; - Store('license',license); //update with tested flag - $rootScope.license_tested = true; - $rootScope.version = license.version; - if (license.valid_key !== undefined && license.valid_key === false) { - // The license is invalid. Stop the user from logging in. - status = 'alert-danger'; - hdr = 'License Error'; - msg = '

There is a problem with the /etc/awx/license file on your Tower server. Check to make sure Tower can access ' + - 'the file.

' + purchase_msg; - Alert(hdr, msg, status, null, false, true); - } else if (license.demo !== undefined && license.demo === true) { - // demo - status = 'alert-info'; - hdr = 'Tower Demo'; - msg = '

Thank you for trying Ansible Tower. You can use this edition to manage up to 10 hosts free.

' + - purchase_msg; - Alert(hdr, msg, status); - } - if (license.date_expired !== undefined && license.date_expired === true) { - // expired - status = 'alert-info'; - hdr = 'License Expired'; - msg = '

Your Ansible Tower License has expired and is no longer compliant. You can continue, but you will be ' + - 'unable to add any additional hosts.

' + purchase_msg; - Alert(hdr, msg, status); - } else if (license.date_warning !== undefined && license.date_warning === true) { - status = 'alert-info'; - hdr = 'License Warning'; - msg = '

Your Ansible Tower license is about to expire!

' + purchase_msg; - Alert(hdr, msg, status); - } - if (license.free_instances !== undefined && parseInt(license.free_instances) <= 0) { - status = 'alert-info'; - hdr = 'License Warning'; - msg = '

Your Ansible Tower license has reached capacity for the number of managed ' + - 'hosts allowed. You will not be able to add any additional hosts.

' + purchase_msg; - Alert(hdr, msg, status, null, true); } } + //if (!access) { + // Alert('Access Denied', 'You do not have access to this function. Please contact your system administrator.'); + //} + //return access; }; - } -]); -*/ - + }]) + .factory('IsAdmin', ['$rootScope', function($rootScope) { + return function() { return ($rootScope.current_user && $rootScope.current_user.is_superuser); }; + }]); diff --git a/awx/ui/static/js/helpers/License.js b/awx/ui/static/js/helpers/License.js new file mode 100644 index 0000000000..baf98feed5 --- /dev/null +++ b/awx/ui/static/js/helpers/License.js @@ -0,0 +1,566 @@ + +/****************************************************** + * Copyright (c) 2014 AnsibleWorks, Inc. + * + * helpers/License.js + * + * Routines for checking and reporting license status + * + */ + +'use strict'; + +angular.module('LicenseHelper', ['RestServices', 'Utilities', 'LicenseUpdateFormDefinition', 'FormGenerator', 'ParseHelper', 'ModalDialog', 'VariablesHelper', 'LicenseFormDefinition', 'AccessHelper']) + + +.factory('CheckLicense', ['$rootScope', '$compile', 'CreateDialog', 'Store', 'LicenseUpdateForm', 'GenerateForm', 'TextareaResize', 'ToJSON', 'GetBasePath', 'Rest', 'ProcessErrors', 'Alert', 'IsAdmin', +function($rootScope, $compile, CreateDialog, Store, LicenseUpdateForm, GenerateForm, TextareaResize, ToJSON, GetBasePath, Rest, ProcessErrors, Alert, IsAdmin) { + 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, 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 = "

The Ansible Tower license is invalid. Please visit " + + "ansible.com/license to obtain a valid license key."; + } + else if (this.getRemainingDays(license.time_remaining) <= 0) { + if (parseInt(license.grace_period_remaining,10) > 86400) { + title = "License Expired"; + html = "

Thank you for using Ansible Tower. The Ansible Tower license " + + "has expired. You will no longer be able to add managed hosts or run playbooks after " + this.getRemainingDays(license.grace_period_remaining) + " days

" + + "

Please visit ansible.com/license to purchse a valid license."; + } else { + title = "License Expired"; + html = "

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 add managed hosts a " + + "valid license key is required.

Please visit ansible.com/license to purchse a license."; + } + } + else if (this.getRemainingDays(license.time_remaining) < 15) { + title = "License Warning"; + html = "

Thank you for using Ansible Tower. The Ansible Tower license " + + "has " + this.getRemainingDays(license.time_remaining) + " remaining. Once the license expires you will no longer be able to add managed hosts or run playbooks.

" + + "

Extend your Ansible Tower license by visiting ansible.com/license."; + } + else if (license.free_instances <= 0) { + title = "Host Count Exceeded"; + html = "

The Ansible Tower license has reached capacity for the number of " + + "managed hosts allowed. No additional hosts can be added.

To extend the Ansible Tower license please visit " + + "ansible.com/license."; + } else { + // license is valid. the following text is displayed in the license viewer + title = "Update License"; // not actually used + html = "

The Ansible Tower license is valid. To extend or renew the license " + + "visit ansible.com/license."; + } + } else { + // No license + title = "License Required"; + html = "

Thank you for trying Ansible Tower. Without a valid license you will not be able to add managed hosts or " + + "run playbooks. A FREE trial license is available for various infrastructure sizes, as well as free unlimited use for up to ten nodes.

" + + "

Visit ansible.com/license to obtain a license key."; + } + + if (IsAdmin()) { + html += " Copy and paste the license key in the field below and click the Submit button."; + } + html += "

"; + + if (IsAdmin()) { + html += GenerateForm.buildHTML(LicenseUpdateForm, { mode: 'edit', showButtons:((includeFormButton) ? true : false), breadCrumbs: false }); + } + html += "
"; + + 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); + if (typeof json_data === 'object' && Object.keys(json_data).length > 0) { + Rest.setUrl(url); + Rest.post(json_data) + .success(function () { + try { + $('#license-modal-dialog').dialog('close'); + } + catch(e) { + // ignore + } + 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"; + } + }, + + test: function() { + var license = Store('license'), + notify = this.shouldNotify(license), + self = this, + scope, height, html, buttons, submitKey; + + self.scope = $rootScope.$new(); + scope = self.scope; + + submitKey = this.postLicense; + + 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 (!notify) { + return true; + } + + 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() { + submitKey(scope.license_json); + }; + + if (IsAdmin()) { + buttons = [{ + label: "Cancel", + onClick: function() { + $('#license-modal-dialog').dialog('close'); + }, + "class": "btn btn-default", + "id": "license-cancel-button" + }, { + label: "Submit", + onClick: function() { + scope.submitLicenseKey(); + }, + "class": "btn btn-primary", + "id": "license-submit-button" + }]; + } else { + buttons = [{ + label: "OK", + onClick: function() { + $('#license-modal-dialog').dialog('close'); + }, + "class": "btn btn-primary", + "id": "license-ok-button" + }]; + } + + height = (IsAdmin()) ? 600 : 350; + + CreateDialog({ + scope: scope, + buttons: buttons, + width: 700, + height: height, + 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() { + if (IsAdmin()) { + TextareaResize({ + scope: scope, + textareaId: 'license_license_json', + modalId: 'license-modal-dialog', + formId: 'license-notification-body', + fld: 'license_json', + parse: true, + onChange: function() { scope.license_json_api_error = ''; } + }); + } + }, + onOpen: function() { + if (IsAdmin()) { + setTimeout(function() { + TextareaResize({ + scope: scope, + textareaId: 'license_license_json', + modalId: 'license-modal-dialog', + formId: 'license-notification-body', + fld: 'license_json', + parse: true, + onChange: function() { scope.license_json_api_error = ''; } + }); + $('#cm-license_json-container .CodeMirror textarea').focus(); + }, 300); + } else { + $('#license-ok-button').focus(); + } + }, + callback: 'LicenseDialogReady' + }); + } + }; +}]) + +.factory('LicenseViewer', ['$location', '$rootScope', '$compile', 'GenerateForm', 'Rest', 'Alert', 'GetBasePath', 'ProcessErrors', 'FormatDate', 'Prompt', 'Empty', 'LicenseForm', 'IsAdmin', 'CreateDialog', 'CheckLicense', 'TextareaResize', +function ($location, $rootScope, $compile, GenerateForm, Rest, Alert, GetBasePath, ProcessErrors, FormatDate, Prompt, Empty, LicenseForm, IsAdmin, CreateDialog, CheckLicense, TextareaResize) { + return { + + createDialog: function(html) { + var self = this, + scope = this.getScope(), + buttons; + + if (scope.removeLicenseDialogReady) { + scope.removeLicenseDialogReady(); + } + scope.removeLicenseDialogReady = scope.$on('LicenseDialogReady', function() { + var e, h; + + e = angular.element(document.getElementById('license-modal-dialog')); + e.empty().html(html); + + if (scope.license_status === 'Invalid License Key') { + $('#license_tabs li:eq(1)').hide(); + } + + scope.parseType = 'json'; + scope.license_json = " "; + h = CheckLicense.getHTML(self.getLicense(),true).body; + $('#license-modal-dialog #license_tabs').append("
  • Update License
  • "); + $('#license-modal-dialog .tab-content').append("
    "); + $('#license-modal-dialog #update_license').html(h); + + setTimeout(function() { + $compile(e)(scope); + $('#license-modal-dialog').dialog('open'); + }, 300); + }); + + scope.submitLicenseKey = function() { + CheckLicense.postLicense(scope.license_json, scope); + }; + + buttons = [{ + label: "OK", + onClick: function() { + $('#license-modal-dialog').dialog('close'); + }, + "class": "btn btn-primary", + "id": "license-ok-button" + }]; + + CreateDialog({ + scope: scope, + buttons: buttons, + width: 700, + height: 600, + minWidth: 400, + title: 'Ansible Tower License', + id: 'license-modal-dialog', + clonseOnEscape: false, + onClose: function() { + if (scope.codeMirror) { + scope.codeMirror.destroy(); + } + $('#license-modal-dialog').empty(); + }, + onResizeStop: 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 = ''; } + }); + } + }, + onOpen: function() { + 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('click', 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 = ''; } + }); + } + }); + }, + callback: 'LicenseDialogReady' + }); + }, + + 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') { + 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, breadCrumbs: false }); + return html; + }, + + loadDefaultScope: function(license_info) { + var fld, dt, days, remainder, hours, minutes, seconds, license, + self = this, + scope = this.getScope(); + + for (fld in self.form.fields) { + if (!Empty(license_info[fld])) { + scope[fld] = license_info[fld]; + } + } + + 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 = scope.time_remaining * 1000; + days = parseInt(scope.time_remaining / 86400000, 10); + remainder = scope.time_remaining - (days * 86400000); + hours = parseInt(remainder / 3600000, 10); + remainder = remainder - (hours * 3600000); + minutes = parseInt(remainder / 60000, 10); + remainder = remainder - (minutes * 60000); + seconds = parseInt(remainder / 1000, 10); + scope.time_remaining = days + ' days ' + ('0' + hours).slice(-2) + ':' + ('0' + minutes).slice(-2) + ':' + + ('0' + seconds).slice(-2); + } + + 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) { + 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'; + } + }, + + getScope: function() { + return this.scope; + }, + + setLicense: function(license_info) { + this.license = license_info; + }, + + getLicense: function() { + return this.license; + }, + + showViewer: function() { + var self = this, + scope = self.scope = $rootScope.$new(); + + if (scope.removeLicenseDataReady) { + scope.removeLicenseDataReady(); + } + scope.removeLicenseDataReady = scope.$on('LicenseDataReady', function(e, data) { + data.license_info.tower_version = data.version; + self.setLicense(data.license_info); + var html = self.getDefaultHTML(data.license_info); + self.loadDefaultScope(data.license_info); + self.createDialog(html); + }); + self.GetLicense(); + }, + + GetLicense: function(callback) { + // Retrieve license detail + var scope = this.getScope(), + url = GetBasePath('config'); + Rest.setUrl(url); + Rest.get() + .success(function (data) { + if (scope && callback) { + scope.$emit(callback, data); + } + else if (scope) { + scope.$emit('LicenseDataReady', data); + } + }) + .error(function (data, status) { + ProcessErrors($rootScope, data, status, null, { hdr: 'Error!', + msg: 'Failed to retrieve license. GET status: ' + status + }); + }); + } + }; +}]); + +/* +.factory('CheckLicense', ['$rootScope', 'Store', 'Alert', '$location', 'Authorization', + function ($rootScope, Store, Alert, $location, Authorization) { + return function () { + // Check license status and alert the user, if needed + var status = 'success', + hdr, msg, + license = Store('license'), + purchase_msg = '

    To purchase a license or extend an existing license ' + + 'visit the Ansible online store, ' + + 'or visit support.ansible.com for assistance.

    '; + + if (license && !Authorization.licenseTested()) { + // This is our first time evaluating the license + license.tested = true; + Store('license',license); //update with tested flag + $rootScope.license_tested = true; + $rootScope.version = license.version; + if (license.valid_key !== undefined && license.valid_key === false) { + // The license is invalid. Stop the user from logging in. + status = 'alert-danger'; + hdr = 'License Error'; + msg = '

    There is a problem with the /etc/awx/license file on your Tower server. Check to make sure Tower can access ' + + 'the file.

    ' + purchase_msg; + Alert(hdr, msg, status, null, false, true); + } else if (license.demo !== undefined && license.demo === true) { + // demo + status = 'alert-info'; + hdr = 'Tower Demo'; + msg = '

    Thank you for trying Ansible Tower. You can use this edition to manage up to 10 hosts free.

    ' + + purchase_msg; + Alert(hdr, msg, status); + } + if (license.date_expired !== undefined && license.date_expired === true) { + // expired + status = 'alert-info'; + hdr = 'License Expired'; + msg = '

    Your Ansible Tower License has expired and is no longer compliant. You can continue, but you will be ' + + 'unable to add any additional hosts.

    ' + purchase_msg; + Alert(hdr, msg, status); + } else if (license.date_warning !== undefined && license.date_warning === true) { + status = 'alert-info'; + hdr = 'License Warning'; + msg = '

    Your Ansible Tower license is about to expire!

    ' + purchase_msg; + Alert(hdr, msg, status); + } + if (license.free_instances !== undefined && parseInt(license.free_instances) <= 0) { + status = 'alert-info'; + hdr = 'License Warning'; + msg = '

    Your Ansible Tower license has reached capacity for the number of managed ' + + 'hosts allowed. You will not be able to add any additional hosts.

    ' + purchase_msg; + Alert(hdr, msg, status, null, true); + } + } + }; + } +]); +*/ \ No newline at end of file diff --git a/awx/ui/static/js/helpers/Variables.js b/awx/ui/static/js/helpers/Variables.js index c01d768fce..65a7f1e495 100644 --- a/awx/ui/static/js/helpers/Variables.js +++ b/awx/ui/static/js/helpers/Variables.js @@ -9,9 +9,9 @@ 'use strict'; angular.module('VariablesHelper', ['Utilities']) - + /** - variables: string containing YAML or JSON | a JSON object. + variables: string containing YAML or JSON | a JSON object. If JSON string, convert to JSON object and run through jsyaml.safeDump() to create a YAML document. If YAML, will attempt to load via jsyaml.safeLoad() and return a YAML document using jsyaml.safeDump(). In all cases @@ -73,11 +73,17 @@ angular.module('VariablesHelper', ['Utilities']) **/ .factory('ToJSON', ['$log', 'ProcessErrors', function($log, ProcessErrors) { - return function(parseType, variables, stringify) { + return function(parseType, variables, stringify, reviver) { var json_data, result; if (parseType === 'json') { try { - json_data = JSON.parse(variables); //make sure JSON parses + //parse a JSON string + if (reviver) { + json_data = JSON.parse(variables, reviver); + } + else { + json_data = JSON.parse(variables); + } } catch(e) { json_data = {}; diff --git a/awx/ui/static/less/ansible-ui.less b/awx/ui/static/less/ansible-ui.less index 05d454d377..8046e7710d 100644 --- a/awx/ui/static/less/ansible-ui.less +++ b/awx/ui/static/less/ansible-ui.less @@ -1224,9 +1224,13 @@ input[type="checkbox"].checkbox-no-label { /* End Jobs Page */ -/* License Accordion */ - #license-collapse .ui-accordion-content { - overflow: hidden; +/* license modal */ + #license-modal-dialog { + textarea:read-only, + input:read-only { + background-color: #F9F9F9; + border: 1px solid #F5F5F5; + } } /* Inventory nav links */ diff --git a/awx/ui/static/lib/ansible/license.js b/awx/ui/static/lib/ansible/license.js deleted file mode 100644 index 4c5ede7116..0000000000 --- a/awx/ui/static/lib/ansible/license.js +++ /dev/null @@ -1,126 +0,0 @@ -/********************************************* - * Copyright (c) 2014 AnsibleWorks, Inc. - * - * License.js - * - * View license info found in /api/vi/config/ - * - *****************************************/ - -'use strict'; - -angular.module('License', ['RestServices', 'Utilities', 'FormGenerator', 'PromptDialog', 'LicenseFormDefinition']) - .factory('ViewLicense', ['$location', '$rootScope', 'GenerateForm', 'Rest', 'Alert', 'GetBasePath', 'ProcessErrors', - 'FormatDate', 'Prompt', 'Empty', 'LicenseForm', - function ($location, $rootScope, GenerateForm, Rest, Alert, GetBasePath, ProcessErrors, FormatDate, Prompt, Empty, - LicenseForm) { - return function () { - - var defaultUrl = GetBasePath('config'), - generator = GenerateForm, - form = angular.copy(LicenseForm), - scope; - - // Retrieve detail record and prepopulate the form - Rest.setUrl(defaultUrl); - Rest.get() - .success(function (data) { - - var fld, dt, days, remainder, hours, minutes, seconds, license; - - for (fld in form.fields) { - if (fld !== 'time_remaining' && fld !== 'license_status') { - if (Empty(data.license_info[fld])) { - delete form.fields[fld]; - } - } - } - - if (data.license_info.is_aws || Empty(data.license_info.license_date)) { - delete form.fields.license_date; - delete form.fields.time_remaining; - } - - scope = generator.inject(form, { mode: 'edit', modal: true, related: false }); - generator.reset(); - - scope.formModalAction = function () { - $('#form-modal').modal("hide"); - }; - - scope.formModalActionLabel = 'OK'; - scope.formModalCancelShow = false; - scope.formModalInfo = 'Purchase/Extend License'; - scope.formModalHeader = "Ansible Tower v." + data.version + ""; - - // Respond to license button - scope.formModalInfoAction = function () { - Prompt({ - hdr: 'Tower Licensing', - body: "

    Ansible Tower licenses can be purchased or extended by visiting " + - "the Ansible online store. Would you like to purchase or extend your license now?

    ", - 'class': 'btn-primary', - action: function () { - window.open('http://www.ansible.com/ansible-pricing', 'storeWindow'); - } - }); - }; - - for (fld in form.fields) { - if (!Empty(data.license_info[fld])) { - scope[fld] = data.license_info[fld]; - } - } - - if (scope.license_date) { - dt = new Date(parseInt(scope.license_date, 10) * 1000); - scope.license_date = FormatDate(dt); - scope.time_remaining = scope.time_remaining * 1000; - days = parseInt(scope.time_remaining / 86400000, 10); - remainder = scope.time_remaining - (days * 86400000); - hours = parseInt(remainder / 3600000, 10); - remainder = remainder - (hours * 3600000); - minutes = parseInt(remainder / 60000, 10); - remainder = remainder - (minutes * 60000); - seconds = parseInt(remainder / 1000, 10); - scope.time_remaining = days + ' days ' + ('0' + hours).slice(-2) + ':' + ('0' + minutes).slice(-2) + ':' + - ('0' + seconds).slice(-2); - } - - if (parseInt(scope.free_instances) <= 0) { - scope.free_instances_class = 'field-failure'; - } else { - scope.free_instances_class = 'field-success'; - } - - license = data.license_info; - if (license.valid_key !== undefined && !license.valid_key) { - scope.license_status = 'Invalid'; - scope.status_color = 'license-invalid'; - } else if (license.demo !== undefined && license.demo) { - scope.license_status = 'Demo'; - scope.status_color = 'license-demo'; - } else if (license.date_expired !== undefined && license.date_expired) { - scope.license_status = 'Expired'; - scope.status_color = 'license-expired'; - } else if (license.date_warning !== undefined && license.date_warning) { - scope.license_status = 'Expiration Warning'; - 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'; - scope.status_color = 'license-valid'; - } - - }) - .error(function (data, status) { - ProcessErrors($rootScope, data, status, null, { hdr: 'Error!', - msg: 'Failed to retrieve license. GET status: ' + status - }); - }); - }; - } - ]); \ No newline at end of file diff --git a/awx/ui/templates/ui/index.html b/awx/ui/templates/ui/index.html index d2e3d144e5..bda08a056e 100644 --- a/awx/ui/templates/ui/index.html +++ b/awx/ui/templates/ui/index.html @@ -58,7 +58,6 @@ - @@ -157,6 +156,7 @@ +