WordPress как на ладони
Недорогой хостинг для сайтов на WordPress: wordpress.jino.ru

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

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

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

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

<?php

// init
if( is_admin() ){
	add_action( 'admin_init', [ 'Post_List_Table_Thumb', 'init' ] );
}

/**
 * Возможность загружать и изменять миниатюры записей из таблицы записей в админ-панели.
 *
 * @author Kama (wp-kama.ru)
 *
 * @version 1.2.2
 */
class Post_List_Table_Thumb {

	// для каких постов включить код. По умолчанию для всех публичных
	static $post_type = array( 'recipe', 'ingredient' );

	static $re_set_attach_for_post_types = array(); // типы записей для которых установка миниатюры пере-прикрепляет файл вложения

	static $meta_key = '_thumbnail_id'; // название мета ключа

	// URL пустой картинки
	static $add_img_url = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAGQAAABkAQMAAABKLAcXAAAABlBMVEUAAAC7u7s37rVJAAAAAXRSTlMAQObYZgAAACJJREFUOMtjGAV0BvL/G0YMr/4/CDwY0rzBFJ704o0CWgMAvyaRh+c6m54AAAAASUVORK5CYII=';

	## инициализация всего
	static function init(){

		$post_type = self::$post_type ?: 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 );
		}

		// общая проверка прав доступа для всех текущих ajax запросов
		add_filter( 'ajaxs_allow_process', function( $allow, $action ){
			if( false !== strpos($action, 'Post_List_Table_Thumb') && ! current_user_can('edit_others_posts') )
				return false;
		}, 10, 2 );
	}

	## добавляет колонку картинки в таблицу терминов
	static function add_image_column( $columns ){
		add_action( 'admin_notices', [ __CLASS__, '_add_js_css_html' ] );

		// колонка без названия..
		return array_slice( $columns, 0, 1 ) + [ 'image'=>'' ] + $columns;
	}

	## заполняет колонку
	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, 1 );

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

			echo '
			<a class="pthumb thickbox" data-post_id="'. $post_id .'" data-attach_id="'. $attach_id .'"
				href="/?TB_inline&inlineId=up-set-post-thumb&width=700&height=500"
				title="Установка миниатюры записи"
			>
				<img src="'. $img_src .'" />
			</a>';
		}
	}

	## html модального окна, css и js
	static function _add_js_css_html( $taxonomy ){
		add_thickbox(); // скрипты модального окна

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

		?>
		<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>

		<!-- модальное окно -->
		<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">Картинки записи</button>
					<button class="button button-small / last-images-js">Последнии из библиотеки</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">Установить миниатюру</button>

					<button class="button / file-btn-js"><span class="dashicons dashicons-format-image" style=""></span> Загрузить картики</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>Снять миниатюру</button>
					<button class="button / del-attach-js"><span class="dashicons dashicons-trash"></span>Удалить файл</button>
				</div>

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

	## script
	static function _script_code(){
		?>
		<script>
			jQuery(document).ready(function($){

				var $thickbox   = $('.pthumb-thickbox'),
					$file       = $thickbox.find('[type="file"]'),
					$images     = $thickbox.find('.images'),
					$fullView   = $thickbox.find('.full-view'),
					$filters    = $thickbox.find('.filters'),
					imageboxTpl = $thickbox.find('#imagebox-tpl-js').html()
				$cur_pthumb = null;

				var showMessage = function( message, type ){
						var $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);
					}

				var _fillImagesBox = function( attachs ){
					$images.empty();

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

					// переберем массив объектов и добавляем елементы
					for( var id in attachs ){
						var attach = attachs[ id ];

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

						// установим текущую миниатюру
						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');

					}
				};

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

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

					var 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 запрос - загружаем все картинки записи
					showMessage( 'Загружаю...' );
					ajaxs( '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 = $('.pthumb[data-post_id="'+ args.post_id +'"]');
						else if( args.attach_id ) args.$pthumb = $('.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 ? '<?= self::$add_img_url ?>' : args.src );
				};

				// клик по "Загрузить любые картинки", когда у записи нет картинок
				$filters.on( 'click', 'button', function(resp){
					var $btn = $(this);

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

				});

				// клик по миниатюре в таблице записей
				$('.wp-list-table').on('click', '.pthumb', function(ev){
					$cur_pthumb = $(this); // Установим текущий объект картинки он хранит ID поста...

					_loadAttachments();
				});

				// клик по миниатюре в модальном окне - выбор картинки
				$images.on('click', '.imagebox', function(){
					var $box = $(this);
					$images.find('.imagebox').removeClass('selected')
					$box.addClass('selected')
				} )

				// клик по кнопке "установить миниатюру"
				$thickbox.find('.set-thumb-js').click(function(){
					var $setbtn            = $(this),
						$selected_imagebox = $images.find('.selected'),
						data = {
							attach_id : $selected_imagebox.data('attach_id'),
							post_id   : $cur_pthumb.data('post_id')
						}

					// AJAX
					showMessage( 'Работаю...' );
					ajaxs( 'Post_List_Table_Thumb::ajaxs_set_post_thumbnail', data, function(resp){
						hideMessage();

						// утановлено, закрываем модалку
						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' );

					})

				});

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

					// AJAX
					showMessage('Снимаю...')
					ajaxs( 'Post_List_Table_Thumb::ajaxs_delete_post_thumbnail', { post_id : $cur_pthumb.data('post_id') }, function(resp){
						$images.find('.imagebox').removeClass('selected')
						showMessage('Миниатюра снята!')

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

				// клик по кнопке "загрузить файлы" - тригер input file
				$thickbox.find('.file-btn-js').click(function(){  $file.trigger('click')  });
				// загружаем файл
				$file.change(function(){

					// AJAX загружаем файлы
					showMessage('Загружаю...');
					ajaxs( '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();
						}
					)

				});

				// клик по "Удалить файл"
				$thickbox.find('.del-attach-js').click(function(){
					var $selected = $images.find('.selected');

					if( ! $selected.length )
						return showMessage('ОШИБКА: Ничего не выбрано...','error');

					if( ! confirm('Точно удалить? Фалй будет удален навсегда!') )
						return;

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

				// двойной клик по картинке в модальном окне - просмотр картинки в полном размере
				$images.on( 'dblclick', '.imagebox', function(){
					$fullView.show().find('img').attr('src', $(this).data('full_url') );
					$thickbox.find('.images').hide();
				});

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

			});
		</script>
		<?php
	}

	// AJAX обработчики ---------

	## получает картинки записи
	static function ajaxs_get_images( $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 );

		// добавим текущую миниатюру
		if( $jx->cur_attach_id && $attach = get_post($jx->cur_attach_id) ){
			array_unshift( $attachs, $attach );
		}

		//$jx->log($attachs); // дебаг

		// добавим ссылку на миниатюру
		foreach( $attachs as & $attach ){
			$attach->thumb_url = wp_get_attachment_image_url( $attach->ID, 'thumbnail' );
		}

		return $attachs;
	}

	static function ajaxs_set_post_thumbnail( $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( 'ОШИБКА: не указано вложение или пост...' ); // die

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

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

		// вложение не прикреплено никуда, прикрепим к текущему посту
		if( ! $attach_post ){
			wp_update_post( [ 'ID'=>$atatch->ID, 'post_parent'=>$post->ID ] );
		}
		// вложение прикреплено к специальному типу записи и текущий тип записи такой же как у записи вложения
		// открепим вложение и прикрепим к текущему посту
		elseif( in_array($attach_post->post_type, self::$re_set_attach_for_post_types) && $attach_post->post_type === $post->post_type ){

			wp_update_post( [ 'ID'=>$atatch->ID, 'post_parent'=>$post->ID ] );

			// удалим миниатюру у прошлого поста, если у него установлена таже самая миниатюра
			if( $attach_post->ID !== $post->ID && get_post_thumbnail_id($attach_post->ID) == $atatch->ID ){
				delete_post_thumbnail( $attach_post->ID );

				$jx->call( 'window.resetPthumbImage', ['clear'=>1, 'post_id'=>$attach_post->ID] ); // очистим миниатюру у прошлого поста
			}
		}

		$jx->success();
	}

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

	static function ajaxs_upload_files( $jx ){
		if( ! $jx->pthumb_files )
			$jx->error('ОШИБКА: Нет файлов...');

		if( ! $jx->post_id )
			$jx->error('ОШИБКА: Не указан ID поста...');

		// фильтр допустимых типов файлов - разрешим только картинки
		add_filter( 'upload_mimes', function( $mimes ){
			return [ 'jpg|jpeg|jpe'=>'image/jpeg', 'gif'=>'image/gif', 'png'=>'image/png', 'webp'=>'image/webp' ];
		} );

		// загружаем файлы
		$results = array();
		foreach( $jx->pthumb_files['compact'] as $filedata ){
			//  чтобы если название в кириллице оно и оставалось в кириллице...
			$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[] = 'ОШИБКА: '. $id->get_error_message() .' Файл: '. esc_html($filedata['name']);
			}
			else
				$results[] = 'OK: '. esc_html($filedata['name']);

		}

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

	}

	static function ajaxs_delete_attach( $jx ){
		if( ! $jx->attach_id )
			$jx->error('ОШИБКА: Не указан ID вложения...');

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

		if( false === $res )
			$jx->error('ОШИБКА: Не удалось удалить...');

		$jx->call( 'window.resetPthumbImage', ['clear'=>1, 'attach_id'=>$jx->attach_id] ); // очистим миниатюру в таблице постов

		$jx->success('Удалено!');
	}

}

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

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

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