Site Notes for WordPress Multisite

This snippet enhances the WordPress Multisite network by introducing a dedicated notes column in the Sites table within the Network Admin dashboard. This feature allows network administrators to add, view, and edit custom notes for each site in the network, facilitating better communication and management. Notes are edited through a tab in the edit sites page.

				
					<?php
/**
 * Add Site Notes tab to the Edit Site screen in Multisite.
 *
 * @param array $links
 * 
 * @return array $links
 */
function site_notes_tab( $links ) {
    $links['site-notes'] = array(
        'label' => __( 'Notes', 'textdomain' ),
        'url'   => 'admin.php?page=site-notes',
        'cap'   => 'manage_network_options',
    );
    return $links;
}
add_filter( 'network_edit_site_nav_links', 'site_notes_tab' );

/**
 * Register hidden submenu page for Site Notes.
 */
function site_notes_page() {
    add_submenu_page(
        '', // Hidden from menus
        __( 'Site Notes', 'textdomain' ),
        __( 'Notes', 'textdomain' ),
        'manage_network_options',
        'site-notes',
        'site_notes_page_callback'
    );
}
add_action( 'network_admin_menu', 'site_notes_page' );

/**
 *  Render a page on the blog settings for updating Site Notes
 */
function site_notes_page_callback() {
    $id   = isset( $_GET['id'] ) ? absint( $_GET['id'] ) : 0;
    $blog = $id ? get_site( $id ) : false;

    if ( ! $id || ! $blog ) {
        wp_die( esc_html__( 'Invalid site ID.', 'textdomain' ) );
    }

    ?>
    <div class="wrap">
        <h1 id="edit-site"><?php echo esc_html( $blog->blogname ); ?></h1>
        <p class="edit-site-actions">
            <a href="<?php echo esc_url( get_home_url( $id, '/' ) ); ?>"><?php esc_html_e( 'Visit', 'textdomain' ); ?></a> |
            <a href="<?php echo esc_url( get_admin_url( $id ) ); ?>"><?php esc_html_e( 'Dashboard', 'textdomain' ); ?></a>
        </p>

        <?php
        network_edit_site_nav(
            array(
                'blog_id'  => $id,
                'selected' => 'site-notes',
            )
        );

        // Success message
        if ( isset( $_GET['updated'] ) && $_GET['updated'] ) {
            echo '<div class="notice notice-success is-dismissible"><p>' . esc_html__( 'Notes updated.', 'textdomain' ) . '</p></div>';
        }
        ?>

        <form method="post" action="edit.php?action=notes_save">
            <?php wp_nonce_field( 'site_notes_save_' . $id, 'site_notes_nonce' ); ?>
            <input type="hidden" name="id" value="<?php echo esc_attr( $id ); ?>" />

            <table class="form-table">
                <tr>
                    <th scope="row">
                        <label for="site_notes"><?php esc_html_e( 'Notes', 'textdomain' ); ?></label>
                    </th>
                    <td>
                        <textarea name="site_notes" id="site_notes" class="large-text" rows="6"><?php
                            echo esc_textarea( get_blog_option( $id, 'site_notes' ) );
                        ?></textarea>
                    </td>
                </tr>
            </table>

            <?php submit_button(); ?>
        </form>
    </div>
    <?php
}

/**
* Save Site Notes and redirect back to page
*/

function site_notes_save() {
    $id = isset( $_POST['id'] ) ? absint( $_POST['id'] ) : 0;

    if ( ! $id ) {
        wp_die( esc_html__( 'Invalid site ID.', 'textdomain' ) );
    }

    check_admin_referer( 'site_notes_save_' . $id, 'site_notes_nonce' );

    if ( isset( $_POST['site_notes'] ) ) {
        update_blog_option(
            $id,
            'site_notes',
            sanitize_textarea_field( wp_unslash( $_POST['site_notes'] ) )
        );
    }

    wp_safe_redirect(
        add_query_arg(
            array(
                'page'    => 'site-notes',
                'id'      => $id,
                'updated' => true,
            ),
            network_admin_url( 'admin.php' )
        )
    );
    exit;
}
add_action( 'network_admin_edit_notes_save', 'site_notes_save' );

/**
 * Add Site Notes column to Network Sites list.
 *
 * @param array $sites_columns
 * 
 * @return array $new_columns
 */
function notes_col( $sites_columns ) {
    $new_columns = [];

    foreach ( $sites_columns as $key => $label ) {
        $new_columns[ $key ] = $label;

        // Insert column after lastupdated
        if ( 'lastupdated' === $key ) {
            $new_columns['site_notes'] = __( 'Notes', 'textdomain' );
        }
    }

    // If lastupdated wasn’t found, stick it at the end
    if ( ! isset( $new_columns['site_notes'] ) ) {
        $new_columns['site_notes'] = __( 'Notes', 'textdomain' );
    }

    return $new_columns;
}
add_filter( 'wpmu_blogs_columns', 'notes_col' );

/**
 *Populate data to Notes column
 *
 * @param string $column_name
 * @param int $blog_id
 */
function notes_col_data( $column_name, $blog_id ) {
    if ( 'site_notes' === $column_name ) {
        $notes = get_blog_option( $blog_id, 'site_notes' );
        echo esc_html( $notes );
    }
}
add_action( 'manage_sites_custom_column', 'notes_col_data', 10, 2 );

?>
				
			

If this code helped you, or you have any questions on implementation, don’t hesitate to reach out.