1
0
mirror of https://github.com/ansible/awx.git synced 2024-11-01 08:21:15 +03:00

Added image upload for custom logo

This commit is contained in:
Ken Hoes 2016-11-14 08:59:13 -05:00
parent 99fd30e84f
commit 8028b7ef75
9 changed files with 221 additions and 57 deletions

View File

@ -1,11 +1,16 @@
.Form-resetValue {
float: right;
@import "./client/src/shared/branding/colors.default.less";
.Form-resetValue, .Form-resetFile {
text-transform: uppercase;
font-weight: normal;
cursor: pointer;
font-size: 12px;
}
.Form-resetValue {
float: right
}
.Form-tab {
min-width: 77px;
}
@ -25,3 +30,22 @@
.Form-tabRow {
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;
}

View File

@ -5,8 +5,8 @@
*************************************************/
export default [
'$scope', '$state', '$stateParams', '$timeout', '$q', 'Alert', 'ClearScope',
'ConfigurationService', 'ConfigurationUtils', 'CreateDialog', 'CreateSelect2', 'ParseTypeChange', 'ProcessErrors',
'$scope', '$rootScope', '$state', '$stateParams', '$timeout', '$q', 'Alert', 'ClearScope',
'ConfigurationService', 'ConfigurationUtils', 'CreateDialog', 'CreateSelect2', 'ParseTypeChange', 'ProcessErrors', 'Store',
'Wait', 'configDataResolve',
//Form definitions
'configurationGithubForm',
@ -20,8 +20,8 @@ export default [
'ConfigurationSystemForm',
'ConfigurationUiForm',
function(
$scope, $state, $stateParams, $timeout, $q, Alert, ClearScope,
ConfigurationService, ConfigurationUtils, CreateDialog, CreateSelect2, ParseTypeChange, ProcessErrors,
$scope, $rootScope, $state, $stateParams, $timeout, $q, Alert, ClearScope,
ConfigurationService, ConfigurationUtils, CreateDialog, CreateSelect2, ParseTypeChange, ProcessErrors, Store,
Wait, configDataResolve,
//Form definitions
configurationGithubForm,
@ -238,6 +238,7 @@ export default [
ConfigurationService.patchConfiguration(payload)
.then(function() {
$scope[key] = $scope.configDataResolve[key].default;
loginUpdate();
})
.catch(function(error) {
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:
var multiselectDropdowns = ['AD_HOC_COMMANDS'];
var formSave = function() {
@ -313,6 +329,7 @@ export default [
Wait('start');
ConfigurationService.patchConfiguration(payload)
.then(function(data) {
loginUpdate();
saveDeferred.resolve(data);
$scope[formTracker.currentFormName()].$setPristine();
})
@ -331,6 +348,8 @@ export default [
return saveDeferred.promise;
};
$scope.toggleForm = function(key) {
$scope[key] = !$scope[key];
Wait('start');

View File

@ -4,8 +4,8 @@
* All Rights Reserved
*************************************************/
export default [
function() {
export default ['$q',
function($q) {
return {
listToArray: function(input) {
@ -64,6 +64,33 @@ export default [
} else {
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;
}
};

View File

@ -4,33 +4,39 @@
* All Rights Reserved
*************************************************/
export default function() {
return {
showHeader: false,
name: 'configuration_ui_template',
showActions: true,
export default function() {
return {
showHeader: false,
name: 'configuration_ui_template',
showActions: true,
fields: {
PENDO_TRACKING_STATE: {
type: 'select',
ngChange: 'changedPendo()',
ngOptions: 'choice.label for choice in PENDO_TRACKING_STATE_options track by choice.value',
reset: 'PENDO_TRACKING_STATE'
}
},
buttons: {
reset: {
ngClick: 'vm.resetAllConfirm()',
label: 'Reset All',
class: 'Form-button--left Form-cancelButton'
},
cancel: {
ngClick: 'vm.formCancel()',
},
save: {
ngClick: 'vm.formSave()',
ngDisabled: true
}
}
};
}
fields: {
PENDO_TRACKING_STATE: {
type: 'select',
ngChange: 'changedPendo()',
ngOptions: 'choice.label for choice in PENDO_TRACKING_STATE_options track by choice.value',
reset: 'PENDO_TRACKING_STATE'
},
CUSTOM_LOGO: {
type: 'custom',
reset: 'CUSTOM_LOGO',
control: `<image-upload key="CUSTOM_LOGO"></image-upload>`
},
},
buttons: {
reset: {
ngClick: 'vm.resetAllConfirm()',
label: 'Reset All',
class: 'Form-button--left Form-cancelButton'
},
cancel: {
ngClick: 'vm.formCancel()',
},
save: {
ngClick: 'vm.formSave()',
ngDisabled: true
}
}
};
}

View File

@ -1,6 +1,4 @@
<div class="tab-pane Configuration-container">
<!-- <div ui-view="form"></div>
<div ng-cloak id="htmlTemplate"> -->
<div class="row">
<div class="col-lg-12">
<div id="configure-ui-form"></div>

View File

@ -22,14 +22,14 @@
export default
angular.module('LoadConfigHelper', ['Utilities'])
.factory('LoadConfig', ['$log', '$rootScope', '$http', '$location',
'ProcessErrors', 'Store',
function($log, $rootScope, $http, $location, ProcessErrors, Store) {
.factory('LoadConfig', ['$log', '$rootScope', '$http', '$location', 'GetBasePath',
'ProcessErrors', 'Rest', 'Store',
function($log, $rootScope, $http, $location, GetBasePath, ProcessErrors, Rest, Store) {
return function() {
// These ettings used to be found in config.js, hardcoded now.
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.
"tooltip_delay": {
"show": 500,
@ -62,22 +62,37 @@ angular.module('LoadConfigHelper', ['Utilities'])
},
};
// Auto-resolving what used to be found when attempting to load local_setting.json
if ($rootScope.loginConfig) {
$rootScope.loginConfig.resolve('config loaded');
}
$rootScope.$emit('ConfigReady');
var configInit = function() {
// Auto-resolving what used to be found when attempting to load local_setting.json
if ($rootScope.loginConfig) {
$rootScope.loginConfig.resolve('config loaded');
}
$rootScope.$emit('ConfigReady');
// 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
// Load new hardcoded settings from above
global.$AnsibleConfig = configSettings;
Store('AnsibleConfig', global.$AnsibleConfig);
$rootScope.$emit('LoadConfig');
global.$AnsibleConfig = configSettings;
Store('AnsibleConfig', global.$AnsibleConfig);
$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);
});
};
}

View File

@ -93,7 +93,7 @@ export default ['$log', '$cookieStore', '$compile', '$window', '$rootScope',
$rootScope.loginConfig.promise.then(function () {
if ($AnsibleConfig.custom_logo) {
scope.customLogo = "custom_console_logo.png";
scope.customLogo = $rootScope.custom_logo;
scope.customLogoPresent = true;
} else {
scope.customLogo = "tower-logo-login.svg";

View File

@ -6,8 +6,12 @@
ng-class="{'is-loggedOut' : !current_user || !current_user.username}">
<div class="LoginModal-header">
<img id="login_modal_image" class="LoginModal-logoImage"
ng-if="!customLogoPresent"
ng-class="{'LoginModal-logoImage--notCustom': !customLogoPresent}"
ng-src="/static/assets/{{ customLogo }}" >
<img id="login_modal_image" class="LoginModal-logoImage"
ng-if="customLogoPresent"
ng-src="{{ customLogo }}" >
</div>
<div class="LoginModal-body">
<div class="LoginModal-alert" ng-show="!sessionExpired && !sessionLimitExpired && !attemptFailed && !thirdPartyAttemptFailed" translate>

View File

@ -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() {
return {