From e29306e69c56ff86e6f35f4e44007ce250ad51de Mon Sep 17 00:00:00 2001 From: Ryan Boren Date: Fri, 29 Feb 2008 09:51:36 +0000 Subject: [PATCH] Post Edit Collision Detection from mdawaffe. fixes #6043 git-svn-id: https://develop.svn.wordpress.org/trunk@7103 602fd350-edb4-49c9-b593-d223f7449a82 --- wp-admin/admin-ajax.php | 50 +++++-- wp-admin/edit-form-advanced.php | 23 ++- wp-admin/edit-page-form.php | 7 +- wp-admin/includes/post.php | 36 ++++- wp-admin/includes/upgrade.php | 4 +- wp-admin/page.php | 11 +- wp-admin/post.php | 11 +- wp-admin/wp-admin.css | 9 ++ wp-includes/classes.php | 4 +- wp-includes/js/autosave.js | 217 +++++++++++++++-------------- wp-includes/js/wp-ajax-response.js | 56 ++++++++ wp-includes/js/wp-lists.js | 66 ++------- wp-includes/script-loader.php | 21 +-- wp-includes/version.php | 2 +- 14 files changed, 314 insertions(+), 203 deletions(-) create mode 100644 wp-includes/js/wp-ajax-response.js diff --git a/wp-admin/admin-ajax.php b/wp-admin/admin-ajax.php index 93814fbcba..ec9c3e316c 100644 --- a/wp-admin/admin-ajax.php +++ b/wp-admin/admin-ajax.php @@ -470,6 +470,8 @@ case 'add-user' : break; case 'autosave' : // The name of this action is hardcoded in edit_post() check_ajax_referer( 'autosave', 'autosavenonce' ); + global $current_user; + $_POST['post_content'] = $_POST['content']; $_POST['post_excerpt'] = $_POST['excerpt']; $_POST['post_status'] = 'draft'; @@ -478,17 +480,36 @@ case 'autosave' : // The name of this action is hardcoded in edit_post() if($_POST['post_type'] == 'page' || empty($_POST['post_category'])) unset($_POST['post_category']); + $do_autosave = (bool) $_POST['autosave']; + $do_lock = true; + + $data = '

' . sprintf( __('Saved at %s.'), date( __('g:i:s a'), current_time( 'timestamp', true ) ) ) . '

'; + + $supplemental = array(); + + $id = 0; if($_POST['post_ID'] < 0) { $_POST['temp_ID'] = $_POST['post_ID']; - $id = wp_write_post(); - if( is_wp_error($id) ) - die($id->get_error_message()); - else - die("$id"); + if ( $do_autosave ) + $id = wp_write_post(); } else { $post_ID = (int) $_POST['post_ID']; $_POST['ID'] = $post_ID; $post = get_post($post_ID); + + if ( $last = wp_check_post_lock( $post->ID ) ) { + $do_autosave = $do_lock = false; + + $last_user = get_userdata( $last ); + $last_user_name = $last_user ? $last_user->display_name : __( 'Someone' ); + $data = new WP_Error( 'locked', sprintf( + $_POST['post_type'] == 'page' ? __( 'Autosave disabled: %s is currently editing this page.' ) : __( 'Autosave disabled: %s is currently editing this post.' ), + wp_specialchars( $last_user_name ) + ) ); + + $supplemental['disable_autosave'] = 'disable'; + } + if ( 'page' == $post->post_type ) { if ( !current_user_can('edit_page', $post_ID) ) die(__('You are not allowed to edit this page.')); @@ -496,10 +517,23 @@ case 'autosave' : // The name of this action is hardcoded in edit_post() if ( !current_user_can('edit_post', $post_ID) ) die(__('You are not allowed to edit this post.')); } - wp_update_post($_POST); + if ( $do_autosave ) + $id = wp_update_post($_POST); + else + $id = $post->ID; } - die('0'); -break; + + if ( $do_lock && $id && is_numeric($id) ) + wp_set_post_lock( $id ); + + $x = new WP_Ajax_Response( array( + 'what' => 'autosave', + 'id' => $id, + 'data' => $id ? $data : '', + 'supplemental' => $supplemental + ) ); + $x->send(); + break; case 'autosave-generate-nonces' : check_ajax_referer( 'autosave', 'autosavenonce' ); $ID = (int) $_POST['post_ID']; diff --git a/wp-admin/edit-form-advanced.php b/wp-admin/edit-form-advanced.php index 1fe4fb841c..487e6b1a70 100644 --- a/wp-admin/edit-form-advanced.php +++ b/wp-admin/edit-form-advanced.php @@ -55,6 +55,15 @@ $saveasdraft = ' + + post_title) && '' == $post->post_title) || (isset($_GET['message']) && 2 > $_GET['message'])) : ?> @@ -189,19 +198,7 @@ endif; ?> -

- - - - -

+

diff --git a/wp-admin/edit-page-form.php b/wp-admin/edit-page-form.php index 72457212c5..9998333941 100644 --- a/wp-admin/edit-page-form.php +++ b/wp-admin/edit-page-form.php @@ -36,6 +36,7 @@ if (isset($mode) && 'bookmarklet' == $mode) + - - - -

+
diff --git a/wp-admin/includes/post.php b/wp-admin/includes/post.php index 64dd32e8c7..35fb43810e 100644 --- a/wp-admin/includes/post.php +++ b/wp-admin/includes/post.php @@ -18,8 +18,7 @@ function edit_post() { $post =& get_post( $post_ID ); $now = time(); $then = strtotime($post->post_date_gmt . ' +0000'); - // Keep autosave_interval in sync with autosave-js.php. - $delta = apply_filters( 'autosave_interval', 120 ) / 2; + $delta = get_option( 'autosave_interval' ) / 2; if ( ($now - $then) < $delta ) return $post_ID; } @@ -619,4 +618,37 @@ function get_sample_permalink_html($id, $new_slug=null) { return $return; } +// false: not locked or locked by current user +// int: user ID of user with lock +function wp_check_post_lock( $post_id ) { + global $current_user; + + if ( !$post = get_post( $post_id ) ) + return false; + + $lock = get_post_meta( $post->ID, '_edit_lock', true ); + $last = get_post_meta( $post->ID, '_edit_last', true ); + + $time_window = apply_filters( 'wp_check_post_lock_window', get_option( 'autosave_interval' ) * 2 ); + + if ( $lock && $lock > time() - $time_window && $last != $current_user->ID ) + return $last; + return false; +} + +function wp_set_post_lock( $post_id ) { + global $current_user; + if ( !$post = get_post( $post_id ) ) + return false; + if ( !$current_user || !$current_user->ID ) + return false; + + $now = time(); + + if ( !add_post_meta( $post->ID, '_edit_lock', $now, true ) ) + update_post_meta( $post->ID, '_edit_lock', $now ); + if ( !add_post_meta( $post->ID, '_edit_last', $current_user->ID, true ) ) + update_post_meta( $post->ID, '_edit_last', $current_user->ID ); +} + ?> diff --git a/wp-admin/includes/upgrade.php b/wp-admin/includes/upgrade.php index 5bbf85a288..d9668c5b31 100644 --- a/wp-admin/includes/upgrade.php +++ b/wp-admin/includes/upgrade.php @@ -198,7 +198,7 @@ function upgrade_all() { if ( $wp_current_db_version < 6124 ) upgrade_230_old_tables(); - if ( $wp_current_db_version < 6689 ) + if ( $wp_current_db_version < 7098 ) upgrade_250(); maybe_disable_automattic_widgets(); @@ -725,6 +725,8 @@ function upgrade_250() { if ( $wp_current_db_version < 6689 ) { populate_roles_250(); } + + add_option('autosave_interval', 60); } // The functions we use to actually do stuff diff --git a/wp-admin/page.php b/wp-admin/page.php index be779f87bd..7b141f3135 100644 --- a/wp-admin/page.php +++ b/wp-admin/page.php @@ -57,9 +57,16 @@ case 'edit': wp_enqueue_script('editor'); wp_enqueue_script('thickbox'); wp_enqueue_script('media-upload'); - - if ( 'draft' == $post->post_status ) + if ( $last = wp_check_post_lock( $post->ID ) ) { + $last_user = get_userdata( $last ); + $last_user_name = $last_user ? $last_user->display_name : __('Somebody'); + $message = sprintf( __( '%s is currently editing this page' ), wp_specialchars( $last_user_name ) ); + $message = str_replace( "'", "\'", "

$message

" ); + add_action('admin_notices', create_function( '', "echo '$message';" ) ); + } else { + wp_set_post_lock( $post->ID ); wp_enqueue_script('autosave'); + } require_once('admin-header.php'); diff --git a/wp-admin/post.php b/wp-admin/post.php index a0c7ddaa3d..b9635e1654 100644 --- a/wp-admin/post.php +++ b/wp-admin/post.php @@ -66,9 +66,16 @@ case 'edit': wp_enqueue_script('editor'); wp_enqueue_script('thickbox'); wp_enqueue_script('media-upload'); - - if ( 'draft' == $post->post_status ) + if ( $last = wp_check_post_lock( $post->ID ) ) { + $last_user = get_userdata( $last ); + $last_user_name = $last_user ? $last_user->display_name : __('Somebody'); + $message = sprintf( __( '%s is currently editing this post' ), wp_specialchars( $last_user_name ) ); + $message = str_replace( "'", "\'", "

$message

" ); + add_action('admin_notices', create_function( '', "echo '$message';" ) ); + } else { + wp_set_post_lock( $post->ID ); wp_enqueue_script('autosave'); + } require_once('admin-header.php'); diff --git a/wp-admin/wp-admin.css b/wp-admin/wp-admin.css index 711014f40f..616d49a9df 100644 --- a/wp-admin/wp-admin.css +++ b/wp-admin/wp-admin.css @@ -1033,6 +1033,15 @@ html, body { margin-right: 8px; } +#poststuff #autosave { + margin: 0; + padding: 0; +} + +#poststuff #autosave div.updated, #poststuff #autosave div.error { + margin: 0 8px 10px 20px; +} + #poststuff .inside { margin: 0 12px 15px; font-size: 11px; diff --git a/wp-includes/classes.php b/wp-includes/classes.php index f2f3561a6d..b54074627f 100644 --- a/wp-includes/classes.php +++ b/wp-includes/classes.php @@ -777,9 +777,11 @@ class WP_Ajax_Response { } $s = ''; - if ( (array) $supplemental ) + if ( (array) $supplemental ) { foreach ( $supplemental as $k => $v ) $s .= "<$k>"; + $s = "$s"; + } if ( false === $action ) $action = $_POST['action']; diff --git a/wp-includes/js/autosave.js b/wp-includes/js/autosave.js index dd29bde341..98d7de119c 100644 --- a/wp-includes/js/autosave.js +++ b/wp-includes/js/autosave.js @@ -1,65 +1,75 @@ var autosaveLast = ''; var autosavePeriodical; -function autosave_start_timer() { - autosaveLast = jQuery('#post #title').val()+jQuery('#post #content').val(); - // Keep autosave_interval in sync with edit_post(). - autosavePeriodical = jQuery.schedule({time: autosaveL10n.autosaveInterval * 1000, func: autosave, repeat: true, protect: true}); +jQuery(function($) { + autosaveLast = $('#post #title').val()+$('#post #content').val(); + autosavePeriodical = $.schedule({time: autosaveL10n.autosaveInterval * 1000, func: function() { autosave(); }, repeat: true, protect: true}); //Disable autosave after the form has been submitted - jQuery("#post #submit").submit(function() { jQuery.cancel(autosavePeriodical); }); - jQuery("#post #save").click(function() { jQuery.cancel(autosavePeriodical); }); - jQuery("#post #submit").click(function() { jQuery.cancel(autosavePeriodical); }); - jQuery("#post #publish").click(function() { jQuery.cancel(autosavePeriodical); }); - jQuery("#post #deletepost").click(function() { jQuery.cancel(autosavePeriodical); }); + $("#post").submit(function() { $.cancel(autosavePeriodical); }); - // Autosave early on for a new post - jQuery("#content").keypress(function() { - if ( 1 === ( jQuery(this).val().length % 15 ) && 1 > parseInt(jQuery("#post_ID").val(),10) ) + // Autosave early on for a new post. Why? Should this only be run once? + $("#content").keypress(function() { + if ( 1 === ( $(this).val().length % 15 ) && 1 > parseInt($("#post_ID").val(),10) ) setTimeout(autosave, 5000); }); -} -addLoadEvent(autosave_start_timer) +}); -function autosave_cur_time() { - var now = new Date(); - return "" + ((now.getHours() >12) ? now.getHours() -12 : now.getHours()) + - ((now.getMinutes() < 10) ? ":0" : ":") + now.getMinutes() + - ((now.getSeconds() < 10) ? ":0" : ":") + now.getSeconds(); -} +// called when autosaving pre-existing post +function autosave_saved(response) { + var res = wpAjax.parseAjaxResponse(response, 'autosave'); // parse the ajax response + var message = ''; -function autosave_update_post_ID(response) { - var res = parseInt(response); - var message; + if ( res && res.responses.length ) { + message = res.responses[0].data; // The saved message or error. + // someone else is editing: disable autosave, set errors + if ( res.responses[0].supplemental && 'disable' == res.responses[0].supplemental['disable_autosave'] ) { + autosave = function() {}; + res = { errors: true }; + } - if(isNaN(res)) { - message = autosaveL10n.errorText.replace(/%response%/g, response); - } else if( res > 0 ) { - message = autosaveL10n.saveText.replace(/%time%/g, autosave_cur_time()); - jQuery('#post_ID').attr({name: "post_ID"}); - jQuery('#post_ID').val(res); - // We need new nonces - jQuery.post(autosaveL10n.requestFile, { - action: "autosave-generate-nonces", - post_ID: res, - autosavenonce: jQuery('#autosavenonce').val(), - post_type: jQuery('#post_type').val() - }, function(html) { - jQuery('#_wpnonce').val(html); - }); - jQuery('#hiddenaction').val('editpost'); - } else { - message = autosaveL10n.failText; + // if no errors: add preview link and slug UI + if ( !res.errors ) { + var postID = parseInt( res.responses[0].id ); + if ( !isNaN(postID) && postID > 0 ) { + autosave_update_preview_link(postID); + autosave_update_slug(postID); + } + } + } + if ( message ) { jQuery('#autosave').html(message); } // update autosave message + autosave_enable_buttons(); // re-enable disabled form buttons + return res; +} + +// called when autosaving new post +function autosave_update_post_ID(response) { + var res = autosave_saved(response); // parse the ajax response do the above + + // if no errors: update post_ID from the temporary value, grab new save-nonce for that new ID + if ( res && res.responses.length && !res.errors ) { + var postID = parseInt( res.responses[0].id ); + if ( !isNaN(postID) && postID > 0 ) { + if ( postID == parseInt(jQuery('#post_ID').val()) ) { return; } // no need to do this more than once + jQuery('#post_ID').attr({name: "post_ID"}); + jQuery('#post_ID').val(postID); + // We need new nonces + jQuery.post(autosaveL10n.requestFile, { + action: "autosave-generate-nonces", + post_ID: postID, + autosavenonce: jQuery('#autosavenonce').val(), + post_type: jQuery('#post_type').val() + }, function(html) { + jQuery('#_wpnonce').val(html); + }); + jQuery('#hiddenaction').val('editpost'); + } } - jQuery('#autosave').html(message); - autosave_update_preview_link(res); - autosave_update_slug(res); - autosave_enable_buttons(); } function autosave_update_preview_link(post_id) { // Add preview button if not already there - if ( ! jQuery('#previewview > *').get()[0] ) { + if ( !jQuery('#previewview > *').size() ) { var post_type = jQuery('#post_type').val(); var previewText = 'page' == post_type ? autosaveL10n.previewPageText : autosaveL10n.previewPostText; jQuery.post(autosaveL10n.requestFile, { @@ -74,79 +84,70 @@ function autosave_update_preview_link(post_id) { function autosave_update_slug(post_id) { // create slug area only if not already there - if ( 'undefined' != typeof make_slugedit_clickable && ! jQuery('#edit-slug-box > *').get()[0] ) { - jQuery.post(slugL10n.requestFile, { - action: 'sample-permalink', - post_id: post_id, - samplepermalinknonce: jQuery('#samplepermalinknonce').val()}, function(data) { + if ( jQuery.isFunction(make_slugedit_clickable) && !jQuery('#edit-slug-box > *').size() ) { + jQuery.post( + slugL10n.requestFile, + { + action: 'sample-permalink', + post_id: post_id, + samplepermalinknonce: jQuery('#samplepermalinknonce').val() + }, + function(data) { jQuery('#edit-slug-box').html(data); make_slugedit_clickable(); - }); + } + ); } } function autosave_loading() { - jQuery('#autosave').html(autosaveL10n.savingText); -} - -function autosave_saved(response) { - var res = parseInt(response); - var message; - - if(isNaN(res)) { - message = autosaveL10n.errorText.replace(/%response%/g, response); - } else { - message = autosaveL10n.saveText.replace(/%time%/g, autosave_cur_time()); - } - jQuery('#autosave').html(message); - autosave_update_preview_link(res); - autosave_update_slug(res); - autosave_enable_buttons(); -} - -function autosave_disable_buttons() { - jQuery("#post #save:enabled").attr('disabled', 'disabled'); - jQuery("#post #submit:enabled").attr('disabled', 'disabled'); - jQuery("#post #publish:enabled").attr('disabled', 'disabled'); - jQuery("#post #deletepost:enabled").attr('disabled', 'disabled'); - setTimeout('autosave_enable_buttons();', 1000); // Re-enable 1 sec later. Just gives autosave a head start to avoid collisions. + jQuery('#autosave').html('

' + autosaveL10n.savingText + '

'); } function autosave_enable_buttons() { - jQuery("#post #save:disabled").attr('disabled', ''); - jQuery("#post #submit:disabled").attr('disabled', ''); - jQuery("#post #publish:disabled").attr('disabled', ''); - jQuery("#post #deletepost:disabled").attr('disabled', ''); + jQuery("#submitpost :button:disabled, #submitpost :submit:disabled").attr('disabled', ''); } -function autosave() { - var rich = ( (typeof tinyMCE != "undefined") && tinyMCE.activeEditor && ! tinyMCE.activeEditor.isHidden() ) ? true : false; +function autosave_disable_buttons() { + jQuery("#submitpost :button:enabled, #submitpost :submit:enabled").attr('disabled', 'disabled'); + setTimeout(autosave_enable_buttons, 1000); // Re-enable 1 sec later. Just gives autosave a head start to avoid collisions. +} + +var autosave = function() { + // (bool) is rich editor enabled and active + var rich = (typeof tinyMCE != "undefined") && tinyMCE.activeEditor && !tinyMCE.activeEditor.isHidden(); var post_data = { - action: "autosave", - post_ID: jQuery("#post_ID").val() || 0, - post_title: jQuery("#title").val() || "", - autosavenonce: jQuery('#autosavenonce').val(), - tags_input: jQuery("#tags-input").val() || "", - post_type: jQuery('#post_type').val() || "" - }; + action: "autosave", + post_ID: jQuery("#post_ID").val() || 0, + post_title: jQuery("#title").val() || "", + autosavenonce: jQuery('#autosavenonce').val(), + tags_input: jQuery("#tags-input").val() || "", + post_type: jQuery('#post_type').val() || "", + autosave: 1 + }; + + // We always send the ajax request in order to keep the post lock fresh. + // This (bool) tells whether or not to write the post to the DB during the ajax request. + var doAutoSave = true; /* Gotta do this up here so we can check the length when tinyMCE is in use */ - if ( rich ) { - // Don't run while the TinyMCE spellcheck is on. - if ( tinyMCE.activeEditor.plugins.spellchecker && tinyMCE.activeEditor.plugins.spellchecker.active ) return; - tinyMCE.triggerSave(); - } + if ( rich ) { tinyMCE.triggerSave(); } post_data["content"] = jQuery("#content").val(); if ( jQuery('#post_name').val() ) post_data["post_name"] = jQuery('#post_name').val(); + // Nothing to save or no change. if(post_data["post_title"].length==0 || post_data["content"].length==0 || post_data["post_title"] + post_data["content"] == autosaveLast) { - return; + doAutoSave = false } autosave_disable_buttons(); + var origStatus = jQuery('#original_post_status').val(); + if ( 'draft' != origStatus ) // autosave currently only turned on for drafts + doAutoSave = false; + autosaveLast = jQuery("#title").val()+jQuery("#content").val(); goodcats = ([]); jQuery("[@name='post_category[]']:checked").each( function(i) { @@ -161,25 +162,27 @@ function autosave() { if( jQuery("#excerpt")) post_data["excerpt"] = jQuery("#excerpt").val(); - if ( rich ) - tinyMCE.triggerSave(); - - post_data["content"] = jQuery("#content").val(); + // Don't run while the TinyMCE spellcheck is on. Why? Who knows. + if ( rich && tinyMCE.activeEditor.plugins.spellchecker && tinyMCE.activeEditor.plugins.spellchecker.active ) { + doAutoSave = false; + } if(parseInt(post_data["post_ID"]) < 1) { post_data["temp_ID"] = post_data["post_ID"]; - jQuery.ajaxSetup({ - success: function(html) { autosave_update_post_ID(html); } - }); + var successCallback = autosave_update_post_ID; // new post } else { - jQuery.ajaxSetup({ - success: function(html) { autosave_saved(html); } - }); + var successCallback = autosave_saved; // pre-existing post } + + if ( !doAutoSave ) { + post_data['autosave'] = 0; + } + jQuery.ajax({ data: post_data, - beforeSend: function() { autosave_loading() }, + beforeSend: doAutoSave ? autosave_loading : null, type: "POST", - url: autosaveL10n.requestFile + url: autosaveL10n.requestFile, + success: successCallback }); } diff --git a/wp-includes/js/wp-ajax-response.js b/wp-includes/js/wp-ajax-response.js new file mode 100644 index 0000000000..28ba769b46 --- /dev/null +++ b/wp-includes/js/wp-ajax-response.js @@ -0,0 +1,56 @@ +wpAjax = jQuery.extend( { + unserialize: function( s ) { + var r = {}; if ( !s ) { return r; } + var q = s.split('?'); if ( q[1] ) { s = q[1]; } + var pp = s.split('&'); + for ( var i in pp ) { + if ( jQuery.isFunction(pp.hasOwnProperty) && !pp.hasOwnProperty(i) ) { continue; } + var p = pp[i].split('='); + r[p[0]] = p[1]; + } + return r; + }, + parseAjaxResponse: function( x, r, e ) { // 1 = good, 0 = strange (bad data?), -1 = you lack permission + var parsed = {}; + var re = jQuery('#' + r).html(''); + if ( x && typeof x == 'object' && x.getElementsByTagName('wp_ajax') ) { + parsed.responses = []; + parsed.errors = false; + var err = ''; + jQuery('response', x).each( function() { + var th = jQuery(this); + var child = jQuery(this.firstChild); + var response = { action: th.attr('action'), what: child.get(0).nodeName, id: child.attr('id'), oldId: child.attr('old_id'), position: child.attr('position') }; + response.data = jQuery( 'response_data', child ).text(); + response.supplemental = {}; + if ( !jQuery( 'supplemental', child ).children().each( function() { + response.supplemental[this.nodeName] = jQuery(this).text(); + } ).size() ) { response.supplemental = false } + response.errors = []; + if ( !jQuery('wp_error', child).each( function() { + var code = jQuery(this).attr('code'); + var anError = { code: code, message: this.firstChild.nodeValue, data: false }; + var errorData = jQuery('wp_error_data[code="' + code + '"]', x); + if ( errorData ) { anError.data = errorData.get(); } + var formField = jQuery( 'form-field', errorData ).text(); + if ( formField ) { code = formField; } + if ( e ) { wpAjax.invalidateForm( jQuery('#' + e + ' :input[name="' + code + '"]' ).parents('.form-field:first') ); } + err += '

' + anError.message + '

'; + response.errors.push( anError ); + parsed.errors = true; + } ).size() ) { response.errors = false; } + parsed.responses.push( response ); + } ); + if ( err.length ) { re.html( '
' + err + '
' ); } + return parsed; + } + if ( isNaN(x) ) { return !re.html('

' + x + '

'); } + x = parseInt(x,10); + if ( -1 == x ) { return !re.html('

' + this.noPerm + '

'); } + else if ( 0 === x ) { return !re.html('

' + this.broken + '

'); } + return true; + }, + invalidateForm: function( jQ ) { + jQ.addClass( 'form-invalid' ).change( function() { jQuery(this).removeClass( 'form-invalid' ); } ); + } +}, wpAjax || { noPerm: 'You do not have permission to do that.', broken: 'AJAX is teh b0rked.' } ); diff --git a/wp-includes/js/wp-lists.js b/wp-includes/js/wp-lists.js index bbff26c516..fcf57317d9 100644 --- a/wp-includes/js/wp-lists.js +++ b/wp-includes/js/wp-lists.js @@ -2,46 +2,6 @@ var currentFormEl = false; var fs = {add:'ajaxAdd',del:'ajaxDel',dim:'ajaxDim',process:'process',recolor:'recolor'}; -wpAjax = { - unserialize: function( s ) { - var r = {}; if ( !s ) { return r; } - var q = s.split('?'); if ( q[1] ) { s = q[1]; } - var pp = s.split('&'); - for ( var i in pp ) { - if ( $.isFunction(pp.hasOwnProperty) && !pp.hasOwnProperty(i) ) { continue; } - var p = pp[i].split('='); - r[p[0]] = p[1]; - } - return r; - }, - parseAjaxResponse: function( x, r, e ) { // 1 = good, 0 = strange (bad data?), -1 = you lack permission - var re = $('#' + r).html(''); - if ( x && typeof x == 'object' && x.getElementsByTagName('wp_ajax') ) { - var errs = $('wp_error', x); - if ( errs.size() ) { - var err = ''; - errs.each( function() { - var code = $(this).attr('code'); - if ( formField = $('wp_error_data[code="' + code + '"] form-field', x).text() ) - code = formField; - wpAjax.invalidateForm( $('#' + e + ' :input[name="' + code + '"]' ).parents('.form-field:first') ); - err += '

' + this.firstChild.nodeValue + '

'; - } ); - return !re.html( '
' + err + '
' ); - } - return true; - } - if ( isNaN(x) ) { return !re.html('

' + x + '

'); } - x = parseInt(x,10); - if ( -1 == x ) { return !re.html('

You do not have permission to do that.

'); } - else if ( 0 === x ) { return !re.html('

AJAX is teh b0rked.

'); } - return true; - }, - invalidateForm: function( jQ ) { - jQ.addClass( 'form-invalid' ).change( function() { $(this).removeClass( 'form-invalid' ); } ); - } -}; - var wpList = { settings: { url: wpListL10n.url, type: 'POST', @@ -127,21 +87,21 @@ var wpList = { if ( !s.data.match(/_ajax_nonce=[a-f0-9]+/) ) { return true; } s.success = function(r) { - if ( !wpAjax.parseAjaxResponse(r, s.response, s.element) ) { return false; } + var res = wpAjax.parseAjaxResponse(r, s.response, s.element); + if ( !res || res.errors ) { return false; } - $(s.what + ' response_data', r).each( function() { - var t = $(this); - wpList.add.call( list, t.text(), $.extend( {}, s, { // this.firstChild.nodevalue - pos: t.parent().attr( 'position' ) || 0, - id: t.parent().attr( 'id' ) || 0, - oldId: t.parent().attr( 'old_id' ) || null + jQuery.each( res.responses, function() { + wpList.add.call( list, this.data, $.extend( {}, s, { // this.firstChild.nodevalue + pos: this.position || 0, + id: this.id || 0, + oldId: this.oldId || null } ) ); } ); if ( $.isFunction(s.addAfter) ) { var o = this.complete; this.complete = function(x,st) { - var _s = $.extend( { xml: x, status: st }, s ); + var _s = $.extend( { xml: x, status: st, parsed: res }, s ); s.addAfter( r, _s ); if ( $.isFunction(o) ) { o(x,st); } }; @@ -194,7 +154,8 @@ var wpList = { } s.success = function(r) { - if ( !wpAjax.parseAjaxResponse(r, s.response, s.element) ) { + var res = wpAjax.parseAjaxResponse(r, s.response, s.element); + if ( !res || res.errors ) { element.stop().css( 'backgroundColor', '#FF3333' ).show().queue( function() { list.wpList.recolor(); $(this).dequeue(); } ); return false; } @@ -202,7 +163,7 @@ var wpList = { var o = this.complete; this.complete = function(x,st) { element.queue( function() { - var _s = $.extend( { xml: x, status: st }, s ); + var _s = $.extend( { xml: x, status: st, parsed: res }, s ); s.delAfter( r, _s ); if ( $.isFunction(o) ) { o(x,st); } } ).dequeue(); @@ -256,7 +217,8 @@ var wpList = { if ( !s.data._ajax_nonce ) { return true; } s.success = function(r) { - if ( !wpAjax.parseAjaxResponse(r, s.response, s.element) ) { + var res = wpAjax.parseAjaxResponse(r, s.response, s.element); + if ( !res || res.errors ) { element.stop().css( 'backgroundColor', '#FF3333' )[isClass?'removeClass':'addClass'](s.dimClass).show().queue( function() { list.wpList.recolor(); $(this).dequeue(); } ); return false; } @@ -264,7 +226,7 @@ var wpList = { var o = this.complete; this.complete = function(x,st) { element.queue( function() { - var _s = $.extend( { xml: x, status: st }, s ); + var _s = $.extend( { xml: x, status: st, parsed: res }, s ); s.dimAfter( r, _s ); if ( $.isFunction(o) ) { o(x,st); } } ).dequeue(); diff --git a/wp-includes/script-loader.php b/wp-includes/script-loader.php index 3f435e8f19..4ba8064d5a 100644 --- a/wp-includes/script-loader.php +++ b/wp-includes/script-loader.php @@ -41,16 +41,19 @@ class WP_Scripts { $this->add( 'prototype', '/wp-includes/js/prototype.js', false, '1.6'); - $this->add( 'autosave', '/wp-includes/js/autosave.js', array('jquery', 'schedule'), '20080221'); + $this->add( 'wp-ajax-response', '/wp-includes/js/wp-ajax-response.js', array('jquery'), '20080229' . mt_rand() ); + $this->localize( 'wp-ajax-response', 'wpAjax', array( + 'noPerm' => 'You do not have permission to do that.', + 'broken' => 'AJAX is teh b0rked.' + ) ); + + $this->add( 'autosave', '/wp-includes/js/autosave.js', array('schedule', 'wp-ajax-response'), '20080221' . mt_rand()); $this->localize( 'autosave', 'autosaveL10n', array( - 'autosaveInterval' => apply_filters('autosave_interval', '60'), - 'errorText' => __('Error: %response%'), - 'failText' => __('Error: Autosave Failed.'), - 'previewPageText' => __('Preview this Page'), - 'previewPostText' => __('Preview this Post'), - 'saveText' => __('Saved at %time%.'), + 'autosaveInterval' => get_option( 'autosave_interval' ), + 'previewPageText' => __('View this Page'), + 'previewPostText' => __('View this Post'), 'requestFile' => get_option( 'siteurl' ) . '/wp-admin/admin-ajax.php', - 'savingText' => __('Saving Draft...') + 'savingText' => __('Saving…') ) ); $this->add( 'wp-ajax', '/wp-includes/js/wp-ajax.js', array('prototype'), '20070306'); @@ -61,7 +64,7 @@ class WP_Scripts { 'whoaText' => __("Slow down, I'm still sending your data!") ) ); - $this->add( 'wp-lists', '/wp-includes/js/wp-lists.js', array('jquery'), '20080228' ); + $this->add( 'wp-lists', '/wp-includes/js/wp-lists.js', array('wp-ajax-response'), '20080228' . mt_rand()); $this->localize( 'wp-lists', 'wpListL10n', array( 'url' => get_option( 'siteurl' ) . '/wp-admin/admin-ajax.php' ) ); diff --git a/wp-includes/version.php b/wp-includes/version.php index 8da44072b5..fb20379f60 100644 --- a/wp-includes/version.php +++ b/wp-includes/version.php @@ -16,6 +16,6 @@ $wp_version = '2.5-beta1'; * * @global int $wp_db_version */ -$wp_db_version = 6846; +$wp_db_version = 7098; ?>