id ) { return; } wp_enqueue_style( 'deadbolt-fields', Bootstrap::url() . 'inc/css/fields.css', [], Bootstrap::script_version() ); wp_enqueue_style( 'deadbolt-pages', Bootstrap::url() . 'inc/css/pages.css', [ 'deadbolt-theme' ], Bootstrap::script_version() ); wp_enqueue_script( 'deadbolt-pages', Bootstrap::url() . 'inc/js/pages.js', [ 'jquery' ], Bootstrap::script_version(), true ); wp_localize_script( 'deadbolt-pages', 'deadboltPages', [ 'ajaxUrl' => admin_url( 'admin-ajax.php' ), 'nonce' => wp_create_nonce( self::NONCE ), 'loading' => __( 'Loading…', 'deadbolt' ), 'loadMore' => __( 'Load More', 'deadbolt' ), 'error' => __( 'The list could not be loaded. Please try again.', 'deadbolt' ), ] ); } // End enqueue() /** * Count the posts of each enabled post type by the access they end up with, in one grouped query. * * @return array post_type => [ logged-in, logged-out, url-param, everyone ] */ public static function counts() : array { global $wpdb; $post_types = array_keys( (array) Settings::get( 'post_types' ) ); $counts = array_fill_keys( $post_types, array_fill_keys( self::COUNTED, 0 ) ); if ( empty( $post_types ) ) { return $counts; } $type_list = implode( ',', array_fill( 0, count( $post_types ), '%s' ) ); $status_list = implode( ',', array_fill( 0, count( self::STATUSES ), '%s' ) ); $rows = $wpdb->get_results( $wpdb->prepare( "SELECT p.post_type, pm.meta_value, COUNT(*) AS total FROM {$wpdb->posts} p LEFT JOIN {$wpdb->postmeta} pm ON pm.post_id = p.ID AND pm.meta_key = %s WHERE p.post_type IN ( {$type_list} ) AND p.post_status IN ( {$status_list} ) GROUP BY p.post_type, pm.meta_value", array_merge( [ Access::META_KEY ], $post_types, self::STATUSES ) ) ); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.InterpolatedNotPrepared, WordPress.DB.PreparedSQLPlaceholders.UnfinishedPrepare, WordPress.DB.PreparedSQLPlaceholders.ReplacementsWrongNumber -- admin overview counts in one grouped query; the interpolated lists only contain placeholders, filled from the array of values. foreach ( $rows as $row ) { $access = self::effective_access( (string) $row->post_type, (string) $row->meta_value ); $counts[ $row->post_type ][ $access ] += (int) $row->total; } $page_id = (int) Settings::get( 'no_access_page' ); $page_type = $page_id ? (string) get_post_type( $page_id ) : ''; if ( isset( $counts[ $page_type ] ) && in_array( get_post_status( $page_id ), self::STATUSES, true ) ) { $access = self::effective_access( $page_type, (string) get_post_meta( $page_id, Access::META_KEY, true ) ); $counts[ $page_type ][ $access ]--; $counts[ $page_type ][ 'everyone' ]++; } return $counts; } // End counts() /** * Get the access a stored rule ends up with, after the post type default. * * @param string $post_type * @param string $raw * @return string One of self::COUNTED. */ private static function effective_access( string $post_type, string $raw ) : string { $access = Access::parse( $raw )[ 'access' ]; $access = 'default' === $access ? Access::post_type_default( $post_type ) : $access; return in_array( $access, self::COUNTED, true ) ? $access : 'everyone'; } // End effective_access() /** * Render the Pages tab. * * @return void */ public static function render_page() : void { if ( ! current_user_can( 'manage_options' ) ) { wp_die( esc_html__( 'You do not have permission to view this page.', 'deadbolt' ) ); } $counts = self::counts(); $options = Access::options(); ?>

$count ) : ?>
labels->name ); ?>
$entry ) { if ( $post_type !== $entry[ 't' ] ) { continue; } $access = self::effective_access( $post_type, (string) $entry[ 'r' ] ); if ( 'everyone' === $access ) { $everyone[] = (int) $post_id; } else { $restricted[] = (int) $post_id; } } $args = [ 'post_type' => $post_type, 'post_status' => self::STATUSES, 'posts_per_page' => self::PER_PAGE, 'paged' => max( 1, $paged ), 'orderby' => 'title', 'order' => 'ASC', 'update_post_term_cache' => false, ]; if ( 'everyone' === Access::post_type_default( $post_type ) ) { $args[ 'post__in' ] = array_values( array_diff( $restricted, [ $page_id ] ) ) ?: [ 0 ]; } else { $args[ 'post__not_in' ] = array_merge( $everyone, [ $page_id ] ); // phpcs:ignore WordPressVIPMinimum.Performance.WPQueryParams.PostNotIn_post__not_in -- admin overview; excludes the few posts opened to everyone. } return new \WP_Query( $args ); } // End query() /** * AJAX: return one page of restricted posts for a post type as table rows. * * @return void */ public function ajax_list() : void { Helpers::verify_ajax( self::NONCE ); $post_type = isset( $_POST[ 'post_type' ] ) ? sanitize_key( wp_unslash( $_POST[ 'post_type' ] ) ) : ''; // phpcs:ignore WordPress.Security.NonceVerification.Missing -- the nonce is verified by Helpers::verify_ajax() above. $paged = isset( $_POST[ 'paged' ] ) ? absint( $_POST[ 'paged' ] ) : 1; // phpcs:ignore WordPress.Security.NonceVerification.Missing -- the nonce is verified by Helpers::verify_ajax() above. if ( ! Access::is_enabled( $post_type ) ) { wp_send_json_error( __( 'Access controls are not enabled for this post type.', 'deadbolt' ) ); } $query = self::query( $post_type, $paged ); $statuses = get_post_stati( [], 'objects' ); $span = count( self::COUNTED ) + 1; $rows = ''; foreach ( $query->posts as $post ) { $title = get_the_title( $post ); $title = '' !== $title ? $title : __( '(no title)', 'deadbolt' ); $status = 'publish' !== $post->post_status && isset( $statuses[ $post->post_status ] ) ? ' — ' . esc_html( $statuses[ $post->post_status ]->label ) . '' : ''; $edit = current_user_can( 'edit_post', $post->ID ) ? (string) get_edit_post_link( $post->ID ) : ''; $name = '' !== $edit ? '' . esc_html( $title ) . '' : esc_html( $title ); $view = 'publish' === $post->post_status ? ' ' . esc_html__( 'View', 'deadbolt' ) . '' : ''; $rows .= '' . $name . $status . $view . '' . esc_html( Access::describe( $post->ID ) ) . '' . Templates::in_use_html( $post->ID ) . ''; } wp_send_json_success( [ 'rows' => $rows, 'more' => $paged < (int) $query->max_num_pages, 'paged' => $paged, 'head' => '' . esc_html__( 'Title', 'deadbolt' ) . '' . esc_html__( 'Access', 'deadbolt' ) . '' . esc_html__( 'Templates', 'deadbolt' ) . '', ] ); } // End ajax_list() } // End class Pages Pages::instance();