= $dismissed_until );
}
/**
* Render the FSE feature-limitation warning notice.
*/
public static function render_feature_limitation_notice() {
if ( ! self::should_show_feature_limitation_notice() ) {
return;
}
?>
__( 'Unauthorized action.', 'advance-menu-manager' ) ), 403 );
}
$user_id = get_current_user_id();
if ( $user_id <= 0 ) {
wp_send_json_error( array( 'message' => __( 'Invalid user.', 'advance-menu-manager' ) ), 400 );
}
update_user_meta(
$user_id,
self::FEATURE_NOTICE_META,
time() + self::FEATURE_NOTICE_DISMISS_SECONDS
);
wp_send_json_success();
}
/**
* Whether Core classic→block converter is available (WP 6.3+).
*
* @return bool
*/
public static function is_supported() {
return class_exists( 'WP_Classic_To_Block_Menu_Converter' );
}
/**
* Pause sync while AMM is still updating menu item meta.
*/
public static function defer() {
self::$deferred = true;
}
/**
* Resume sync and flush any pending menu IDs.
*
* @param array $extra_menu_ids Extra classic menu IDs to sync now.
* @param array $aliases Optional name/slug aliases keyed by menu ID.
*/
public static function resume( $extra_menu_ids = array(), $aliases = array() ) {
self::$deferred = false;
$menu_ids = array_unique(
array_filter(
array_map( 'absint', array_merge( self::$pending_menu_ids, (array) $extra_menu_ids ) )
)
);
self::$pending_menu_ids = array();
foreach ( $menu_ids as $menu_id ) {
$menu_aliases = isset( $aliases[ $menu_id ] ) ? (array) $aliases[ $menu_id ] : array();
self::sync_classic_menu( $menu_id, $menu_aliases );
}
}
/**
* Handle core menu updates.
*
* @param int $menu_id Classic menu term ID.
*/
public static function on_wp_update_nav_menu( $menu_id ) {
$menu_id = absint( $menu_id );
if ( $menu_id <= 0 ) {
return;
}
if ( self::$deferred ) {
self::$pending_menu_ids[] = $menu_id;
return;
}
self::sync_classic_menu( $menu_id );
}
/**
* Convert a classic menu and overwrite matching wp_navigation posts.
*
* @param int $menu_id Classic menu term ID.
* @param array $aliases Extra titles/slugs to match (e.g. previous menu name).
* @return int Number of wp_navigation posts updated.
*/
public static function sync_classic_menu( $menu_id, $aliases = array() ) {
if ( ! self::is_supported() ) {
return 0;
}
$menu_id = absint( $menu_id );
if ( $menu_id <= 0 ) {
return 0;
}
$menu = wp_get_nav_menu_object( $menu_id );
if ( ! $menu || is_wp_error( $menu ) ) {
return 0;
}
$block_markup = WP_Classic_To_Block_Menu_Converter::convert( $menu );
if ( is_wp_error( $block_markup ) ) {
return 0;
}
$match_tokens = self::build_match_tokens( $menu, $aliases );
$navigations = self::get_navigation_posts();
$updated = 0;
foreach ( $navigations as $navigation ) {
if ( ! self::navigation_matches( $navigation, $match_tokens ) ) {
continue;
}
$result = wp_update_post(
array(
'ID' => (int) $navigation->ID,
'post_content' => $block_markup,
),
true
);
if ( ! is_wp_error( $result ) ) {
$updated++;
}
}
/**
* Fires after a classic menu has been synced to matching Navigation posts.
*
* @param int $menu_id Classic menu term ID.
* @param WP_Term $menu Classic menu object.
* @param int $updated Number of Navigation posts updated.
* @param string $block_markup Serialized Navigation inner blocks.
*/
do_action( 'dsamm_classic_menu_synced_to_navigation', $menu_id, $menu, $updated, $block_markup );
return $updated;
}
/**
* Build match tokens from classic menu + aliases.
*
* @param WP_Term $menu Classic menu.
* @param array $aliases Extra names/slugs.
* @return array
*/
private static function build_match_tokens( $menu, $aliases = array() ) {
$tokens = array(
self::normalize_token( $menu->name ),
self::normalize_token( $menu->slug ),
self::normalize_token( sanitize_title( $menu->name ) ),
);
foreach ( (array) $aliases as $alias ) {
if ( ! is_string( $alias ) || '' === $alias ) {
continue;
}
$tokens[] = self::normalize_token( $alias );
$tokens[] = self::normalize_token( sanitize_title( $alias ) );
}
return array_values( array_unique( array_filter( $tokens ) ) );
}
/**
* Whether a Navigation post matches classic menu tokens.
*
* @param WP_Post $navigation Navigation post.
* @param array $tokens Normalized match tokens.
* @return bool
*/
private static function navigation_matches( $navigation, $tokens ) {
$candidates = array(
self::normalize_token( $navigation->post_title ),
self::normalize_token( $navigation->post_name ),
self::normalize_token( sanitize_title( $navigation->post_title ) ),
);
foreach ( $candidates as $candidate ) {
if ( '' !== $candidate && in_array( $candidate, $tokens, true ) ) {
return true;
}
}
return false;
}
/**
* Normalize a title/slug for comparison.
*
* @param string $value Raw value.
* @return string
*/
private static function normalize_token( $value ) {
$value = is_string( $value ) ? trim( $value ) : '';
if ( '' === $value ) {
return '';
}
return strtolower( $value );
}
/**
* Fetch all Navigation menu posts.
*
* @return WP_Post[]
*/
private static function get_navigation_posts() {
$posts = get_posts(
array(
'post_type' => 'wp_navigation',
'post_status' => array( 'publish', 'draft', 'private' ),
'numberposts' => -1,
'orderby' => 'ID',
'order' => 'ASC',
'update_post_meta_cache' => false,
'update_post_term_cache' => false,
)
);
return is_array( $posts ) ? $posts : array();
}
}