Index: wp-includes/post-template.php
===================================================================
--- wp-includes/post-template.php	(revision 18708)
+++ wp-includes/post-template.php	(working copy)
@@ -1267,6 +1267,7 @@
  * @since 2.6.0
  *
  * @uses date_i18n()
+ * @uses human_time_diff
  *
  * @param int|object $revision Revision ID or revision object.
  * @param bool $link Optional, default is true. Link to revisions's page?
@@ -1278,24 +1279,28 @@
 
 	if ( !in_array( $revision->post_type, array( 'post', 'page', 'revision' ) ) )
 		return false;
-
+	
+	//returns i18n'd string e.g., "5 min"
+	$diff = human_time_diff( strtotime( $revision->post_date ) );
+	$diff = sprintf( __( '%s ago' ), $diff );
+	
 	/* translators: revision date format, see http://php.net/date */
 	$datef = _x( 'j F, Y @ G:i', 'revision date format');
-	/* translators: 1: date */
-	$autosavef = __( '%1$s [Autosave]' );
-	/* translators: 1: date */
-	$currentf  = __( '%1$s [Current Revision]' );
-
 	$date = date_i18n( $datef, strtotime( $revision->post_modified ) );
+	
 	if ( $link && current_user_can( 'edit_post', $revision->ID ) && $link = get_edit_post_link( $revision->ID ) )
-		$date = "<a href='$link'>$date</a>";
+		$timestamp = '<a href="' . $link . '" class="timestamp" title="' . $date . '" id="' . strtotime( $revision->post_date ) .'">' . $diff . '</a>';
+	else 
+		$timestamp = '<span class="timestamp" title="' . $date . '" id="' . strtotime( $revision->post_date ) .'">' . $diff . '</span>';
 
 	if ( !wp_is_post_revision( $revision ) )
-		$date = sprintf( $currentf, $date );
+		$timestamp .= __( ' [Autosave]' );
 	elseif ( wp_is_post_autosave( $revision ) )
-		$date = sprintf( $autosavef, $date );
+		$timestamp .= __( ' [Current Revision]' );
+	elseif ( wp_is_post_conflicted( $revision ) )
+		$timestamp .=  __( ' [Conflicted Revision]' );
 
-	return $date;
+	return $timestamp;
 }
 
 /**
Index: wp-includes/post.php
===================================================================
--- wp-includes/post.php	(revision 18708)
+++ wp-includes/post.php	(working copy)
@@ -4709,6 +4709,10 @@
 	$return['post_status']   = 'inherit';
 	$return['post_type']     = 'revision';
 	$return['post_name']     = $autosave ? "$post[ID]-autosave" : "$post[ID]-revision";
+	
+	if ( wp_check_post_lock( $post->ID ) )
+		$return['post_name'] .= "-conflicted";
+	
 	$return['post_date']     = isset($post['post_modified']) ? $post['post_modified'] : '';
 	$return['post_date_gmt'] = isset($post['post_modified_gmt']) ? $post['post_modified_gmt'] : '';
 
@@ -4856,6 +4860,24 @@
 }
 
 /**
+ * Determines if the specified post is a conflicted revision
+ *
+ * @package WordPress
+ * @subpackage Post_Revisions
+ * @since 3.3
+ *
+ * @param int|object $post Post ID or post object.
+ * @return bool|int False if not a revision, ID of conflicted revision's parent otherwise
+ */
+function wp_is_post_conflicted( $post ) {
+	if ( !$post = wp_get_post_revision( $post ) )
+		return false;
+	if ( strrpos( $post->post_name, 'conflicted' ) === false )
+		return false;
+	return (int) $post->post_parent;
+}
+
+/**
  * Inserts post data into the posts table as a post revision.
  *
  * @package WordPress
Index: wp-includes/js/autosave.dev.js
===================================================================
--- wp-includes/js/autosave.dev.js	(revision 18708)
+++ wp-includes/js/autosave.dev.js	(working copy)
@@ -87,8 +87,145 @@
 			delayed_autosave();
 		});
 	}
+
+	//lock override toggle	
+	$('#lock_override').click( function( event ) {
+		event.preventDefault();
+
+	 jQuery.post( ajaxurl, {
+			action: 'override_edit_lock',
+			post_ID: $("#post_ID").val() || 0,
+			autosavenonce: $('#autosavenonce').val() || 0
+		},function(data) {
+				if ( data ) {
+					//hide the lock notice
+					$('#lock_override').parent().parent().fadeOut( 'slow' );
+					
+					autosaveL10n.hasLock = true;
+					delayed_autosave();
+				
+				} else {
+					alert( lockError );
+				}
+			}
+		);   
+		
+	});
+	
+	//HTML5 Lock Override Notifications permission check
+	//must be performed concurrent with a user action
+	$('input').keyup( function() {
+		if ( window.webkitNotifications )
+			window.webkitNotifications.requestPermission( );
+	});
+
+
+	//automatically refresh all timestamps every minute with actual human time diff
+	setInterval( "updateTimestamps()", 60000 ); //60k = 1 minute
+	
+	// remove the post lock on unload
+	window.onbeforeunload = function () {  
+		
+		if ( autosaveL10n.hasLock == false )
+			return;
+		
+		$.ajax({
+			type: 'POST',
+			url: ajaxurl,
+			async: false,
+			data: {
+				action: 'wp-remove-post-lock',
+				_wpnonce: $('#_wpnonce').val(),
+				post_ID: $('#post_ID').val(),
+				user_ID: $('#user_ID').val(),
+			}
+		});	
+		
+ 	};
+
 });
 
+//Javscript version of the WP human time diff PHP function, allows time stamps to by dynamically updated
+function human_time_diff( from, to  ) {
+
+		//allow $to to be optional; adjust to server's GMT offset so timezones stay in sync
+		d = new Date();
+		to = to || ( d.getTime() / 1000 ) + parseInt( autosaveL10n.offset );
+		
+		//caclulate difference in seconds
+		diff = Math.abs(to - from);
+		
+		//less than one hour; therefore display minutes
+		if (diff <= 3600) {
+		
+			//convert seconds to minutes
+			mins = Math.floor(diff / 60);
+			
+			//roundup 
+			if (mins <= 1) {
+				mins = 1;
+			}
+			
+			if ( mins == 1) //singular
+				return autosaveL10n.minute.replace( '%d', mins);
+			else //plural
+				return autosaveL10n.minutes.replace( '%d', mins);
+				
+		//if greater than an hour but less than a day, display as hours
+		} else if ((diff <= 86400) && (diff > 3600)) {
+		
+			//convert seconds to hours
+			hours = Math.floor(diff / 3600);
+	
+			//roundup
+			if (hours <= 1) {
+				hours = 1;
+			}
+			
+			if ( hours == 1) //singular
+				return autosaveL10n.hour.replace( '%d', hours);
+			else //plural
+				return autosaveL10n.hours.replace( '%d', hours);
+		
+		//if it's more than a day, display as days
+		} else if (diff >= 86400) {
+		
+			//convert seconds to days
+			days = Math.floor(diff / 86400);
+			
+			//roundup
+			if (days <= 1) {
+				days = 1;
+			}
+			
+			if ( days == 1) //singular
+ 				return autosaveL10n.day.replace( '%d', days);
+			else //plural
+				return autosaveL10n.days.replace( '%d', days);
+		}
+}
+
+//loop through all timestamps and update
+function updateTimestamps() {
+
+    //loop through all timestamps and update the timestamp
+    jQuery('.timestamp').each( function(){
+    	jQuery(this).text( human_time_diff( jQuery(this).attr('id') ) ); 
+    });
+    
+}
+
+//HTML5 Lock Override Notifications
+function lock_override_notice( notice ) {
+    if ( window.webkitNotifications.checkPermission() > 0 ) {
+    	window.webkitNotifications.RequestPermission( notice );
+    } else {
+    	window.webkitNotifications.createNotification(
+    	autosaveL10n.logoURL, autosaveL10n.lostLockNoticeTitle, notice ).show();
+    }
+}
+
+
 function autosave_parse_response(response) {
 	var res = wpAjax.parseAjaxResponse(response, 'autosave'), message = '', postID, sup;
 
@@ -102,9 +239,25 @@
 				res = { errors: true };
 			}
 
-			if ( sup['alert'] ) {
+			//if the #locknotice is present, this is a refresh, no need to alert again
+			if ( sup['alert'] && jQuery('#locknotice').length == 0 ) {
 				jQuery('#autosave-alert').remove();
 				jQuery('#titlediv').after('<div id="autosave-alert" class="error below-h2"><p>' + sup['alert'] + '</p></div>');
+			
+				//Lock Override alert
+				//todo: right now the only possible alert is file lock loss, but probably should confirm here
+				if ( window.webkitNotifications ) {
+					//browser supports html5 Notifications
+					lock_override_notice( sup['alert'] );
+				} else {
+					//browser does not support lock override notice, send old school alert
+					alert( sup['alert'] );
+				}
+		    	
+		    	//submit the form so that we can save whatever we've got 
+		    	//and then reload the page with the lock
+				jQuery('#post').submit();
+				
 			}
 
 			jQuery.each(sup, function(selector, value) {
Index: wp-includes/script-loader.php
===================================================================
--- wp-includes/script-loader.php	(revision 18708)
+++ wp-includes/script-loader.php	(working copy)
@@ -285,7 +285,7 @@
 
 		$scripts->add( 'postbox', "/wp-admin/js/postbox$suffix.js", array('jquery-ui-sortable'), '20110916', 1 );
 
-		$scripts->add( 'post', "/wp-admin/js/post$suffix.js", array('suggest', 'wp-lists', 'postbox'), '20110524', 1 );
+		$scripts->add( 'post', "/wp-admin/js/post$suffix.js", array('suggest', 'wp-lists', 'postbox'), '20110824', 1 );
 		$scripts->add_script_data( 'post', 'postL10n', array(
 			'ok' => __('OK'),
 			'cancel' => __('Cancel'),
@@ -472,12 +472,25 @@
  * @since 2.5.0
  */
 function wp_just_in_time_script_localization() {
-
+	global $post;
+	
 	wp_localize_script( 'autosave', 'autosaveL10n', array(
 		'autosaveInterval' => AUTOSAVE_INTERVAL,
 		'savingText' => __('Saving Draft&#8230;'),
-		'saveAlert' => __('The changes you made will be lost if you navigate away from this page.')
-	) );
+		'saveAlert' => __('The changes you made will be lost if you navigate away from this page.'),
+		'logoURL' => admin_url('images/logo.gif'),
+		'lockOverrideError' => __('There was an error overriding the lock.'),
+		'lostLockNoticeTitle' => __('Lost Lock'),
+		'unlockNotice' => __('Releasing so that others can edit...'),
+		'hasLock' => ( function_exists( 'wp_check_post_lock') && !wp_check_post_lock( $post->ID ) ) ? true : false,
+		'offset' => get_option( 'gmt_offset' ) * 3600,
+		'minute' => __('%d mins'),
+		'minutes' => __('%d mins'),
+		'hour' => __('%d hour'),
+		'hours' => __('%d hours'),
+		'day' => __('%d day'),
+		'days' => __('%d days'),
+ 	) );
 
 }
 
Index: wp-admin/admin-ajax.php
===================================================================
--- wp-admin/admin-ajax.php	(revision 18708)
+++ wp-admin/admin-ajax.php	(working copy)
@@ -958,7 +958,10 @@
 		$data = __( 'Autosave disabled.' );
 
 		$supplemental['disable_autosave'] = 'disable';
-		$alert .= sprintf( __( '%s is currently editing this article. If you update it, you will overwrite the changes.' ), esc_html( $last_user_name ) );
+		
+		//TODO: this language needs to be updated
+		$alert .= sprintf( __( '%1$s currently has the post "%2$s" checked out. Your changes will be saved as a conflicted revision.' ), esc_html( $last_user_name ), $post->post_title );
+			
 	}
 
 	if ( 'page' == $post->post_type ) {
@@ -988,7 +991,8 @@
 			$id = $post->ID;
 	}
 
-	if ( $do_lock && ( isset( $_POST['auto_draft'] ) && ( $_POST['auto_draft'] != '1' ) ) && $id && is_numeric($id) )
+	//auto_draft will either be 1 (and thus ID ='s 0) or not POST'd at all
+	if ( $do_lock && !isset( $_POST['auto_draft'] ) && $id && is_numeric($id) )
 		wp_set_post_lock( $id );
 
 	if ( $nonce_age == 2 ) {
@@ -1548,7 +1552,75 @@
 
 	echo json_encode( array( 'message' => $message, 'last_edited' => $last_edited ) );
 	die();
+	break; 
+case 'override_edit_lock':
+
+	//TODO: Does this exist if not in edit mode?
+	check_ajax_referer( 'autosave', 'autosavenonce' );
+
+	$post_id = isset($_POST['post_ID']) ? (int) $_POST['post_ID'] : 0;
+	
+	if ( !current_user_can('edit_post', $post_id) )
+		die('-1');
+
+		
+	if (	!$post_id || 
+			!current_user_can( 'edit_post' , $post_id ) || 
+			!current_user_can( 'override_edit_lock' ) 
+			)
+				wp_die( __( 'Not authorized' ) );
+			
+	//not locked or locked to self
+	if ( !( $current_owner = wp_check_post_lock( $post_id) ) ) 
+		die( '-1' );
+		
+	wp_set_post_lock( $post_id );
+		
+	$current_user = wp_get_current_user();
+		
+	//if ( apply_filters( 'send_document_override_notice', $send_notice ) )
+	//	$this->send_override_notice( $postID, $current_owner, $current_user->ID );
+			
+	do_action( 'edit_lock_override', $post_id, $current_user->ID, $current_owner );
+		
+	die( '1' );
+
 	break;
+case 'wp-remove-post-lock' :
+
+	$post_id = isset($_POST['post_ID']) ? (int) $_POST['post_ID'] : 0;
+	if ( !current_user_can('edit_post', $post_id) )
+		die();
+
+	$post = $post_type = null;
+
+	if ( $post_id ) {
+		$post = get_post($post_id);
+		if ( $post )
+			$post_type = $post->post_type;
+	}
+
+	check_ajax_referer('update-' . $post_type . '_' . $post_id);
+
+	sleep(4); //why are we sleeping?
+
+	//TODO: Can this be simplified with wp_check_post_lock?
+	
+	if ( !$lock = get_post_meta( $post_id, '_edit_lock', true ) )
+		die('-1');
+
+	$lock = explode( ':', $lock );
+	$user_id = isset( $lock[1] ) ? $lock[1] : get_post_meta( $post_id, '_edit_last', true );
+	$locked = (int) $lock[0];
+
+	if ( time() - $locked < 5 ) // give a few seconds for the lock to be refreshed if the user reloaded the page
+		die('0');
+
+	if ( $user_id == get_current_user_id() )
+		delete_post_meta( $post_id, '_edit_lock' );
+
+	die('1');
+	break;
 default :
 	do_action( 'wp_ajax_' . $_POST['action'] );
 	die('0');
Index: wp-admin/includes/post.php
===================================================================
--- wp-admin/includes/post.php	(revision 18708)
+++ wp-admin/includes/post.php	(working copy)
@@ -189,7 +189,17 @@
 				break;
 		}
 	}
-
+	
+	//above this point has been read only, starting now, we're going to begin writing, 
+	//so do checks here if we do want to break stuff
+	
+	//if there's a post lock, and we're not the owner, hijack the save and save a revision instead
+	$lock_owner = wp_check_post_lock( $post_ID ); 
+	if ( $lock_owner ) {
+		$revision_ID = _wp_put_post_revision( $post_data ); //TODO: IS this properly sanitized?
+		return false; //maybe we want to return revision_ID here?
+	}
+	
 	// Post Formats
 	if ( current_theme_supports( 'post-formats' ) && isset( $post_data['post_format'] ) ) {
 		$formats = get_theme_support( 'post-formats' );
@@ -1270,11 +1280,15 @@
 			$message = __( 'Warning: %s is currently editing this page' );
 			break;
 		default:
-			$message = __( 'Warning: %s is currently editing this.' );
+			$cpt = get_post_type_object( $post->post_type );
+			$message = __( 'Warning: %s is currently editing this ' . $cpt->labels->singular_name . '.' );
 	}
+	
+	if ( current_user_can( 'override_edit_lock' ) )
+		$message .= '. If you believe this is in error, you can <a href="#" id="lock_override">override the lock</a>, but their changes will be lost.';
 
 	$message = sprintf( $message, esc_html( $last_user_name ) );
-	echo "<div class='error'><p>$message</p></div>";
+	echo "<div class='error' id=\"locknotice\"><p>$message</p></div>";
 }
 
 /**
Index: wp-admin/includes/schema.php
===================================================================
--- wp-admin/includes/schema.php	(revision 18708)
+++ wp-admin/includes/schema.php	(working copy)
@@ -382,6 +382,7 @@
 	populate_roles_270();
 	populate_roles_280();
 	populate_roles_300();
+	populate_roles_330();
 }
 
 /**
@@ -623,6 +624,19 @@
 	}
 }
 
+function populate_roles_330() {
+	
+	$roles = array('administrator', 'editor');
+	foreach ($roles as $role) {
+		$role =& get_role($role);
+		if ( empty($role) )
+			continue;
+
+		$role->add_cap('override_edit_lock');
+	}
+	
+}
+
 /**
  * populate network settings
  *
Index: wp-admin/post.php
===================================================================
--- wp-admin/post.php	(revision 18708)
+++ wp-admin/post.php	(working copy)
@@ -51,10 +51,13 @@
  * @param int $post_id Optional. Post ID.
  */
 function redirect_post($post_id = '') {
-	if ( isset($_POST['save']) || isset($_POST['publish']) ) {
+	
+	if ( wp_check_post_lock( $post_id ) ) {
+		$location = add_query_arg( 'message', 11, get_edit_post_link( $post_id, 'url' ) );
+	} else if ( isset($_POST['save']) || isset($_POST['publish']) ) {
 		$status = get_post_status( $post_id );
-
-		if ( isset( $_POST['publish'] ) ) {
+		
+		 if ( isset( $_POST['publish'] ) ) {
 			switch ( $status ) {
 				case 'pending':
 					$message = 8;
@@ -69,6 +72,8 @@
 				$message = 'draft' == $status ? 10 : 1;
 		}
 
+		
+
 		$location = add_query_arg( 'message', $message, get_edit_post_link( $post_id, 'url' ) );
 	} elseif ( isset($_POST['addmeta']) && $_POST['addmeta'] ) {
 		$location = add_query_arg( 'message', 2, wp_get_referer() );
@@ -175,8 +180,9 @@
 		add_action('admin_notices', '_admin_notice_post_locked' );
 	} else {
 		wp_set_post_lock( $post->ID );
-		wp_enqueue_script('autosave');
 	}
+	
+	wp_enqueue_script('autosave');
 
 	$title = $post_type_object->labels->edit_item;
 	$post = get_post_to_edit($post_id);
Index: wp-admin/js/post.dev.js
===================================================================
--- wp-admin/js/post.dev.js	(revision 18708)
+++ wp-admin/js/post.dev.js	(working copy)
@@ -643,4 +643,5 @@
 	}
 
 	wptitlehint();
+ 	
 });
Index: wp-admin/edit-form-advanced.php
===================================================================
--- wp-admin/edit-form-advanced.php	(revision 18708)
+++ wp-admin/edit-form-advanced.php	(working copy)
@@ -27,6 +27,9 @@
 $user_ID = isset($user_ID) ? (int) $user_ID : 0;
 $action = isset($action) ? $action : '';
 
+if ( $user = wp_check_post_lock( $post_ID ) )
+	$user = get_user_by( 'id' , $user );
+
 $messages = array();
 $messages['post'] = array(
 	 0 => '', // Unused. Messages start at index 1.
@@ -43,6 +46,7 @@
 		// translators: Publish box date format, see http://php.net/date
 		date_i18n( __( 'M j, Y @ G:i' ), strtotime( $post->post_date ) ), esc_url( get_permalink($post_ID) ) ),
 	10 => sprintf( __('Post draft updated. <a target="_blank" href="%s">Preview post</a>'), esc_url( add_query_arg( 'preview', 'true', get_permalink($post_ID) ) ) ),
+	11 => sprintf( __( 'Your save conflicted against %s\'s version. Your copy was saved as conflicted.'), $user->display_name ), //TODO: This need better language
 );
 $messages['page'] = array(
 	 0 => '', // Unused. Messages start at index 1.
@@ -56,6 +60,7 @@
 	 8 => sprintf( __('Page submitted. <a target="_blank" href="%s">Preview page</a>'), esc_url( add_query_arg( 'preview', 'true', get_permalink($post_ID) ) ) ),
 	 9 => sprintf( __('Page scheduled for: <strong>%1$s</strong>. <a target="_blank" href="%2$s">Preview page</a>'), date_i18n( __( 'M j, Y @ G:i' ), strtotime( $post->post_date ) ), esc_url( get_permalink($post_ID) ) ),
 	10 => sprintf( __('Page draft updated. <a target="_blank" href="%s">Preview page</a>'), esc_url( add_query_arg( 'preview', 'true', get_permalink($post_ID) ) ) ),
+	11 => sprintf( __( 'Your save conflicted against %s\'s version. Your copy was saved as conflicted.'), $user->display_name ), //TODO: This need better language
 );
 
 $messages = apply_filters( 'post_updated_messages', $messages );
