Commit d3842924 authored by Steven Wittens's avatar Steven Wittens

- #47510: Show JavaScript alert when PHP errors occur

parent afde6515
......@@ -1171,6 +1171,8 @@ function drupal_call_js($function) {
/**
* Converts a PHP variable into its Javascript equivalent.
*
* We use HTML-safe strings, i.e. with <, > and & escaped.
*/
function drupal_to_js($var) {
switch (gettype($var)) {
......@@ -1180,8 +1182,18 @@ function drupal_to_js($var) {
return $var;
case 'resource':
case 'string':
return '"'. str_replace(array("\r", "\n"), array('\r', '\n'), addslashes($var)) .'"';
return '"'. str_replace(array("\r", "\n", "<", ">", "&"),
array('\r', '\n', '\x3c', '\x3e', '\x26'),
addslashes($var)) .'"';
case 'array':
if (array_keys($var) === range(0, sizeof($var) - 1)) {
$output = array();
foreach($var as $v) {
$output[] = drupal_to_js($v);
}
return '[ '. implode(', ', $output) .' ]';
}
// Fall through
case 'object':
$output = array();
foreach ($var as $k => $v) {
......
......@@ -113,24 +113,8 @@ function HTTPPost(uri, callbackFunction, callbackParameter, object) {
* window.parent.iframeHandler() after submission.
*/
function redirectFormButton(uri, button, handler) {
// Insert the iframe
// Note: some browsers require the literal name/id attributes on the tag,
// some want them set through JS. We do both.
var div = document.createElement('div');
div.innerHTML = '<iframe name="redirect-target" id="redirect-target" class="redirect"></iframe>';
var iframe = div.firstChild;
with (iframe) {
name = 'redirect-target';
setAttribute('name', 'redirect-target');
id = 'redirect-target';
}
with (iframe.style) {
position = 'absolute';
height = '1px';
width = '1px';
visibility = 'hidden';
}
document.body.appendChild(iframe);
// Make sure we have an iframe to target
createIframe();
// Trap the button
button.onmouseover = button.onfocus = function() {
......@@ -147,11 +131,34 @@ function redirectFormButton(uri, button, handler) {
handler.onsubmit();
// Set iframe handler for later
window.iframeHandler = function (data) {
window.iframeHandler = function () {
var iframe = $('redirect-target');
// Restore form submission
button.form.action = action;
button.form.target = target;
handler.oncomplete(data);
// Get response from iframe body
try {
response = (iframe.contentWindow || iframe.contentDocument || iframe).document.body.innerHTML;
if (window.opera) {
// Opera-hack: it returns innerHTML sanitized.
response = response.replace(/&quot;/g, '"');
}
}
catch (e) {
response = null;
}
// Recreate the iframe: re-using an old iframe can sometimes cause browser bugs.
createIframe();
response = parseJson(response);
// Check response code
if (response.status == 0) {
handler.onerror(response.data);
return;
}
handler.oncomplete(response.data);
}
return true;
......@@ -300,6 +307,55 @@ function stopEvent(event) {
}
}
/**
* Parse a JSON response.
*
* The result is either the JSON object, or an object with 'status' 0 and 'data' an error message.
*/
function parseJson(data) {
if (data.substring(0,1) != '{') {
return { status: 0, data: data.length ? data : 'Unspecified error' };
}
return eval('(' + data + ');');
}
/**
* Create an invisible iframe for form submissions.
*/
function createIframe() {
// Delete any previous iframe
deleteIframe();
// Note: some browsers require the literal name/id attributes on the tag,
// some want them set through JS. We do both.
window.iframeHandler = function () {};
var div = document.createElement('div');
div.id = 'redirect-holder';
div.innerHTML = '<iframe name="redirect-target" id="redirect-target" class="redirect" onload="window.iframeHandler();"></iframe>';
var iframe = div.firstChild;
with (iframe) {
name = 'redirect-target';
setAttribute('name', 'redirect-target');
id = 'redirect-target';
}
with (iframe.style) {
position = 'absolute';
height = '1px';
width = '1px';
visibility = 'hidden';
}
document.body.appendChild(div);
}
/**
* Delete the invisible iframe for form submissions.
*/
function deleteIframe() {
var holder = $('redirect-holder');
if (typeof holder != 'undefined') {
removeNode(holder);
}
}
/**
* Wrapper around document.getElementById().
*/
......
......@@ -3,7 +3,7 @@
* the DOM afterwards through progressBar.element.
*
* method is the function which will perform the HTTP request to get the
* progress bar status. Either HTTPGet or HTTPPost.
* progress bar state. Either HTTPGet or HTTPPost.
*
* e.g. pb = new progressBar('myProgressBar');
* some_element.appendChild(pb.element);
......@@ -18,14 +18,14 @@ function progressBar(id, callback, method) {
this.element.id = id;
this.element.className = 'progress';
this.element.innerHTML = '<div class="percentage"></div>'+
'<div class="status">&nbsp;</div>'+
'<div class="message">&nbsp;</div>'+
'<div class="bar"><div class="filled"></div></div>';
}
/**
* Set the percentage and status message for the progressbar.
*/
progressBar.prototype.setProgress = function (percentage, status) {
progressBar.prototype.setProgress = function (percentage, message) {
var divs = this.element.getElementsByTagName('div');
var div;
for (var i = 0; div = divs[i]; ++i) {
......@@ -37,12 +37,12 @@ progressBar.prototype.setProgress = function (percentage, status) {
divs[i].innerHTML = percentage + '%';
}
}
if (hasClass(divs[i], 'status')) {
divs[i].innerHTML = status;
if (hasClass(divs[i], 'message')) {
divs[i].innerHTML = message;
}
}
if (this.callback) {
this.callback(percentage, status, this);
this.callback(percentage, message, this);
}
}
......@@ -84,12 +84,16 @@ progressBar.prototype.receivePing = function (string, xmlhttp, pb) {
if (xmlhttp.status != 200) {
return alert('An HTTP error '+ xmlhttp.status +' occured.\n'+ pb.uri);
}
// Split into values
var matches = string.length > 0 ? string.split('|') : [];
// Update progress
if (matches.length >= 2) {
pb.setProgress(matches[0], matches[1]);
// Parse response
var progress = parseJson(string);
// Display errors
if (progress.status == 0) {
alert(progress.data);
return;
}
// Update display
pb.setProgress(progress.percentage, progress.message);
// Schedule next timer
pb.timer = setTimeout(function() { pb.sendPing(); }, pb.delay);
}
......@@ -13,7 +13,7 @@ if (isJsEnabled()) {
}
var progress = new progressBar('updateprogress', updateCallback, HTTPPost);
progress.setProgress(-1, 'Starting updates...');
progress.setProgress(-1, 'Starting updates');
$('progress').appendChild(progress.element);
progress.startMonitoring('update.php?op=do_update', 0);
}
......
......@@ -54,9 +54,22 @@ jsUpload.prototype.onsubmit = function () {
*/
jsUpload.prototype.oncomplete = function (data) {
// Remove progressbar
removeNode(this.progress);
removeNode(this.progress.element);
this.progress = null;
// Replace form and re-attach behaviour
$(this.wrapper).innerHTML = data;
uploadAutoAttach();
}
/**
* Handler for the form redirection error.
*/
jsUpload.prototype.onerror = function (error) {
alert('An error occurred:\n\n'+ error);
// Remove progressbar
removeNode(this.progress.element);
this.progress = null;
// Undo hide
$(this.hide).style.position = 'static';
$(this.hide).style.left = '0px';
}
......@@ -543,6 +543,6 @@ function upload_js() {
$output = theme('status_messages') . form_render($form);
// We send the updated file attachments form.
print drupal_call_js('window.parent.iframeHandler', $output);
print drupal_to_js(array('status' => TRUE, 'data' => $output));
exit;
}
......@@ -543,6 +543,6 @@ function upload_js() {
$output = theme('status_messages') . form_render($form);
// We send the updated file attachments form.
print drupal_call_js('window.parent.iframeHandler', $output);
print drupal_to_js(array('status' => TRUE, 'data' => $output));
exit;
}
......@@ -396,7 +396,7 @@ function update_progress_page() {
*
* @return
* An array indicating the status after doing updates. The first element is
* the overall percent finished. The second element is a status message.
* the overall percentage finished. The second element is a status message.
*/
function update_do_updates() {
while (($update = reset($_SESSION['update_remaining']))) {
......@@ -412,12 +412,12 @@ function update_do_updates() {
}
if ($_SESSION['update_total']) {
$percent = floor(($_SESSION['update_total'] - count($_SESSION['update_remaining']) + $update_finished) / $_SESSION['update_total'] * 100);
$percentage = floor(($_SESSION['update_total'] - count($_SESSION['update_remaining']) + $update_finished) / $_SESSION['update_total'] * 100);
}
else {
$percent = 100;
$percentage = 100;
}
return array($percent, isset($update['module']) ? 'Updating '. $update['module'] .' module' : 'Updating complete');
return array($percentage, isset($update['module']) ? 'Updating '. $update['module'] .' module' : 'Updating complete');
}
function update_do_update_page() {
......@@ -437,26 +437,27 @@ function update_do_update_page() {
}
ini_set('display_errors', FALSE);
print implode('|', update_do_updates());
list($percentage, $message) = update_do_updates();
print drupal_to_js(array('status' => TRUE, 'percentage' => $percentage, 'message' => $message));
}
function update_progress_page_nojs() {
$new_op = 'do_update_nojs';
if ($_SERVER['REQUEST_METHOD'] == 'GET') {
list($percent, $message) = update_do_updates();
if ($percent == 100) {
list($percentage, $message) = update_do_updates();
if ($percentage == 100) {
$new_op = 'finished';
}
}
else {
// This is the first page so return some output immediately.
$percent = 0;
$percentage = 0;
$message = 'Starting updates';
}
drupal_set_html_head('<meta http-equiv="Refresh" content="0; URL=update.php?op='. $new_op .'">');
drupal_set_title('Updating');
$output = theme('progress_bar', $percent, $message);
$output = theme('progress_bar', $percentage, $message);
$output .= '<p>Updating your site will take a few seconds.</p>';
return $output;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment