<?php
/*
Plugin Name: Reorder Multisite Sites Dropdown
Plugin URI: https://www.kodeala.com
Description: Adds advanced ordering controls to the WordPress Multisite My Sites admin bar dropdown, allowing administrators to organize sites with drag-and-drop, manual ordering, groups, and dividers.
Author: Kodeala
Version: 2.0
Network: true
Tags: reorder, multisite, my sites, dropdown, menu
Requires at least: 4.5
Tested up to: 6.9
Requires PHP: 7.0
Stable tag: 2.0
License: GPLv2 or later
License URI: http://www.gnu.org/licenses/gpl-2.0.html
*/

if ( ! defined( 'ABSPATH' ) ) {
    exit;
}

if ( ! is_multisite() ) {
    return;
}

/**
 * Generate a unique group ID for saved structure items.
 *
 * @return string
 */
function kodeala_msdd_new_gid() {
    return 'g_' . wp_generate_uuid4();
}

/**
 * Return the SVG markup for the drag/move icon.
 *
 * @return string
 */
function kodeala_msdd_move_svg() {
    return '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 640" class="msdd-icon msdd-icon-move" aria-hidden="true"><path d="M288 104C288 81.9 270.1 64 248 64L200 64C177.9 64 160 81.9 160 104L160 152C160 174.1 177.9 192 200 192L248 192C270.1 192 288 174.1 288 152L288 104zM288 296C288 273.9 270.1 256 248 256L200 256C177.9 256 160 273.9 160 296L160 344C160 366.1 177.9 384 200 384L248 384C270.1 384 288 366.1 288 344L288 296zM160 488L160 536C160 558.1 177.9 576 200 576L248 576C270.1 576 288 558.1 288 536L288 488C288 465.9 270.1 448 248 448L200 448C177.9 448 160 465.9 160 488zM480 104C480 81.9 462.1 64 440 64L392 64C369.9 64 352 81.9 352 104L352 152C352 174.1 369.9 192 392 192L440 192C462.1 192 480 174.1 480 152L480 104zM352 296L352 344C352 366.1 369.9 384 392 384L440 384C462.1 384 480 366.1 480 344L480 296C480 273.9 462.1 256 440 256L392 256C369.9 256 352 273.9 352 296zM480 488C480 465.9 462.1 448 440 448L392 448C369.9 448 352 465.9 352 488L352 536C352 558.1 369.9 576 392 576L440 576C462.1 576 480 558.1 480 536L480 488z"/></svg>';
}

/**
 * Return the SVG markup for the unwrap/remove icon.
 *
 * @return string
 */
function kodeala_msdd_unwrap_svg() {
    return '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 640" class="msdd-icon msdd-icon-unwrap" aria-hidden="true"><path d="M183.1 137.4C170.6 124.9 150.3 124.9 137.8 137.4C125.3 149.9 125.3 170.2 137.8 182.7L275.2 320L137.9 457.4C125.4 469.9 125.4 490.2 137.9 502.7C150.4 515.2 170.7 515.2 183.2 502.7L320.5 365.3L457.9 502.6C470.4 515.1 490.7 515.1 503.2 502.6C515.7 490.1 515.7 469.8 503.2 457.3L365.8 320L503.1 182.6C515.6 170.1 515.6 149.8 503.1 137.3C490.6 124.8 470.3 124.8 457.8 137.3L320.5 274.7L183.1 137.4z"/></svg>';
}

/**
 * Allowed HTML tags/attributes for inline SVG icon output.
 *
 * @return array
 */
function kodeala_msdd_allowed_svg_html() {
    return array(
        'svg'  => array(
            'xmlns'       => true,
            'viewbox'     => true,
            'class'       => true,
            'aria-hidden' => true,
        ),
        'path' => array(
            'd' => true,
        ),
    );
}

/**
 * Normalize the full saved structure into a trusted array shape.
 *
 * @param mixed $raw Raw structure from option storage.
 *
 * @return array
 */
function kodeala_msdd_normalize_structure( $raw ) {
    if ( empty( $raw ) ) {
        return array();
    }

    if ( is_array( $raw ) && ! empty( $raw ) ) {
        $first = reset( $raw );
        if ( ! is_array( $first ) || ! isset( $first['type'] ) ) {
            $out = array();

            foreach ( $raw as $maybe_id ) {
                if ( is_numeric( $maybe_id ) ) {
                    $id = (int) $maybe_id;
                    if ( $id > 0 ) {
                        $out[] = array( 'type' => 'site', 'id' => $id );
                    }
                }
            }

            return $out;
        }
    }

    if ( ! is_array( $raw ) ) {
        return array();
    }

    $structure = array();

    foreach ( $raw as $item ) {
        $norm = kodeala_msdd_normalize_item( $item );
        if ( $norm ) {
            $structure[] = $norm;
        }
    }

    return $structure;
}

/**
 * Sanitize the stored setting value for compatibility with register_setting().
 *
 * @param mixed $value Raw setting value.
 *
 * @return array
 */
function kodeala_msdd_sanitize_setting( $value ) {
    return kodeala_msdd_normalize_structure( $value );
}

/**
 * Normalize a single structure item (site, divider, or group).
 *
 * @param mixed $item Item candidate from saved data.
 *
 * @return array|null
 */
function kodeala_msdd_normalize_item( $item ) {
    if ( ! is_array( $item ) || empty( $item['type'] ) ) {
        return null;
    }

    $type = sanitize_key( $item['type'] );

    if ( $type === 'divider' ) {
        return array( 'type' => 'divider' );
    }

    if ( $type === 'site' ) {
        $id = isset( $item['id'] ) && is_numeric( $item['id'] ) ? (int) $item['id'] : 0;
        if ( $id > 0 ) {
            return array( 'type' => 'site', 'id' => $id );
        }
        return null;
    }

    if ( $type === 'group' ) {
        $gid   = ! empty( $item['gid'] ) ? sanitize_text_field( $item['gid'] ) : kodeala_msdd_new_gid();
        $title = isset( $item['title'] ) ? sanitize_text_field( $item['title'] ) : '';
        $items = array();

        if ( ! empty( $item['items'] ) && is_array( $item['items'] ) ) {
            foreach ( $item['items'] as $sub ) {
                $sub_norm = kodeala_msdd_normalize_item( $sub );
                if ( $sub_norm && in_array( $sub_norm['type'], array( 'site', 'divider', 'group' ), true ) ) {
                    $items[] = $sub_norm;
                }
            }
        }

        return array(
                'type'  => 'group',
                'gid'   => $gid,
                'title' => $title,
                'items' => $items,
        );
    }

    return null;
}

/**
 * Recursively extract site IDs referenced in a structure.
 *
 * @param mixed $structure Structure array to inspect.
 *
 * @return int[]
 */
function kodeala_msdd_extract_site_ids_from_structure( $structure ) {
    $ids = array();

    if ( empty( $structure ) || ! is_array( $structure ) ) {
        return $ids;
    }

    foreach ( $structure as $item ) {
        if ( ! is_array( $item ) || empty( $item['type'] ) ) {
            continue;
        }

        if ( $item['type'] === 'site' && ! empty( $item['id'] ) && is_numeric( $item['id'] ) ) {
            $ids[] = (int) $item['id'];
            continue;
        }

        if ( $item['type'] === 'group' && ! empty( $item['items'] ) && is_array( $item['items'] ) ) {
            $child_ids = kodeala_msdd_extract_site_ids_from_structure( $item['items'] );
            if ( ! empty( $child_ids ) ) {
                $ids = array_merge( $ids, $child_ids );
            }
        }
    }

    return array_values( array_unique( $ids ) );
}

/**
 * Append any missing site IDs to the root structure level.
 *
 * @param mixed $structure    Existing structure.
 * @param mixed $all_site_ids All current multisite blog IDs.
 *
 * @return array
 */
function kodeala_msdd_append_missing_sites_to_root( $structure, $all_site_ids ) {
    $structure    = is_array( $structure ) ? $structure : array();
    $all_site_ids = is_array( $all_site_ids ) ? $all_site_ids : array();

    $existing = kodeala_msdd_extract_site_ids_from_structure( $structure );

    foreach ( $all_site_ids as $sid ) {
        $sid = (int) $sid;
        if ( $sid > 0 && ! in_array( $sid, $existing, true ) ) {
            $structure[] = array( 'type' => 'site', 'id' => $sid );
        }
    }

    return $structure;
}

/**
 * Render nested editor rows for all structure items.
 *
 * @param mixed $items List of structure items.
 * @param int   $depth Current nesting depth.
 *
 * @return void
 */
function kodeala_msdd_echo_editor_items( $items, $depth = 0 ) {
    if ( empty( $items ) || ! is_array( $items ) ) {
        return;
    }

    $count = 0;

    foreach ( $items as $item ) {
        if ( ! is_array( $item ) || empty( $item['type'] ) ) {
            continue;
        }

        $pos = $count + 1;

        if ( $item['type'] === 'divider' ) {
            echo '<div class="mysites-sortable-row msdd-item msdd-divider" data-type="divider" style="--msdd-depth:' . (int) $depth . '">';
            echo '<div class="mysites-moverow">' . wp_kses( kodeala_msdd_move_svg(), kodeala_msdd_allowed_svg_html() ) . '</div>';
            echo '<div class="mysites-title"><em>' . esc_html__( 'Divider', 'reorder-multisite-sites-dropdown' ) . '</em></div>';
            echo '<div class="mysites-url"></div>';
            echo '<div class="mysites-input">';
            echo '<button type="button" class="button button-small msdd-remove-divider" aria-label="' . esc_attr__( 'Remove divider', 'reorder-multisite-sites-dropdown' ) . '">' . wp_kses( kodeala_msdd_unwrap_svg(), kodeala_msdd_allowed_svg_html() ) . '<span>' . esc_html__( 'Remove', 'reorder-multisite-sites-dropdown' ) . '</span></button>';
            echo '<input class="msdd-order-input" oninput="this.value=this.value.replace(/[^0-9]/g, \'\');" type="number" value="' . esc_attr( $pos ) . '" />';
            echo '</div>';
            echo '</div>';
            $count++;
            continue;
        }

        if ( $item['type'] === 'site' ) {
            $site_id = ! empty( $item['id'] ) && is_numeric( $item['id'] ) ? (int) $item['id'] : 0;
            if ( $site_id > 0 ) {
                switch_to_blog( $site_id );
                $site_url = home_url( '/' );

                echo '<div class="mysites-sortable-row msdd-item msdd-site" data-type="site" data-site-id="' . (int) $site_id . '" style="--msdd-depth:' . (int) $depth . '">';
                echo '<div class="mysites-moverow">' . wp_kses( kodeala_msdd_move_svg(), kodeala_msdd_allowed_svg_html() ) . '</div>';
                echo '<div class="mysites-title">' . esc_html( get_bloginfo( 'blogname' ) ) . '</div>';
                echo '<div class="mysites-url"><a href="' . esc_url( $site_url ) . '">' . esc_html( $site_url ) . '</a></div>';
                echo '<div class="mysites-input"><input class="msdd-order-input" oninput="this.value=this.value.replace(/[^0-9]/g, \'\');" type="number" value="' . esc_attr( $pos ) . '" /></div>';
                echo '</div>';

                restore_current_blog();
                $count++;
            }
            continue;
        }

        if ( $item['type'] === 'group' ) {
            $gid   = ! empty( $item['gid'] ) ? $item['gid'] : kodeala_msdd_new_gid();
            $title = isset( $item['title'] ) ? $item['title'] : '';
            $group_items = ( ! empty( $item['items'] ) && is_array( $item['items'] ) ) ? $item['items'] : array();

            echo '<details class="msdd-block msdd-block-group" data-type="group" data-gid="' . esc_attr( $gid ) . '" open style="--msdd-depth:' . (int) $depth . '">';
            echo '<summary class="msdd-block-header">';
            echo '<span class="msdd-block-handle" aria-hidden="true">' . wp_kses( kodeala_msdd_move_svg(), kodeala_msdd_allowed_svg_html() ) . '</span>';
            echo '<span class="msdd-chevron" aria-hidden="true"></span>';

            echo '<input type="text" class="msdd-group-title" value="' . esc_attr( $title ) . '" placeholder="' . esc_attr__( 'Group title…', 'reorder-multisite-sites-dropdown' ) . '" />';
            echo '<button type="button" class="button button-primary msdd-unwrap">' . wp_kses( kodeala_msdd_unwrap_svg(), kodeala_msdd_allowed_svg_html() ) . '<span class="msdd-unwrap-text">' . esc_html__( 'Unwrap', 'reorder-multisite-sites-dropdown' ) . '</span></button>';
            echo '<div class="mysites-input msdd-group-input"><input class="msdd-order-input" oninput="this.value=this.value.replace(/[^0-9]/g, \'\');" type="number" value="' . esc_attr( $pos ) . '" /></div>';
            echo '</summary>';

            echo '<div class="msdd-group-items msdd-connected">';
            kodeala_msdd_echo_editor_items( $group_items, $depth + 1 );
            echo '</div>';

            echo '</details>';
            $count++;
            continue;
        }
    }
}
/**
 * Output admin bar styles for custom divider and group-label nodes.
 *
 * @return void
 */
function kodeala_msdd_adminbar_styles() {
    ?>
    <style>
        #wp-admin-bar-my-sites-list .kodeala-msdd-divider{
            height:5px;
        }
        #wp-admin-bar-my-sites-list .kodeala-msdd-divider > .ab-item {
            pointer-events: none;
            border-top: 1px solid rgba(255,255,255,.25);
            margin: 6px 0;
            height: 0;
            padding: 0;
        }

        #wp-admin-bar-my-sites-list .kodeala-msdd-divider > .ab-item.ab-empty-item {
            height: 0;
            min-height: 0;
            line-height: 0;
        }

        #wp-admin-bar-my-sites-list .kodeala-msdd-divider > .ab-item:before { content: ""; }

        #wp-admin-bar-my-sites-list .kodeala-msdd-group-label > .ab-item {
            pointer-events: none;
            font-weight: 600;
            opacity: 1;
            cursor: default;
            background: #2c3338;
            color: #fff;
            margin: 6px 0;
            padding: 0 8px;
        }

    </style>
    <?php
}
add_action( 'admin_head', 'kodeala_msdd_adminbar_styles' );
add_action( 'wp_head', 'kodeala_msdd_adminbar_styles' );

/**
 * Enqueue plugin CSS/JS assets for the network settings UI.
 *
 * @return void
 */
function kodeala_msdd_scripts() {
    wp_enqueue_style( 'kodeala-style-css', plugins_url( '/css/style.css', __FILE__ ), array(), '2.0.0' );
    wp_enqueue_script( 'jquery-ui-sortable' );
    wp_enqueue_script( 'jquery-ui-draggable' );
    wp_enqueue_script( 'jquery-ui-droppable' );
    wp_enqueue_script( 'kodeala-functions-js', plugins_url( '/js/functions.js', __FILE__ ), array( 'jquery', 'jquery-ui-sortable', 'jquery-ui-draggable', 'jquery-ui-droppable' ), '1.2.0', true );
}
add_action( 'admin_enqueue_scripts', 'kodeala_msdd_scripts' );

class kodeala_msdd_Settings_Page {
    /**
     * Base slug used for settings page routes and actions.
     *
     * @var string
     */
    protected $kodeala_msdd_settings_slug = 'reorder-my-sites';

    /**
     * Register admin menu and save handler hooks.
     *
     * @return void
     */
    public function __construct() {
        add_action( 'network_admin_menu', array( $this, 'kodeala_msdd_menu_and_fields' ) );
        add_action( 'network_admin_edit_' . $this->kodeala_msdd_settings_slug . '-update', array( $this, 'kodeala_msdd_update' ) );
    }

    /**
     * Render the sortable structure editor field content.
     *
     * @return void
     */
    public function kodeala_msdd_function() {
        $subsites     = get_sites();
        $all_site_ids = array();
        foreach ( $subsites as $subsite ) {
            $all_site_ids[] = (int) get_object_vars( $subsite )['blog_id'];
        }

        $structure = kodeala_msdd_normalize_structure( get_site_option( 'kodeala_msdd', '' ) );
        $structure = kodeala_msdd_append_missing_sites_to_root( $structure, $all_site_ids );

        echo '<div class="mysites-sortable-controls">';
        echo '<button type="button" class="button button-primary msdd-add" data-msdd-kind="group"><span class="dashicons dashicons-plus" aria-hidden="true"></span>' . esc_html__( 'Add Group', 'reorder-multisite-sites-dropdown' ) . '</button>';
        echo '<button type="button" class="button button-primary msdd-add" data-msdd-kind="divider"><span class="dashicons dashicons-minus" aria-hidden="true"></span>' . esc_html__( 'Add Divider', 'reorder-multisite-sites-dropdown' ) . '</button>';		echo '</div>';

        echo '<input type="hidden" id="kodeala_msdd_json" name="kodeala_msdd_json" value="" />';
        echo '<div class="msdd-card">';
        echo '<div class="msdd-table-head">';
        echo '<div class="msdd-th msdd-th-handle"></div>';
        echo '<div class="msdd-th msdd-th-name">' . esc_html__( 'SITE NAME / GROUP', 'reorder-multisite-sites-dropdown' ) . '</div>';
        echo '<div class="msdd-th msdd-th-url">' . esc_html__( 'SITE URL', 'reorder-multisite-sites-dropdown' ) . '</div>';
        echo '<div class="msdd-th msdd-th-order">' . esc_html__( 'ORDER', 'reorder-multisite-sites-dropdown' ) . '</div>';
        echo '</div>';
        echo '<div class="msdd-outer">';
        kodeala_msdd_echo_editor_items( $structure );
        echo '</div>';
        echo '</div>';
    }

    /**
     * Register submenu page, settings section, and settings field.
     *
     * @return void
     */
    public function kodeala_msdd_menu_and_fields() {
        add_submenu_page(
                'settings.php',
                __( 'Reorder My Sites Dropdown Menu', 'reorder-multisite-sites-dropdown' ),
                __( 'Reorder My Sites', 'reorder-multisite-sites-dropdown' ),
                'manage_network_options',
                $this->kodeala_msdd_settings_slug . '-page',
                array( $this, 'kodeala_msdd_create_page' )
        );

        add_settings_section( 'default-section', '', '', $this->kodeala_msdd_settings_slug . '-page' );
        register_setting(
                $this->kodeala_msdd_settings_slug . '-page',
                'kodeala_msdd',
                array(
                        'sanitize_callback' => 'kodeala_msdd_sanitize_setting',
                )
        );
        add_settings_field(
                'kodeala_msdd',
                '',
                array( $this, 'kodeala_msdd_function' ),
                $this->kodeala_msdd_settings_slug . '-page',
                'default-section'
        );
    }

    /**
     * Render the full network settings page wrapper and form.
     *
     * @return void
     */
    public function kodeala_msdd_create_page() {
        $updated = filter_input( INPUT_GET, 'updated', FILTER_SANITIZE_FULL_SPECIAL_CHARS );
        $reset   = filter_input( INPUT_GET, 'reset', FILTER_SANITIZE_FULL_SPECIAL_CHARS );

        if ( 'true' === $updated ) {
            ?>
            <div id="message" class="updated notice is-dismissible"><p><?php esc_html_e( 'Options Saved', 'reorder-multisite-sites-dropdown' ); ?></p></div>
            <?php
        } elseif ( 'true' === $reset ) {
            ?>
            <div id="message" class="updated notice is-dismissible"><p><?php esc_html_e( 'Order Reset', 'reorder-multisite-sites-dropdown' ); ?></p></div>
            <?php
        }
        ?>
        <div class="wrap mysites-settings">
            <h1><?php echo esc_html( get_admin_page_title() ); ?></h1>
            <form action="edit.php?action=<?php echo esc_attr( $this->kodeala_msdd_settings_slug ); ?>-update" method="POST">
                <?php
                settings_fields( $this->kodeala_msdd_settings_slug . '-page' );
                do_settings_sections( $this->kodeala_msdd_settings_slug . '-page' );
                submit_button( __( 'Save Changes', 'reorder-multisite-sites-dropdown' ), 'primary', 'submit', false );
                echo '&nbsp;';
                submit_button(
                        __( 'Reset', 'reorder-multisite-sites-dropdown' ),
                        'secondary',
                        'kodeala_msdd_reset',
                        false,
                        array( 'onclick' => "return confirm('Reset the My Sites order back to default?');" )
                );
                ?>
            </form>
        </div>
        <?php
    }

    /**
     * Process settings form submission (save or reset).
     *
     * @return void
     */
    public function kodeala_msdd_update() {
        $nonce_action = $this->kodeala_msdd_settings_slug . '-page-options';
        $nonce_value  = isset( $_POST['_wpnonce'] ) ? sanitize_text_field( wp_unslash( $_POST['_wpnonce'] ) ) : '';
        if ( '' === $nonce_value || ! wp_verify_nonce( $nonce_value, $nonce_action ) ) {
            wp_die( esc_html__( 'Security check failed.', 'reorder-multisite-sites-dropdown' ) );
        }

        $reset_requested = isset( $_POST['kodeala_msdd_reset'] ) ? sanitize_key( wp_unslash( $_POST['kodeala_msdd_reset'] ) ) : '';
        if ( '' !== $reset_requested ) {
            delete_site_option( 'kodeala_msdd' );
            wp_safe_redirect(
                    add_query_arg(
                            array( 'page' => $this->kodeala_msdd_settings_slug . '-page', 'reset' => 'true' ),
                            network_admin_url( 'settings.php' )
                    )
            );
            exit;
        }

        $structure = array();
        $raw_json = isset( $_POST['kodeala_msdd_json'] ) ? sanitize_textarea_field( wp_unslash( $_POST['kodeala_msdd_json'] ) ) : '';
        if ( '' !== $raw_json ) {
            $decoded = json_decode( $raw_json, true );
            if ( is_array( $decoded ) ) {
                $structure = kodeala_msdd_normalize_structure( $decoded );
            }
        }

        if ( ! empty( $structure ) ) {
            update_site_option( 'kodeala_msdd', $structure );
        } else {
            delete_site_option( 'kodeala_msdd' );
        }

        wp_safe_redirect(
                add_query_arg(
                        array( 'page' => $this->kodeala_msdd_settings_slug . '-page', 'updated' => 'true' ),
                        network_admin_url( 'settings.php' )
                )
        );
        exit;
    }
}

new kodeala_msdd_Settings_Page();

class kodeala_msdd_reorder_mysite_dd {
    /**
     * Register the admin bar menu reorder hook.
     *
     * @return void
     */
    public function __construct() {
        add_action( 'admin_bar_menu', array( $this, 'kodeala_msdd_admin_bar_menu' ), 100 );
    }

    /**
     * Rebuild the "My Sites" admin bar dropdown using saved structure.
     *
     * @return void
     */
    public function kodeala_msdd_admin_bar_menu() {
        global $wp_admin_bar;

        $sites    = $wp_admin_bar->user->blogs;
        $subsites = get_sites();

        $structure = kodeala_msdd_normalize_structure( get_site_option( 'kodeala_msdd' ) );
        if ( empty( $structure ) ) {
            return;
        }

        $all_site_ids = array();
        foreach ( $subsites as $subsite ) {
            $all_site_ids[] = (int) get_object_vars( $subsite )['blog_id'];
        }
        $structure = kodeala_msdd_append_missing_sites_to_root( $structure, $all_site_ids );

        if ( ! method_exists( $wp_admin_bar, 'get_nodes' ) ) {
            return;
        }

        $all_nodes = $wp_admin_bar->get_nodes();
        if ( empty( $all_nodes ) || ! is_array( $all_nodes ) ) {
            return;
        }

        $children = array();
        foreach ( $all_nodes as $node_id => $node ) {
            $parent = ! empty( $node->parent ) ? $node->parent : '';
            if ( $parent ) {
                if ( ! isset( $children[ $parent ] ) ) {
                    $children[ $parent ] = array();
                }
                $children[ $parent ][] = $node_id;
            }
        }

        $blog_payloads = array();
        foreach ( $sites as $blog_id => $blog_obj ) {
            $top_id = 'blog-' . (int) $blog_id;
            if ( ! isset( $all_nodes[ $top_id ] ) ) {
                continue;
            }
            $stack     = array( $top_id );
            $collected = array();
            while ( ! empty( $stack ) ) {
                $current = array_shift( $stack );
                if ( isset( $all_nodes[ $current ] ) ) {
                    $collected[ $current ] = $all_nodes[ $current ];
                    if ( ! empty( $children[ $current ] ) ) {
                        foreach ( $children[ $current ] as $child_id ) {
                            $stack[] = $child_id;
                        }
                    }
                }
            }
            $blog_payloads[ (int) $blog_id ] = $collected;
            foreach ( array_keys( $collected ) as $nid ) {
                $wp_admin_bar->remove_node( $nid );
            }
        }

        $divider_i = 0;
        $label_i   = 0;

        // Re-add a site's full node tree (top node + descendants) to the admin bar.
        $add_blog_payload = function( $blog_id ) use ( $wp_admin_bar, $blog_payloads ) {
            $blog_id = (int) $blog_id;
            if ( $blog_id <= 0 || empty( $blog_payloads[ $blog_id ] ) ) {
                return;
            }

            $payload = $blog_payloads[ $blog_id ];
            $top_id  = 'blog-' . $blog_id;

            if ( isset( $payload[ $top_id ] ) ) {
                $n = $payload[ $top_id ];
                $wp_admin_bar->add_node(
                        array(
                                'id'     => $n->id,
                                'parent' => $n->parent,
                                'title'  => $n->title,
                                'href'   => $n->href,
                                'meta'   => (array) $n->meta,
                        )
                );
                unset( $payload[ $top_id ] );
            }

            foreach ( $payload as $n ) {
                $wp_admin_bar->add_node(
                        array(
                                'id'     => $n->id,
                                'parent' => $n->parent,
                                'title'  => $n->title,
                                'href'   => $n->href,
                                'meta'   => (array) $n->meta,
                        )
                );
            }
        };

        // Insert a visual divider row in the My Sites list.
        $add_divider = function() use ( $wp_admin_bar, &$divider_i ) {
            $wp_admin_bar->add_node(
                    array(
                            'id'     => 'kodeala-msdd-divider-' . ( ++$divider_i ),
                            'parent' => 'my-sites-list',
                            'title'  => '',
                            'href'   => false,
                            'meta'   => array( 'class' => 'kodeala-msdd-divider' ),
                    )
            );
        };

        // Recursively render structure items: divider, site payload, or group with optional label.
        $render_items = function( $items ) use ( &$render_items, $add_divider, $add_blog_payload, $wp_admin_bar, &$label_i ) {
            if ( empty( $items ) || ! is_array( $items ) ) {
                return;
            }
            foreach ( $items as $item ) {
                if ( ! is_array( $item ) || empty( $item['type'] ) ) {
                    continue;
                }
                if ( $item['type'] === 'divider' ) {
                    $add_divider();
                    continue;
                }
                if ( $item['type'] === 'site' ) {
                    $add_blog_payload( isset( $item['id'] ) ? (int) $item['id'] : 0 );
                    continue;
                }
                if ( $item['type'] === 'group' ) {
                    $title = isset( $item['title'] ) ? $item['title'] : '';
                    if ( $title !== '' ) {
                        $wp_admin_bar->add_node(
                                array(
                                        'id'     => 'kodeala-msdd-group-label-' . ( ++$label_i ),
                                        'parent' => 'my-sites-list',
                                        'title'  => esc_html( $title ),
                                        'href'   => false,
                                        'meta'   => array( 'class' => 'kodeala-msdd-group-label' ),
                                )
                        );
                    }
                    $child_items = ! empty( $item['items'] ) && is_array( $item['items'] ) ? $item['items'] : array();
                    $render_items( $child_items );
                }
            }
        };

        $render_items( $structure );
    }
}

new kodeala_msdd_reorder_mysite_dd();

?>
