true ), 'names' );
$out = array();
foreach ( $types as $type ) {
if ( 'attachment' === $type ) {
continue;
}
if ( get_option( 'wpatt_enable_metabox_' . $type, '1' ) !== '1' ) {
continue;
}
$out[] = $type;
}
return $out;
}
foreach ( wpatt_column_post_types() as $wpatt_type ) {
add_filter( "manage_{$wpatt_type}_posts_columns", 'wpatt_add_files_column' );
add_action( "manage_{$wpatt_type}_posts_custom_column", 'wpatt_render_files_column', 10, 2 );
}
unset( $wpatt_type );
/**
* Insert the column just before Date, the conventional slot for extra metadata.
*
* @param array $columns Existing columns.
* @return array
*/
function wpatt_add_files_column( $columns ) {
$label = esc_html_x( 'Files', 'list table column heading', 'wp-attachments' );
$screen = function_exists( 'get_current_screen' ) ? get_current_screen() : null;
$out = array();
// List switched off for the whole post type: say so once, here.
if ( $screen && $screen->post_type && ! wpatt_is_frontend_enabled( $screen->post_type ) ) {
$off = __( 'The list of files is not shown on the site for this content type.', 'wp-attachments' );
$label = '' . $label . wpatt_eye_off_svg()
. '' . esc_html( $off ) . '';
}
foreach ( $columns as $key => $value ) {
if ( 'date' === $key ) {
$out['wpa_files'] = $label;
}
$out[ $key ] = $value;
}
if ( ! isset( $out['wpa_files'] ) ) {
$out['wpa_files'] = $label;
}
return $out;
}
/**
* Attachment counts for every row currently on screen.
*
* Primed once from the main query and reused, so a 20 row screen costs one
* extra query rather than twenty.
*
* @return array Parent post ID => number of attachments.
*/
function wpatt_get_attachment_counts() {
static $counts = null;
if ( null !== $counts ) {
return $counts;
}
global $wp_query, $wpdb;
$counts = array();
$ids = array();
if ( isset( $wp_query->posts ) && is_array( $wp_query->posts ) ) {
foreach ( $wp_query->posts as $post ) {
$ids[] = is_object( $post ) ? (int) $post->ID : (int) $post;
}
}
$ids = array_filter( array_unique( $ids ) );
if ( empty( $ids ) ) {
return $counts;
}
$placeholders = implode( ',', array_fill( 0, count( $ids ), '%d' ) );
// Trashed attachments are excluded, to match what the metabox lists.
$rows = $wpdb->get_results(
$wpdb->prepare(
"SELECT post_parent, COUNT(*) AS total
FROM {$wpdb->posts}
WHERE post_type = 'attachment'
AND post_status != 'trash'
AND post_parent IN ($placeholders)
GROUP BY post_parent",
$ids
)
);
foreach ( $rows as $row ) {
$counts[ (int) $row->post_parent ] = (int) $row->total;
}
return $counts;
}
/**
* Render one cell.
*
* @param string $column Column key.
* @param int $post_id Row post ID.
*/
function wpatt_render_files_column( $column, $post_id ) {
if ( 'wpa_files' !== $column ) {
return;
}
$counts = wpatt_get_attachment_counts();
$count = isset( $counts[ $post_id ] ) ? $counts[ $post_id ] : 0;
if ( ! $count ) {
echo '—';
echo '' . esc_html__( 'No files attached', 'wp-attachments' ) . '';
return;
}
// Media Library, filtered to this parent. WordPress passes post_parent
// from the query string straight into the attachments query, so no extra
// handling is needed at the other end.
$url = add_query_arg(
array(
'mode' => 'list',
'post_parent' => (int) $post_id,
),
admin_url( 'upload.php' )
);
$label = sprintf(
/* translators: %s: number of attached files. */
_n( 'View %s attached file', 'View %s attached files', $count, 'wp-attachments' ),
number_format_i18n( $count )
);
printf(
'%3$s%4$s%2$s',
esc_url( $url ),
esc_attr( $label ),
wpatt_paperclip_svg(),
esc_html( number_format_i18n( $count ) )
);
// Whether this post shows its list on the site, as set in its metabox.
// When the whole post type is off, every row would carry the same mark,
// so the column heading explains it once instead.
if ( ! wpatt_is_frontend_enabled( get_post_type( $post_id ) ) ) {
return;
}
$shown = wpatt_is_display_enabled( $post_id );
$state = $shown ? __( 'Shown on the site', 'wp-attachments' ) : __( 'Not shown on the site', 'wp-attachments' );
printf(
'%3$s%2$s',
$shown ? 'is-shown' : 'is-hidden',
esc_attr( $state ),
$shown ? wpatt_eye_svg() : wpatt_eye_off_svg()
);
}
/**
* Eye: the list is shown on the frontend for this post.
*/
function wpatt_eye_svg() {
return '';
}
/**
* Crossed-out eye: the list is hidden on the frontend for this post.
*/
function wpatt_eye_off_svg() {
return '';
}
/**
* Paperclip mark. Without it the cell is a bare digit that could be a count of
* anything; with it the column reads at a glance.
*/
function wpatt_paperclip_svg() {
return '';
}
/**
* Tell people why the Media Library is showing a subset.
*
* Landing on a filtered list with no explanation is disorienting: WordPress
* gives no hint that post_parent is in play.
*/
add_action(
'admin_notices',
function () {
$screen = function_exists( 'get_current_screen' ) ? get_current_screen() : null;
if ( ! $screen || 'upload' !== $screen->base || empty( $_GET['post_parent'] ) ) {
return;
}
$parent = get_post( absint( wp_unslash( $_GET['post_parent'] ) ) );
if ( ! $parent ) {
return;
}
printf(
'