WordPress как на ладони
rgbcode is looking for WordPress developers. Очень Удобный и Быстрый Хостинг для сайтов на WordPress. Пользуюсь сам и вам рекомендую!

Установка миниатюры записи из таблицы записей в админке

В этой заметке вы найдете код, который позволяет устанавливать миниатюры записи (поста) со страницы редактирования записей в админ-панели. Картинки также можно загружать/удалять и т.д.

Создайте плагин из кода ниже. Или создайте файл из кода ниже и подключите его в файл темы functions.php.

ВАЖНО: Код работает на основе плагина AJAX Simply, без него работать не будет!

GitHub
<?php

if( ! is_admin() ){
	return;
}

add_action( 'admin_init', [ Kama_Post_List_Table_Thumb::class, 'init' ] );

/**
 * Ability to upload and modify post thumbnails from the posts list table in the admin panel.
 *
 * @author Kama (wp-kama.ru)
 *
 * @require PHP 7.4
 *
 * @version 1.2.3
 */
class Kama_Post_List_Table_Thumb {

	use Kama_Post_List_Table_Thumb__ajax_handlers;

	/** @var array For which posts to include the code. By default, for all public ones. */
	private static array $post_types = [ 'post', 'handbook', 'article' ];

	/** @var array Post types for which setting a thumbnail re-attaches the attachment file. */
	private static array $re_set_attach_for_post_types = [];

	/** @var string Meta key name. */
	private static string $meta_key = '_thumbnail_id';

	/** @var string Empty image Url. */
	private static string $add_img_url = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAGQAAABkAQM' .
	                                     'AAABKLAcXAAAABlBMVEUAAAC7u7s37rVJAAAAAXRSTlMAQObYZgAAACJJREFUOMtjGA' .
	                                     'V0BvL/G0YMr/4/CDwY0rzBFJ704o0CWgMAvyaRh+c6m54AAAAASUVORK5CYII=';

	public static function init( $args = [] ) {

		$post_type = self::$post_types ?: get_post_types( [ 'public' => true ], 'names' );

		foreach( $post_type as $ptype ){
			add_filter( "manage_{$ptype}_posts_columns", [ __CLASS__, 'add_image_column' ] );
			add_filter( "manage_{$ptype}_posts_custom_column", [ __CLASS__, 'fill_image_column' ], 10, 2 );
		}

		// general access cap check for all ajax requests
		add_filter( 'ajaxs_allow_process', static function( $allow, $action ) {
			if(
				str_contains( $action, Kama_Post_List_Table_Thumb::class )
				&& ! current_user_can( 'edit_others_posts' )
			){
				return false;
			}

			return $allow;
		}, 10, 2 );
	}

	public static function add_image_column( $columns ) {

		add_action( 'admin_notices', [ __CLASS__, '_js_css_html' ] );

		// untitled column.
		return array_slice( $columns, 0, 1 ) + [ 'image' => '' ] + $columns;
	}

	public static function fill_image_column( $colname, $post_id ){

		if( $colname === 'image' && current_user_can( 'edit_post', $post_id ) ){
			$attach_id = get_post_meta( $post_id, self::$meta_key, true );

			$img_src = $attach_id ? wp_get_attachment_image_url( $attach_id, 'thumbnail' ) : self::$add_img_url;

			?>
			<a class="pthumb thickbox" data-post_id="<?= (int) $post_id ?>" data-attach_id="<?= (int) $attach_id ?>"
			   href="/?TB_inline&inlineId=up-set-post-thumb&width=700&height=500"
			   title="Setting post thumbnail"
			>
				<img src="<?= esc_attr( $img_src ) ?>" alt=""/>
			</a>
			<?php
		}
	}

	# modal window html, css and js
	public static function _js_css_html(){
		// modal window scripts
		add_thickbox();

		add_action( 'admin_print_footer_scripts', [ __CLASS__, '_js' ], 99 );

		self::_css();
		?>
		<!-- modal window -->
		<div id="up-set-post-thumb" style="display:none;">
			<div class="pthumb-thickbox">

				<script type="text/template" id="imagebox-tpl-js">
					<div class="imagebox" data-attach_id="{attach_id}" data-full_url="{full_url}">
						<img src="{attach_url}" alt="" />
					</div>
				</script>

				<div class="filters">
					<button class="button button-small active / post-images-js">Post Images</button>
					<button class="button button-small / last-images-js">Last from Library</button>
				</div>

				<div class="full-view" style="display:none;">
					<div class="close"><span class="dashicons dashicons-no-alt"></span></div>
					<img src="" alt="">
				</div>

				<div class="images"></div>

				<div class="ajax-res" style="display:none;"></div>

				<div class="footer">
					<button class="button-primary / set-thumb-js">Set Thumbnail</button>

					<button class="button / file-btn-js"><span class="dashicons dashicons-format-image" style=""></span> Upload Images</button>
					<input type="file" name="pthumb_files[]" multiple accept="image/*" style="display:none;" />

					<button class="button / del-post-thumb-js"><span class="dashicons dashicons-no-alt"></span>Unset Thumbnail</button>
					<button class="button / del-attach-js"><span class="dashicons dashicons-trash"></span>Delete File</button>
				</div>

			</div>
		</div>
		<?php
	}

	public static function _css(){
		?>
		<style>
			.column-image{ width:50px; text-align:center; }
			.pthumb{ display:block; }
			.pthumb img{ width:35px; background:#eee; border-radius:3px; cursor:pointer;  }
			.pthumb:hover img{ opacity:.7; }

			.pthumb-thickbox{ box-sizing:border-box; }
			.pthumb-thickbox *{ box-sizing:inherit; }

			.pthumb-thickbox{ display:flex; flex-direction:column; height:calc(100% + 17px); margin:-2px -15px -15px; }
			.pthumb-thickbox > *{ padding:15px; }

			.pthumb-thickbox .images{ flex-grow:1; height:1px; overflow-y:scroll; }
			.pthumb-thickbox .filters{ background:#eee; padding-top:5px;padding-bottom:5px; }
			.pthumb-thickbox .filters .active{ font-weight:700; }

			.pthumb-thickbox .imagebox{ float:left; padding:10px; margin:0 1em 1em 0; background:#eee; border:1px solid #ddd; cursor:pointer; }
			/*.pthumb-thickbox .imagebox:nth-child(5n){ margin-right:0; }*/
			.pthumb-thickbox .imagebox:hover{ outline:5px solid #9ca4ac; }
			.pthumb-thickbox .imagebox.selected{ outline:5px solid #2d3238; }
			.pthumb-thickbox .imagebox img{ width:100px; height:100px; display:block; }

			.pthumb-thickbox .footer{ display:flex; justify-content:space-between; height:60px; border-top:1px solid #ddd; background:#eee; box-shadow:0 0 1em #b5b9bb; z-index:1; }
			.pthumb-thickbox .footer .dashicons{ margin-top:6px; font-size:15px; }

			.pthumb-thickbox .ajax-res{ position:absolute; width:100%; bottom:60px; padding-right:60px; }
			.pthumb-thickbox .ajax-res{ background:#ddeacb; color:#3d6703; }
			.pthumb-thickbox .ajax-res.err{ background:#ffebeb; color:#951212; }

			.pthumb-thickbox .full-view{ position:relative; flex-grow:1; max-height:100%; text-align:center; background:#7d7d7d; }
			.pthumb-thickbox .full-view img{ max-width:100%; max-height:380px; }

			.pthumb-thickbox .close{ position:absolute; top:0; right:0; padding:15px; background:rgba(0,0,0,.2); color:#fff; cursor:pointer; }
		</style>
		<?php
	}

	public static function _js(){
		?>
		<script>
		document.addEventListener( 'DOMContentLoaded', function(){

			let $thickbox = jQuery( '.pthumb-thickbox' )
			let $file = $thickbox.find( '[type="file"]' )
			let $images = $thickbox.find( '.images' )
			let $fullView = $thickbox.find( '.full-view' )
			let $filters = $thickbox.find( '.filters' )
			let imageboxTpl = $thickbox.find( '#imagebox-tpl-js' ).html()
			let $cur_pthumb = null
			const addImgSrc = '<?= self::$add_img_url ?>'

			let showMessage = function( message, type ){

					let $res = $thickbox.find( '.ajax-res' ).removeClass( 'err' )

					$res.html( message + '<div class="close" onclick="jQuery(this).parent().slideUp(100)"><span class="dashicons dashicons-no-alt"></span></div>' )
						.addClass( type === 'error' ? 'err' : '' ).slideDown( 100 )

					setTimeout( function(){
						$res.slideUp( 100 )
					}, 25000 )
				},
				hideMessage = function(){
					$thickbox.find( '.ajax-res' ).slideUp( 100 )
				}

			let _fillImagesBox = function( attachs ){
				$images.empty()

				if( typeof attachs === 'string' ){
					$images.html( attachs )
					return
				}

				// go through the array of objects and add elements.
				for( let id in attachs ){
					let attach = attachs[id]

					$images.append(
						imageboxTpl.replace( '{attach_url}', attach.thumb_url )
							.replace( '{attach_id}', attach.ID )
							.replace( '{full_url}', attach.guid )
					)

					// set the current thumbnail
					if( $cur_pthumb.data( 'attach_id' ) ){
						$images.find( '[data-attach_id="' + $cur_pthumb.data( 'attach_id' ) + '"]' ).addClass( 'selected' )
					}
					else{
						$images.find( '.imagebox:first' ).addClass( 'selected' )
					}

				}
			}

			let _loadAttachments = function( filter_name ){
				$images.empty() // clear images

				$filters.find( 'button' ).removeClass( 'active' ) // clear filters

				let data = {
					post_id      : $cur_pthumb.data( 'post_id' ),
					cur_attach_id: $cur_pthumb.data( 'attach_id' )
				}

				if( filter_name === 'last_media' ){
					delete data.post_id

					$filters.find( '.last-images-js' ).addClass( 'active' )
				}
				else {
					$filters.find( '.post-images-js' ).addClass( 'active' )
				}

				// AJAX request - load all images of the post
				showMessage( 'Loading...' )
				ajaxs( 'Kama_Post_List_Table_Thumb::ajaxs_get_images', data, function( resp ){
					hideMessage()

					if( resp.toString() === '' )
						_loadAttachments( 'last_media' )
					else
						_fillImagesBox( resp )
				} )

			}

			window.resetPthumbImage = function( args ){
				// args: post_id, attach_id, src, clear, $pthumb
				if( ! args.$pthumb ){
					if( args.post_id ) args.$pthumb = jQuery( '.pthumb[data-post_id="' + args.post_id + '"]' )
					else if( args.attach_id ) args.$pthumb = jQuery( '.pthumb[data-attach_id="' + args.attach_id + '"]' )
				}

				var new_attach_id = args.clear ? '' : args.attach_id
				args.$pthumb.data( 'attach_id', new_attach_id ).attr( 'data-attach_id', new_attach_id )
				args.$pthumb.find( 'img' ).attr( 'src', args.clear ? addImgSrc : args.src )
			}

			// Click on "Upload any images" when the post has no images
			$filters.on( 'click', 'button', function( resp ){
				var $btn = jQuery( this )

				if( $btn.hasClass( 'post-images-js' ) ){
					_loadAttachments()
				}
				if( $btn.hasClass( 'last-images-js' ) ){
					_loadAttachments( 'last_media' )
				}
			} )

			// Click on the thumbnail in the post table
			jQuery( '.wp-list-table' ).on( 'click', '.pthumb', function( ev ){
				$cur_pthumb = jQuery( this )

				_loadAttachments()
			} )

			// Click on the thumbnail in the modal window - image selection
			$images.on( 'click', '.imagebox', function(){
				let $box = jQuery( this )
				$images.find( '.imagebox' ).removeClass( 'selected' )
				$box.addClass( 'selected' )
			} )

			// Click on the "Set Thumbnail" button
			$thickbox.find( '.set-thumb-js' ).on( 'click', function(){

				let $setbtn = jQuery( this )
				let $selected_imagebox = $images.find( '.selected' )
				let data = {
					attach_id: $selected_imagebox.data( 'attach_id' ),
					post_id  : $cur_pthumb.data( 'post_id' )
				}

				// AJAX
				showMessage( 'Wait...' )
				ajaxs( 'Kama_Post_List_Table_Thumb::ajaxs_set_post_thumbnail', data, function( resp ){
					hideMessage()

					// Set, close the modal
					if( resp.success ){
						jQuery( '#TB_closeWindowButton' ).trigger( 'click' )

						resetPthumbImage( {
							$pthumb  : $cur_pthumb,
							attach_id: data.attach_id,
							src      : $selected_imagebox.find( 'img' ).attr( 'src' )
						} )
					}
					else
						showMessage( resp.data, 'error' )

				} )

			} )

			// Click on the "Remove Thumbnail" button
			$thickbox.find( '.del-post-thumb-js' ).on( 'click', function(){
				if( ! $cur_pthumb.data( 'attach_id' ) ){
					showMessage( 'ОШИБКА: У текущей записи миниатюра не установлена...', 'error' )
					return
				}

				// AJAX
				showMessage( 'Unsetting...' )
				ajaxs( 'Kama_Post_List_Table_Thumb::ajaxs_delete_post_thumbnail',
					{ post_id: $cur_pthumb.data( 'post_id' ) },
					function( resp ){
						$images.find( '.imagebox' ).removeClass( 'selected' )
						showMessage( 'Thumbnail cancelled!' )

						resetPthumbImage( { clear: 1, $pthumb: $cur_pthumb } )
					}
				)
			} )

			// Click on the "Upload Files" button - trigger input file
			$thickbox.find( '.file-btn-js' ).on( 'click', function(){
				$file.trigger( 'click' )
			} )

			// Upload file
			$file.change( function(){

				// AJAX upload files
				showMessage( 'Loading...' )
				ajaxs( 'Kama_Post_List_Table_Thumb::ajaxs_upload_files',
					{
						post_id: $cur_pthumb.data( 'post_id' ),
						foo    : $thickbox
					},
					function( resp ){
						showMessage( resp.data, (resp.success ? 'success' : 'error') )

						if( resp.success ){
							_loadAttachments()
						}
					}
				)

			} )

			// Click on "Delete File"
			$thickbox.find( '.del-attach-js' ).on( 'click', function(){
				let $selected = $images.find( '.selected' )

				if( ! $selected.length ){
					return showMessage( 'ERROR: Nothing has been selected....', 'error' )
				}

				if( !confirm( 'Are you sure to delete? The file will be permanently deleted!' ) ){
					return
				}

				// AJAX
				ajaxs( 'Kama_Post_List_Table_Thumb::ajaxs_delete_attach',
					{ attach_id: $selected.data( 'attach_id' ) },
					function( resp ){
						if( resp.success ){
							$selected.remove()
						}
						else{
							showMessage( resp.data )
						}
					}
				)
			} )

			// Double click on the image in the modal window - view the image in full size
			$images.on( 'dblclick', '.imagebox', function(){
				$fullView.show().find( 'img' ).attr( 'src', this.dataset['full_url'] )
				$thickbox.find( '.images' ).hide()
			} )

			$fullView.find( '.close' ).on( 'click', function(){
				$fullView.hide()
				$images.show()
			} )

		} )
		</script>
		<?php
	}

}

trait Kama_Post_List_Table_Thumb__ajax_handlers {

	public static function ajaxs_get_images( AJAX_Simply_Core $jx ) {

		$args = [
			'post_type'      => 'attachment',
			'post_mime_type' => 'image',
			'order_by'       => 'post_date',
			'order'          => 'DESC',
			'numberposts'    => $jx->limit ?: 100,
		];

		if( $jx->post_id ){
			$args['post_parent'] = $jx->post_id;
		}
		if( $jx->cur_attach_id ){
			$args['exclude'] = $jx->cur_attach_id;
		}

		$attachs = get_posts( $args );

		// add the current thumbnail
		if( $jx->cur_attach_id && $attach = get_post( $jx->cur_attach_id ) ){
			array_unshift( $attachs, $attach );
		}

		// add a link to the thumbnail
		foreach( $attachs as $attach ){
			$attach->thumb_url = wp_get_attachment_image_url( $attach->ID, 'thumbnail' );
		}

		return $attachs;
	}

	public static function ajaxs_set_post_thumbnail( AJAX_Simply_Core $jx ) {

		$atatch = $jx->attach_id ? get_post( $jx->attach_id ) : 0;
		$post = $jx->post_id ? get_post( $jx->post_id ) : 0;

		if( ! $atatch || ! $post ){
			$jx->error( 'ERROR: no attachment or post...' ); // die
		}

		$attach_post = $atatch->post_parent ? get_post( $atatch->post_parent ) : 0;

		set_post_thumbnail( $post->ID, $atatch->ID );

		// the attachment is not attached anywhere, let's attach it to the current post
		if( ! $attach_post ){
			wp_update_post( [ 'ID' => $atatch->ID, 'post_parent' => $post->ID ] );
		}
		// the attachment is attached to a special post type and the current post type is the same as the attachment post type
		// detach the attachment and attach it to the current post
		elseif(
			in_array( $attach_post->post_type, self::$re_set_attach_for_post_types, true )
			&& $attach_post->post_type === $post->post_type
		){
			wp_update_post( [ 'ID' => $atatch->ID, 'post_parent' => $post->ID ] );

			// remove the thumbnail from the previous post if the same thumbnail is set for it
			if( $attach_post->ID !== $post->ID && (int) get_post_thumbnail_id( $attach_post->ID ) === (int) $atatch->ID ){
				delete_post_thumbnail( $attach_post->ID );

				$jx->call( 'window.resetPthumbImage', [ 'clear'   => 1,
				                                        'post_id' => $attach_post->ID,
				] ); // clear the thumbnail of the previous post
			}
		}

		$jx->success();
	}

	public static function ajaxs_delete_post_thumbnail( AJAX_Simply_Core $jx ) {
		return delete_post_thumbnail( $jx->post_id );
	}

	public static function ajaxs_upload_files( AJAX_Simply_Core $jx ) {
		if( ! $jx->pthumb_files ){
			$jx->error( 'ERROR: No files...' );
		}

		if( ! $jx->post_id ){
			$jx->error( 'ERROR: Post ID is not specified...' );
		}

		// filter for allowed file types - allow only images
		add_filter( 'upload_mimes', static function( $mimes ) {
			return [
				'jpg|jpeg|jpe' => 'image/jpeg',
				'gif'          => 'image/gif',
				'png'          => 'image/png',
				'webp'         => 'image/webp',
			];
		} );

		// uploading files
		$results = [];
		foreach( $jx->pthumb_files['compact'] as $filedata ){
			// so that if the name is in Cyrillic, it remains in Cyrillic...
			$name = sanitize_text_field( preg_replace( '/\.(?:jpg|jpeg|png|gif|webp)$/', '', $filedata['name'] ) );

			$id = media_handle_sideload( $filedata, $jx->post_id, $name );

			if( is_wp_error( $id ) ){
				@ unlink( $filedata['tmp_name'] );
				$results[] = 'ERROR: ' . $id->get_error_message() . ' File: ' . esc_html( $filedata['name'] );
			}
			else{
				$results[] = 'OK: ' . esc_html( $filedata['name'] );
			}
		}

		$jx->success( implode( '<br>', $results ) );
	}

	public static function ajaxs_delete_attach( AJAX_Simply_Core $jx ) {
		if( ! $jx->attach_id ){
			$jx->error( 'ERROR: Attachment ID not specified...' );
		}

		$res = wp_delete_attachment( $jx->attach_id, $force_delete = true );

		if( false === $res ){
			$jx->error( 'ERROR: Failed to delete...' );
		}

		// clean the thumbnail in the posts table
		$jx->call( 'window.resetPthumbImage', [ 'clear' => 1, 'attach_id' => $jx->attach_id ] );

		$jx->success( 'Deleted!' );
	}

}

После установки кода получим:

Теоретически, можно сделать тоже самое через стандартное окно media загрузчика, но там придется разбираться с кучей всего. Мне нужен был такой функционал и проще (быстрее) было написать код выше. Если есть альтернативные решения такой задачи, пожалуйста поделитесь в комментариях.

5 комментариев
    Войти