1
0
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:
Chris Houseknecht 2014-03-20 12:25:20 -04:00
parent 6f11502528
commit 1bc9ab68eb
9 changed files with 300 additions and 36 deletions

View File

@ -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: {}

View File

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

View File

@ -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');
};
};

View File

@ -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;
};

View File

@ -83,3 +83,7 @@ table.ui-datepicker-calendar {
background-color: #000;
opacity: .6;
}
.ui-dialog-content.ui-widget-content {
padding-top: 20px;
}

View 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 });
};
}]);

View File

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

View File

@ -47,6 +47,8 @@
</div>
</div>
</div>
<div id="host-modal-dialog" style="display: none;" class="dialog-content"></div>
</div>
</div>

View File

@ -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>