is_main_query() ) { return $clauses; } if ( 'mlsimport_property' !== $query->get( 'post_type' ) || 'mlsimport_featured' !== $query->get( 'orderby' ) ) { return $clauses; } global $wpdb; $clauses['join'] .= $wpdb->prepare( " LEFT JOIN {$wpdb->postmeta} mlsimport_featured_pm ON ( {$wpdb->posts}.ID = mlsimport_featured_pm.post_id AND mlsimport_featured_pm.meta_key = %s )", self::META ); $order = ( 'ASC' === strtoupper( (string) $query->get( 'order' ) ) ) ? 'ASC' : 'DESC'; $clauses['orderby'] = "( mlsimport_featured_pm.meta_value = '1' ) $order, {$wpdb->posts}.post_date DESC"; return $clauses; } /** * Offer the two bulk actions in the Properties list dropdown. * * @param array $actions Bulk actions (key => label). * @return array */ public static function bulk_actions( $actions ): array { $actions = (array) $actions; $actions['mlsimport_mark_featured'] = __( 'Mark featured', 'mlsimport' ); $actions['mlsimport_remove_featured'] = __( 'Remove featured', 'mlsimport' ); return $actions; } /** * Apply "Mark featured" / "Remove featured" to the ticked listings. * * WordPress has already verified the bulk nonce and that the user may edit * this post type before calling the filter; each post is still checked * individually so a listing the user cannot edit is skipped. * * @param string $redirect Where WordPress sends the browser afterwards. * @param string $action The chosen bulk action key. * @param array $post_ids The ticked post IDs. * @return string The redirect URL, carrying the changed count for notice(). */ public static function handle_bulk( $redirect, $action, $post_ids ) { // Step 1: leave every bulk action that is not ours untouched. if ( 'mlsimport_mark_featured' !== $action && 'mlsimport_remove_featured' !== $action ) { return $redirect; } $value = 'mlsimport_mark_featured' === $action ? '1' : ''; $changed = 0; foreach ( (array) $post_ids as $post_id ) { $post_id = (int) $post_id; // Step 2: only our post type, and only listings this user may edit. The // capability check is skipped under WP-CLI, which has no current user. if ( 'mlsimport_property' !== get_post_type( $post_id ) ) { continue; } if ( ! ( defined( 'WP_CLI' ) && WP_CLI ) && ! current_user_can( 'edit_post', $post_id ) ) { continue; } // Step 3: write the same meta the edit-screen checkbox writes... update_post_meta( $post_id, self::META, $value ); // Step 4: ...then re-index, which is what moves the flag into the fast // listings table the front end sorts and badges from. if ( class_exists( 'Mlsimport_Standalone_Reindex' ) ) { Mlsimport_Standalone_Reindex::rebuild_post( $post_id ); } ++$changed; /** Fires after a listing's featured flag changed from the list screen. @since 7.2.2 */ do_action( 'mlsimport_property_featured_changed', $post_id, '1' === $value ); } // Step 5: hand the count to notice() through the redirect URL. return add_query_arg( 'mlsimport_featured_changed', $changed, (string) $redirect ); } /** * Confirm a finished bulk action on the Properties list. * * @return void */ public static function notice(): void { // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- read-only count for a notice. if ( ! isset( $_GET['mlsimport_featured_changed'] ) ) { return; } // phpcs:ignore WordPress.Security.NonceVerification.Recommended $count = absint( wp_unslash( $_GET['mlsimport_featured_changed'] ) ); /* translators: %s: number of listings. */ $text = sprintf( _n( '%s listing updated.', '%s listings updated.', $count, 'mlsimport' ), number_format_i18n( $count ) ); echo '
' . esc_html( $text ) . '