From 5f4f4a2fb9cb9881133da4ecace466782dcbece0 Mon Sep 17 00:00:00 2001 From: John Mitchell Date: Mon, 5 Mar 2018 22:36:38 -0500 Subject: [PATCH 1/6] make pendo license settings opt out whenever license is added --- .../client/src/license/license.controller.js | 131 ++++++++------- .../client/src/license/license.partial.html | 13 ++ .../authenticationServices/pendo.service.js | 153 ++++++++++-------- 3 files changed, 169 insertions(+), 128 deletions(-) diff --git a/awx/ui/client/src/license/license.controller.js b/awx/ui/client/src/license/license.controller.js index c5b5fcf532..9f160dc9c6 100644 --- a/awx/ui/client/src/license/license.controller.js +++ b/awx/ui/client/src/license/license.controller.js @@ -10,34 +10,42 @@ export default ['Wait', '$state', '$scope', '$rootScope', 'ProcessErrors', 'CheckLicense', 'moment','$window', 'ConfigService', 'FeaturesService', 'pendoService', 'i18n', 'config', - function( Wait, $state, $scope, $rootScope, - ProcessErrors, CheckLicense, moment, $window, ConfigService, - FeaturesService, pendoService, i18n, config){ + function(Wait, $state, $scope, $rootScope, ProcessErrors, CheckLicense, moment, + $window, ConfigService, FeaturesService, pendoService, i18n, config) { - var calcDaysRemaining = function(seconds){ - // calculate the number of days remaining on the license - var duration = moment.duration(seconds, 'seconds').asDays(); - duration = Math.floor(duration); - if(duration < 0 ){ - duration = 0; - } - duration = (duration!==1) ? `${duration} Days` : `${duration} Day`; - return duration; - }; + var calcDaysRemaining = function(seconds) { + // calculate the number of days remaining on the license + var duration = moment.duration(seconds, 'seconds').asDays(); - var calcExpiresOn = function(seconds){ + duration = Math.floor(duration); + if(duration < 0){ + duration = 0; + } + + duration = (duration!==1) ? `${duration} Days` : `${duration} Day`; + + return duration; + }; + + var calcExpiresOn = function(seconds) { // calculate the expiration date of the license return moment.unix(seconds).calendar(); }; - var reset = function(){ + var reset = function() { document.getElementById('License-form').reset(); }; - var init = function(config){ + var init = function(config) { // license/license.partial.html compares fileName $scope.fileName = N_("No file selected."); - $scope.title = $rootScope.licenseMissing ? ($rootScope.BRAND_NAME + i18n._(" License")) : i18n._("License Management"); + + if ($rootScope.licenseMissing) { + $scope.title = $rootScope.BRAND_NAME + i18n._(" License"); + } else { + $scope.title = i18n._("License Management") + } + $scope.license = config; $scope.license.version = config.version.split('-')[0]; $scope.time = {}; @@ -45,71 +53,80 @@ export default $scope.time.expiresOn = calcExpiresOn($scope.license.license_info.license_date); $scope.valid = CheckLicense.valid($scope.license.license_info); $scope.compliant = $scope.license.license_info.compliant; + $scope.newLicense = {}; + + $scope.newLicense.pendo = true; }; init(config); - $scope.getKey = function(event){ + $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(){ + raw.onload = function() { try { $scope.newLicense.file = JSON.parse(raw.result); - } - catch(err) { - ProcessErrors($rootScope, null, null, null, {msg: i18n._('Invalid file format. Please upload valid JSON.')}); + } catch(err) { + ProcessErrors($rootScope, null, null, null, + {msg: i18n._('Invalid file format. Please upload valid JSON.')}); } }; + try { raw.readAsText(event.target.files[0]); - } - catch(err) { - ProcessErrors($rootScope, null, null, null, {msg: i18n._('Invalid file format. Please upload valid JSON.')}); + } catch(err) { + ProcessErrors($rootScope, null, null, null, + {msg: i18n._('Invalid file format. Please upload valid JSON.')}); } }; + // 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(){ + $scope.fakeClick = function() { if($scope.user_is_superuser) { $('#License-file').click(); } }; - $scope.downloadLicense = function(){ + $scope.downloadLicense = function() { $window.open('https://www.ansible.com/license', '_blank'); }; - $scope.newLicense = {}; - $scope.submit = function(){ - Wait('start'); - CheckLicense.post($scope.newLicense.file, $scope.newLicense.eula) - .then(() => { - reset(); + $scope.submit = function() { + Wait('start'); + CheckLicense.post($scope.newLicense.file, $scope.newLicense.eula) + .then(() => { + reset(); + ConfigService.delete(); - ConfigService.getConfig().then(function(config){ - delete($rootScope.features); - FeaturesService.get(); - pendoService.issuePendoIdentity(); - if($rootScope.licenseMissing === true){ - $state.go('dashboard', { - licenseMissing: false - }); - } - else{ - init(config); - $scope.success = true; - $rootScope.licenseMissing = false; - // for animation purposes - var successTimeout = setTimeout(function(){ - $scope.success = false; - clearTimeout(successTimeout); - }, 4000); - } - }); - }); - }; - } -]; + ConfigService.getConfig() + .then(function(config) { + delete($rootScope.features); + FeaturesService.get(); + + if ($scope.newLicense.pendo) { + pendoService.updatePendoTrackingState('detailed'); + pendoService.issuePendoIdentity(); + } + + if ($rootScope.licenseMissing === true) { + $state.go('dashboard', { + licenseMissing: false + }); + } else { + init(config); + $scope.success = true; + $rootScope.licenseMissing = false; + // for animation purposes + var successTimeout = setTimeout(function() { + $scope.success = false; + clearTimeout(successTimeout); + }, 4000); + } + }); + }); + }; +}]; diff --git a/awx/ui/client/src/license/license.partial.html b/awx/ui/client/src/license/license.partial.html index 7b8bfe3499..dd3e8dad32 100644 --- a/awx/ui/client/src/license/license.partial.html +++ b/awx/ui/client/src/license/license.partial.html @@ -122,6 +122,19 @@ +
+
+ +
+
Save successful! diff --git a/awx/ui/client/src/login/authenticationServices/pendo.service.js b/awx/ui/client/src/login/authenticationServices/pendo.service.js index 977fddc15c..77eba980ce 100644 --- a/awx/ui/client/src/login/authenticationServices/pendo.service.js +++ b/awx/ui/client/src/login/authenticationServices/pendo.service.js @@ -5,87 +5,78 @@ *************************************************/ -export default - [ '$rootScope', 'Rest', 'GetBasePath', 'ProcessErrors', '$q', - 'ConfigService', '$log', 'AppStrings', - function ($rootScope, Rest, GetBasePath, ProcessErrors, $q, - ConfigService, $log, AppStrings) { - return { +export default ['$rootScope', 'Rest', 'GetBasePath', 'ProcessErrors', '$q', 'ConfigService', '$log', + 'AppStrings', + function ($rootScope, Rest, GetBasePath, ProcessErrors, $q, ConfigService, $log, AppStrings) { + return { setPendoOptions: function (config) { - var tower_version = config.version.split('-')[0], - trial = (config.trial) ? config.trial : false, - options = { + const tower_version = config.version.split('-')[0]; + const trial = (config.trial) ? config.trial : false; + let options = { apiKey: AppStrings.get('PENDO_API_KEY'), visitor: { - id: null, - role: null, + id: null, + role: null, }, account: { - id: null, - planLevel: config.license_type, - planPrice: config.instance_count, - creationDate: config.license_date, - trial: trial, - tower_version: tower_version, - ansible_version: config.ansible_version + id: null, + planLevel: config.license_type, + planPrice: config.instance_count, + creationDate: config.license_date, + trial: trial, + tower_version: tower_version, + ansible_version: config.ansible_version } }; - if(config.analytics_status === 'detailed'){ + + if (config.analytics_status === 'detailed') { this.setDetailed(options, config); - } - else if(config.analytics_status === 'anonymous'){ + } else if (config.analytics_status === 'anonymous') { this.setAnonymous(options); } - return options; + return options; }, setDetailed: function(options, config) { - // Detailed mode - // VisitorId: userid+hash of license_key - // AccountId: hash of license_key from license - + // config.deployment_id is a hash of the tower license_key options.visitor.id = $rootScope.current_user.id + '@' + config.deployment_id; options.account.id = config.deployment_id; }, setAnonymous: function (options) { - //Anonymous mode - // VisitorId: - // AccountId: - options.visitor.id = 0; options.account.id = "tower.ansible.com"; }, setRole: function(options) { var deferred = $q.defer(); - if($rootScope.current_user.is_superuser === true){ + + if ($rootScope.current_user.is_superuser === true) { options.visitor.role = 'admin'; deferred.resolve(options); + } else { + Rest.setUrl(GetBasePath('users') + $rootScope.current_user.id + + '/admin_of_organizations/'); + Rest.get() + .then(function (response) { + if (response.data.count > 0) { + options.visitor.role = "orgadmin"; + deferred.resolve(options); + } else { + options.visitor.role = "user"; + deferred.resolve(options); + } + }) + .catch(function (response) { + ProcessErrors($rootScope, response.data, response.status, null, { + hdr: 'Error!', + msg: 'Failed to get admin of org user list. GET returned status: ' + + response.status }); + deferred.reject('Could not resolve pendo role.'); + }); } - else{ - var url = GetBasePath('users') + $rootScope.current_user.id + '/admin_of_organizations/'; - Rest.setUrl(url); - var promise = Rest.get(); - promise.then(function (response) { - if(response.data.count > 0 ) { - options.visitor.role = "orgadmin"; - deferred.resolve(options); - } - else { - options.visitor.role = "user"; - deferred.resolve(options); - } - }); - promise.catch(function (response) { - ProcessErrors($rootScope, response.data, response.status, null, { - hdr: 'Error!', - msg: 'Failed to get inventory name. GET returned status: ' + - response.status }); - deferred.reject('Could not resolve pendo role.'); - }); - } + return deferred.promise; }, @@ -107,24 +98,44 @@ export default config.analytics_status = c.analytics_status; config.version = c.version; config.ansible_version = c.ansible_version; - if(config.analytics_status === 'detailed' || config.analytics_status === 'anonymous'){ - this.bootstrap(); - options = this.setPendoOptions(config); - this.setRole(options).then(function(options){ - $log.debug('Pendo status is '+ config.analytics_status + '. Object below:'); - $log.debug(options); - /* jshint ignore:start */ - pendo.initialize(options); - /* jshint ignore:end */ - }, function(reason){ - // reject function for setRole - $log.debug(reason); - }); - } - else { + + if (config.analytics_status === 'detailed' || + config.analytics_status === 'anonymous') { + this.bootstrap(); + options = this.setPendoOptions(config); + this.setRole(options) + .then(function(options){ + $log.debug('Pendo status is '+ config.analytics_status + + '. Object below:'); + $log.debug(options); + + /* jshint ignore:start */ + pendo.initialize(options); + /* jshint ignore:end */ + }, function(reason){ + // reject function for setRole + $log.debug(reason); + }); + } else { $log.debug('Pendo is turned off.'); } - } + }, + + updatePendoTrackingState: function(tracking_type) { + if (tracking_type === 'off' || tracking_type === 'anonymous' || + tracking_type === 'detailed') { + Rest.setUrl(`${GetBasePath('settings')}ui`); + Rest.patch({ PENDO_TRACKING_STATE: tracking_type }) + .catch(function ({data, status}) { + ProcessErrors($rootScope, data, status, null, { + hdr: 'Error!', + msg: 'Failed to patch PENDO_TRACKING_STATE in settings: ' + + status }); + }); + } else { + throw new Error(`Can't update pendo tracking state in settings to + "${tracking_type}"`); + } + } }; - } -]; + }]; From 2b4a53147e392bd790de35e5cce9d698c02c563c Mon Sep 17 00:00:00 2001 From: John Mitchell Date: Tue, 6 Mar 2018 11:44:14 -0500 Subject: [PATCH 2/6] turn pendo tracking off in settings when checkbox is unchecked --- awx/ui/client/src/license/license.controller.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/awx/ui/client/src/license/license.controller.js b/awx/ui/client/src/license/license.controller.js index 9f160dc9c6..4f2a2b3cc9 100644 --- a/awx/ui/client/src/license/license.controller.js +++ b/awx/ui/client/src/license/license.controller.js @@ -110,6 +110,8 @@ export default if ($scope.newLicense.pendo) { pendoService.updatePendoTrackingState('detailed'); pendoService.issuePendoIdentity(); + } else { + pendoService.updatePendoTrackingState('off'); } if ($rootScope.licenseMissing === true) { From 8b10d64d73def8bccd4868dca4bd712aa15161c0 Mon Sep 17 00:00:00 2001 From: John Mitchell Date: Wed, 7 Mar 2018 11:56:43 -0500 Subject: [PATCH 3/6] update code formatting based on feedback --- .../client/src/license/license.controller.js | 34 +++++++++---------- .../authenticationServices/pendo.service.js | 9 ++--- 2 files changed, 22 insertions(+), 21 deletions(-) diff --git a/awx/ui/client/src/license/license.controller.js b/awx/ui/client/src/license/license.controller.js index 4f2a2b3cc9..39d49d215e 100644 --- a/awx/ui/client/src/license/license.controller.js +++ b/awx/ui/client/src/license/license.controller.js @@ -13,9 +13,9 @@ export default function(Wait, $state, $scope, $rootScope, ProcessErrors, CheckLicense, moment, $window, ConfigService, FeaturesService, pendoService, i18n, config) { - var calcDaysRemaining = function(seconds) { + const calcDaysRemaining = function(seconds) { // calculate the number of days remaining on the license - var duration = moment.duration(seconds, 'seconds').asDays(); + let duration = moment.duration(seconds, 'seconds').asDays(); duration = Math.floor(duration); if(duration < 0){ @@ -27,16 +27,16 @@ export default return duration; }; - var calcExpiresOn = function(seconds) { + const calcExpiresOn = function(seconds) { // calculate the expiration date of the license return moment.unix(seconds).calendar(); }; - var reset = function() { + const reset = function() { document.getElementById('License-form').reset(); }; - var init = function(config) { + const init = function(config) { // license/license.partial.html compares fileName $scope.fileName = N_("No file selected."); @@ -64,7 +64,7 @@ export default // Mimic HTML5 spec, show filename $scope.fileName = event.target.files[0].name; // Grab the key from the raw license file - var raw = new FileReader(); + const raw = new FileReader(); // readAsFoo runs async raw.onload = function() { try { @@ -116,18 +116,18 @@ export default if ($rootScope.licenseMissing === true) { $state.go('dashboard', { - licenseMissing: false - }); - } else { + licenseMissing: false + }); + } else { init(config); - $scope.success = true; - $rootScope.licenseMissing = false; - // for animation purposes - var successTimeout = setTimeout(function() { - $scope.success = false; - clearTimeout(successTimeout); - }, 4000); - } + $scope.success = true; + $rootScope.licenseMissing = false; + // for animation purposes + const successTimeout = setTimeout(function() { + $scope.success = false; + clearTimeout(successTimeout); + }, 4000); + } }); }); }; diff --git a/awx/ui/client/src/login/authenticationServices/pendo.service.js b/awx/ui/client/src/login/authenticationServices/pendo.service.js index 77eba980ce..e09441e817 100644 --- a/awx/ui/client/src/login/authenticationServices/pendo.service.js +++ b/awx/ui/client/src/login/authenticationServices/pendo.service.js @@ -50,7 +50,7 @@ export default ['$rootScope', 'Rest', 'GetBasePath', 'ProcessErrors', '$q', 'Con }, setRole: function(options) { - var deferred = $q.defer(); + const deferred = $q.defer(); if ($rootScope.current_user.is_superuser === true) { options.visitor.role = 'admin'; @@ -91,9 +91,10 @@ export default ['$rootScope', 'Rest', 'GetBasePath', 'ProcessErrors', '$q', 'Con }, issuePendoIdentity: function () { - var options, - c = ConfigService.get(), - config = c.license_info; + const c = ConfigService.get(); + + let options; + let config = c.license_info; config.analytics_status = c.analytics_status; config.version = c.version; From d9f5eab40466e6f3f6f7f593181f491b32fe3395 Mon Sep 17 00:00:00 2001 From: John Mitchell Date: Wed, 7 Mar 2018 13:26:47 -0500 Subject: [PATCH 4/6] add separator above checkbox --- awx/ui/client/src/license/license.block.less | 4 ++++ awx/ui/client/src/license/license.partial.html | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/awx/ui/client/src/license/license.block.less b/awx/ui/client/src/license/license.block.less index aa306a1983..0625568fff 100644 --- a/awx/ui/client/src/license/license.block.less +++ b/awx/ui/client/src/license/license.block.less @@ -139,3 +139,7 @@ .License-detailsGroup { margin-bottom: 20px; } + +.License-detailsGroup--withSeparator { + border-top: 1px solid @default-icon-hov; +} diff --git a/awx/ui/client/src/license/license.partial.html b/awx/ui/client/src/license/license.partial.html index dd3e8dad32..bd438a4776 100644 --- a/awx/ui/client/src/license/license.partial.html +++ b/awx/ui/client/src/license/license.partial.html @@ -122,7 +122,7 @@
-
+