mirror of
https://github.com/ansible/awx.git
synced 2024-11-02 01:21:21 +03:00
Moved jqueryui dialog creation to a shared module along with textarea auto-sizing. Inventory host edit dialog is now constructed using this new module. Fixed host enabled flag on the host edit dialog so that it is disabled for externally managed hosts.
This commit is contained in:
parent
6f11502528
commit
1bc9ab68eb
@ -30,7 +30,7 @@ angular.module('HostFormDefinition', [])
|
||||
"</blockquote>",
|
||||
dataTitle: 'Host Name',
|
||||
dataPlacement: 'right',
|
||||
dataContainer: '#form-modal .modal-content'
|
||||
dataContainer: 'body'
|
||||
},
|
||||
description: {
|
||||
label: 'Description',
|
||||
@ -48,7 +48,7 @@ angular.module('HostFormDefinition', [])
|
||||
"are part of an external inventory, this flag cannot be changed. It will be set by the inventory sync process.</p>",
|
||||
dataTitle: 'Host Enabled',
|
||||
dataPlacement: 'right',
|
||||
dataContainer: '#form-modal .modal-content',
|
||||
dataContainer: 'body',
|
||||
ngDisabled: 'has_inventory_sources == true'
|
||||
},
|
||||
variables: {
|
||||
@ -68,7 +68,7 @@ angular.module('HostFormDefinition', [])
|
||||
'<p>View YAML examples at <a href="http://docs.ansible.com/YAMLSyntax.html" target="_blank">docs.ansible.com</a></p>',
|
||||
dataTitle: 'Host Variables',
|
||||
dataPlacement: 'right',
|
||||
dataContainer: '#form-modal .modal-content'
|
||||
dataContainer: 'body'
|
||||
},
|
||||
inventory: {
|
||||
type: 'hidden',
|
||||
@ -78,6 +78,7 @@ angular.module('HostFormDefinition', [])
|
||||
},
|
||||
|
||||
buttons: { //for now always generates <button> tags
|
||||
/*
|
||||
save: {
|
||||
ngClick: 'formSave()', //$scope.function to call on click, optional
|
||||
ngDisabled: true //Disable when $pristine or $invalid, optional
|
||||
@ -86,6 +87,7 @@ angular.module('HostFormDefinition', [])
|
||||
ngClick: 'formReset()',
|
||||
ngDisabled: true //Disabled when $pristine
|
||||
}
|
||||
*/
|
||||
},
|
||||
|
||||
related: {}
|
||||
|
@ -703,8 +703,19 @@ function(SchedulerInit, Rest, Wait, SetSchedulesInnerDialogSize) {
|
||||
// remove lingering popover <div> elements
|
||||
$(this).remove();
|
||||
});
|
||||
if (properties_scope.codeMirror) {
|
||||
properties_scope.codeMirror.destroy();
|
||||
}
|
||||
if (sources_scope.codeMirror) {
|
||||
sources_scope.codeMirror.destroy();
|
||||
}
|
||||
$('#group-modal-dialog').dialog('destroy');
|
||||
$('#group-modal-dialog').hide();
|
||||
$('#properties-tab').empty();
|
||||
$('#sources-tab').empty();
|
||||
$('#schedules-list').empty();
|
||||
$('#schedules-form').empty();
|
||||
$('#schedules-detail').empty();
|
||||
modal_scope.cancelModal();
|
||||
},
|
||||
open: function () {
|
||||
|
@ -14,7 +14,7 @@
|
||||
angular.module('HostsHelper', [ 'RestServices', 'Utilities', 'ListGenerator', 'HostListDefinition',
|
||||
'SearchHelper', 'PaginationHelpers', 'ListGenerator', 'AuthService', 'HostsHelper',
|
||||
'InventoryHelper', 'RelatedSearchHelper', 'InventoryFormDefinition', 'SelectionHelper',
|
||||
'HostGroupsFormDefinition', 'VariablesHelper'
|
||||
'HostGroupsFormDefinition', 'VariablesHelper', 'ModalDialog'
|
||||
])
|
||||
|
||||
.factory('SetEnabledMsg', [ function() {
|
||||
@ -420,10 +420,10 @@ function($rootScope, $location, $log, $routeParams, Rest, Alert, HostForm, Gener
|
||||
|
||||
.factory('HostsEdit', ['$rootScope', '$location', '$log', '$routeParams', 'Rest', 'Alert', 'HostForm', 'GenerateForm',
|
||||
'Prompt', 'ProcessErrors', 'GetBasePath', 'HostsReload', 'ParseTypeChange', 'Wait', 'Find', 'SetStatus', 'ApplyEllipsis',
|
||||
'WatchInventoryWindowResize', 'ToJSON', 'ParseVariableString',
|
||||
'WatchInventoryWindowResize', 'ToJSON', 'ParseVariableString', 'CreateDialog', 'TextareaResize',
|
||||
function($rootScope, $location, $log, $routeParams, Rest, Alert, HostForm, GenerateForm, Prompt, ProcessErrors,
|
||||
GetBasePath, HostsReload, ParseTypeChange, Wait, Find, SetStatus, ApplyEllipsis, WatchInventoryWindowResize, ToJSON,
|
||||
ParseVariableString) {
|
||||
ParseVariableString, CreateDialog, TextareaResize) {
|
||||
return function(params) {
|
||||
|
||||
var parent_scope = params.scope,
|
||||
@ -432,14 +432,64 @@ function($rootScope, $location, $log, $routeParams, Rest, Alert, HostForm, Gener
|
||||
generator = GenerateForm,
|
||||
form = HostForm,
|
||||
defaultUrl = GetBasePath('hosts') + host_id + '/',
|
||||
scope = generator.inject(form, { mode: 'edit', modal: true, related: false, show_modal: false }),
|
||||
scope = parent_scope.$new(),
|
||||
master = {},
|
||||
relatedSets = {};
|
||||
relatedSets = {},
|
||||
buttons;
|
||||
|
||||
generator.inject(HostForm, { mode: 'edit', id: 'host-modal-dialog', breadCrumbs: false, related: false, scope: scope });
|
||||
generator.reset();
|
||||
scope.formModalActionLabel = 'Save';
|
||||
scope.formModalHeader = 'Host Properties';
|
||||
scope.formModalCancelShow = true;
|
||||
|
||||
buttons = [{
|
||||
label: "Cancel",
|
||||
onClick: function() {
|
||||
scope.cancelModal();
|
||||
},
|
||||
icon: "fa-times",
|
||||
"class": "btn btn-default",
|
||||
"id": "host-cancel-button"
|
||||
},{
|
||||
label: "Save",
|
||||
onClick: function() {
|
||||
scope.saveModal();
|
||||
},
|
||||
icon: "fa-check",
|
||||
"class": "btn btn-primary",
|
||||
"id": "host-save-button"
|
||||
}];
|
||||
|
||||
CreateDialog({
|
||||
scope: scope,
|
||||
buttons: buttons,
|
||||
width: 675,
|
||||
height: 750,
|
||||
minWidth: 400,
|
||||
title: 'Host Properties',
|
||||
id: 'host-modal-dialog',
|
||||
onClose: function() {
|
||||
Wait('stop');
|
||||
scope.codeMirror.destroy();
|
||||
$('#host-modal-dialog').empty();
|
||||
WatchInventoryWindowResize();
|
||||
},
|
||||
onResizeStop: function() {
|
||||
TextareaResize({
|
||||
scope: scope,
|
||||
textareaId: 'host_variables',
|
||||
modalId: 'host-modal-dialog',
|
||||
formId: 'host_form'
|
||||
});
|
||||
},
|
||||
onOpen: function() {
|
||||
TextareaResize({
|
||||
scope: scope,
|
||||
textareaId: 'host_variables',
|
||||
modalId: 'host-modal-dialog',
|
||||
formId: 'host_form'
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
scope.parseType = 'yaml';
|
||||
|
||||
if (scope.hostVariablesLoadedRemove) {
|
||||
@ -447,7 +497,7 @@ function($rootScope, $location, $log, $routeParams, Rest, Alert, HostForm, Gener
|
||||
}
|
||||
scope.hostVariablesLoadedRemove = scope.$on('hostVariablesLoaded', function() {
|
||||
var callback = function() { Wait('stop'); };
|
||||
$('#form-modal').modal('show');
|
||||
$('#host-modal-dialog').dialog('open');
|
||||
ParseTypeChange({ scope: scope, field_id: 'host_variables', onReady: callback });
|
||||
});
|
||||
|
||||
@ -496,6 +546,7 @@ function($rootScope, $location, $log, $routeParams, Rest, Alert, HostForm, Gener
|
||||
}
|
||||
}
|
||||
scope.variable_url = data.related.variable_data;
|
||||
scope.has_inventory_sources = data.has_inventory_sources;
|
||||
scope.$emit('hostLoaded');
|
||||
})
|
||||
.error( function(data, status) {
|
||||
@ -512,8 +563,8 @@ function($rootScope, $location, $log, $routeParams, Rest, Alert, HostForm, Gener
|
||||
var host = Find({ list: parent_scope.hosts, key: 'id', val: host_id }),
|
||||
old_name = host.name;
|
||||
host.name = scope.name;
|
||||
host.enabled = scope.enabled;
|
||||
host.enabled_flag = scope.enabled;
|
||||
host.enabled = (scope.enabled) ? true : false;
|
||||
host.enabled_flag = host.enabled;
|
||||
SetStatus({ scope: parent_scope, host: host });
|
||||
|
||||
// Update any titles attributes created by ApplyEllipsis
|
||||
@ -522,40 +573,44 @@ function($rootScope, $location, $log, $routeParams, Rest, Alert, HostForm, Gener
|
||||
$('#hosts_table .host-name a[title="' + old_name + '"]').attr('title', host.name);
|
||||
ApplyEllipsis('#hosts_table .host-name a');
|
||||
// Close modal
|
||||
Wait('stop');
|
||||
$('#form-modal').modal('hide');
|
||||
$('#host-modal-dialog').dialog('close');
|
||||
}, 2000);
|
||||
}
|
||||
else {
|
||||
// Close modal
|
||||
Wait('stop');
|
||||
$('#form-modal').modal('hide');
|
||||
$('#host-modal-dialog').dialog('close');
|
||||
}
|
||||
// Restore ellipsis response to window resize
|
||||
WatchInventoryWindowResize();
|
||||
});
|
||||
|
||||
// Save changes to the parent
|
||||
scope.formModalAction = function() {
|
||||
|
||||
scope.saveModal = function() {
|
||||
|
||||
Wait('start');
|
||||
var fld, data={};
|
||||
|
||||
data.variables = ToJSON(scope.parseType, scope.variables, true);
|
||||
for (fld in form.fields) {
|
||||
data[fld] = scope[fld];
|
||||
try {
|
||||
data.variables = ToJSON(scope.parseType, scope.variables, true);
|
||||
for (fld in form.fields) {
|
||||
data[fld] = scope[fld];
|
||||
}
|
||||
data.inventory = inventory_id;
|
||||
Rest.setUrl(defaultUrl);
|
||||
Rest.put(data)
|
||||
.success( function() {
|
||||
scope.$emit('saveCompleted');
|
||||
})
|
||||
.error( function(data, status) {
|
||||
Wait('stop');
|
||||
ProcessErrors(scope, data, status, form,
|
||||
{ hdr: 'Error!', msg: 'Failed to update host: ' + host_id + '. PUT returned status: ' + status });
|
||||
});
|
||||
}
|
||||
catch(e) {
|
||||
// ignore. ToJSON will have already alerted the user
|
||||
}
|
||||
data.inventory = inventory_id;
|
||||
Rest.setUrl(defaultUrl);
|
||||
Rest.put(data)
|
||||
.success( function() {
|
||||
scope.$emit('saveCompleted');
|
||||
})
|
||||
.error( function(data, status) {
|
||||
Wait('stop');
|
||||
ProcessErrors(scope, data, status, form,
|
||||
{ hdr: 'Error!', msg: 'Failed to update host: ' + host_id + '. PUT returned status: ' + status });
|
||||
});
|
||||
};
|
||||
|
||||
// Cancel
|
||||
@ -568,7 +623,7 @@ function($rootScope, $location, $log, $routeParams, Rest, Alert, HostForm, Gener
|
||||
};
|
||||
|
||||
scope.cancelModal = function() {
|
||||
WatchInventoryWindowResize();
|
||||
$('#host-modal-dialog').dialog('close');
|
||||
};
|
||||
|
||||
};
|
||||
|
@ -124,7 +124,7 @@ angular.module('VariablesHelper', ['Utilities'])
|
||||
var i, keys = Object.keys(objToSort), newObj = {};
|
||||
keys = keys.sort();
|
||||
for (i=0; i < keys.length; i++) {
|
||||
if (typeof objToSort[keys[i]] === 'object' && !Array.isArray(objToSort[keys[i]])) {
|
||||
if (typeof objToSort[keys[i]] === 'object' && objToSort[keys[i]] !== null && !Array.isArray(objToSort[keys[i]])) {
|
||||
newObj[keys[i]] = sortIt(objToSort[keys[i]]);
|
||||
}
|
||||
else {
|
||||
@ -133,7 +133,6 @@ angular.module('VariablesHelper', ['Utilities'])
|
||||
}
|
||||
return newObj;
|
||||
}
|
||||
|
||||
newObj = sortIt(variableObj);
|
||||
return newObj;
|
||||
};
|
||||
|
@ -83,3 +83,7 @@ table.ui-datepicker-calendar {
|
||||
background-color: #000;
|
||||
opacity: .6;
|
||||
}
|
||||
|
||||
.ui-dialog-content.ui-widget-content {
|
||||
padding-top: 20px;
|
||||
}
|
||||
|
189
awx/ui/static/lib/ansible/Modal.js
Normal file
189
awx/ui/static/lib/ansible/Modal.js
Normal file
@ -0,0 +1,189 @@
|
||||
/************************************
|
||||
*
|
||||
* Copyright (c) 2014 AnsibleWorks, Inc.
|
||||
*
|
||||
* Modal.js
|
||||
*
|
||||
* Create a draggable, resizable modal dialog using jQueryUI.
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
angular.module('ModalDialog', ['Utilities', 'ParseHelper'])
|
||||
|
||||
/**
|
||||
*
|
||||
* CreateDialog({
|
||||
* scope: - Required, $scope associated with the #id DOM element
|
||||
* buttons: - Required, Array of button objects. See example below.
|
||||
* width: - Desired width of modal dialog on open. Defaults to 500.
|
||||
* height: - Desired height of modal on open. Defaults to 600.
|
||||
* minWidth: - Minimum width that must be maintained regardless of reize attempts. Defaults to 400.
|
||||
* title: - Modal window title, optional
|
||||
* onResizeStop: - Function to call when user stops resizing the dialog, optional
|
||||
* onClose: - Function to call after window closes, optional
|
||||
* onOpen: - Function to call after window opens, optional
|
||||
* 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
|
||||
* option to respond to dialog created event.
|
||||
*/
|
||||
.factory('CreateDialog', ['Empty', function(Empty) {
|
||||
|
||||
return function(params) {
|
||||
|
||||
var scope = params.scope,
|
||||
buttonSet = params.buttons,
|
||||
width = params.width || 500,
|
||||
height = params.height || 600,
|
||||
minWidth = params.minWidth || 300,
|
||||
title = params.title || '',
|
||||
onResizeStop = params.onResizeStop,
|
||||
onClose = params.onClose,
|
||||
onOpen = params.onOpen,
|
||||
callback = params.callback,
|
||||
buttons,
|
||||
id = params.id,
|
||||
x, y, wh, ww;
|
||||
|
||||
if (Empty(buttonSet)) {
|
||||
// Default button object
|
||||
buttonSet = [{
|
||||
label: "OK",
|
||||
onClick: function() {
|
||||
scope.modalOK();
|
||||
},
|
||||
icon: "",
|
||||
"class": "btn btn-primary",
|
||||
"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();
|
||||
x = (width > ww) ? ww - 10 : width;
|
||||
y = (height > wh) ? wh - 10 : height;
|
||||
|
||||
// Create the modal
|
||||
$('#' + id).dialog({
|
||||
buttons: buttons,
|
||||
modal: true,
|
||||
width: x,
|
||||
height: y,
|
||||
autoOpen: false,
|
||||
minWidth: minWidth,
|
||||
title: title,
|
||||
create: function () {
|
||||
// Fix the close button
|
||||
$('.ui-dialog[aria-describedby="' + id + '"]').find('.ui-dialog-titlebar button').empty().attr({'class': 'close'}).text('x');
|
||||
|
||||
// Make buttons bootstrapy
|
||||
$('.ui-dialog[aria-describedby="' + id + '"]').find('.ui-dialog-buttonset button').each(function () {
|
||||
var txt = $(this).text(), self = $(this);
|
||||
buttonSet.forEach(function(btn) {
|
||||
if (txt === btn.label) {
|
||||
self.attr({ "class": btn['class'], "id": btn.id });
|
||||
if (btn.icon) {
|
||||
self.empty().html('<i class="fa ' + btn.icon + '"></i> ' + btn.label);
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
setTimeout(function() {
|
||||
scope.$apply(function() {
|
||||
scope.$emit(callback);
|
||||
});
|
||||
}, 300);
|
||||
},
|
||||
resizeStop: function () {
|
||||
// for some reason, after resizing dialog the form and fields (the content) doesn't expand to 100%
|
||||
var dialog = $('.ui-dialog[aria-describedby="' + id + '"]'),
|
||||
content = dialog.find('#' + id);
|
||||
content.width(dialog.width() - 28);
|
||||
if (onResizeStop) {
|
||||
onResizeStop();
|
||||
}
|
||||
},
|
||||
close: function () {
|
||||
// Destroy on close
|
||||
$('.tooltip').each(function () {
|
||||
// Remove any lingering tooltip <div> elements
|
||||
$(this).remove();
|
||||
});
|
||||
$('.popover').each(function () {
|
||||
// remove lingering popover <div> elements
|
||||
$(this).remove();
|
||||
});
|
||||
$('#' + id).dialog('destroy');
|
||||
$('#' + id).hide();
|
||||
if (onClose) {
|
||||
onClose();
|
||||
}
|
||||
},
|
||||
open: function () {
|
||||
if (onOpen) {
|
||||
onOpen();
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
}])
|
||||
|
||||
/**
|
||||
* 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
|
||||
* })
|
||||
*
|
||||
* 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,
|
||||
textarea,
|
||||
formHeight, model, windowHeight, offset, rows;
|
||||
|
||||
function waitStop() {
|
||||
Wait('stop');
|
||||
}
|
||||
|
||||
// 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) {
|
||||
model = textarea.attr('ng-model');
|
||||
scope[model] = scope.codeMirror.getValue();
|
||||
scope.codeMirror.destroy();
|
||||
}
|
||||
textarea.attr('rows', 1);
|
||||
formHeight = $('#' + formId).height();
|
||||
windowHeight = $('#' + modalId).height() - 20; //leave a margin of 20px
|
||||
offset = Math.floor(windowHeight - formHeight);
|
||||
rows = Math.floor(offset / 20);
|
||||
rows = (rows < 6) ? 6 : rows;
|
||||
textarea.attr('rows', rows);
|
||||
while(rows > 6 && $('#' + formId).height() > $('#' + modalId).height()) {
|
||||
rows--;
|
||||
textarea.attr('rows', rows);
|
||||
}
|
||||
ParseTypeChange({ scope: scope, field_id: textareaId, onReady: waitStop });
|
||||
};
|
||||
}]);
|
@ -483,6 +483,7 @@ angular.module('FormGenerator', ['GeneratorHelpers', 'ngCookies', 'Utilities'])
|
||||
html += (field.falseValue !== undefined) ? Attr(field, 'falseValue') : "";
|
||||
html += (field.checked) ? "checked " : "";
|
||||
html += (field.readonly) ? "disabled " : "";
|
||||
html += (field.ngDisabled) ? "ng-disabled=\"" + field.ngDisabled + "\" " : "";
|
||||
html += " > ";
|
||||
|
||||
if (label) {
|
||||
|
@ -47,6 +47,8 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="host-modal-dialog" style="display: none;" class="dialog-content"></div>
|
||||
|
||||
</div>
|
||||
</div>
|
@ -62,6 +62,7 @@
|
||||
<script src="{{ STATIC_URL }}lib/ansible/pwdmeter.js"></script>
|
||||
<script src="{{ STATIC_URL }}lib/ansible/InventoryTree.js"></script>
|
||||
<script src="{{ STATIC_URL }}lib/ansible/Timer.js"></script>
|
||||
<script src="{{ STATIC_URL }}lib/ansible/Modal.js"></script>
|
||||
<script src="{{ STATIC_URL }}js/controllers/Authentication.js"></script>
|
||||
<script src="{{ STATIC_URL }}js/controllers/Organizations.js"></script>
|
||||
<script src="{{ STATIC_URL }}js/controllers/Users.js"></script>
|
||||
|
Loading…
Reference in New Issue
Block a user