Implements AJAX form posting.
This is somewhat of a hack for Essex, since the long-term solution is a reworking of the way AJAX is handled. But it solves problems in the interim and provides a significantly better experience. Thanks to Andy Chong for pushing forward with the initial attempts which lead to this patch. Fixes bug 943518. Change-Id: Ia65d926d3d406b07301e23b4c87de60c66ddec75
This commit is contained in:
parent
0a640c300e
commit
c11cd9d1c5
@ -16,6 +16,7 @@
|
||||
|
||||
import os
|
||||
|
||||
from django import http
|
||||
from django.views import generic
|
||||
|
||||
|
||||
@ -62,6 +63,14 @@ class ModalFormView(generic.TemplateView):
|
||||
self.object = self.get_object(*args, **kwargs)
|
||||
form, handled = self.maybe_handle()
|
||||
if handled:
|
||||
if self.request.is_ajax():
|
||||
# TODO(gabriel): This is not a long-term solution to how
|
||||
# AJAX should be handled, but it's an expedient solution
|
||||
# until the blueprint for AJAX handling is architected
|
||||
# and implemented.
|
||||
response = http.HttpResponse()
|
||||
response['X-Horizon-Location'] = handled['location']
|
||||
return response
|
||||
return handled
|
||||
context = self.get_context_data(**kwargs)
|
||||
context[self.context_form_name] = form
|
||||
|
@ -68,7 +68,7 @@ class HorizonMiddleware(object):
|
||||
messages.error(request, unicode(exception))
|
||||
if request.is_ajax():
|
||||
response_401 = http.HttpResponse(status=401)
|
||||
response_401["REDIRECT_URL"] = redirect_to
|
||||
response_401['X-Horizon-Location'] = redirect_to
|
||||
return response_401
|
||||
return shortcuts.redirect(redirect_to)
|
||||
|
||||
|
@ -1,9 +1,81 @@
|
||||
horizon.modals.success = function (data, textStatus, jqXHR) {
|
||||
$('body').append(data);
|
||||
$('.modal span.help-block').hide();
|
||||
$('.modal:last').modal();
|
||||
|
||||
horizon.datatables.validate_button();
|
||||
|
||||
// TODO(tres): Find some better way to deal with grouped form fields.
|
||||
var volumeField = $("#id_volume");
|
||||
if(volumeField) {
|
||||
var volumeContainer = volumeField.parent().parent();
|
||||
var deviceContainer = $("#id_device_name").parent().parent();
|
||||
var deleteOnTermContainer = $("#id_delete_on_terminate").parent().parent();
|
||||
|
||||
function toggle_fields(show) {
|
||||
if(show) {
|
||||
volumeContainer.removeClass("hide");
|
||||
deviceContainer.removeClass("hide");
|
||||
deleteOnTermContainer.removeClass("hide");
|
||||
} else {
|
||||
volumeContainer.addClass("hide");
|
||||
deviceContainer.addClass("hide");
|
||||
deleteOnTermContainer.addClass("hide");
|
||||
}
|
||||
}
|
||||
|
||||
if(volumeField.find("option").length == 1) {
|
||||
toggle_fields(false);
|
||||
} else {
|
||||
var disclosureElement = $("<div />").addClass("volume_boot_disclosure").text("Boot From Volume");
|
||||
|
||||
volumeContainer.before(disclosureElement);
|
||||
|
||||
disclosureElement.click(function() {
|
||||
if(volumeContainer.hasClass("hide")) {
|
||||
disclosureElement.addClass("on");
|
||||
toggle_fields(true);
|
||||
} else {
|
||||
disclosureElement.removeClass("on");
|
||||
toggle_fields(false);
|
||||
}
|
||||
});
|
||||
|
||||
toggle_fields(false);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
horizon.addInitFunction(function() {
|
||||
$(document).on('click', '.modal:not(.static_page) .cancel', function (evt) {
|
||||
$(this).closest('.modal').modal('hide');
|
||||
return false;
|
||||
});
|
||||
|
||||
$(document).on('submit', '.modal:not(.static_page) form', function (evt) {
|
||||
var $form = $(this);
|
||||
evt.preventDefault();
|
||||
$.ajax({
|
||||
type: "POST",
|
||||
url: $form.attr('action'),
|
||||
data: $form.serialize(),
|
||||
success: function (data, textStatus, jqXHR) {
|
||||
// TODO(gabriel): This isn't a long-term solution for AJAX redirects.
|
||||
// https://blueprints.launchpad.net/horizon/+spec/global-ajax-communication
|
||||
var header = jqXHR.getResponseHeader("X-Horizon-Location");
|
||||
if (header) {
|
||||
location.href = header;
|
||||
}
|
||||
$form.closest(".modal").modal("hide");
|
||||
horizon.modals.success(data, textStatus, jqXHR);
|
||||
},
|
||||
error: function(jqXHR, status, errorThrown) {
|
||||
$form.closest(".modal").modal("hide");
|
||||
horizon.alert("error", "There was an error submitting the form. Please try again.");
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// Handle all modal hidden event to remove them as default
|
||||
$(document).on('hidden', '.modal', function () {
|
||||
$(this).remove();
|
||||
@ -12,9 +84,9 @@ horizon.addInitFunction(function() {
|
||||
$('.ajax-modal').click(function (evt) {
|
||||
var $this = $(this);
|
||||
$.ajax($this.attr('href'), {
|
||||
error: function(jqXHR, status, errorThrown){
|
||||
error: function(jqXHR, status, errorThrown) {
|
||||
if (jqXHR.status === 401){
|
||||
var redir_url = jqXHR.getResponseHeader("REDIRECT_URL");
|
||||
var redir_url = jqXHR.getResponseHeader("X-Horizon-Location");
|
||||
if (redir_url){
|
||||
location.href = redir_url;
|
||||
} else {
|
||||
@ -22,53 +94,7 @@ horizon.addInitFunction(function() {
|
||||
}
|
||||
}
|
||||
},
|
||||
success: function (data, status, jqXHR) {
|
||||
$('body').append(data);
|
||||
$('.modal span.help-block').hide();
|
||||
$('.modal:last').modal();
|
||||
|
||||
horizon.datatables.validate_button();
|
||||
|
||||
// TODO(tres): Find some better way to deal with grouped form fields.
|
||||
var volumeField = $("#id_volume");
|
||||
if(volumeField) {
|
||||
var volumeContainer = volumeField.parent().parent();
|
||||
var deviceContainer = $("#id_device_name").parent().parent();
|
||||
var deleteOnTermContainer = $("#id_delete_on_terminate").parent().parent();
|
||||
|
||||
function toggle_fields(show) {
|
||||
if(show) {
|
||||
volumeContainer.removeClass("hide");
|
||||
deviceContainer.removeClass("hide");
|
||||
deleteOnTermContainer.removeClass("hide");
|
||||
} else {
|
||||
volumeContainer.addClass("hide");
|
||||
deviceContainer.addClass("hide");
|
||||
deleteOnTermContainer.addClass("hide");
|
||||
}
|
||||
}
|
||||
|
||||
if(volumeField.find("option").length == 1) {
|
||||
toggle_fields(false);
|
||||
} else {
|
||||
var disclosureElement = $("<div />").addClass("volume_boot_disclosure").text("Boot From Volume");
|
||||
|
||||
volumeContainer.before(disclosureElement);
|
||||
|
||||
disclosureElement.click(function() {
|
||||
if(volumeContainer.hasClass("hide")) {
|
||||
disclosureElement.addClass("on");
|
||||
toggle_fields(true);
|
||||
} else {
|
||||
disclosureElement.removeClass("on");
|
||||
toggle_fields(false);
|
||||
}
|
||||
});
|
||||
|
||||
toggle_fields(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
success: horizon.modals.success
|
||||
});
|
||||
evt.preventDefault();
|
||||
});
|
||||
|
@ -213,7 +213,7 @@ class HorizonTests(BaseHorizonTests):
|
||||
resp = client.get(url, HTTP_X_REQUESTED_WITH='XMLHttpRequest')
|
||||
# Response should be HTTP 401 with redirect header
|
||||
self.assertEquals(resp.status_code, 401)
|
||||
self.assertEquals(resp["REDIRECT_URL"],
|
||||
self.assertEquals(resp["X-Horizon-Location"],
|
||||
"?".join([urlresolvers.reverse("horizon:auth_login"),
|
||||
"next=%s" % url]))
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user