$value ) { if ( 'title' === $key ) { $new_columns['fedistream_photo'] = ''; } $new_columns[ $key ] = $value; if ( 'title' === $key ) { $new_columns['fedistream_type'] = __( 'Type', 'wp-fedistream' ); $new_columns['fedistream_albums'] = __( 'Albums', 'wp-fedistream' ); $new_columns['fedistream_tracks'] = __( 'Tracks', 'wp-fedistream' ); } } // Remove date, we'll add it back at the end. unset( $new_columns['date'] ); $new_columns['date'] = __( 'Date', 'wp-fedistream' ); return $new_columns; } /** * Render artist column content. * * @param string $column Column name. * @param int $post_id Post ID. * @return void */ public function artist_column_content( string $column, int $post_id ): void { switch ( $column ) { case 'fedistream_photo': $thumbnail = get_the_post_thumbnail( $post_id, array( 40, 40 ) ); if ( $thumbnail ) { echo wp_kses_post( $thumbnail ); } else { echo ''; } break; case 'fedistream_type': $type = get_post_meta( $post_id, '_fedistream_artist_type', true ); $types = array( 'solo' => __( 'Solo', 'wp-fedistream' ), 'band' => __( 'Band', 'wp-fedistream' ), 'duo' => __( 'Duo', 'wp-fedistream' ), 'collective' => __( 'Collective', 'wp-fedistream' ), ); echo esc_html( $types[ $type ] ?? __( 'Solo', 'wp-fedistream' ) ); break; case 'fedistream_albums': $count = $this->count_posts_by_meta( 'fedistream_album', '_fedistream_album_artist', $post_id ); echo '' . esc_html( $count ) . ''; break; case 'fedistream_tracks': $count = $this->count_tracks_by_artist( $post_id ); echo esc_html( $count ); break; } } /** * Define sortable artist columns. * * @param array $columns Sortable columns. * @return array Modified columns. */ public function artist_sortable_columns( array $columns ): array { $columns['fedistream_type'] = 'fedistream_type'; return $columns; } /** * Define album list columns. * * @param array $columns Default columns. * @return array Modified columns. */ public function album_columns( array $columns ): array { $new_columns = array(); foreach ( $columns as $key => $value ) { if ( 'title' === $key ) { $new_columns['fedistream_artwork'] = ''; } $new_columns[ $key ] = $value; if ( 'title' === $key ) { $new_columns['fedistream_artist'] = __( 'Artist', 'wp-fedistream' ); $new_columns['fedistream_type'] = __( 'Type', 'wp-fedistream' ); $new_columns['fedistream_tracks'] = __( 'Tracks', 'wp-fedistream' ); $new_columns['fedistream_release_date'] = __( 'Release Date', 'wp-fedistream' ); } } unset( $new_columns['date'] ); $new_columns['date'] = __( 'Date', 'wp-fedistream' ); return $new_columns; } /** * Render album column content. * * @param string $column Column name. * @param int $post_id Post ID. * @return void */ public function album_column_content( string $column, int $post_id ): void { switch ( $column ) { case 'fedistream_artwork': $thumbnail = get_the_post_thumbnail( $post_id, array( 40, 40 ) ); if ( $thumbnail ) { echo wp_kses_post( $thumbnail ); } else { echo ''; } break; case 'fedistream_artist': $artist_id = get_post_meta( $post_id, '_fedistream_album_artist', true ); if ( $artist_id ) { $artist = get_post( $artist_id ); if ( $artist ) { echo '' . esc_html( $artist->post_title ) . ''; } } else { echo '' . esc_html__( 'No artist', 'wp-fedistream' ) . ''; } break; case 'fedistream_type': $type = get_post_meta( $post_id, '_fedistream_album_type', true ); $types = array( 'album' => __( 'Album', 'wp-fedistream' ), 'ep' => __( 'EP', 'wp-fedistream' ), 'single' => __( 'Single', 'wp-fedistream' ), 'compilation' => __( 'Compilation', 'wp-fedistream' ), 'live' => __( 'Live', 'wp-fedistream' ), 'remix' => __( 'Remix', 'wp-fedistream' ), ); echo esc_html( $types[ $type ] ?? __( 'Album', 'wp-fedistream' ) ); break; case 'fedistream_tracks': $count = get_post_meta( $post_id, '_fedistream_album_total_tracks', true ); echo '' . esc_html( $count ?: 0 ) . ''; break; case 'fedistream_release_date': $date = get_post_meta( $post_id, '_fedistream_album_release_date', true ); if ( $date ) { echo esc_html( date_i18n( get_option( 'date_format' ), strtotime( $date ) ) ); } else { echo ''; } break; } } /** * Define sortable album columns. * * @param array $columns Sortable columns. * @return array Modified columns. */ public function album_sortable_columns( array $columns ): array { $columns['fedistream_type'] = 'fedistream_type'; $columns['fedistream_release_date'] = 'fedistream_release_date'; $columns['fedistream_artist'] = 'fedistream_artist'; return $columns; } /** * Define track list columns. * * @param array $columns Default columns. * @return array Modified columns. */ public function track_columns( array $columns ): array { $new_columns = array(); foreach ( $columns as $key => $value ) { if ( 'title' === $key ) { $new_columns['fedistream_artwork'] = ''; } $new_columns[ $key ] = $value; if ( 'title' === $key ) { $new_columns['fedistream_artists'] = __( 'Artists', 'wp-fedistream' ); $new_columns['fedistream_album'] = __( 'Album', 'wp-fedistream' ); $new_columns['fedistream_duration'] = __( 'Duration', 'wp-fedistream' ); $new_columns['fedistream_plays'] = __( 'Plays', 'wp-fedistream' ); } } unset( $new_columns['date'] ); $new_columns['date'] = __( 'Date', 'wp-fedistream' ); return $new_columns; } /** * Render track column content. * * @param string $column Column name. * @param int $post_id Post ID. * @return void */ public function track_column_content( string $column, int $post_id ): void { switch ( $column ) { case 'fedistream_artwork': $thumbnail = get_the_post_thumbnail( $post_id, array( 40, 40 ) ); if ( ! $thumbnail ) { // Try album artwork. $album_id = get_post_meta( $post_id, '_fedistream_track_album', true ); if ( $album_id ) { $thumbnail = get_the_post_thumbnail( $album_id, array( 40, 40 ) ); } } if ( $thumbnail ) { echo wp_kses_post( $thumbnail ); } else { echo ''; } break; case 'fedistream_artists': $artists = get_post_meta( $post_id, '_fedistream_track_artists', true ); if ( is_array( $artists ) && ! empty( $artists ) ) { $artist_links = array(); foreach ( $artists as $artist_id ) { $artist = get_post( $artist_id ); if ( $artist ) { $artist_links[] = '' . esc_html( $artist->post_title ) . ''; } } echo wp_kses_post( implode( ', ', $artist_links ) ); } else { echo '' . esc_html__( 'No artist', 'wp-fedistream' ) . ''; } break; case 'fedistream_album': $album_id = get_post_meta( $post_id, '_fedistream_track_album', true ); if ( $album_id ) { $album = get_post( $album_id ); if ( $album ) { echo '' . esc_html( $album->post_title ) . ''; } } else { echo '' . esc_html__( 'Single', 'wp-fedistream' ) . ''; } break; case 'fedistream_duration': $duration = get_post_meta( $post_id, '_fedistream_track_duration', true ); if ( $duration ) { $minutes = floor( $duration / 60 ); $seconds = $duration % 60; echo esc_html( sprintf( '%d:%02d', $minutes, $seconds ) ); } else { echo ''; } break; case 'fedistream_plays': $plays = $this->get_track_plays( $post_id ); echo esc_html( number_format_i18n( $plays ) ); break; } } /** * Define sortable track columns. * * @param array $columns Sortable columns. * @return array Modified columns. */ public function track_sortable_columns( array $columns ): array { $columns['fedistream_duration'] = 'fedistream_duration'; $columns['fedistream_plays'] = 'fedistream_plays'; $columns['fedistream_album'] = 'fedistream_album'; return $columns; } /** * Define playlist list columns. * * @param array $columns Default columns. * @return array Modified columns. */ public function playlist_columns( array $columns ): array { $new_columns = array(); foreach ( $columns as $key => $value ) { if ( 'title' === $key ) { $new_columns['fedistream_cover'] = ''; } $new_columns[ $key ] = $value; if ( 'title' === $key ) { $new_columns['fedistream_tracks'] = __( 'Tracks', 'wp-fedistream' ); $new_columns['fedistream_duration'] = __( 'Duration', 'wp-fedistream' ); $new_columns['fedistream_visibility'] = __( 'Visibility', 'wp-fedistream' ); } } unset( $new_columns['date'] ); $new_columns['date'] = __( 'Date', 'wp-fedistream' ); return $new_columns; } /** * Render playlist column content. * * @param string $column Column name. * @param int $post_id Post ID. * @return void */ public function playlist_column_content( string $column, int $post_id ): void { switch ( $column ) { case 'fedistream_cover': $thumbnail = get_the_post_thumbnail( $post_id, array( 40, 40 ) ); if ( $thumbnail ) { echo wp_kses_post( $thumbnail ); } else { echo ''; } break; case 'fedistream_tracks': $count = get_post_meta( $post_id, '_fedistream_playlist_track_count', true ); echo esc_html( $count ?: 0 ); break; case 'fedistream_duration': $duration = get_post_meta( $post_id, '_fedistream_playlist_total_duration', true ); if ( $duration ) { if ( $duration >= 3600 ) { $hours = floor( $duration / 3600 ); $minutes = floor( ( $duration % 3600 ) / 60 ); echo esc_html( sprintf( '%d:%02d:%02d', $hours, $minutes, $duration % 60 ) ); } else { $minutes = floor( $duration / 60 ); $seconds = $duration % 60; echo esc_html( sprintf( '%d:%02d', $minutes, $seconds ) ); } } else { echo ''; } break; case 'fedistream_visibility': $visibility = get_post_meta( $post_id, '_fedistream_playlist_visibility', true ) ?: 'public'; $labels = array( 'public' => __( 'Public', 'wp-fedistream' ), 'unlisted' => __( 'Unlisted', 'wp-fedistream' ), 'private' => __( 'Private', 'wp-fedistream' ), ); $icons = array( 'public' => 'dashicons-visibility', 'unlisted' => 'dashicons-hidden', 'private' => 'dashicons-lock', ); echo ' '; echo esc_html( $labels[ $visibility ] ?? __( 'Public', 'wp-fedistream' ) ); break; } } /** * Define sortable playlist columns. * * @param array $columns Sortable columns. * @return array Modified columns. */ public function playlist_sortable_columns( array $columns ): array { $columns['fedistream_tracks'] = 'fedistream_track_count'; $columns['fedistream_duration'] = 'fedistream_duration'; $columns['fedistream_visibility'] = 'fedistream_visibility'; return $columns; } /** * Handle custom column sorting. * * @param \WP_Query $query The query object. * @return void */ public function handle_sorting( \WP_Query $query ): void { if ( ! is_admin() || ! $query->is_main_query() ) { return; } $orderby = $query->get( 'orderby' ); switch ( $orderby ) { case 'fedistream_type': $post_type = $query->get( 'post_type' ); if ( 'fedistream_artist' === $post_type ) { $query->set( 'meta_key', '_fedistream_artist_type' ); } elseif ( 'fedistream_album' === $post_type ) { $query->set( 'meta_key', '_fedistream_album_type' ); } $query->set( 'orderby', 'meta_value' ); break; case 'fedistream_release_date': $query->set( 'meta_key', '_fedistream_album_release_date' ); $query->set( 'orderby', 'meta_value' ); break; case 'fedistream_artist': $query->set( 'meta_key', '_fedistream_album_artist' ); $query->set( 'orderby', 'meta_value_num' ); break; case 'fedistream_duration': $post_type = $query->get( 'post_type' ); if ( 'fedistream_track' === $post_type ) { $query->set( 'meta_key', '_fedistream_track_duration' ); } elseif ( 'fedistream_playlist' === $post_type ) { $query->set( 'meta_key', '_fedistream_playlist_total_duration' ); } $query->set( 'orderby', 'meta_value_num' ); break; case 'fedistream_track_count': $query->set( 'meta_key', '_fedistream_playlist_track_count' ); $query->set( 'orderby', 'meta_value_num' ); break; case 'fedistream_visibility': $query->set( 'meta_key', '_fedistream_playlist_visibility' ); $query->set( 'orderby', 'meta_value' ); break; case 'fedistream_album': $query->set( 'meta_key', '_fedistream_track_album' ); $query->set( 'orderby', 'meta_value_num' ); break; } } /** * Count posts by meta value. * * @param string $post_type Post type. * @param string $meta_key Meta key. * @param mixed $meta_value Meta value. * @return int Count. */ private function count_posts_by_meta( string $post_type, string $meta_key, $meta_value ): int { $query = new \WP_Query( array( 'post_type' => $post_type, 'posts_per_page' => -1, 'post_status' => 'publish', 'meta_key' => $meta_key, 'meta_value' => $meta_value, 'fields' => 'ids', ) ); return $query->found_posts; } /** * Count tracks by artist. * * @param int $artist_id Artist post ID. * @return int Track count. */ private function count_tracks_by_artist( int $artist_id ): int { global $wpdb; // Count tracks where artist is in the artists array. // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching $count = $wpdb->get_var( $wpdb->prepare( "SELECT COUNT(*) FROM $wpdb->posts p INNER JOIN $wpdb->postmeta pm ON p.ID = pm.post_id WHERE p.post_type = 'fedistream_track' AND p.post_status = 'publish' AND pm.meta_key = '_fedistream_track_artists' AND pm.meta_value LIKE %s", '%"' . $artist_id . '"%' ) ); // Also check serialized format. if ( ! $count ) { // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching $count = $wpdb->get_var( $wpdb->prepare( "SELECT COUNT(*) FROM $wpdb->posts p INNER JOIN $wpdb->postmeta pm ON p.ID = pm.post_id WHERE p.post_type = 'fedistream_track' AND p.post_status = 'publish' AND pm.meta_key = '_fedistream_track_artists' AND pm.meta_value LIKE %s", '%i:' . $artist_id . ';%' ) ); } return (int) $count; } /** * Get track play count. * * @param int $track_id Track post ID. * @return int Play count. */ private function get_track_plays( int $track_id ): int { global $wpdb; $table = $wpdb->prefix . 'fedistream_plays'; // Check if table exists. // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching $table_exists = $wpdb->get_var( $wpdb->prepare( 'SHOW TABLES LIKE %s', $table ) ); if ( ! $table_exists ) { return 0; } // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching $count = $wpdb->get_var( $wpdb->prepare( "SELECT COUNT(*) FROM $table WHERE track_id = %d", $track_id ) ); return (int) $count; } }