From 2ee0a5cf3ac32764a978d81226c7a7060339a4bc Mon Sep 17 00:00:00 2001
From: Paul Biron <paul@sparrowhawkcomputing.com>
Date: Wed, 5 Sep 2018 10:52:18 -0600
Subject: [PATCH] add post lock display to Media Library

Only handles the list mode parts, grid mode will require more changes.
---
 src/js/_enqueues/admin/post-lock.js                | 52 ++++++++++++++++++++++
 .../includes/class-wp-media-list-table.php         | 33 +++++++++++++-
 src/wp-admin/upload.php                            |  3 ++
 src/wp-includes/script-loader.php                  |  2 +
 4 files changed, 88 insertions(+), 2 deletions(-)
 create mode 100644 src/js/_enqueues/admin/post-lock.js

diff --git a/src/js/_enqueues/admin/post-lock.js b/src/js/_enqueues/admin/post-lock.js
new file mode 100644
index 0000000..ba473cc
--- /dev/null
+++ b/src/js/_enqueues/admin/post-lock.js
@@ -0,0 +1,52 @@
+/* global wp.heartbeat */
+/**
+ * This file contains the functions needed for updating display of post locks.
+ *
+ * @since 5.0
+ */
+
+( function( $ ) {
+	// Show/hide locks on posts.
+	$( document ).on( 'heartbeat-tick.wp-check-locked-posts', function( e, data ) {
+		var locked = data['wp-check-locked-posts'] || {};
+
+		$('#the-list tr').each( function(i, el) {
+			var key = el.id, row = $(el), lock_data, avatar;
+
+			if ( locked.hasOwnProperty( key ) ) {
+				if ( ! row.hasClass('wp-locked') ) {
+					lock_data = locked[key];
+					row.find('.column-title .locked-text').text( lock_data.text );
+					row.find('.check-column checkbox').prop('checked', false);
+
+					if ( lock_data.avatar_src ) {
+						avatar = $( '<img class="avatar avatar-18 photo" width="18" height="18" alt="" />' ).attr( 'src', lock_data.avatar_src.replace( /&amp;/g, '&' ) );
+						row.find('.column-title .locked-avatar').empty().append( avatar );
+					}
+					row.addClass('wp-locked');
+				}
+			} else if ( row.hasClass('wp-locked') ) {
+				// Make room for the CSS animation
+				row.removeClass('wp-locked').delay(1000).find('.locked-info span').empty();
+			}
+		});
+	}).on( 'heartbeat-send.wp-check-locked-posts', function( e, data ) {
+		var check = [];
+
+		$('#the-list tr').each( function(i, el) {
+			if ( el.id ) {
+				check.push( el.id );
+			}
+		});
+
+		if ( check.length ) {
+			data['wp-check-locked-posts'] = check;
+		}
+	}).ready( function() {
+
+		// Set the heartbeat interval to 15 sec.
+		if ( typeof wp !== 'undefined' && wp.heartbeat ) {
+			wp.heartbeat.interval( 15 );
+		}
+	});
+} )( jQuery );
diff --git a/src/wp-admin/includes/class-wp-media-list-table.php b/src/wp-admin/includes/class-wp-media-list-table.php
index 4be710f..1098a87 100644
--- a/src/wp-admin/includes/class-wp-media-list-table.php
+++ b/src/wp-admin/includes/class-wp-media-list-table.php
@@ -361,6 +361,16 @@ class WP_Media_List_Table extends WP_List_Table {
 																		?>
 			</label>
 			<input type="checkbox" name="media[]" id="cb-select-<?php echo $post->ID; ?>" value="<?php echo $post->ID; ?>" />
+			<div class="locked-indicator">
+				<span class="locked-indicator-icon" aria-hidden="true"></span>
+				<span class="screen-reader-text"><?php
+				printf(
+					/* translators: %s: post title */
+					__( '&#8220;%s&#8221; is locked' ),
+					_draft_or_post_title()
+				);
+				?></span>
+			</div>
 			<?php
 		}
 	}
@@ -387,6 +397,18 @@ class WP_Media_List_Table extends WP_List_Table {
 				esc_attr( sprintf( __( '&#8220;%s&#8221; (Edit)' ), $title ) )
 			);
 			$link_end = '</a>';
+
+			$lock_holder = wp_check_post_lock( $post->ID );
+
+			if ( $lock_holder ) {
+				$lock_holder = get_userdata( $lock_holder );
+				$locked_avatar = get_avatar( $lock_holder->ID, 18 );
+				$locked_text = esc_html( sprintf( __( '%s is currently editing' ), $lock_holder->display_name ) );
+			} else {
+				$locked_avatar = $locked_text = '';
+			}
+
+			echo '<div class="locked-info"><span class="locked-avatar">' . $locked_avatar . '</span> <span class="locked-text">' . $locked_text . "</span></div>\n";
 		}
 
 		$class = $thumb ? ' class="has-media-icon"' : '';
@@ -632,8 +654,15 @@ class WP_Media_List_Table extends WP_List_Table {
 				continue;
 			}
 			$post_owner = ( get_current_user_id() == $post->post_author ) ? 'self' : 'other';
-			?>
-			<tr id="post-<?php echo $post->ID; ?>" class="<?php echo trim( ' author-' . $post_owner . ' status-' . $post->post_status ); ?>">
+
+			$classes = trim( ' author-' . $post_owner . ' status-' . $post->post_status );
+
+			$lock_holder = wp_check_post_lock( $post->ID );
+			if ( $lock_holder ) {
+				$classes .= ' wp-locked';
+			}
+		?>
+			<tr id="post-<?php echo $post->ID; ?>" class="<?php echo $classes; ?>">
 				<?php $this->single_row_columns( $post ); ?>
 			</tr>
 			<?php
diff --git a/src/wp-admin/upload.php b/src/wp-admin/upload.php
index 3afd24f..c0402a0 100644
--- a/src/wp-admin/upload.php
+++ b/src/wp-admin/upload.php
@@ -210,6 +210,9 @@ if ( $doaction ) {
 
 $wp_list_table->prepare_items();
 
+wp_enqueue_script('post-lock');
+wp_enqueue_script('heartbeat');
+
 $title       = __( 'Media Library' );
 $parent_file = 'upload.php';
 
diff --git a/src/wp-includes/script-loader.php b/src/wp-includes/script-loader.php
index 226627e..da9367c 100644
--- a/src/wp-includes/script-loader.php
+++ b/src/wp-includes/script-loader.php
@@ -898,6 +898,8 @@ function wp_default_scripts( &$scripts ) {
 			)
 		);
 
+		$scripts->add( 'post-lock', "/wp-admin/js/post-lock$suffix.js", array( 'jquery' ), false, 1 );
+
 		$scripts->add( 'plugin-install', "/wp-admin/js/plugin-install$suffix.js", array( 'jquery', 'jquery-ui-core', 'thickbox' ), false, 1 );
 		did_action( 'init' ) && $scripts->localize(
 			'plugin-install',
-- 
2.7.0.windows.1

