get_col( $wpdb->prepare( "SELECT DISTINCT meta_value FROM {$wpdb->postmeta} WHERE meta_key = %s AND meta_value <> '' ORDER BY meta_value", '_admirly_lang' ) ); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery wp_cache_set( 'admirly_review_langs', $langs, 'admirly', 5 * MINUTE_IN_SECONDS ); } if ( count( $langs ) < 2 ) { return; } $cur = sanitize_key( (string) ( $_GET['admirly_lang'] ?? '' ) ); // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- read-only list filter echo ''; } /** Applies the list filters (language; extensions add theirs through `admirly_admin_meta_query`). */ public function applyFilters( \WP_Query $q ): void { if ( ! is_admin() || ! $q->is_main_query() || $q->get( 'post_type' ) !== Plugin::CPT ) { return; } $meta = []; $lang = sanitize_key( (string) ( $_GET['admirly_lang'] ?? '' ) ); // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- read-only list filter if ( $lang !== '' ) { $meta[] = [ 'key' => '_admirly_lang', 'value' => $lang, ]; } /** @param array> $meta meta_query clauses for the reviews list. */ $meta = (array) apply_filters( 'admirly_admin_meta_query', $meta ); if ( $meta !== [] ) { $q->set( 'meta_query', $meta ); // phpcs:ignore WordPress.DB.SlowDBQuery } } public function assets( string $hook ): void { $screen = get_current_screen(); if ( $screen && ( $screen->post_type === Plugin::CPT || strpos( (string) $hook, 'admirly' ) !== false ) ) { wp_enqueue_style( 'admirly-admin', ADMIRLY_URL . 'assets/css/admin.css', [], ADMIRLY_VERSION ); } } /** @param array $cols @return array */ public function columns( array $cols ): array { $out = [ 'cb' => $cols['cb'] ?? '', 'title' => __( 'Name', 'admirly' ), 'admirly_role' => __( 'Who', 'admirly' ), 'admirly_rating' => __( 'Rating', 'admirly' ), 'admirly_text' => __( 'Review', 'admirly' ), 'admirly_lang' => __( 'Language', 'admirly' ), 'date' => __( 'Date', 'admirly' ), ]; /** @param array $out column id => heading */ return (array) apply_filters( 'admirly_admin_columns', $out ); } public function columnContent( string $col, int $postId ): void { $post = get_post( $postId ); if ( ! $post ) { return; } $item = Cpt::item( $post ); switch ( $col ) { case 'admirly_lang': echo esc_html( $item['lang'] !== '' ? strtoupper( $item['lang'] ) : '—' ); break; case 'admirly_role': echo esc_html( Cpt::cardLabel( $item ) ); break; case 'admirly_rating': echo '' . esc_html( str_repeat( '★', $item['rating'] ) . str_repeat( '☆', 5 - $item['rating'] ) ) . ''; break; case 'admirly_text': echo esc_html( wp_trim_words( $item['text'], 18 ) ); break; default: /** @param string $col Column id. @param array $item Review data. */ do_action( 'admirly_admin_column', $col, $item ); } } /** @param array $actions @return array */ public function rowActions( array $actions, \WP_Post $post ): array { if ( $post->post_type !== Plugin::CPT || ! current_user_can( 'edit_others_posts' ) ) { return $actions; } $new = []; if ( $post->post_status === 'pending' || $post->post_status === 'draft' ) { $new['admirly_approve'] = '' . esc_html__( 'Approve', 'admirly' ) . ''; } if ( $post->post_status !== 'trash' ) { $new['admirly_reject'] = '' . esc_html__( 'Reject', 'admirly' ) . ''; } unset( $actions['inline hide-if-no-js'], $actions['view'] ); return $new + $actions; } public static function actionUrl( string $action, int $postId ): string { return wp_nonce_url( admin_url( 'admin-post.php?action=' . $action . '&post=' . $postId ), $action . '_' . $postId ); } public function approve(): void { $this->changeStatus( 'admirly_approve', 'publish', 'approved' ); } public function reject(): void { $this->changeStatus( 'admirly_reject', 'trash', 'rejected' ); } private function changeStatus( string $action, string $status, string $msg ): void { $postId = absint( $_GET['post'] ?? 0 ); $nonce = sanitize_text_field( wp_unslash( (string) ( $_GET['_wpnonce'] ?? '' ) ) ); if ( ! $postId || ! current_user_can( 'edit_others_posts' ) || ! wp_verify_nonce( $nonce, $action . '_' . $postId ) ) { wp_die( esc_html__( 'Not allowed.', 'admirly' ), 403 ); } $post = get_post( $postId ); if ( ! $post || $post->post_type !== Plugin::CPT ) { wp_die( esc_html__( 'Review not found.', 'admirly' ), 404 ); } if ( $status === 'trash' ) { wp_trash_post( $postId ); } else { wp_update_post( [ 'ID' => $postId, 'post_status' => $status, ] ); } wp_safe_redirect( add_query_arg( [ 'post_type' => Plugin::CPT, 'admirly_msg' => $msg, ], admin_url( 'edit.php' ) ) ); exit; } /** @param array $a @return array */ public function bulkActions( array $a ): array { $a['admirly_bulk_approve'] = __( 'Approve', 'admirly' ); return $a; } /** @param int[] $ids */ public function handleBulk( string $redirect, string $action, array $ids ): string { if ( $action !== 'admirly_bulk_approve' || ! current_user_can( 'edit_others_posts' ) ) { return $redirect; } foreach ( $ids as $id ) { $p = get_post( (int) $id ); if ( $p && $p->post_type === Plugin::CPT ) { wp_update_post( [ 'ID' => $p->ID, 'post_status' => 'publish', ] ); } } return add_query_arg( 'admirly_msg', 'approved', $redirect ); } public function notices(): void { $msg = sanitize_key( (string) ( $_GET['admirly_msg'] ?? '' ) ); // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- display-only notice key if ( $msg === 'approved' ) { echo '

' . esc_html__( 'Review published.', 'admirly' ) . '

'; } elseif ( $msg === 'rejected' ) { echo '

' . esc_html__( 'Review rejected (moved to trash).', 'admirly' ) . '

'; } } public function pendingBadge(): void { global $menu; $count = (int) ( wp_count_posts( Plugin::CPT )->pending ?? 0 ); if ( $count < 1 || ! is_array( $menu ) ) { return; } foreach ( $menu as $k => $m ) { if ( isset( $m[2] ) && $m[2] === 'edit.php?post_type=' . Plugin::CPT ) { $menu[ $k ][0] .= ' ' . $count . ''; // phpcs:ignore WordPress.WP.GlobalVariablesOverride } } } public function metaBox(): void { add_meta_box( 'admirly_fields', __( 'Review details', 'admirly' ), [ $this, 'renderMetaBox' ], Plugin::CPT, 'side', 'high' ); } public function renderMetaBox( \WP_Post $post ): void { $item = Cpt::item( $post ); wp_nonce_field( 'admirly_meta_' . $post->ID, 'admirly_meta_nonce' ); $f = Plugin::fields(); $sel = static function ( array $items, string $current ): void { echo ''; foreach ( $items as $it ) { echo ''; } if ( $current !== '' && ! in_array( $current, FieldConfig::slugs( $items ), true ) ) { echo ''; } }; ?>



$item @param array $f Field definitions. @param callable $sel Prints