0 ) {
printf(
'
',
esc_html(
sprintf(
/* translators: %d: number of updated items */
_n( 'Priority updated for %d item.', 'Priority updated for %d items.', $count, '4wp-seo-helper' ),
$count
)
)
);
}
}
if ( isset( $_GET['forwp_priority_error'] ) ) {
echo '' . esc_html__( 'Could not update priority.', '4wp-seo-helper' ) . '
';
}
if ( isset( $_GET['forwp_type_excluded'] ) ) {
$slug = sanitize_key( wp_unslash( (string) $_GET['forwp_type_excluded'] ) );
$label = self::post_type_label( $slug );
$undo = self::include_type_url( $slug );
printf(
'',
esc_html(
sprintf(
/* translators: %s: post type label */
__( '“%s” is excluded from SEO Inventory. This is saved for next time.', '4wp-seo-helper' ),
$label
)
),
'' . esc_html__( 'Undo', '4wp-seo-helper' ) . ''
);
}
if ( isset( $_GET['forwp_type_included'] ) ) {
$slug = sanitize_key( wp_unslash( (string) $_GET['forwp_type_included'] ) );
printf(
'',
esc_html(
sprintf(
/* translators: %s: post type label */
__( '“%s” is shown in SEO Inventory again.', '4wp-seo-helper' ),
self::post_type_label( $slug )
)
)
);
}
// phpcs:enable WordPress.Security.NonceVerification.Recommended
}
private static function render_tabs( string $active, string $export_url = '' ): void {
$tabs = [
'inventory' => __( 'Inventory', '4wp-seo-helper' ),
'queue' => __( 'Priority queue', '4wp-seo-helper' ),
];
?>
get_languages();
$show_lang = count( $languages ) > 1;
// phpcs:disable WordPress.Security.NonceVerification.Recommended -- Read-only admin inventory filters.
$jump_post_id = isset( $_GET['forwp_post'] ) ? absint( wp_unslash( $_GET['forwp_post'] ) ) : 0;
$filters = [
'page' => isset( $_GET['paged'] ) ? max( 1, absint( wp_unslash( $_GET['paged'] ) ) ) : 1,
'post_type' => self::get_requested_post_type(),
'taxonomy' => self::get_requested_taxonomy(),
'term' => self::get_requested_term(),
'lang' => sanitize_key( wp_unslash( (string) ( $_GET['lang'] ?? '' ) ) ),
'status' => sanitize_key( wp_unslash( (string) ( $_GET['status'] ?? 'publish' ) ) ),
'missing' => sanitize_key( wp_unslash( (string) ( $_GET['missing'] ?? '' ) ) ),
'max_score' => isset( $_GET['max_score'] ) ? absint( wp_unslash( $_GET['max_score'] ) ) : 0,
'search' => sanitize_text_field( wp_unslash( (string) ( $_GET['s'] ?? '' ) ) ),
// Never isolate to a single row — toolbar jump highlights inside the full list.
'post_id' => 0,
];
// phpcs:enable WordPress.Security.NonceVerification.Recommended
$taxonomy_rows = TaxonomyDiscovery::get_labeled(
'' === $filters['post_type'] ? [] : [ $filters['post_type'] ]
);
$taxonomy_slugs = array_column( $taxonomy_rows, 'slug' );
if ( '' !== $filters['taxonomy'] && ! in_array( $filters['taxonomy'], $taxonomy_slugs, true ) ) {
$filters['taxonomy'] = '';
$filters['term'] = 0;
}
$term_rows = '' !== $filters['taxonomy'] ? TaxonomyDiscovery::get_terms( $filters['taxonomy'] ) : [];
if ( $filters['term'] > 0 ) {
$term_ids = array_column( $term_rows, 'id' );
if ( ! in_array( $filters['term'], $term_ids, true ) ) {
$filters['term'] = 0;
}
}
if ( $jump_post_id > 0 ) {
$per_page = (int) get_user_option( Menu::INVENTORY_PER_PAGE_OPTION );
if ( $per_page < 1 ) {
$per_page = Menu::INVENTORY_PER_PAGE_DEFAULT;
}
$filters['page'] = $repository->find_page_for_post(
$jump_post_id,
[
'post_type' => $filters['post_type'],
'taxonomy' => $filters['taxonomy'],
'term' => $filters['term'],
'lang' => $filters['lang'],
'status' => $filters['status'],
'missing' => $filters['missing'],
'max_score' => ! empty( $filters['max_score'] ) ? $filters['max_score'] : null,
'search' => $filters['search'],
],
$per_page
);
}
$table = new InventoryListTable( $repository );
$table->set_show_language( $show_lang );
$table->set_show_gsc_metrics( self::should_show_gsc_metrics() );
$table->set_show_gsc_indexing( self::should_show_gsc_indexing() );
$table->set_filters( $filters );
$table->set_redirect_base(
add_query_arg(
array_filter(
[
'page' => Menu::INVENTORY_PAGE_SLUG,
'view' => 'inventory',
Menu::INVENTORY_POST_TYPE_ARG => $filters['post_type'],
Menu::INVENTORY_TAXONOMY_ARG => $filters['taxonomy'],
Menu::INVENTORY_TERM_ARG => $filters['term'] > 0 ? (string) $filters['term'] : '',
'lang' => $filters['lang'],
'status' => $filters['status'],
'missing' => $filters['missing'],
'max_score' => ! empty( $filters['max_score'] ) ? (string) $filters['max_score'] : '',
's' => $filters['search'],
'paged' => $filters['page'] > 1 ? (string) $filters['page'] : '',
'orderby' => self::request_orderby(),
'order' => self::request_order(),
]
),
admin_url( 'admin.php' )
)
);
$table->prepare_items();
$export_url = wp_nonce_url(
add_query_arg(
array_filter(
[
'action' => 'forwp_seo_inventory_export',
Menu::INVENTORY_POST_TYPE_ARG => $filters['post_type'],
Menu::INVENTORY_TAXONOMY_ARG => $filters['taxonomy'],
Menu::INVENTORY_TERM_ARG => $filters['term'] > 0 ? (string) $filters['term'] : '',
'lang' => $filters['lang'],
'status' => $filters['status'],
'missing' => $filters['missing'],
'max_score' => ! empty( $filters['max_score'] ) ? (string) $filters['max_score'] : '',
's' => $filters['search'],
]
),
admin_url( 'admin-post.php' )
),
'forwp_seo_inventory_export'
);
?>
is_enabled() ) : ?>
is_connected() || '' === GscAdmin::get_site_property() ) ) : ?>
0 ) : ?>
get_lanes_with_items()['lanes'];
?>
Menu::INVENTORY_PAGE_SLUG,
'view' => 'queue',
'priority' => $lane_id,
],
admin_url( 'admin.php' )
);
?>
$item
*/
private static function render_compact_lane_item( array $item ): void {
$no_focus = ! empty( $item['seo_no_focus'] );
$seo = $item['seo_score'] ?? null;
if ( $no_focus && ( null === $seo || 0 === (int) $seo ) ) {
$score_label = (string) ( $item['seo_score_label'] ?? __( 'No focus', '4wp-seo-helper' ) );
$class = 'na';
} else {
$score_label = (string) ( null !== $seo ? (int) $seo : (int) ( $item['completeness'] ?? 0 ) );
$class = (int) $score_label >= 71 ? 'good' : ( (int) $score_label >= 41 ? 'medium' : 'low' );
}
$title = (string) ( $item['wp_title'] ?? '' );
?>
__( 'All queued', '4wp-seo-helper' ),
'1' => PriorityLabels::get_formatted( 1 ),
'2' => PriorityLabels::get_formatted( 2 ),
'3' => PriorityLabels::get_formatted( 3 ),
];
?>
$label ) {
$args = [
'page' => Menu::INVENTORY_PAGE_SLUG,
'view' => 'queue',
];
if ( 'all' !== $key ) {
$args['priority'] = $key;
}
$url = add_query_arg( $args, admin_url( 'admin.php' ) );
$is_active = ( null === $active_priority && 'all' === $key )
|| ( is_int( $active_priority ) && (string) $active_priority === $key );
$links[] = sprintf(
'- %s
',
esc_url( $url ),
$is_active ? ' class="current"' : '',
esc_html( $label )
);
}
echo wp_kses_post( implode( ' | ', $links ) );
?>
get_languages() ) > 1;
// phpcs:disable WordPress.Security.NonceVerification.Recommended -- Read-only admin queue page.
$paged = isset( $_GET['paged'] ) ? max( 1, absint( wp_unslash( $_GET['paged'] ) ) ) : 1;
// phpcs:enable WordPress.Security.NonceVerification.Recommended
$table = new InventoryListTable( $repository );
$table->set_show_language( $show_lang );
$table->set_show_gsc_metrics( self::should_show_gsc_metrics() );
$table->set_show_gsc_indexing( self::should_show_gsc_indexing() );
$table->set_filters( [] );
$table->set_redirect_base(
add_query_arg(
array_filter(
[
'page' => Menu::INVENTORY_PAGE_SLUG,
'view' => 'queue',
'priority' => $active_priority ? (string) $active_priority : '',
'paged' => $paged > 1 ? (string) $paged : '',
'orderby' => self::request_orderby(),
'order' => self::request_order(),
]
),
admin_url( 'admin.php' )
)
);
$table->set_priority_filter( $active_priority ?? 'queued' );
$table->prepare_items();
?>
Menu::INVENTORY_PAGE_SLUG,
'view' => 'inventory',
'forwp_type_excluded' => $slug,
]
),
admin_url( 'admin.php' )
)
);
exit;
}
public static function handle_include_type(): void {
if ( ! current_user_can( 'manage_options' ) ) {
wp_die( esc_html__( 'Insufficient permissions.', '4wp-seo-helper' ) );
}
check_admin_referer( 'forwp_seo_inventory_include_type' );
$slug = self::get_requested_post_type();
if ( '' !== $slug ) {
ExcludedPostTypes::remove( $slug );
}
wp_safe_redirect(
add_query_arg(
array_filter(
[
'page' => Menu::INVENTORY_PAGE_SLUG,
'view' => 'inventory',
'forwp_type_included' => $slug,
Menu::INVENTORY_POST_TYPE_ARG => $slug,
]
),
admin_url( 'admin.php' )
)
);
exit;
}
private static function exclude_type_url( string $slug ): string {
return wp_nonce_url(
add_query_arg(
[
'action' => 'forwp_seo_inventory_exclude_type',
Menu::INVENTORY_POST_TYPE_ARG => $slug,
],
admin_url( 'admin-post.php' )
),
'forwp_seo_inventory_exclude_type'
);
}
private static function include_type_url( string $slug ): string {
return wp_nonce_url(
add_query_arg(
[
'action' => 'forwp_seo_inventory_include_type',
Menu::INVENTORY_POST_TYPE_ARG => $slug,
],
admin_url( 'admin-post.php' )
),
'forwp_seo_inventory_include_type'
);
}
private static function post_type_label( string $slug ): string {
$object = get_post_type_object( $slug );
if ( $object instanceof \WP_Post_Type ) {
return (string) $object->labels->singular_name;
}
return $slug;
}
private static function render_excluded_types_note(): void {
$excluded = ExcludedPostTypes::get();
if ( [] === $excluded ) {
return;
}
$parts = [];
foreach ( $excluded as $slug ) {
$parts[] = sprintf(
'%2$s',
esc_url( self::include_type_url( $slug ) ),
esc_html( self::post_type_label( $slug ) )
);
}
echo '';
echo esc_html__( 'Excluded post types:', '4wp-seo-helper' );
echo ' ';
echo wp_kses_post( implode( ', ', $parts ) );
echo ' — ';
echo '';
echo esc_html__( 'manage in Settings', '4wp-seo-helper' );
echo '';
echo '
';
}
public static function url_for_post( int $post_id ): string {
$post_id = absint( $post_id );
$url = add_query_arg(
[
'page' => Menu::INVENTORY_PAGE_SLUG,
'view' => 'inventory',
'forwp_post' => $post_id,
],
admin_url( 'admin.php' )
);
return $url . '#forwp-seo-row-' . $post_id;
}
public static function get_requested_post_type(): string {
// phpcs:disable WordPress.Security.NonceVerification.Recommended -- Read-only inventory type filter.
if ( isset( $_GET[ Menu::INVENTORY_POST_TYPE_ARG ] ) ) {
$raw = (string) wp_unslash( $_GET[ Menu::INVENTORY_POST_TYPE_ARG ] );
} elseif ( isset( $_GET['post_type'] ) ) {
$raw = (string) wp_unslash( $_GET['post_type'] );
} else {
$raw = '';
}
// phpcs:enable WordPress.Security.NonceVerification.Recommended
return sanitize_key( $raw );
}
public static function get_requested_taxonomy(): string {
// phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Read-only inventory taxonomy filter.
$raw = isset( $_GET[ Menu::INVENTORY_TAXONOMY_ARG ] )
? (string) wp_unslash( $_GET[ Menu::INVENTORY_TAXONOMY_ARG ] )
: '';
return sanitize_key( $raw );
}
public static function get_requested_term(): int {
// phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Read-only inventory term filter.
if ( ! isset( $_GET[ Menu::INVENTORY_TERM_ARG ] ) ) {
return 0;
}
return absint( wp_unslash( $_GET[ Menu::INVENTORY_TERM_ARG ] ) );
}
private static function request_orderby(): string {
// phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Read-only list table sort.
$orderby = isset( $_GET['orderby'] ) ? sanitize_key( wp_unslash( (string) $_GET['orderby'] ) ) : '';
return 'gsc_index_status' === $orderby ? 'gsc_index_status' : '';
}
private static function request_order(): string {
if ( '' === self::request_orderby() ) {
return '';
}
// phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Read-only list table sort.
$order = isset( $_GET['order'] ) ? strtolower( sanitize_key( wp_unslash( (string) $_GET['order'] ) ) ) : 'asc';
return 'desc' === $order ? 'desc' : 'asc';
}
public static function should_show_gsc_metrics(): bool {
if ( ! Release::is_module_public( Release::MODULE_GSC ) || ! GscModule::get_instance()->is_enabled() ) {
return false;
}
$admin = GscAdmin::get_instance();
if ( ! $admin->is_menu_visible() ) {
return false;
}
$property = GscAdmin::get_site_property();
if ( '' === $property ) {
return false;
}
return ( new GscRepository() )->has_fact_period(
$property,
'web',
ReportPeriod::period_key_current()
);
}
public static function should_show_gsc_indexing(): bool {
return Release::is_module_public( Release::MODULE_GSC )
&& GscModule::get_instance()->is_enabled();
}
}