Установка миниатюры записи из таблицы записей в админке
В этой заметке вы найдете код, который позволяет устанавливать миниатюры записи (поста) со страницы редактирования записей в админ-панели. Картинки также можно загружать/удалять и т.д.
Создайте плагин из кода ниже. Или создайте файл из кода ниже и подключите его в файл темы functions.php.
ВАЖНО: Код работает на основе плагина AJAX Simply, без него работать не будет!
<?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 загрузчика, но там придется разбираться с кучей всего. Мне нужен был такой функционал и проще (быстрее) было написать код выше. Если есть альтернативные решения такой задачи, пожалуйста поделитесь в комментариях.