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

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

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

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

<?php

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

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

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

	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",  array( __CLASS__, 'add_image_column' ) );
			add_filter("manage_{$ptype}_posts_custom_column", array( __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', array( __CLASS__, '_add_js_css_html' ) );

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

	## заполняет колонку
	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', array( __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'),
						$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' ];
		} );

		// загружаем файлы
		$results = array();
		foreach( $jx->pthumb_files['compact'] as $filedata ){
			//  чтобы если название в кириллице оно и оставалось в кириллице...
			$name = sanitize_text_field( preg_replace( '/\.(?:jpg|jpeg|png|gif)$/', '', $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('Удалено!');
	}

}

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

Код работает на основе плагина AJAX Simply.

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

3 коммента
  • Антон

    Мне кажется в связке с Gutenberg, немного не отображаются в сплывающем окне файлы , постоянно горит значок (Загружаю...) Но возможно это мой частный случай.

    Видео проблемы - https://www.youtube.com/watch?v=uzsWoOCditQ&t=2s

    Ответить21 день назад #
    • Kama7021

      Скрипт не отрабатывает как положено, какая-то ошибка там... В консоли что видно?

      Ответить7 дней назад #
  • Спасибо за код! И за подробные комментарии! Поможет начинающим в изучении темы

    Ответить21 день назад #
Здравствуйте, !     Войти . Зарегистрироваться