Users may want to sync some of their details from your login handler's database to the community. The data that can be synced is:
- Display name
- Email address
- Profile photo
- Cover photo
- Status updates
For example, users who have logged in with Facebook can use their Facebook profile photo on the community, and optionally update the email address used on the community if they change it on Facebook. Your login handler can provide this syncing too.
Even if the user chooses to not sync this data, it is also used in certain other areas, for example, when viewing a member in the AdminCP, it will show their display name and profile photo for all of the login handlers they are logged in with, even if they are not being synced.
There are several methods to implement. You should implement all of the methods that your login handler can provide the data for and leave the others unimplemented (for example, if your login handler doesn't have profile photos, you can skip that method).
Here is are the skeletons for the methods you need to implement:
/** * Get user's identifier (doesn't have to be a number) * May return NULL if server doesn't support this * * @param \IPS\Member $member Member * @return string|NULL * @throws \IPS\Login\Exception The token is invalid and the user needs to reauthenticate * @throws \DomainException General error where it is safe to show a message to the user * @throws \RuntimeException Unexpected error from service */ public function userId( \IPS\Member $member ) { return NULL; } /** * Get user's profile photo * May return NULL if server doesn't support this * * @param \IPS\Member $member Member * @return \IPS\Http\Url|NULL * @throws \IPS\Login\Exception The token is invalid and the user needs to reauthenticate * @throws \DomainException General error where it is safe to show a message to the user * @throws \RuntimeException Unexpected error from service */ public function userProfilePhoto( \IPS\Member $member ) { return NULL; } /** * Get user's profile name * May return NULL if server doesn't support this * * @param \IPS\Member $member Member * @return string|NULL * @throws \IPS\Login\Exception The token is invalid and the user needs to reauthenticate * @throws \DomainException General error where it is safe to show a message to the user * @throws \RuntimeException Unexpected error from service */ public function userProfileName( \IPS\Member $member ) { return NULL; } /** * Get user's email address * May return NULL if server doesn't support this * * @param \IPS\Member $member Member * @return string|NULL * @throws \IPS\Login\Exception The token is invalid and the user needs to reauthenticate * @throws \DomainException General error where it is safe to show a message to the user * @throws \RuntimeException Unexpected error from service */ public function userEmail( \IPS\Member $member ) { return NULL; } /** * Get user's cover photo * May return NULL if server doesn't support this * * @param \IPS\Member $member Member * @return \IPS\Http\Url|NULL * @throws \IPS\Login\Exception The token is invalid and the user needs to reauthenticate * @throws \DomainException General error where it is safe to show a message to the user * @throws \RuntimeException Unexpected error from service */ public function userCoverPhoto( \IPS\Member $member ) { return NULL; } /** * Get user's statuses since a particular date * * @param \IPS\Member $member Member * @param \IPS\DateTime|NULL $since Date/Time to get statuses since then, or NULL to get the latest one * @return array * @throws \IPS\Login\Exception The token is invalid and the user needs to reauthenticate * @throws \DomainException General error where it is safe to show a message to the user * @throws \RuntimeException Unexpected error from service */ public function userStatuses( \IPS\Member $member, \IPS\DateTime $since = NULL ) { return array(); } /** * Get link to user's remote profile * May return NULL if server doesn't support this * * @param string $identifier The ID Nnumber/string from remote service * @param string $username The username from remote service * @return \IPS\Http\Url|NULL * @throws \IPS\Login\Exception The token is invalid and the user needs to reauthenticate * @throws \DomainException General error where it is safe to show a message to the user * @throws \RuntimeException Unexpected error from service */ public function userLink( $identifier, $username ) { return NULL; }
In order to configure which syncing options are available, a section will need to be added to the Account Settings page for your Login Handler. The convention that all default Login Handlers follow is to allow the admin the option to always show a section (Invision Community will automatically create a page that allows users to log in with the handler linking their accounts if they're not already logged in with it), only show a section if the user has already logged in with that method, or not show one at all (which won't allow them to sync their details).
To follow this convention you just need to add a setting for that - the code to check its value is already present:
/**
* ACP Settings Form
*
* @param string $url URL to redirect user to after successful submission
* @return array List of settings to save - settings will be stored to core_login_methods.login_settings DB field
* @code
return array( 'savekey' => new \IPS\Helpers\Form\[Type]( ... ), ... );
* @endcode
*/
public function acpForm()
{
$return = array();
// ... any other settings you already have ...
$return['show_in_ucp'] = new \IPS\Helpers\Form\Radio( 'login_handler_show_in_ucp', isset( $this->settings['show_in_ucp'] ) ? $this->settings['show_in_ucp'] : 'disabled', FALSE, array(
'options' => array(
'always' => 'login_handler_show_in_ucp_always',
'loggedin' => 'login_handler_show_in_ucp_loggedin',
'disabled' => 'login_handler_show_in_ucp_disabled'
),
) );
return $return;
}
Alternatively you can override the showInUcp() method to implement other logic to determine if your login handler should show:
/**
* Show in Account Settings?
*
* @param \IPS\Member|NULL $member The member, or NULL for if it should show generally
* @return bool
*/
public function showInUcp( \IPS\Member $member = NULL )
{
// ...
}
You should also implement the logoForUcp() method which determines the logo to use alongside the link to your Login Handler's area of the Account Settings. You can return an \IPS\Http\Url object, or a string with a Fontawesome icon name:
/**
* Get logo to display in user cp sidebar
*
* @return \IPS\Http\Url|string
*/
public function logoForUcp()
{
return 'sign-in';
}
Finally you need to implement a syncOptions() method which returns the things which can be synced. The convention is to offer profile photo and cover photo syncing automatically if your Login Handler supports it, and have settings to control if email and username syncing is offered.
To do this, you might implement settings like this:
/** * ACP Settings Form * * @param string $url URL to redirect user to after successful submission * @return array List of settings to save - settings will be stored to core_login_methods.login_settings DB field * @code return array( 'savekey' => new \IPS\Helpers\Form\[Type]( ... ), ... ); * @endcode */ public function acpForm() { $return = array(); // ... any other settings you already have ... $return['show_in_ucp'] = new \IPS\Helpers\Form\Radio( 'login_handler_show_in_ucp', isset( $this->settings['show_in_ucp'] ) ? $this->settings['show_in_ucp'] : 'disabled', FALSE, array( 'options' => array( 'always' => 'login_handler_show_in_ucp_always', 'loggedin' => 'login_handler_show_in_ucp_loggedin', 'disabled' => 'login_handler_show_in_ucp_disabled' ), 'toggles' => array( 'always' => array( 'login_update_name_changes_inc_optional', 'login_update_email_changes_inc_optional' ), 'loggedin' => array( 'login_update_name_changes_inc_optional', 'login_update_email_changes_inc_optional' ), 'disabled' => array( 'login_update_name_changes_no_optional', 'login_update_email_changes_no_optional' ), ) ) ); $return['update_name_changes_inc_optional'] = new \IPS\Helpers\Form\Radio( 'login_update_name_changes_inc_optional', isset( $this->settings['update_name_changes'] ) ? $this->settings['update_name_changes'] : 'disabled', FALSE, array( 'options' => array( 'force' => 'login_update_changes_yes', 'optional' => 'login_update_changes_optional', 'disabled' => 'login_update_changes_no', ) ), NULL, NULL, NULL, 'login_update_name_changes_inc_optional' ); $return['update_name_changes_no_optional'] = new \IPS\Helpers\Form\Radio( 'login_update_name_changes_no_optional', ( isset( $this->settings['update_name_changes'] ) and $this->settings['update_name_changes'] != 'optional' ) ? $this->settings['update_name_changes'] : 'disabled', FALSE, array( 'options' => array( 'force' => 'login_update_changes_yes', 'disabled' => 'login_update_changes_no', ) ), NULL, NULL, NULL, 'login_update_name_changes_no_optional' ); $return['update_email_changes_inc_optional'] = new \IPS\Helpers\Form\Radio( 'login_update_email_changes_inc_optional', isset( $this->settings['update_email_changes'] ) ? $this->settings['update_email_changes'] : 'force', FALSE, array( 'options' => array( 'force' => 'login_update_changes_yes', 'optional' => 'login_update_changes_optional', 'disabled' => 'login_update_changes_no', ) ), NULL, NULL, NULL, 'login_update_email_changes_inc_optional' ); $return['update_email_changes_no_optional'] = new \IPS\Helpers\Form\Radio( 'login_update_email_changes_no_optional', ( isset( $this->settings['update_email_changes'] ) and $this->settings['update_email_changes'] != 'optional' ) ? $this->settings['update_email_changes'] : 'force', FALSE, array( 'options' => array( 'force' => 'login_update_changes_yes', 'disabled' => 'login_update_changes_no', ) ), NULL, NULL, NULL, 'login_update_email_changes_no_optional' ); \IPS\Member::loggedIn()->language()->words['login_update_name_changes_inc_optional'] = \IPS\Member::loggedIn()->language()->addToStack('login_update_name_changes'); \IPS\Member::loggedIn()->language()->words['login_update_name_changes_no_optional'] = \IPS\Member::loggedIn()->language()->addToStack('login_update_name_changes'); \IPS\Member::loggedIn()->language()->words['login_update_email_changes_inc_optional'] = \IPS\Member::loggedIn()->language()->addToStack('login_update_email_changes'); \IPS\Member::loggedIn()->language()->words['login_update_email_changes_no_optional'] = \IPS\Member::loggedIn()->language()->addToStack('login_update_email_changes'); return $return; } /** * Save Handler Settings * * @param array $values Values from form * @return array */ public function acpFormSave( &$values ) { $_values = $values; $settings = parent::acpFormSave( $values ); if ( $_values['login_handler_show_in_ucp'] == 'never' ) { $settings['update_name_changes'] = $_values['login_update_name_changes_no_optional']; $settings['update_email_changes'] = $_values['login_update_email_changes_no_optional']; } else { $settings['update_name_changes'] = $_values['login_update_name_changes_inc_optional']; $settings['update_email_changes'] = $_values['login_update_email_changes_inc_optional']; } unset( $settings['update_name_changes_inc_optional'] ); unset( $settings['update_name_changes_no_optional'] ); unset( $settings['update_email_changes_inc_optional'] ); unset( $settings['update_email_changes_no_optional'] ); return $settings; }
And then implement the actual syncOptions() method like this:
/** * Syncing Options * * @param \IPS\Member $member The member we're asking for (can be used to not show certain options iof the user didn't grant those scopes) * @param bool $defaultOnly If TRUE, only returns which options should be enabled by default for a new account * @return array */ public function syncOptions( \IPS\Member $member, $defaultOnly = FALSE ) { $return = array(); if ( isset( $this->settings['update_email_changes'] ) and $this->settings['update_email_changes'] === 'optional' ) { $return[] = 'email'; } if ( isset( $this->settings['update_name_changes'] ) and $this->settings['update_name_changes'] === 'optional' ) { $return[] = 'name'; } $return[] = 'photo'; $return[] = 'cover'; return $return; }
After implementing this, there user will see the options to sync data in the area for your Login Handler in the Account Settings. Once they have configured it, Invision Community will automatically handle the syncing.
Report Document