admin_url( 'admin-ajax.php' ),
'nonce' => wp_create_nonce( 'metaforge_post_order_nonce' ),
)
);
}
public function admin_menu() {
// 1. Main Settings Page (hidden from sidebar, accessible via header nav)
add_submenu_page(
null,
'Post Order',
'Post Order',
'manage_metaforge',
'metaforge_post_order',
array( $this, 'settings_page' )
);
// 2. Dynamic Re-order Submenus
$settings = get_option( $this->settings_key, array() );
$enabled_types = $settings['enabled_post_types'] ?? array();
if ( ! empty( $enabled_types ) ) {
foreach ( $enabled_types as $post_type ) {
$pt_obj = get_post_type_object( $post_type );
if ( ! $pt_obj ) {
continue;
}
add_submenu_page(
'edit.php?post_type=' . $post_type,
'Re-order ' . $pt_obj->labels->name,
'Re-order',
'edit_posts', // Standard WP capability for editing posts
'metaforge_reorder_' . $post_type,
function () use ( $post_type, $pt_obj ) {
$this->reorder_page( $post_type, $pt_obj );
}
);
}
}
}
public function register_settings() {
register_setting( 'metaforge_post_order_group', $this->settings_key, array( $this, 'sanitize_settings' ) );
}
public function sanitize_settings( $input ) {
$sanitized = array();
$sanitized['enabled_post_types'] = is_array( $input['enabled_post_types'] ?? null ) ? array_map( 'sanitize_key', $input['enabled_post_types'] ) : array();
$sanitized['auto_apply'] = isset( $input['auto_apply'] ) ? '1' : '0';
$sanitized['admin_auto_apply'] = isset( $input['admin_auto_apply'] ) ? '1' : '0';
return $sanitized;
}
/**
* Settings Page UI
*/
public function settings_page() {
if ( ! current_user_can( 'manage_metaforge' ) ) {
return;
}
$settings = get_option(
$this->settings_key,
array(
'enabled_post_types' => array(),
'auto_apply' => '0',
'admin_auto_apply' => '0',
)
);
$post_types = get_post_types( array( 'public' => true ), 'objects' );
unset( $post_types['attachment'] );
// phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Read-only notice query flag.
$saved = ( isset( $_GET['settings-updated'] ) && sanitize_text_field( wp_unslash( $_GET['settings-updated'] ) ) === 'true' );
?>
$post_type,
'posts_per_page' => -1,
'orderby' => 'menu_order',
'order' => 'ASC',
'post_status' => 'any',
)
);
?>
Re-order labels->name ); ?>
'Permission denied.' ) );
}
$order = isset( $_POST['order'] ) && is_array( $_POST['order'] ) ? array_map( 'intval', wp_unslash( $_POST['order'] ) ) : array();
if ( empty( $order ) ) {
wp_send_json_error( array( 'message' => 'No order data received.' ) );
}
global $wpdb;
// Build single batched CASE WHEN query for high performance with 100+ items
$case_sql = array();
$ids = array();
foreach ( $order as $index => $post_id ) {
$post_id = absint( $post_id );
if ( $post_id <= 0 ) {
continue;
}
$case_sql[] = $wpdb->prepare( 'WHEN ID = %d THEN %d', $post_id, (int) $index );
$ids[] = $post_id;
}
if ( ! empty( $case_sql ) && ! empty( $ids ) ) {
$ids_placeholder = implode( ',', $ids );
$cases_string = implode( ' ', $case_sql );
// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- Batched case update with prepared clauses and sanitized integer IDs.
$wpdb->query( "UPDATE {$wpdb->posts} SET menu_order = CASE {$cases_string} ELSE menu_order END WHERE ID IN ({$ids_placeholder})" );
foreach ( $ids as $pid ) {
clean_post_cache( $pid );
}
}
wp_send_json_success( array( 'message' => 'Order saved successfully!' ) );
}
/**
* Apply custom order to queries
*/
public function apply_custom_order( $query ) {
// Don't modify if suppressed or if orderby is already set manually in a specific way
if ( $query->get( 'metaforge_suppress_sort' ) ) {
return;
}
$settings = get_option( $this->settings_key, array() );
if ( empty( $settings['enabled_post_types'] ) ) {
return;
}
$post_type = $query->get( 'post_type' );
// Handle array of post types
if ( is_array( $post_type ) ) {
$is_enabled = false;
foreach ( $post_type as $pt ) {
if ( in_array( $pt, $settings['enabled_post_types'] ) ) {
$is_enabled = true;
break;
}
}
} else {
$is_enabled = in_array( $post_type, $settings['enabled_post_types'] );
}
if ( ! $is_enabled ) {
return;
}
// Check if we should apply in admin
if ( is_admin() ) {
if ( empty( $settings['admin_auto_apply'] ) || $settings['admin_auto_apply'] !== '1' ) {
return;
}
// Only apply to the main list table query in admin
if ( ! $query->is_main_query() ) {
return;
}
} else {
// Frontend
if ( empty( $settings['auto_apply'] ) || $settings['auto_apply'] !== '1' ) {
return;
}
}
// Apply sorting
$query->set( 'orderby', 'menu_order' );
$query->set( 'order', 'ASC' );
}
}