Miller-Media / expire-passwords

Require certain users to change their passwords on a regular basis.
https://wordpress.org/plugins/expire-passwords/
GNU General Public License v2.0
7 stars 4 forks source link

Multisite support? #8

Open roytanck opened 4 years ago

roytanck commented 4 years ago

Thank you for continuing this plugin. Team members found it to work well on a single site WordPress install. We're looking into deploying it on multisite installs as well, and I'm wondering whether this is supported.

The plugin seems to check user roles before determining whether a user's password should expire. On multisite, the same user can have different roles on different subsites. Does Expire User Passwords take this into account? Would you recommend running the plugin in multisite, and if so, should we network-activate it?

kkerley commented 3 years ago

I'd also love to know about multisite support.

kkerley commented 3 years ago

@roytanck I ended up extending this a bit for multisite. I don't know what your needs are but maybe this can help. I added this function to the very bottom of the expire-user-passwords.php file, right before the final Expire_User_Passwords::instance(); line:

if ( is_multisite() && is_main_site() ) :
    add_action(
        'update_option_user_expass_settings',
        function( $old_value, $new_value ) {
            // Check for the 'sync_settings_to_network' key
            // If present, loop through all spokes and save the main site's option values to each spoke
            if ( array_key_exists( 'sync_settings_to_network', $_REQUEST ) ) :
                global $wpdb;

                // Save the original blog id.
                $original_spoke_id = get_current_blog_id();
                $spoke_ids         = get_sites( [ 'fields' => 'ids' ] );

                write_to_debug_log( 'should broadcast to the entire network' );

                foreach ( $spoke_ids as $id ) :
                    switch_to_blog( $id );

                    // Get the user_expass_settings option from the spoke
                    $spoke_expass_settings = get_option( 'user_expass_settings' );

                    // If the option doesn't exist, change this variable into a skeleton object matching the required format
                    if ( ! $spoke_expass_settings ) :
                        $spoke_expass_settings = [
                            'limit' => '',
                            'roles' => [],
                        ];
                    endif;

                    // and override it with the main site's user_expass_settings values, gathered from the $_REQUEST object
                    $spoke_expass_settings['limit'] = $_REQUEST['user_expass_settings']['limit'];
                    $spoke_expass_settings['roles'] = $_REQUEST['user_expass_settings']['roles'];

                    // Finally, save the option values to the spoke
                    update_option( 'user_expass_settings', $spoke_expass_settings );
                endforeach;

                // restore the current spoke and reset the $GLOBALS so WP doesn't think it's still in 'switched' state
                switch_to_blog( $original_spoke_id );
                $GLOBALS['_wp_switched_stack'] = [];
                $GLOBALS['switched']           = false;
            endif;
        },
        10,
        2
    );
endif;

"Spoke" is our internal word for blogs/subsites.

In addition, in render_submenu_page() in includes/class-settings.php, I've updated the form to the following:

            <form method="post" action="options.php">
                <?php

                settings_fields( 'user_expass_settings_page' );

                do_settings_sections( 'user_expass_settings_page' );

                // check for multisite and if this is the master site. If so, output an additional checkbox to allow for network-wide syncing
                if ( is_multisite() && is_main_site() ) :
                    ?>
                    <div class="master-site-controls">
                        <label>
                            <input type="checkbox" id="sync_settings_to_network" name="sync_settings_to_network" >
                            Sync these settings to the entire network? <br />(They can still be overridden on each spoke)
                        </label>
                    </div>
                    <?php
                endif;
                submit_button();

                ?>
            </form>

The additional checkbox added to the form, which only appears on the main site of your multisite network, sets a flag in the $_REQUEST object that the function above this form code checks for. If present, it then loops through each blog/subsite/spoke of the network and assigns those settings to each.

It still allows overriding on a specific blog/subsite/spoke and overwriting the overrides if done again from the main site and broadcast out to the network.

Hope this helps.

roytanck commented 3 years ago

@kkerley Thank you for sharing your code. My team has opted to create our own (internal) plugin solution, which I'm unfortunately unable to share.