PHP /**
* Snippet Name: Enhanced Media Library
* Snippet Author: coding-bunny.com
* Description: Enhance the WordPress media library.
* Version: 1.0.2
*/
if ( ! defined ( ' ABSPATH ' ) ) {
exit;
}
add_action ( ' admin_head ' , ' cbaml_styles ' ) ;
add_action ( ' admin_footer ' , ' cbaml_scripts ' ) ;
add_action ( ' wp_ajax_cbaml_save_rating ' , ' cbaml_save_rating ' ) ;
add_action ( ' wp_ajax_cbaml_save_label ' , ' cbaml_save_label ' ) ;
add_action ( ' wp_ajax_cbaml_save_thumbsize ' , ' cbaml_save_thumbsize ' ) ;
add_action ( ' wp_ajax_cbaml_save_theme ' , ' cbaml_save_theme ' ) ;
add_action ( ' wp_ajax_cbaml_get_media ' , ' cbaml_get_media ' ) ;
add_action ( ' wp_ajax_cbaml_delete_media ' , ' cbaml_delete_media ' ) ;
function cbaml_styles () {
if ( ! current_user_can ( ' upload_files ' )) return;
$ screen = get_current_screen () ;
if ( $ screen && $ screen -> id === ' upload ' ) {
?>
< style >
: root {
-- bg1 : #f7f8fa;
-- bg2 : #fff;
-- bg3 : #eceef0;
-- text1 : #23272f;
-- text2 : #7a7a7a;
-- text3 : #b0b0b0;
-- border : #d7dadf;
-- accent : #3858e9;
-- danger : #fa5252;
-- warning : #ffd43b;
-- shadow1 : rgba ( 0 , 0 , 0 , 0 . 04 ) ;
-- shadow2 : rgba ( 0 , 0 , 0 , 0 . 13 ) ;
}
[ data - theme = " dark " ] {
-- bg1 : #16191c;
-- bg2 : #23272f;
-- bg3 : #262a33;
-- text1 : #fff;
-- text2 : #b0b0b0;
-- text3 : #888;
-- border : #23272f;
-- accent : #4a9eff;
-- danger : #ff6b6b;
-- warning : #ffe066;
-- shadow1 : rgba ( 0 , 0 , 0 , 0 . 13 ) ;
-- shadow2 : rgba ( 0 , 0 , 0 , 0 . 24 ) ;
}
. upload - php . wp - list - table . media ,
. upload - php . tablenav ,
. upload - php . wp - filter ,
. upload - php . view -switch ,
.upload-php .media-toolbar,
.upload-php .attachments-browser,
.upload-php .media-frame,
.upload-php .media-toolbar-secondary,
.upload-php .media-toolbar-primary,
.upload-php .media-frame-content,
.upload-php .media-sidebar { display : none ! important ; }
. cbaml - container { background :var ( -- bg1 ) ; border - radius : 3 px ; padding : 20 px ; margin : 20 px 0 ; box - shadow : 0 2 px 10 px var ( -- shadow1 ) ; }
. cbaml - controls { background :var ( -- bg2 ) ; padding : 15 px 15 px 10 px 15 px ; border - radius : 3 px ; margin - bottom : 18 px ; border : 1 px solid var ( -- border ) ; }
. cbaml - row { display : flex ; flex - wrap : wrap ; gap : 14 px ; align - items : center ; margin - bottom : 12 px ; }
. cbaml - row : last - child { margin - bottom : 0 ; }
. cbaml - group { display : flex ; align - items : center ; gap : 8 px ; }
. cbaml - label { font - weight : 600 ; color :var ( -- text1 ) ; font - size : 14 px ; min - width : 70 px ; }
. cbaml - theme { background :var ( -- bg3 ) ; border : none ; color :var ( -- text1 ) ; padding : 6 px 14 px ; border - radius : 3 px ; cursor : pointer ; font - size : 13 px ; font - weight : 600 ; transition : background . 2 s ; display : flex ; align - items : center ; gap : 5 px ; }
. cbaml - theme : hover { background :var ( -- accent ) ; color : #fff;}
. cbaml - search { flex : 1 ; min - width : 180 px ; padding : 9 px 16 px ; border : 1 px solid var ( -- border ) ; border - radius : 3 px ; font - size : 15 px ; background :var ( -- bg3 ) ; color :var ( -- text1 ) ; transition : border . 2 s ; }
. cbaml - search : focus { outline : none ; border - color :var ( -- accent ) ; background :var ( -- bg2 ) ; }
. cbaml - slider { width : 120 px ; margin : 0 7 px ; height : 5 px ; background :var ( -- border ) ; border - radius : 3 px ;- webkit - appearance : none ; }
. cbaml - slider ::- webkit - slider - thumb , . cbaml - slider ::- moz - range - thumb { width : 16 px ; height : 16 px ; border - radius : 2 px ; background :var ( -- accent ) ; cursor : pointer ; border : none ; }
. cbaml - slidervalue { font - size : 13 px ; color :var ( -- text2 ) ; min - width : 45 px ; font - weight : 600 ; background :var ( -- bg3 ) ; color :var ( -- text1 ) ; padding : 2 px 7 px ; border - radius : 3 px ; }
. cbaml - filters { display : flex ; gap : 7 px ; flex - wrap : wrap ; }
. cbaml - filter { padding : 6 px 13 px ; background :var ( -- bg3 ) ; border : 1 px solid var ( -- border ) ; border - radius : 3 px ; cursor : pointer ; font - size : 13 px ; font - weight : 600 ; color :var ( -- text1 ) ; transition : background . 2 s , color . 2 s , border . 2 s ; }
. cbaml - filter . active , . cbaml - filter : focus { background :var ( -- accent ) ; color : #fff;border-color:var(--accent);}
. cbaml - filter : hover : not ( . active ) { background :var ( -- border ) ; }
. cbaml - labels { display : flex ; gap : 5 px ; flex - wrap : wrap ; align - items : center ; }
. cbaml - labelcolor { width : 22 px ; height : 22 px ; border - radius : 3 px ; cursor : pointer ; border : 2 px solid transparent ; transition : border . 2 s , box - shadow . 2 s , background . 2 s ; position : relative ; flex - shrink : 0 ; }
. cbaml - labelcolor . active , . cbaml - labelcolor : hover { border - color :var ( -- accent ) ; box - shadow : 0 1 px 3 px var ( -- shadow2 ) ; }
. cbaml - labelcolor . red { background : #e74c3c;}
. cbaml - labelcolor . blue { background : #3498db;}
. cbaml - labelcolor . green { background : #2ecc71;}
. cbaml - labelcolor . yellow { background : #f1c40f;}
. cbaml - labelcolor . purple { background : #9b59b6;}
. cbaml - labelcolor . orange { background : #e67e22;}
. cbaml - labelcolor . pink { background : #ff69b4;}
. cbaml - labelcolor . teal { background : #1abc9c;}
. cbaml - labelcolor . none { background :var ( -- bg2 ) ; border - color :var ( -- border ) ; }
. cbaml - labelcolor . none :: before { content : ' × ' ; position : absolute ; top : 50 %; left : 50 %; transform : translate ( - 50 % , - 50 % ) ; color :var ( -- text3 ) ; font - weight : bold ; font - size : 15 px ; }
. cbaml - gallery { display : grid ; gap : 14 px ; margin - top : 16 px ; transition : all 0 . 2 s ; }
. cbaml - list - view { display : block ; }
. cbaml - list - table { width : 100 %; border - collapse : collapse ; }
. cbaml - list - table th , . cbaml - list - table td { padding : 8 px 6 px ; font - size : 13 px ; }
. cbaml - list - table th { background :var ( -- bg3 ) ; color :var ( -- text1 ) ; font - weight : 600 ; text - align : left ; }
. cbaml - list - table tr { border - bottom : 1 px solid var ( -- border ) ; }
. cbaml - list - table td . cbaml - thumbcell img { height : 40 px ; width : auto ; }
. cbaml - list - table td . cbaml - list - actions { white - space : nowrap ; }
. cbaml - view - toggle { margin - left : 16 px ; border : none ; background :var ( -- bg3 ) ; color :var ( -- text1 ) ; font - size : 15 px ; padding : 6 px 14 px ; border - radius : 3 px ; font - weight : 600 ; cursor : pointer ; transition : background . 2 s ; }
. cbaml - view - toggle . active , . cbaml - view - toggle : focus { background :var ( -- accent ) ; color : #fff;}
. cbaml - item { background :var ( -- bg2 ) ; border - radius : 3 px ; padding : 10 px ; box - shadow : 0 2 px 8 px var ( -- shadow1 ) ; position : relative ; cursor : pointer ; border : 1 . 5 px solid transparent ; transition : border . 2 s , box - shadow . 2 s , transform . 18 s ; }
. cbaml - item : hover { border - color :var ( -- accent ) ; box - shadow : 0 4 px 12 px var ( -- shadow2 ) ; transform : translateY ( - 2 px ) ; }
. cbaml - item . selected { border - color :var ( -- accent ) ; box - shadow : 0 0 0 2 px var ( -- accent ) ; }
. cbaml - itemlabel { position : absolute ; top : 8 px ; right : 8 px ; width : 20 px ; height : 20 px ; border - radius : 2 px ; border : 1 . 5 px solid #fff;z-index:3;}
. cbaml - itemlabel . red { background : #e74c3c;}
. cbaml - itemlabel . blue { background : #3498db;}
. cbaml - itemlabel . green { background : #2ecc71;}
. cbaml - itemlabel . yellow { background : #f1c40f;}
. cbaml - itemlabel . purple { background : #9b59b6;}
. cbaml - itemlabel . orange { background : #e67e22;}
. cbaml - itemlabel . pink { background : #ff69b4;}
. cbaml - itemlabel . teal { background : #1abc9c;}
. cbaml - thumb { width : 100 %;object- fit : contain ; border - radius : 2 px ; margin - bottom : 7 px ; background :var ( -- bg3 ) ; transition : transform . 18 s ; box - shadow : none ; }
. cbaml - item : hover . cbaml - thumb { transform : scale ( 1 . 01 ) ; }
. cbaml - filename { font - size : 12 px ; font - weight : 600 ; color :var ( -- text1 ) ; margin - bottom : 6 px ; word -break:break- word ; line - height : 1 . 35 ; max - height : 2 . 5 em ; overflow : hidden ; }
. cbaml - meta { font - size : 12 px ; color :var ( -- text2 ) ; margin - bottom : 6 px ; }
. cbaml - ratingwrap { display : flex ; align - items : center ; justify - content : space - between ; margin - bottom : 7 px ; padding : 3 px 3 px 3 px 5 px ; background :var ( -- bg3 ) ; border - radius : 2 px ; }
. cbaml - stars { display : flex ; gap : 1 px ; align - items : center ; }
. cbaml - star { cursor : pointer ; color :var ( -- border ) ; font - size : 16 px ; transition : color . 13 s , transform . 13 s ; user - select : none ; }
. cbaml - star . filled , . cbaml - star . hover { color :var ( -- warning ) ; }
. cbaml - star . remove - hover { color :var ( -- danger ) ; }
. cbaml - star : hover , . cbaml - star . hover { transform : scale ( 1 . 12 ) ; }
. cbaml - star . remove - hover { transform : scale ( 1 . 16 ) ; }
. cbaml - remove { cursor : pointer ; color :var ( -- danger ) ; font - size : 15 px ; font - weight : bold ; margin - left : 4 px ; padding : 0 4 px ; border - radius : 2 px ; background : transparent ; border : none ; transition : background . 15 s ; display : flex ; align - items : center ; justify - content : center ; }
. cbaml - remove : hover { background :var ( -- danger ) ; color : #fff;}
. cbaml - ratingcount { font - size : 11 px ; color :var ( -- text3 ) ; font - weight : 600 ; padding : 0 4 px ; border - radius : 2 px ; }
. cbaml - labelselect { display : none ; gap : 3 px ; justify - content : center ; margin - bottom : 7 px ; }
. cbaml - item . selected . cbaml - labelselect , . cbaml - item : hover . cbaml - labelselect { display : flex ; }
. cbaml - dot { width : 14 px ; height : 14 px ; border - radius : 2 px ; cursor : pointer ; border : 1 . 5 px solid transparent ; transition : border . 15 s , box - shadow . 15 s ; flex - shrink : 0 ; position : relative ; }
. cbaml - dot . active , . cbaml - dot : hover { border - color :var ( -- accent ) ; box - shadow : 0 1 px 2 px var ( -- shadow2 ) ; }
. cbaml - dot . red { background : #e74c3c;}
. cbaml - dot . blue { background : #3498db;}
. cbaml - dot . green { background : #2ecc71;}
. cbaml - dot . yellow { background : #f1c40f;}
. cbaml - dot . purple { background : #9b59b6;}
. cbaml - dot . orange { background : #e67e22;}
. cbaml - dot . pink { background : #ff69b4;}
. cbaml - dot . teal { background : #1abc9c;}
. cbaml - dot . none { background :var ( -- bg2 ) ; border - color :var ( -- border ) ; }
. cbaml - dot . none :: before { content : ' × ' ; position : absolute ; top : 50 %; left : 50 %; transform : translate ( - 50 % , - 50 % ) ; color :var ( -- text3 ) ; font - weight : bold ; font - size : 11 px ; }
. cbaml - actions { display : flex ; gap : 5 px ; opacity : 0 ; transition : opacity . 13 s ; }
. cbaml - item : hover . cbaml - actions { opacity : 1 ; }
. cbaml - action { flex : 1 ; padding : 4 px 8 px ; font - size : 12 px ; border : none ; border - radius : 2 px ; cursor : pointer ; font - weight : 600 ; background :var ( -- accent ) ; color : #fff;transition:background .13s;}
. cbaml - action . delete { background :var ( -- danger ) ; }
. cbaml - action : hover { filter : brightness ( 1 . 05 ) ; }
. cbaml - bulk { background :var ( -- accent ) ; color : #fff;padding:13px 16px;border-radius:3px;margin-bottom:16px;display:none;align-items:center;justify-content:space-between;}
. cbaml - bulk . show { display : flex ; }
. cbaml - bulkcontrols { display : flex ; gap : 8 px ; align - items : center ; flex - wrap : wrap ; }
. cbaml - bulkbtn { background : rgba ( 255 , 255 , 255 , 0 . 17 ) ; border : none ; color : #fff;padding:5px 10px;border-radius:3px;cursor:pointer;font-size:13px;font-weight:600;transition:background .13s;}
. cbaml - bulkbtn : hover { background : rgba ( 255 , 255 , 255 , 0 . 24 ) ; }
. cbaml - stats { background :var ( -- bg2 ) ; padding : 9 px 15 px ; border - radius : 3 px ; margin - bottom : 12 px ; display : flex ; justify - content : space - between ; align - items : center ; font - size : 13 px ; color :var ( -- text2 ) ; border : 1 px solid var ( -- border ) ; }
. cbaml - statsleft { font - weight : 600 ; color :var ( -- text1 ) ; }
. cbaml - statsright { color :var ( -- accent ) ; font - weight : 500 ; }
. cbaml - spinner { display : inline - block ; width : 32 px ; height : 32 px ; border : 4 px solid var ( -- border ) ; border - top : 4 px solid var ( -- accent ) ; border - radius : 50 %; animation : spin 1 s linear infinite ; margin : 40 px auto ; }
@ keyframes spin { 0 % { transform : rotate ( 0 deg ) ; } 100 % { transform : rotate ( 360 deg ) ; }}
. cbaml - empty { text - align : center ; color :var ( -- text2 ) ; padding : 42 px 12 px ; font - size : 17 px ; }
. cbaml - emptyicon { font - size : 42 px ; margin - bottom : 12 px ; opacity : 0 . 6 ; }
@ media ( max - width : 1200 px ) {
. cbaml - row { flex - direction : column ; align - items : stretch ; }
. cbaml - search { min - width : auto ; }
}
@ media ( max - width : 768 px ) {
. cbaml - container , . cbaml - controls { padding : 10 px ; }
. cbaml - filters , . cbaml - labels { justify - content : center ; }
. cbaml - bulk { flex - direction : column ; gap : 10 px ; text - align : center ; }
. cbaml - bulkcontrols { justify - content : center ; }
}
</ style >
<? php
}
}
function cbaml_scripts () {
if ( ! current_user_can ( ' upload_files ' )) return;
$ screen = get_current_screen () ;
if ( $ screen && $ screen -> id === ' upload ' ) {
$ user_id = get_current_user_id () ;
$ saved_size = get_user_meta ( $ user_id , ' cbaml_thumbsize ' , true ) ;
$ saved_theme = get_user_meta ( $ user_id , ' cbaml_theme ' , true ) ;
$ thumbsize = $ saved_size ? intval ( $ saved_size ) : 150 ;
$ theme = ( $ saved_theme && intval ( $ saved_theme ) === 1 ) ? ' true ' : ' false ' ;
$ ajaxurl = esc_url_raw ( admin_url ( ' admin-ajax.php ' )) ;
$ nonce_media = wp_create_nonce ( ' cbaml_media ' ) ;
$ nonce_size = wp_create_nonce ( ' cbaml_size ' ) ;
$ nonce_theme = wp_create_nonce ( ' cbaml_theme ' ) ;
$ nonce_rating = wp_create_nonce ( ' cbaml_rating ' ) ;
$ nonce_label = wp_create_nonce ( ' cbaml_label ' ) ;
?>
< script >
jQuery ( document ) . ready ( function ( $ ) {
let selected = new Set () ;
let fRating = ' all ' , fLabel = ' all ' , fType = ' all ' , fDate = ' all ' , fSort = ' date_desc ' ;
let items = [], filtered = [] ;
let thumbsize = <? php echo $ thumbsize ; ?>;
let theme = <? php echo $ theme ; ?>;
let page = 1 , perPage = 30 , more = false;
let view = ' grid ' ;
window . cbamlItems = items ;
function applyTheme () {
if ( theme ){
$ ( ' body ' ) . attr ( ' data-theme ' , ' dark ' ) ;
$ ( ' .cbaml-themeicon ' ) . text ( ' ☀️ ' ) ;
$ ( ' .cbaml-themetext ' ) . text ( ' Light ' ) ;
} else {
$ ( ' body ' ) . removeAttr ( ' data-theme ' ) ;
$ ( ' .cbaml-themeicon ' ) . text ( ' 🌙 ' ) ;
$ ( ' .cbaml-themetext ' ) . text ( ' Dark ' ) ;
}
}
function sanitize ( str ) {
return $ ( ' <div/> ' ) . text ( str ) . html () ;
}
function getType ( fn , mime ) {
const ext = fn . split ( ' . ' ) . pop () . toLowerCase () ;
if ( /^ image\ //.test(mime) || ['jpg','jpeg','png','gif','svg','webp','bmp'].includes(ext)) return 'image';
if ( /^ video\ //.test(mime) || ['mp4','mov','avi','webm','mkv'].includes(ext)) return 'video';
if ( /^ audio\ //.test(mime) || ['mp3','wav','ogg','aac'].includes(ext)) return 'audio';
if ( ext === ' pdf ' ) return ' pdf ' ;
if ([ ' doc ' , ' docx ' , ' odt ' , ' rtf ' ] . includes ( ext )) return ' doc ' ;
return ' other ' ;
}
function render () {
filtered = items . filter ( item => {
if ( fRating !== ' all ' && ( parseInt ( item . rating ) || 0 ) !== parseInt ( fRating )) return false;
if ( fLabel !== ' all ' ){
const label = item . label || '' ;
if ( fLabel === ' none ' && label !== '' ) return false;
if ( fLabel !== ' none ' && label !== fLabel ) return false;
}
if ( fType !== ' all ' && getType ( item . filename , item . mime ) !== fType ) return false;
if ( fDate !== ' all ' ){
const d = new Date ( item . raw_date ), now = new Date () ;
if ( fDate === ' today ' && d . toDateString () !== now . toDateString ()) return false;
if ( fDate === ' 7days ' && ( now - d ) / 864e5 > 7 ) return false;
if ( fDate === ' 30days ' && ( now - d ) / 864e5 > 30 ) return false;
if ( fDate === ' year ' && ( now - d ) / 864e5 > 365 ) return false;
}
const s = $ ( ' .cbaml-search ' ) . val () . toLowerCase () ;
if ( s &&! item . filename . toLowerCase () . includes ( s )) return false;
return true;
}) ;
if ( fSort === ' az ' ) filtered . sort (( a , b ) => a . filename . localeCompare ( b . filename )) ;
else if ( fSort === ' za ' ) filtered . sort (( a , b ) => b . filename . localeCompare ( a . filename )) ;
else if ( fSort === ' date_asc ' ) filtered . sort (( a , b ) =>new Date ( a . raw_date ) -new Date ( b . raw_date )) ;
else filtered . sort (( a , b ) =>new Date ( b . raw_date ) -new Date ( a . raw_date )) ;
renderPage () ;
stats () ;
}
function renderPage () {
const g = $ ( ' .cbaml-gallery ' ) ;
let start = 0 , end = page * perPage ;
let show = filtered . slice ( 0 , end ) ; more = end < filtered . length ;
if ( ! show . length ){ g . html ( ' <div class="cbaml-empty"><div class="cbaml-emptyicon">🔍</div>No items found.</div> ' ) ; $ ( ' .cbaml-loadmore ' ) . hide () ;return; }
if ( view === ' list ' ){ renderList ( show ) ; }
else { renderGrid ( show ) ; }
if ( more ) $ ( ' .cbaml-loadmore ' ) . show () ;else $ ( ' .cbaml-loadmore ' ) . hide () ;
}
function renderGrid ( show ) {
let html = '' ;
show . forEach ( item => {
const rating = parseInt ( item . rating ) || 0 , label = item . label || '' , stars = starHtml ( rating ), dots = dotHtml ( label ) ;
const sel = selected . has ( item . id ) ? ' selected ' : '' ;
html += `
<div class="cbaml-item ${ sel } " data-id="${sanitize(item.id)}" data-rating="${sanitize(rating)}" data-label="${sanitize(label)}">
${label? ` < div class= " cbaml-itemlabel ${sanitize(label)} " ></ div > ` :''}
<img src="${sanitize(item.thumbnail)}" alt="${sanitize(item.filename)}" class="cbaml-thumb" loading="lazy" style="height: ${ thumbsize } px;">
<div class="cbaml-filename" title="${sanitize(item.filename)}">${sanitize(item.filename)}</div>
<div class="cbaml-meta">${sanitize(item.dimensions)} • ${sanitize(item.filesize)} • ${sanitize(item.date)}</div>
<div class="cbaml-ratingwrap">
<div class="cbaml-stars" data-id="${sanitize(item.id)}">
${ stars }
<button type="button" class="cbaml-remove" data-id="${sanitize(item.id)}" title="Remove rating">×</button>
</div>
<span class="cbaml-ratingcount">${sanitize(rating)}/5</span>
</div>
<div class="cbaml-labelselect" data-id="${sanitize(item.id)}"> ${ dots } </div>
<div class="cbaml-actions">
<button class="cbaml-action edit" data-id="${sanitize(item.id)}">Edit</button>
<button class="cbaml-action delete" data-id="${sanitize(item.id)}">Delete</button>
</div>
</div> ` ;
}) ;
$ ( ' .cbaml-gallery ' ) . removeClass ( ' cbaml-list-view ' ) . html ( html ) ;
}
function renderList ( show ) {
let html = ` <table class="cbaml-list-table"><thead>
<tr>
<th>Thumb</th>
<th>Filename</th>
<th>Type</th>
<th>Dimensions</th>
<th>Size</th>
<th>Date</th>
<th>Rating</th>
<th>Label</th>
<th>Actions</th>
</tr></thead><tbody> ` ;
show . forEach ( item => {
const rating = parseInt ( item . rating ) || 0 , label = item . label || '' ;
let stars = '' ;
for ( let i = 1 ; i <= 5 ; i ++ ) stars += ` <span class="cbaml-star${i<=rating?' filled':''}" data-rating=" ${ i } " style="font-size:14px;">★</span> ` ;
let dot = label ? ` <span class="cbaml-dot ${sanitize(label)} active"></span> ` : ` <span class="cbaml-dot none active"></span> ` ;
html += `
<tr class="cbaml-list-row" data-id="${sanitize(item.id)}" data-rating="${sanitize(rating)}" data-label="${sanitize(label)}">
<td class="cbaml-thumbcell"><img src="${sanitize(item.thumbnail)}" alt="${sanitize(item.filename)}" style="height:40px;"></td>
<td>${sanitize(item.filename)}</td>
<td>${sanitize(getType(item.filename,item.mime))}</td>
<td>${sanitize(item.dimensions)}</td>
<td>${sanitize(item.filesize)}</td>
<td>${sanitize(item.date)}</td>
<td>
<span class="cbaml-stars" data-id="${sanitize(item.id)}"> ${ stars } </span>
<button type="button" class="cbaml-remove" data-id="${sanitize(item.id)}" title="Remove rating" style="margin-left:5px;">×</button>
</td>
<td>
<div class="cbaml-labelselect" data-id="${sanitize(item.id)}" style="display:inline-flex;gap:2px;vertical-align:middle;">
${dotHtml(label)}
</div>
</td>
<td class="cbaml-list-actions">
<button class="cbaml-action edit" data-id="${sanitize(item.id)}">Edit</button>
<button class="cbaml-action delete" data-id="${sanitize(item.id)}">Delete</button>
</td>
</tr> ` ;
}) ;
html += ' </tbody></table> ' ;
$ ( ' .cbaml-gallery ' ) . addClass ( ' cbaml-list-view ' ) . html ( html ) ;
}
function starHtml ( rating ) {
let s = '' ;for ( let i = 1 ; i <= 5 ; i ++ ){ const f = i <= rating ? ' filled ' : '' ; s += ` <span class="cbaml-star ${ f } " data-rating=" ${ i } ">★</span> ` ; } return s ;
}
function dotHtml ( active ) {
const colors = [ ' red ' , ' blue ' , ' green ' , ' yellow ' , ' purple ' , ' orange ' , ' pink ' , ' teal ' ] ;
let d = ` <div class="cbaml-dot none${active===''?' active':''}" data-label="" title="No label"></div> ` ;
colors . forEach ( c => { d += ` <div class="cbaml-dot ${ c } ${active===c?' active':''}" data-label=" ${ c } " title="${c.charAt(0).toUpperCase()+c.slice(1)}"></div> ` ; }) ;
return d ;
}
function stats () {
const total = items . length , visible = $ ( ' .cbaml-item,.cbaml-list-row ' ) . length , sel = selected . size ;
$ ( ' .cbaml-total ' ) . text ( ` ${ visible } of ${ total } items ` ) ;
$ ( ' .cbaml-statsright ' ) . text ( sel > 0 ? ` ${ sel } selected ` : '' ) ;
}
function grid () {
const min = Math . max ( thumbsize , 120 ) ;
$ ( ' .cbaml-gallery ' ) . css ( ' grid-template-columns ' , ` repeat(auto-fill,minmax( ${ min } px,1fr)) ` ) ;
}
function load () {
$ . ajax ({
url: ' <?php echo $ajaxurl; ?> ' , type : ' POST ' , data : { action : ' cbaml_get_media ' , nonce : ' <?php echo $nonce_media; ?> ' },
success: function ( r ){
if ( r . success ){ items = r . data ; window . cbamlItems = items ; page = 1 ; render () ; }
},
error: function (){ $ ( ' .cbaml-gallery ' ) . html ( ' <div class="cbaml-empty"><div class="cbaml-emptyicon">😞</div>Error loading media.</div> ' ) ; }
}) ;
}
function saveThumb () {
$ . ajax ({ url : ' <?php echo $ajaxurl; ?> ' , type : ' POST ' , data : { action : ' cbaml_save_thumbsize ' , size : thumbsize , nonce : ' <?php echo $nonce_size; ?> ' }}) ;
}
function saveTheme () {
$ . ajax ({ url : ' <?php echo $ajaxurl; ?> ' , type : ' POST ' , data : { action : ' cbaml_save_theme ' , theme : theme ? ' true ' : ' false ' , nonce : ' <?php echo $nonce_theme; ?> ' }}) ;
}
function saveRating ( id , r ) {
$ . ajax ({ url : ' <?php echo $ajaxurl; ?> ' , type : ' POST ' , data : { action : ' cbaml_save_rating ' , item_id : id , rating : r , nonce : ' <?php echo $nonce_rating; ?> ' }}) ;
}
function saveLabel ( id , l ) {
$ . ajax ({ url : ' <?php echo $ajaxurl; ?> ' , type : ' POST ' , data : { action : ' cbaml_save_label ' , item_id : id , label : l , nonce : ' <?php echo $nonce_label; ?> ' }}) ;
}
function bind () {
$ ( ' .cbaml-theme ' ) . on ( ' click ' , function (){
theme =! theme ;
applyTheme () ;
saveTheme () ;
}) ;
$ ( ' .cbaml-slider ' ) . on ( ' input ' , function (){
thumbsize = parseInt ( $ ( this ) . val ()) ;
$ ( ' .cbaml-slidervalue ' ) . text ( thumbsize + ' px ' ) ;
$ ( ' .cbaml-thumb ' ) . css ( ' height ' , thumbsize + ' px ' ) ;
grid () ;
}) . on ( ' change ' , saveThumb ) ;
$ ( document ) . on ( ' click ' , ' .cbaml-remove ' , function ( e ){
e . stopPropagation () ;const id = $ ( this ) . data ( ' id ' ) ; setRating ( id , 0 ) ;
}) ;
$ ( document ) . on ( ' click ' , ' .cbaml-star ' , function ( e ){
e . stopPropagation () ;
const $ s = $ ( this ), r =$ s . data ( ' rating ' ), id =$ s . closest ( ' .cbaml-stars ' ) . data ( ' id ' ) ;
const $ item = view === ' list ' ? $ ( ' .cbaml-list-row[data-id=" ' + id + ' "] ' ) : $ ( ` .cbaml-item[data-id=" ${ id } "] ` ) ;
const curr = parseInt ( $ item . data ( ' rating ' )) || 0 ;
setRating ( id , curr === r ? 0 : r ) ;
}) ;
$ ( document ) . on ( ' mouseenter ' , ' .cbaml-star ' , function (){
const $ t = $ ( this ), r =$ t . data ( ' rating ' ), $ s =$ t . closest ( ' .cbaml-stars ' ) . find ( ' .cbaml-star ' ), id =$ t . closest ( ' .cbaml-stars ' ) . data ( ' id ' ) ;
const $ item = view === ' list ' ? $ ( ' .cbaml-list-row[data-id=" ' + id + ' "] ' ) : $ ( ` .cbaml-item[data-id=" ${ id } "] ` ) ;
const curr = parseInt ( $ item . data ( ' rating ' )) || 0 ;
$ s . removeClass ( ' hover remove-hover ' ) ;
if ( curr === r ){ $ t . addClass ( ' remove-hover ' ) ; } else { $ s . slice ( 0 , r ) . addClass ( ' hover ' ) ; }
}) ;
$ ( document ) . on ( ' mouseleave ' , ' .cbaml-stars ' , function (){ $ ( this ) . find ( ' .cbaml-star ' ) . removeClass ( ' hover remove-hover ' ) ; }) ;
$ ( document ) . on ( ' click ' , ' .cbaml-dot ' , function ( e ){
e . stopPropagation () ;
const $ d = $ ( this ), label =$ d . data ( ' label ' ), id =$ d . closest ( ' .cbaml-labelselect ' ) . data ( ' id ' ) ;
setLabel ( id , label ) ;
}) ;
$ ( document ) . on ( ' click ' , ' .cbaml-item ' , function ( e ){
if ( $ ( e . target ) . is ( ' .cbaml-star,.cbaml-action,.cbaml-dot,.cbaml-remove ' )) return;
const id = parseInt ( $ ( this ) . data ( ' id ' )) ;
if ( e . ctrlKey || e . metaKey ){ toggle ( id ) ; }
else { clearSel () ; select ( id ) ; }
}) ;
$ ( ' .cbaml-search ' ) . on ( ' input ' , debounce ( function (){ render () ; }, 300 )) ;
$ ( ' .cbaml-sort ' ) . on ( ' change ' , function (){ fSort = $ ( this ) . val () ; render () ; }) ;
$ ( ' .cbaml-type ' ) . on ( ' change ' , function (){ fType = $ ( this ) . val () ; render () ; }) ;
$ ( ' .cbaml-date ' ) . on ( ' change ' , function (){ fDate = $ ( this ) . val () ; render () ; }) ;
$ ( document ) . on ( ' click ' , ' .cbaml-filter[data-filter] ' , function (){
$ ( ' .cbaml-filter[data-filter] ' ) . removeClass ( ' active ' ) ; $ ( this ) . addClass ( ' active ' ) ;
fRating = $ ( this ) . data ( ' filter ' ) ; render () ;
}) ;
$ ( document ) . on ( ' click ' , ' [data-label-filter] ' , function (){
$ ( ' [data-label-filter] ' ) . removeClass ( ' active ' ) ; $ ( this ) . addClass ( ' active ' ) ;
fLabel = $ ( this ) . data ( ' label-filter ' ) ; render () ;
}) ;
$ ( document ) . on ( ' click ' , ' .cbaml-bulkbtn.rate ' , function (){
const r = $ ( this ) . data ( ' rating ' ) ; selected . forEach ( id => setRating ( id , r )) ;
}) ;
$ ( document ) . on ( ' click ' , ' .cbaml-bulklabel ' , function (){
const l = $ ( this ) . data ( ' label ' ) ; selected . forEach ( id => setLabel ( id , l )) ;
}) ;
$ ( document ) . on ( ' click ' , ' .cbaml-bulkbtn.deselect ' , function (){ clearSel () ; }) ;
$ ( document ) . on ( ' keydown ' , function ( e ){
if ( e . ctrlKey && e . key === ' a ' ){ e . preventDefault () ; selectAll () ; }
if ( e . key >= ' 1 ' && e . key <= ' 5 ' && selected . size > 0 ) selected . forEach ( id => setRating ( id , parseInt ( e . key ))) ;
if ( e . key === ' 0 ' && selected . size > 0 ) selected . forEach ( id => setRating ( id , 0 )) ;
}) ;
$ ( document ) . on ( ' click ' , ' .cbaml-action.edit ' , function ( e ){
e . stopPropagation () ;
const id = $ ( this ) . data ( ' id ' ) ;
window . open ( ' <?php echo esc_url(admin_url( ' post . php ' )); ?>?post= ' + encodeURIComponent ( id ) + ' &action=edit ' , ' _blank ' ) ;
}) ;
$ ( document ) . on ( ' click ' , ' .cbaml-action.delete ' , function ( e ){
e . stopPropagation () ;const id = $ ( this ) . data ( ' id ' ) ;
if ( confirm ( ' Are you sure? ' )){
$ . ajax ({ url : ' <?php echo $ajaxurl; ?> ' , type : ' POST ' , data : { action : ' cbaml_delete_media ' , nonce : ' <?php echo $nonce_media; ?> ' , item_id : id },
success: function ( r ){
if ( r . success ){ items = items . filter ( i => i . id != id ) ; selected . delete ( id ) ; render () ; }
else alert ( r . data || ' Delete error ' ) ;
}
}) ;
}
}) ;
$ ( ' .cbaml-loadmore ' ) . on ( ' click ' , function (){ page += 1 ; renderPage () ; }) ;
$ ( ' .cbaml-view-toggle ' ) . on ( ' click ' , function (){
$ ( ' .cbaml-view-toggle ' ) . removeClass ( ' active ' ) ;
$ ( this ) . addClass ( ' active ' ) ;
view = $ ( this ) . data ( ' view ' ) ;
page = 1 ;
render () ;
}) ;
}
function setRating ( id , r ){
const $ item = view === ' list ' ? $ ( ' .cbaml-list-row[data-id=" ' + id + ' "] ' ) : $ ( ` .cbaml-item[data-id=" ${ id } "] ` ), $ s =$ item . find ( ' .cbaml-star ' ) ;
$ s . removeClass ( ' filled ' ) ;if ( r > 0 ) $ s . slice ( 0 , r ) . addClass ( ' filled ' ) ;
$ item . find ( ' .cbaml-ratingcount ' ) . text ( ` ${ r } /5 ` ) ;
$ item . attr ( ' data-rating ' , r ) ;
const it = items . find ( i => i . id == id ) ;if ( it ) it . rating = r ;
saveRating ( id , r ) ;
}
function setLabel ( id , l ){
const $ item = view === ' list ' ? $ ( ' .cbaml-list-row[data-id=" ' + id + ' "] ' ) : $ ( ` .cbaml-item[data-id=" ${ id } "] ` ), $ d =$ item . find ( ' .cbaml-dot ' ) ;
$ d . removeClass ( ' active ' ) ;$ d . filter ( ` [data-label=" ${ l } "] ` ) . addClass ( ' active ' ) ;
$ item . attr ( ' data-label ' , l ) ;
$ item . find ( ' .cbaml-itemlabel ' ) . remove () ;
if ( l &&! view === ' list ' ) $ item . prepend ( ` <div class="cbaml-itemlabel ${ l } "></div> ` ) ;
const it = items . find ( i => i . id == id ) ;if ( it ) it . label = l ;
saveLabel ( id , l ) ;
}
function toggle ( id ){ if ( selected . has ( id )){ selected . delete ( id ) ; $ ( ` .cbaml-item[data-id=" ${ id } "] ` ) . removeClass ( ' selected ' ) ; } else { selected . add ( id ) ; $ ( ` .cbaml-item[data-id=" ${ id } "] ` ) . addClass ( ' selected ' ) ; } bulk () ; }
function select ( id ){ selected . add ( id ) ; $ ( ` .cbaml-item[data-id=" ${ id } "] ` ) . addClass ( ' selected ' ) ; bulk () ; }
function clearSel (){ selected . clear () ; $ ( ' .cbaml-item ' ) . removeClass ( ' selected ' ) ; bulk () ; }
function selectAll (){ $ ( ' .cbaml-item:visible ' ) . each ( function (){ const id = parseInt ( $ ( this ) . data ( ' id ' )) ; selected . add ( id ) ; $ ( this ) . addClass ( ' selected ' ) ; }) ; bulk () ; }
function bulk (){ const c = selected . size ;if ( c > 0 ){ $ ( ' .cbaml-bulk ' ) . addClass ( ' show ' ) ; $ ( ' .cbaml-bulkcount ' ) . text ( ` ${ c } selected ` ) ; } else { $ ( ' .cbaml-bulk ' ) . removeClass ( ' show ' ) ; } stats () ; }
function debounce ( f , w ){ let t ;return function ( ...a ){ const l = () => { clearTimeout ( t ) ; f ( ... a ) ; } ; clearTimeout ( t ) ; t = setTimeout ( l , w ) ; } ; }
function layout () {
const html = `
<div class="cbaml-container">
<div style="text-align:right;margin-bottom:8px;">
<button class="cbaml-view-toggle active" data-view="grid" title="Grid view">▥</button>
<button class="cbaml-view-toggle" data-view="list" title="List view">☰</button>
</div>
<div class="cbaml-stats">
<div class="cbaml-statsleft"><span class="cbaml-total">Loading...</span></div>
<div class="cbaml-statsright"></div>
</div>
<div class="cbaml-bulk">
<span class="cbaml-bulkcount">0 selected</span>
<div class="cbaml-bulkcontrols">
<button class="cbaml-bulkbtn rate" data-rating="1">★</button>
<button class="cbaml-bulkbtn rate" data-rating="2">★★</button>
<button class="cbaml-bulkbtn rate" data-rating="3">★★★</button>
<button class="cbaml-bulkbtn rate" data-rating="4">★★★★</button>
<button class="cbaml-bulkbtn rate" data-rating="5">★★★★★</button>
<button class="cbaml-bulkbtn rate" data-rating="0">Remove Rating</button>
<div class="cbaml-labels">
<div class="cbaml-labelcolor red cbaml-bulklabel" data-label="red" title="Red"></div>
<div class="cbaml-labelcolor blue cbaml-bulklabel" data-label="blue" title="Blue"></div>
<div class="cbaml-labelcolor green cbaml-bulklabel" data-label="green" title="Green"></div>
<div class="cbaml-labelcolor yellow cbaml-bulklabel" data-label="yellow" title="Yellow"></div>
<div class="cbaml-labelcolor purple cbaml-bulklabel" data-label="purple" title="Purple"></div>
<div class="cbaml-labelcolor orange cbaml-bulklabel" data-label="orange" title="Orange"></div>
<div class="cbaml-labelcolor pink cbaml-bulklabel" data-label="pink" title="Pink"></div>
<div class="cbaml-labelcolor teal cbaml-bulklabel" data-label="teal" title="Teal"></div>
<div class="cbaml-labelcolor none cbaml-bulklabel" data-label="" title="Remove label"></div>
</div>
<button class="cbaml-bulkbtn deselect">Deselect</button>
</div>
</div>
<div class="cbaml-controls">
<div class="cbaml-row">
<input type="text" class="cbaml-search" placeholder="🔍 Search filename...">
<div class="cbaml-group"><span class="cbaml-label">Sort:</span>
<select class="cbaml-sort">
<option value="date_desc">Newest</option>
<option value="date_asc">Oldest</option>
<option value="az">A → Z</option>
<option value="za">Z → A</option>
</select>
</div>
<div class="cbaml-group"><span class="cbaml-label">Type:</span>
<select class="cbaml-type">
<option value="all">All</option>
<option value="image">Images</option>
<option value="video">Videos</option>
<option value="audio">Audio</option>
<option value="pdf">PDF</option>
<option value="doc">Doc</option>
<option value="other">Other</option>
</select>
</div>
<div class="cbaml-group"><span class="cbaml-label">Date:</span>
<select class="cbaml-date">
<option value="all">All</option>
<option value="today">Today</option>
<option value="7days">Last 7 days</option>
<option value="30days">Last 30 days</option>
<option value="year">Last year</option>
</select>
</div>
<div class="cbaml-group"><span class="cbaml-label">Size:</span>
<input type="range" class="cbaml-slider" min="100" max="300" value=" ${ thumbsize } ">
<span class="cbaml-slidervalue"> ${ thumbsize } px</span>
</div>
<button class="cbaml-theme">
<span class="cbaml-themeicon">${theme?'☀️':'🌙'}</span>
<span class="cbaml-themetext">${theme?'Light':'Dark'}</span>
</button>
</div>
<div class="cbaml-row">
<div class="cbaml-group">
<span class="cbaml-label">Rating:</span>
<div class="cbaml-filters">
<button class="cbaml-filter active" data-filter="all">All</button>
<button class="cbaml-filter" data-filter="5">★★★★★</button>
<button class="cbaml-filter" data-filter="4">★★★★</button>
<button class="cbaml-filter" data-filter="3">★★★</button>
<button class="cbaml-filter" data-filter="2">★★</button>
<button class="cbaml-filter" data-filter="1">★</button>
<button class="cbaml-filter" data-filter="0">No rating</button>
</div>
</div>
</div>
<div class="cbaml-row">
<div class="cbaml-group">
<span class="cbaml-label">Labels:</span>
<div class="cbaml-filters">
<button class="cbaml-filter active" data-label-filter="all">All</button>
</div>
<div class="cbaml-labels">
<div class="cbaml-labelcolor red" data-label-filter="red" title="Red"></div>
<div class="cbaml-labelcolor blue" data-label-filter="blue" title="Blue"></div>
<div class="cbaml-labelcolor green" data-label-filter="green" title="Green"></div>
<div class="cbaml-labelcolor yellow" data-label-filter="yellow" title="Yellow"></div>
<div class="cbaml-labelcolor purple" data-label-filter="purple" title="Purple"></div>
<div class="cbaml-labelcolor orange" data-label-filter="orange" title="Orange"></div>
<div class="cbaml-labelcolor pink" data-label-filter="pink" title="Pink"></div>
<div class="cbaml-labelcolor teal" data-label-filter="teal" title="Teal"></div>
<div class="cbaml-labelcolor none" data-label-filter="none" title="No label"></div>
</div>
</div>
</div>
</div>
<div class="cbaml-gallery"><div class="cbaml-spinner"></div></div>
<div style="text-align:center;margin:18px 0 0 0;">
<button class="cbaml-loadmore" style="display:none;padding:8px 24px;border:none;border-radius:3px;background:var(--accent);color:#fff;font-size:14px;font-weight:400;cursor:pointer;">LOAD MORE...</button>
</div>
</div> ` ;
$ ( ' .wrap h1 ' ) . after ( html ) ;
}
layout () ; bind () ; load () ; grid () ; applyTheme () ;
} ) ;
</ script >
<? php
}
}
function cbaml_check_permissions () {
if ( ! current_user_can ( ' upload_files ' )) {
wp_send_json_error ( ' Permission denied ' , 403 ) ;
exit;
}
}
function cbaml_get_media () {
cbaml_check_permissions () ;
check_ajax_referer ( ' cbaml_media ' , ' nonce ' ) ;
$ args = array (
' post_type ' => ' attachment ' ,
' post_status ' => ' any ' ,
' posts_per_page ' => - 1 ,
' orderby ' => ' date ' ,
' order ' => ' DESC '
) ;
$ attachments = get_posts ( $ args ) ;
$ items = array () ;
foreach ( $ attachments as $ a ) {
$ fp = get_attached_file ( $ a -> ID ) ;
if ( !$ fp || ! file_exists ( $ fp )) continue;
$ m = wp_get_attachment_metadata ( $ a -> ID ) ;
$ rating = get_post_meta ( $ a -> ID , ' _media_rating ' , true ) ?: 0 ;
$ label = get_post_meta ( $ a -> ID , ' _media_label ' , true ) ?: '' ;
$ size = filesize ( $ fp ) ;
$ thumb = wp_get_attachment_image_url ( $ a -> ID , ' medium ' ) ?: wp_get_attachment_url ( $ a -> ID ) ;
$ orig = wp_get_attachment_url ( $ a -> ID ) ;
$ mime = get_post_mime_type ( $ a -> ID ) ;
$ items [] = array (
' id ' => ( int ) $ a -> ID ,
' filename ' => esc_html ( basename ( $ fp )),
' thumbnail ' => esc_url_raw ( $ thumb ),
' original_url ' => esc_url_raw ( $ orig ),
' dimensions ' => isset ( $ m [ ' width ' ]) ? (( int ) $ m [ ' width ' ] . ' × ' . ( int ) $ m [ ' height ' ]) : ' N/A ' ,
' filesize ' => size_format ( $ size ),
' date ' => esc_html ( date_i18n ( ' Y-m-d ' , strtotime ( $ a -> post_date ))),
' raw_date ' => esc_html ( $ a -> post_date ),
' rating ' => ( int ) $ rating ,
' label ' => sanitize_text_field ( $ label ),
' mime ' => sanitize_mime_type ( $ mime ),
) ;
}
wp_send_json_success ( $ items ) ;
}
function cbaml_save_rating () {
cbaml_check_permissions () ;
check_ajax_referer ( ' cbaml_rating ' , ' nonce ' ) ;
$ id = isset ( $ _POST [ ' item_id ' ]) ? intval ( $ _POST [ ' item_id ' ]) : 0 ;
$ r = isset ( $ _POST [ ' rating ' ]) ? intval ( $ _POST [ ' rating ' ]) : 0 ;
if ( $ id && $ r >= 0 && $ r <= 5 ) {
update_post_meta ( $ id , ' _media_rating ' , $ r ) ;
wp_send_json_success () ;
}
wp_send_json_error ( ' Invalid request ' ) ;
}
function cbaml_save_label () {
cbaml_check_permissions () ;
check_ajax_referer ( ' cbaml_label ' , ' nonce ' ) ;
$ id = isset ( $ _POST [ ' item_id ' ]) ? intval ( $ _POST [ ' item_id ' ]) : 0 ;
$ l = isset ( $ _POST [ ' label ' ]) ? sanitize_text_field ( $ _POST [ ' label ' ]) : '' ;
$ ok = [ ' red ' , ' blue ' , ' green ' , ' yellow ' , ' purple ' , ' orange ' , ' pink ' , ' teal ' , '' ] ;
if ( $ id && in_array ( $ l , $ ok , true )) {
update_post_meta ( $ id , ' _media_label ' , $ l ) ;
wp_send_json_success () ;
}
wp_send_json_error ( ' Invalid request ' ) ;
}
function cbaml_save_thumbsize () {
cbaml_check_permissions () ;
check_ajax_referer ( ' cbaml_size ' , ' nonce ' ) ;
$ size = isset ( $ _POST [ ' size ' ]) ? intval ( $ _POST [ ' size ' ]) : 0 ;
$ user = get_current_user_id () ;
if ( $ size >= 100 && $ size <= 300 && $ user ) {
update_user_meta ( $ user , ' cbaml_thumbsize ' , $ size ) ;
wp_send_json_success () ;
}
wp_send_json_error ( ' Invalid request ' ) ;
}
function cbaml_save_theme () {
cbaml_check_permissions () ;
check_ajax_referer ( ' cbaml_theme ' , ' nonce ' ) ;
$ theme = ( isset ( $ _POST [ ' theme ' ]) && $ _POST [ ' theme ' ] === ' true ' ) ? 1 : 0 ;
$ user = get_current_user_id () ;
if ( $ user ) {
update_user_meta ( $ user , ' cbaml_theme ' , $ theme ) ;
wp_send_json_success () ;
}
wp_send_json_error ( ' Invalid request ' ) ;
}
function cbaml_delete_media () {
cbaml_check_permissions () ;
check_ajax_referer ( ' cbaml_media ' , ' nonce ' ) ;
$ id = isset ( $ _POST [ ' item_id ' ]) ? intval ( $ _POST [ ' item_id ' ]) : 0 ;
if ( $ id && current_user_can ( ' delete_post ' , $ id )) {
$ deleted = wp_delete_attachment ( $ id , true ) ;
if ( $ deleted ) {
wp_send_json_success () ;
}
wp_send_json_error ( ' Delete failed ' ) ;
}
wp_send_json_error ( ' Permission denied ' ) ;
}
add_filter ( ' attachment_fields_to_edit ' , function ( $ fields , $ post ) {
if ( ! current_user_can ( ' upload_files ' )) return $ fields ;
$ rating = get_post_meta ( $ post -> ID , ' _media_rating ' , true ) ?: 0 ;
$ label = get_post_meta ( $ post -> ID , ' _media_label ' , true ) ?: '' ;
$ fields [ ' media_rating ' ] = array (
' label ' => ' Rating ' ,
' input ' => ' html ' ,
' html ' => ' <select name="attachments[ ' . $ post -> ID . ' ][media_rating]">
<option value="0" ' . selected ( $ rating , 0 , false ) . ' >None</option>
<option value="1" ' . selected ( $ rating , 1 , false ) . ' >★ (1/5)</option>
<option value="2" ' . selected ( $ rating , 2 , false ) . ' >★★ (2/5)</option>
<option value="3" ' . selected ( $ rating , 3 , false ) . ' >★★★ (3/5)</option>
<option value="4" ' . selected ( $ rating , 4 , false ) . ' >★★★★ (4/5)</option>
<option value="5" ' . selected ( $ rating , 5 , false ) . ' >★★★★★ (5/5)</option>
</select> '
) ;
$ fields [ ' media_label ' ] = array (
' label ' => ' Label ' ,
' input ' => ' html ' ,
' html ' => ' <select name="attachments[ ' . $ post -> ID . ' ][media_label]">
<option value="" ' . selected ( $ label , '' , false ) . ' >None</option>
<option value="red" ' . selected ( $ label , ' red ' , false ) . ' >Red</option>
<option value="blue" ' . selected ( $ label , ' blue ' , false ) . ' >Blue</option>
<option value="green" ' . selected ( $ label , ' green ' , false ) . ' >Green</option>
<option value="yellow" ' . selected ( $ label , ' yellow ' , false ) . ' >Yellow</option>
<option value="purple" ' . selected ( $ label , ' purple ' , false ) . ' >Purple</option>
<option value="orange" ' . selected ( $ label , ' orange ' , false ) . ' >Orange</option>
<option value="pink" ' . selected ( $ label , ' pink ' , false ) . ' >Pink</option>
<option value="teal" ' . selected ( $ label , ' teal ' , false ) . ' >Teal</option>
</select> '
) ;
return $ fields ;
}, 10 , 2 ) ;
add_filter ( ' attachment_fields_to_save ' , function ( $ post , $ att ) {
if ( ! current_user_can ( ' upload_files ' )) return $ post ;
if ( isset ( $ att [ ' media_rating ' ])) update_post_meta ( $ post [ ' ID ' ], ' _media_rating ' , intval ( $ att [ ' media_rating ' ])) ;
if ( isset ( $ att [ ' media_label ' ])) update_post_meta ( $ post [ ' ID ' ], ' _media_label ' , sanitize_text_field ( $ att [ ' media_label ' ])) ;
return $ post ;
}, 10 , 2 ) ;
?>