christianwach / civicrm-wp-member-sync

CiviCRM WordPress Member Sync plugin keeps a WordPress user in sync with a CiviCRM membership by granting either a role or capabilities to a WordPress user who has that membership.
https://wordpress.org/plugins/civicrm-wp-member-sync/
GNU General Public License v2.0
17 stars 10 forks source link

Feature Request: Ability to Define Username When WordPress User is Created #16

Closed mikejr83 closed 7 years ago

mikejr83 commented 7 years ago

Thanks for a great plugin!

Use Case / User Story:

I've been working setting up a site for our club to move us off paper records. One major task is setting up all the current memberships based off what we have in our Excel sheet. I've been able to get all the "contacts" into the system and want to use the membership import functionality to create memberships for all the contacts which we have marked as "current".

As part of the setup of the member sync plugin I would like to have a setting to define how to build the username for the newly created WordPress user.

Other Info

I currently see this function in code:

public function wp_create_user( $civi_contact )

In the function the first attempt to create the user using the civicrm "display_name":

// create username from display name
$user_name = sanitize_title( sanitize_user( $civi_contact['display_name'] ) );

// check if we have a user with that username
$user_id = username_exists( $user_name );

It would be nice to have that changed to pick up the setting.

christianwach commented 7 years ago

@mikejr83 Thanks for the suggestion. I'd be happy to add a filter for $user_name before the user is created so that you can define your own.

Cheers, Christian

christianwach commented 7 years ago

@mikejr83 Does the latest commit solve this for you?

mikejr83 commented 7 years ago

Hey Christian,

Thanks for the quick turn around. I've been a .NET developer doing ASP.NET and MVC.NET for the last 10+ years. I haven't touched PHP at all during my career! This is all a learning experience from the syntax to the best practice construction of these classes.

I made a quick adjustment to the the wp_create_user function:

$contact_user_name = substr($civi_contact['first_name'], 0, 1) . $civi_contact['last_name'];

// create username from display name
// $user_name = sanitize_title( sanitize_user( $civi_contact['display_name'] ) );
$user_name = sanitize_title( sanitize_user( $contact_user_name ) );

That gives me exactly what I want. After looking at your commit it seems like I need to learn how to use filters in WordPress. From my cursory glance the code looks good and assuming filters do what I think they do then it should be good to go. I pulled latest and will update the plugin when I figure out how to utilize the filter.

Thanks a bunch for the quick responses!

Mike G.

mikejr83 commented 7 years ago

Ok. I think I got it to work! Lots of reading on how to get a plugin setup so I could encapsulate my code in an area that won't get killed on version changes and theme changes.

<?php
/*
   Plugin Name: Flying Gators Tweaks
   Description: a plugin to enhance the flyinggatorsrc.com site.
   Version: 0.1
   Author: Mike Gardner
   Author URI: http://blog.itsnotfound.com
   License: GPL2
*/

// store reference to this file
define( 'FLYING_GATORS_PLUGIN_FILE', __FILE__ );

class Flying_Gators {
    public function __construct() {
        // When the plugin has loaded initialize.
        add_action( 'plugins_loaded', array( $this, 'initialize' ) );
    }

    /**
     * Handle plugin activation.
     */
    public function activate() {
        // No op
    }

    /**
     * Handle plugin deactivation.
     */
    public function deactivate() {
        // No op
    }

    /**
     * Handler for the filter 'civi_wp_member_sync_new_username'.
     * Uses the $civi_contact argument to create a username with the first letter of the first name and the last name.
     *
     * @param str $user_name The previously-generated WordPress username
     * @param array $civi_contact The CiviCRM contact data
     * @return str $user_name The modified WordPress username
     */
    function create_wp_username_from_member_sync($user_name, $civi_contact) {
        $contact_user_name = substr($civi_contact['first_name'], 0, 1) . $civi_contact['last_name'];

        $contact_user_name = sanitize_title( sanitize_user( $contact_user_name ) );

        return $contact_user_name;
    }

    /**
     * Handles the initialization of the plugin. (Seems to be called on every page request)
     */
    public function initialize() {
        // Setup a filter 'handler' for the 'civi_wp_member_sync_new_username'. 
        // The handler will be on 'this' class and be the function called 'create_wp_username_from_member_sync'
        // The handler will execute with 'priority' 10 and accept 2 arguments.
        add_filter('civi_wp_member_sync_new_username', array( $this, 'create_wp_username_from_member_sync'), 10 , 2);
    }
} // class ends

// declare as global for external reference
global $flying_gators;

// init plugin
$flying_gators = new Flying_Gators;

// plugin activation
register_activation_hook( __FILE__, array( $flying_gators, 'activate' ) );

// plugin deactivation
register_deactivation_hook( __FILE__, array( $flying_gators, 'deactivate' ) );

Is this about correct? After activating my plugin and then running the sync the names are picked up with what I need.

Thanks!

christianwach commented 7 years ago

@mikejr83 Your code looks spot on to me, Mike. You could perhaps simplify it a bit - i.e. remove the activation hooks and methods and the define() none of which are being used. Glad you've successfully implemented your first WordPress plugin!

Cheers, Christian

mikejr83 commented 7 years ago

Thanks Christian! I used your plugin as a template so it started with a pretty much copy&paste layout and I started removing things as needed.

One last question (hopefully). When I use the import function for members, your sync plugin is called, correct? Meaning that in regards to the SE question that I posted your change here plus my use of the filter will create the usernames as desired during the import process. Thanks again fro the quick responses!

christianwach commented 7 years ago

When I use the import function for members, your sync plugin is called, correct?

If you mean the Manual Sync process, then yes it should be.

Meaning that in regards to the SE question that I posted your change here plus my use of the filter will create the usernames as desired during the import process.

Yes. Though I always recommend testing on a copy of your live data!

andyburnsco commented 5 years ago

I took a portion of this code and added it a to a code snippets plugin and while it constructs the name correctly, it fails if the username already exists. Wondering why?

function create_wp_username_from_member_sync($user_name, $civi_contact) {
        $contact_user_name = $civi_contact['first_name'] . $civi_contact['last_name'];

        $contact_user_name = sanitize_title( sanitize_user( $contact_user_name ) );

        return $contact_user_name;
    }

    /**
     * Handles the initialization of the code.
     */
    add_filter('civi_wp_member_sync_new_username', 'create_wp_username_from_member_sync', 10 , 2);
christianwach commented 5 years ago

@andyburnsco Yes, that's expected behaviour as discussed in #27. The filter runs after the username is made unique.

You need to run your constructed username through unique_username() again - either by calling the method directly or by copying the code into your plugin.

The direct call would be:

$contact_user_name = civicrm_wpms()->users->unique_username( $contact_user_name, $civi_contact );

Add this prior to return $contact_user_name; and you should be okay.

andyburnsco commented 5 years ago

Thanks for the assist :) I can just tinker with code...

The final function looks like


        // create username with first and last name
        $contact_user_name = $civi_contact['first_name'] . $civi_contact['last_name'];

        $contact_user_name = sanitize_title( sanitize_user( $contact_user_name ) );

        // Ensure username is unique
        $contact_user_name = civicrm_wpms()->users->unique_username( $contact_user_name, $civi_contact );

        // Remove dash if making username unique
        $contact_user_name = str_replace('-', '', $contact_user_name);

        return $contact_user_name;
    }

    /**
     * Handles the initialization of the code.
     */
    add_filter('civi_wp_member_sync_new_username', 'create_wp_username_from_member_sync', 10 , 2);

This will put it in format firstlast and append a digit to make unique with no dashes.