mirror of
https://github.com/ansible/awx.git
synced 2026-05-09 10:27:37 -02:30
Added image upload for custom logo
This commit is contained in:
@@ -1,11 +1,16 @@
|
|||||||
.Form-resetValue {
|
@import "./client/src/shared/branding/colors.default.less";
|
||||||
float: right;
|
|
||||||
|
.Form-resetValue, .Form-resetFile {
|
||||||
text-transform: uppercase;
|
text-transform: uppercase;
|
||||||
font-weight: normal;
|
font-weight: normal;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.Form-resetValue {
|
||||||
|
float: right
|
||||||
|
}
|
||||||
|
|
||||||
.Form-tab {
|
.Form-tab {
|
||||||
min-width: 77px;
|
min-width: 77px;
|
||||||
}
|
}
|
||||||
@@ -25,3 +30,22 @@
|
|||||||
.Form-tabRow {
|
.Form-tabRow {
|
||||||
width: 80%;
|
width: 80%;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
input.Form-filePicker {
|
||||||
|
width: 0.1px;
|
||||||
|
height: 0.1px;
|
||||||
|
opacity: 0;
|
||||||
|
overflow: hidden;
|
||||||
|
position: absolute;
|
||||||
|
z-index: -1;
|
||||||
|
}
|
||||||
|
label#filePickerButton {
|
||||||
|
cursor: pointer;
|
||||||
|
background-color: #fff;
|
||||||
|
color: @default-interface-txt;
|
||||||
|
}
|
||||||
|
input#filePickerText {
|
||||||
|
cursor: default;
|
||||||
|
border-radius: 0 5px 5px 0;
|
||||||
|
background-color: #fff;
|
||||||
|
}
|
||||||
|
|||||||
@@ -5,8 +5,8 @@
|
|||||||
*************************************************/
|
*************************************************/
|
||||||
|
|
||||||
export default [
|
export default [
|
||||||
'$scope', '$state', '$stateParams', '$timeout', '$q', 'Alert', 'ClearScope',
|
'$scope', '$rootScope', '$state', '$stateParams', '$timeout', '$q', 'Alert', 'ClearScope',
|
||||||
'ConfigurationService', 'ConfigurationUtils', 'CreateDialog', 'CreateSelect2', 'ParseTypeChange', 'ProcessErrors',
|
'ConfigurationService', 'ConfigurationUtils', 'CreateDialog', 'CreateSelect2', 'ParseTypeChange', 'ProcessErrors', 'Store',
|
||||||
'Wait', 'configDataResolve',
|
'Wait', 'configDataResolve',
|
||||||
//Form definitions
|
//Form definitions
|
||||||
'configurationGithubForm',
|
'configurationGithubForm',
|
||||||
@@ -20,8 +20,8 @@ export default [
|
|||||||
'ConfigurationSystemForm',
|
'ConfigurationSystemForm',
|
||||||
'ConfigurationUiForm',
|
'ConfigurationUiForm',
|
||||||
function(
|
function(
|
||||||
$scope, $state, $stateParams, $timeout, $q, Alert, ClearScope,
|
$scope, $rootScope, $state, $stateParams, $timeout, $q, Alert, ClearScope,
|
||||||
ConfigurationService, ConfigurationUtils, CreateDialog, CreateSelect2, ParseTypeChange, ProcessErrors,
|
ConfigurationService, ConfigurationUtils, CreateDialog, CreateSelect2, ParseTypeChange, ProcessErrors, Store,
|
||||||
Wait, configDataResolve,
|
Wait, configDataResolve,
|
||||||
//Form definitions
|
//Form definitions
|
||||||
configurationGithubForm,
|
configurationGithubForm,
|
||||||
@@ -238,6 +238,7 @@ export default [
|
|||||||
ConfigurationService.patchConfiguration(payload)
|
ConfigurationService.patchConfiguration(payload)
|
||||||
.then(function() {
|
.then(function() {
|
||||||
$scope[key] = $scope.configDataResolve[key].default;
|
$scope[key] = $scope.configDataResolve[key].default;
|
||||||
|
loginUpdate();
|
||||||
})
|
})
|
||||||
.catch(function(error) {
|
.catch(function(error) {
|
||||||
ProcessErrors($scope, error, status, formDefs[formTracker.getCurrent()],
|
ProcessErrors($scope, error, status, formDefs[formTracker.getCurrent()],
|
||||||
@@ -271,6 +272,21 @@ export default [
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function loginUpdate() {
|
||||||
|
// Updates the logo and app config so that logos are properly shown
|
||||||
|
// on logout after modifying.
|
||||||
|
if($scope.CUSTOM_LOGO) {
|
||||||
|
$rootScope.custom_logo = $scope.$parent.CUSTOM_LOGO;
|
||||||
|
global.$AnsibleConfig.custom_logo = true;
|
||||||
|
Store('AnsibleConfig', global.$AnsibleConfig);
|
||||||
|
} else {
|
||||||
|
$rootScope.custom_logo = '';
|
||||||
|
global.$AnsibleConfig.custom_logo = false;
|
||||||
|
Store('AnsibleConfig', global.$AnsibleConfig);
|
||||||
|
}
|
||||||
|
$scope.$broadcast('loginUpdated');
|
||||||
|
}
|
||||||
|
|
||||||
// Some dropdowns are listed as "list" type in the API even though they're a dropdown:
|
// Some dropdowns are listed as "list" type in the API even though they're a dropdown:
|
||||||
var multiselectDropdowns = ['AD_HOC_COMMANDS'];
|
var multiselectDropdowns = ['AD_HOC_COMMANDS'];
|
||||||
var formSave = function() {
|
var formSave = function() {
|
||||||
@@ -313,6 +329,7 @@ export default [
|
|||||||
Wait('start');
|
Wait('start');
|
||||||
ConfigurationService.patchConfiguration(payload)
|
ConfigurationService.patchConfiguration(payload)
|
||||||
.then(function(data) {
|
.then(function(data) {
|
||||||
|
loginUpdate();
|
||||||
saveDeferred.resolve(data);
|
saveDeferred.resolve(data);
|
||||||
$scope[formTracker.currentFormName()].$setPristine();
|
$scope[formTracker.currentFormName()].$setPristine();
|
||||||
})
|
})
|
||||||
@@ -331,6 +348,8 @@ export default [
|
|||||||
return saveDeferred.promise;
|
return saveDeferred.promise;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
$scope.toggleForm = function(key) {
|
$scope.toggleForm = function(key) {
|
||||||
$scope[key] = !$scope[key];
|
$scope[key] = !$scope[key];
|
||||||
Wait('start');
|
Wait('start');
|
||||||
|
|||||||
@@ -4,8 +4,8 @@
|
|||||||
* All Rights Reserved
|
* All Rights Reserved
|
||||||
*************************************************/
|
*************************************************/
|
||||||
|
|
||||||
export default [
|
export default ['$q',
|
||||||
function() {
|
function($q) {
|
||||||
|
|
||||||
return {
|
return {
|
||||||
listToArray: function(input) {
|
listToArray: function(input) {
|
||||||
@@ -64,6 +64,33 @@ export default [
|
|||||||
} else {
|
} else {
|
||||||
return input;
|
return input;
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
imageProcess: function(file) {
|
||||||
|
var deferred = $q.defer();
|
||||||
|
var SIZELIMIT = 1000000; // 1 MB
|
||||||
|
var ACCEPTEDFORMATS = ['image/png', 'image/gif', 'image/jpeg']; //Basic check
|
||||||
|
|
||||||
|
if(file.size < SIZELIMIT && ACCEPTEDFORMATS.indexOf(file.type) !== -1) {
|
||||||
|
var reader = new FileReader();
|
||||||
|
reader.readAsDataURL(file);
|
||||||
|
reader.onload = function () {
|
||||||
|
deferred.resolve(reader.result);
|
||||||
|
};
|
||||||
|
reader.onerror = function () {
|
||||||
|
deferred.reject('File could not be parsed');
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
var error = 'Error: ';
|
||||||
|
if(file.size > SIZELIMIT) {
|
||||||
|
error += 'Must be under ' + SIZELIMIT / 1000000 + 'MB. ';
|
||||||
|
}
|
||||||
|
if(ACCEPTEDFORMATS.indexOf(file.type) === -1) {
|
||||||
|
error += 'Wrong file type - must be png, gif, or jpg.';
|
||||||
|
}
|
||||||
|
deferred.reject(error);
|
||||||
|
}
|
||||||
|
return deferred.promise;
|
||||||
}
|
}
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -4,33 +4,39 @@
|
|||||||
* All Rights Reserved
|
* All Rights Reserved
|
||||||
*************************************************/
|
*************************************************/
|
||||||
|
|
||||||
export default function() {
|
export default function() {
|
||||||
return {
|
return {
|
||||||
showHeader: false,
|
showHeader: false,
|
||||||
name: 'configuration_ui_template',
|
name: 'configuration_ui_template',
|
||||||
showActions: true,
|
showActions: true,
|
||||||
|
|
||||||
fields: {
|
fields: {
|
||||||
PENDO_TRACKING_STATE: {
|
PENDO_TRACKING_STATE: {
|
||||||
type: 'select',
|
type: 'select',
|
||||||
ngChange: 'changedPendo()',
|
ngChange: 'changedPendo()',
|
||||||
ngOptions: 'choice.label for choice in PENDO_TRACKING_STATE_options track by choice.value',
|
ngOptions: 'choice.label for choice in PENDO_TRACKING_STATE_options track by choice.value',
|
||||||
reset: 'PENDO_TRACKING_STATE'
|
reset: 'PENDO_TRACKING_STATE'
|
||||||
}
|
},
|
||||||
},
|
CUSTOM_LOGO: {
|
||||||
buttons: {
|
type: 'custom',
|
||||||
reset: {
|
reset: 'CUSTOM_LOGO',
|
||||||
ngClick: 'vm.resetAllConfirm()',
|
control: `<image-upload key="CUSTOM_LOGO"></image-upload>`
|
||||||
label: 'Reset All',
|
},
|
||||||
class: 'Form-button--left Form-cancelButton'
|
},
|
||||||
},
|
|
||||||
cancel: {
|
buttons: {
|
||||||
ngClick: 'vm.formCancel()',
|
reset: {
|
||||||
},
|
ngClick: 'vm.resetAllConfirm()',
|
||||||
save: {
|
label: 'Reset All',
|
||||||
ngClick: 'vm.formSave()',
|
class: 'Form-button--left Form-cancelButton'
|
||||||
ngDisabled: true
|
},
|
||||||
}
|
cancel: {
|
||||||
}
|
ngClick: 'vm.formCancel()',
|
||||||
};
|
},
|
||||||
}
|
save: {
|
||||||
|
ngClick: 'vm.formSave()',
|
||||||
|
ngDisabled: true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,6 +1,4 @@
|
|||||||
<div class="tab-pane Configuration-container">
|
<div class="tab-pane Configuration-container">
|
||||||
<!-- <div ui-view="form"></div>
|
|
||||||
<div ng-cloak id="htmlTemplate"> -->
|
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-lg-12">
|
<div class="col-lg-12">
|
||||||
<div id="configure-ui-form"></div>
|
<div id="configure-ui-form"></div>
|
||||||
|
|||||||
@@ -22,14 +22,14 @@
|
|||||||
export default
|
export default
|
||||||
angular.module('LoadConfigHelper', ['Utilities'])
|
angular.module('LoadConfigHelper', ['Utilities'])
|
||||||
|
|
||||||
.factory('LoadConfig', ['$log', '$rootScope', '$http', '$location',
|
.factory('LoadConfig', ['$log', '$rootScope', '$http', '$location', 'GetBasePath',
|
||||||
'ProcessErrors', 'Store',
|
'ProcessErrors', 'Rest', 'Store',
|
||||||
function($log, $rootScope, $http, $location, ProcessErrors, Store) {
|
function($log, $rootScope, $http, $location, GetBasePath, ProcessErrors, Rest, Store) {
|
||||||
return function() {
|
return function() {
|
||||||
|
|
||||||
// These ettings used to be found in config.js, hardcoded now.
|
// These ettings used to be found in config.js, hardcoded now.
|
||||||
var configSettings = {
|
var configSettings = {
|
||||||
// custom_logo: true, // load /var/lib/awx/public/static/assets/custom_console_logo.png as the login modal header. if false, will load the standard tower console logo
|
//custom_logo: false, // load /var/lib/awx/public/static/assets/custom_console_logo.png as the login modal header. if false, will load the standard tower console logo
|
||||||
// custom_login_info: "example notice", // have a notice displayed in the login modal for users. note that, as a security measure, custom html is not supported and will be escaped.
|
// custom_login_info: "example notice", // have a notice displayed in the login modal for users. note that, as a security measure, custom html is not supported and will be escaped.
|
||||||
"tooltip_delay": {
|
"tooltip_delay": {
|
||||||
"show": 500,
|
"show": 500,
|
||||||
@@ -62,22 +62,37 @@ angular.module('LoadConfigHelper', ['Utilities'])
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
// Auto-resolving what used to be found when attempting to load local_setting.json
|
var configInit = function() {
|
||||||
if ($rootScope.loginConfig) {
|
// Auto-resolving what used to be found when attempting to load local_setting.json
|
||||||
$rootScope.loginConfig.resolve('config loaded');
|
if ($rootScope.loginConfig) {
|
||||||
}
|
$rootScope.loginConfig.resolve('config loaded');
|
||||||
$rootScope.$emit('ConfigReady');
|
}
|
||||||
|
$rootScope.$emit('ConfigReady');
|
||||||
|
|
||||||
// Load new hardcoded settings from above
|
// Load new hardcoded settings from above
|
||||||
// TODO Add a check for a custom image to add to the settings.
|
|
||||||
// Update flag to true
|
|
||||||
// in loginModal.controller load the base64 src
|
|
||||||
// change partial to use base65 in the img src
|
|
||||||
|
|
||||||
global.$AnsibleConfig = configSettings;
|
global.$AnsibleConfig = configSettings;
|
||||||
Store('AnsibleConfig', global.$AnsibleConfig);
|
Store('AnsibleConfig', global.$AnsibleConfig);
|
||||||
$rootScope.$emit('LoadConfig');
|
$rootScope.$emit('LoadConfig');
|
||||||
|
};
|
||||||
|
|
||||||
|
// Retrieve the custom logo information - update configSettings from above
|
||||||
|
$http({
|
||||||
|
method: 'GET',
|
||||||
|
url: '/api',
|
||||||
|
})
|
||||||
|
.success(function(response) {
|
||||||
|
if(response.custom_logo) {
|
||||||
|
configSettings.custom_logo = true;
|
||||||
|
$rootScope.custom_logo = response.custom_logo;
|
||||||
|
configInit();
|
||||||
|
} else {
|
||||||
|
configSettings.custom_logo = false;
|
||||||
|
configInit();
|
||||||
|
}
|
||||||
|
}).error(function(error) {
|
||||||
|
console.log(error);
|
||||||
|
});
|
||||||
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -93,7 +93,7 @@ export default ['$log', '$cookieStore', '$compile', '$window', '$rootScope',
|
|||||||
|
|
||||||
$rootScope.loginConfig.promise.then(function () {
|
$rootScope.loginConfig.promise.then(function () {
|
||||||
if ($AnsibleConfig.custom_logo) {
|
if ($AnsibleConfig.custom_logo) {
|
||||||
scope.customLogo = "custom_console_logo.png";
|
scope.customLogo = $rootScope.custom_logo;
|
||||||
scope.customLogoPresent = true;
|
scope.customLogoPresent = true;
|
||||||
} else {
|
} else {
|
||||||
scope.customLogo = "tower-logo-login.svg";
|
scope.customLogo = "tower-logo-login.svg";
|
||||||
|
|||||||
@@ -6,8 +6,12 @@
|
|||||||
ng-class="{'is-loggedOut' : !current_user || !current_user.username}">
|
ng-class="{'is-loggedOut' : !current_user || !current_user.username}">
|
||||||
<div class="LoginModal-header">
|
<div class="LoginModal-header">
|
||||||
<img id="login_modal_image" class="LoginModal-logoImage"
|
<img id="login_modal_image" class="LoginModal-logoImage"
|
||||||
|
ng-if="!customLogoPresent"
|
||||||
ng-class="{'LoginModal-logoImage--notCustom': !customLogoPresent}"
|
ng-class="{'LoginModal-logoImage--notCustom': !customLogoPresent}"
|
||||||
ng-src="/static/assets/{{ customLogo }}" >
|
ng-src="/static/assets/{{ customLogo }}" >
|
||||||
|
<img id="login_modal_image" class="LoginModal-logoImage"
|
||||||
|
ng-if="customLogoPresent"
|
||||||
|
ng-src="{{ customLogo }}" >
|
||||||
</div>
|
</div>
|
||||||
<div class="LoginModal-body">
|
<div class="LoginModal-body">
|
||||||
<div class="LoginModal-alert" ng-show="!sessionExpired && !sessionLimitExpired && !attemptFailed && !thirdPartyAttemptFailed" translate>
|
<div class="LoginModal-alert" ng-show="!sessionExpired && !sessionLimitExpired && !attemptFailed && !thirdPartyAttemptFailed" translate>
|
||||||
|
|||||||
@@ -123,6 +123,77 @@ angular.module('AWDirectives', ['RestServices', 'Utilities', 'JobsHelper'])
|
|||||||
};
|
};
|
||||||
}])
|
}])
|
||||||
|
|
||||||
|
// imageUpload
|
||||||
|
//
|
||||||
|
// Accepts image and returns base64 information with basic validation
|
||||||
|
// Can eventually expand to handle all uploads with different endpoints and handlers
|
||||||
|
//
|
||||||
|
.directive('imageUpload', ['ConfigurationUtils', function(ConfigurationUtils) {
|
||||||
|
return {
|
||||||
|
restrict: 'E',
|
||||||
|
scope: {
|
||||||
|
key: '@'
|
||||||
|
},
|
||||||
|
template: `
|
||||||
|
<div class="input-group">
|
||||||
|
<label class="input-group-addon Form-filePicker--pickerButton" id="filePickerButton" for="filePicker" ng-click="update($event)">BROWSE</label>
|
||||||
|
<input type="text" class="form-control Form-filePicker--textBox" id="filePickerText" placeholder="Choose file" readonly>
|
||||||
|
<input type="file" name="file" class="Form-filePicker" id="filePicker" onchange="angular.element(this).scope().fileChange(this.files)"/>
|
||||||
|
</div>
|
||||||
|
<!-- Update when API supports file name saving
|
||||||
|
<div ng-if="imagePresent" class="Form-filePicker--selectedFile">
|
||||||
|
Custom logo has been uploaded.
|
||||||
|
</div>-->
|
||||||
|
<!-- Thumbnail feature
|
||||||
|
<div class="thumbnail">
|
||||||
|
<img src="{{image}}" alt="Current logo">
|
||||||
|
</div> -->
|
||||||
|
<div class="error" id="filePickerError"></div>`,
|
||||||
|
|
||||||
|
link: function(scope) {
|
||||||
|
var fieldKey = scope.key;
|
||||||
|
var filePickerText = angular.element(document.getElementById('filePickerText'));
|
||||||
|
var filePickerError = angular.element(document.getElementById('filePickerError'));
|
||||||
|
var filePickerButton = angular.element(document.getElementById('filePickerButton'));
|
||||||
|
|
||||||
|
scope.imagePresent = global.$AnsibleConfig.custom_logo;
|
||||||
|
|
||||||
|
scope.$on('loginUpdated', function() {
|
||||||
|
scope.imagePresent = global.$AnsibleConfig.custom_logo;
|
||||||
|
});
|
||||||
|
|
||||||
|
scope.update = function(e) {
|
||||||
|
if(scope.$parent[fieldKey]) {
|
||||||
|
e.preventDefault();
|
||||||
|
scope.$parent[fieldKey] = '';
|
||||||
|
filePickerButton.html('BROWSE');
|
||||||
|
filePickerText.val('');
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// Nothing exists so open file picker
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
scope.fileChange = function(file) {
|
||||||
|
filePickerError.html('');
|
||||||
|
|
||||||
|
ConfigurationUtils.imageProcess(file[0])
|
||||||
|
.then(function(result) {
|
||||||
|
scope.$parent[fieldKey] = result;
|
||||||
|
filePickerText.val(file[0].name);
|
||||||
|
filePickerButton.html('REMOVE');
|
||||||
|
}).catch(function(error) {
|
||||||
|
filePickerText.html(file[0].name);
|
||||||
|
filePickerError.text(error);
|
||||||
|
}).finally(function() {
|
||||||
|
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}])
|
||||||
|
|
||||||
|
|
||||||
.directive('surveyCheckboxes', function() {
|
.directive('surveyCheckboxes', function() {
|
||||||
return {
|
return {
|
||||||
|
|||||||
Reference in New Issue
Block a user