
Everything posted by Mark
-
4.3.3
New features for GDPR compliance: New feature for administrators to download an XML file of all personal information held. New setting to automatically prune IP address records. New option when deleting a member to anonymize content submitted by them. New setting to automatically add links to privacy policies of integrated third party services such as Google Analytics or Facebook Pixel to your privacy policy if they are enabled. Fixes an issue where Calendar events submitted in different timezones to the user may show at the wrong time. Other minor bug fixes and improvements. Learn more about GDPR compliance features in this release
-
4.3.2
Version 4.3.2 is a small maintenance update to fix issues reported since 4.3.1, including: Promotes non-functional when "Our Picks" disabled. Various emoji fixes, including skintones and mobile issues. Online stats. Numerous IE11 fixes. PayPal billing agreements failing due to lack of address.
-
4.2.9
This is a maintenance to release to the 4.2 series address security issues. As we prepare for our next large release, version 4.3, we do large security audits. We decided to apply those security enhancements to the 4.2 series so you can get them now if you prefer to not upgrade to 4.3 yet. We would like to thank @newbie LAC for his assistance.
-
Advanced Features
Handling Account Changes You may want to change the display name, email address and/or password in your Login Handler's database when the user changes those details locally. This is especially likely for Username/Password Handlers. There are lots of possible combinations of login handlers. A community might be using your login handler in combination with many other different handlers, or they might be using only your login handler and even have disabled the standard login handler. The two Username/Password Handlers in Invision Community (MySQL database and LDAP) both provide settings to allow the administrator to control if this syncing should be done and so it is recommended that you do the same (you can copy the code for creating these settings). You will implement several methods. Here is some sample code for the methods you need to implement, including the code for creating the settings: /** * 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(); $return[] = 'account_management_settings'; $return['sync_name_changes'] = new \IPS\Helpers\Form\Radio( 'login_sync_name_changes', isset( $this->settings['sync_name_changes'] ) ? $this->settings['sync_name_changes'] : 1, FALSE, array( 'options' => array( 1 => 'login_sync_changes_yes', 0 => 'login_sync_changes_no', ) ) ); if ( \IPS\Settings::i()->allow_email_changes == 'normal' ) { $return['sync_email_changes'] = new \IPS\Helpers\Form\Radio( 'login_sync_email_changes', isset( $this->settings['sync_email_changes'] ) ? $this->settings['sync_email_changes'] : 1, FALSE, array( 'options' => array( 1 => 'login_sync_changes_yes', 0 => 'login_sync_changes_no', ) ) ); } if ( \IPS\Settings::i()->allow_password_changes == 'normal' ) { $return['sync_password_changes'] = new \IPS\Helpers\Form\Radio( 'login_sync_password_changes', isset( $this->settings['sync_password_changes'] ) ? $this->settings['sync_password_changes'] : 1, FALSE, array( 'options' => array( 1 => 'login_sync_changes_yes', 0 => 'login_sync_password_changes_no', ) ) ); } return $return; } /** * Can this handler process a password change for a member? * * @return bool */ public function canChangePassword( \IPS\Member $member ) { if ( !isset( $this->settings['sync_password_changes'] ) or $this->settings['sync_password_changes'] ) { // NOTE: This looks up if there is a core_login_links record for the member (i.e. if they have signed in with // your login handler before). return $this->canProcess( $member ); } return FALSE; } /** * Change Email Address * * @param \IPS\Member $member The member * @param string $oldEmail Old Email Address * @param string $newEmail New Email Address * @return void * @throws \IPS\Db\Exception */ public function changeEmail( \IPS\Member $member, $oldEmail, $newEmail ) { if ( !isset( $this->settings['sync_email_changes'] ) or $this->settings['sync_email_changes'] ) { // @todo - actually change the email in the Login Handler database } } /** * Change Password * * @param \IPS\Member $member The member * @param string $newPassword New Password * @return void * @throws \IPS\Db\Exception */ public function changePassword( \IPS\Member $member, $newPassword ) { if ( !isset( $this->settings['sync_password_changes'] ) or $this->settings['sync_password_changes'] ) { // @todo - actually change the password in the Login Handler database } } /** * Change Username * * @param \IPS\Member $member The member * @param string $oldUsername Old Username * @param string $newUsername New Username * @return void * @throws \IPS\Db\Exception */ public function changeUsername( \IPS\Member $member, $oldUsername, $newUsername ) { if ( !isset( $this->settings['sync_name_changes'] ) or $this->settings['sync_name_changes'] ) { // @todo - actually change the username in the Login Handler database } } Checking if Email or Username Is In Use You may want to prevent users using email addresses or usernames which exist in your Login Handler's database even before that user has created their local account. This is especially likely for Username/Password Handlers. To do this you will implement several methods. Here is are the skeletons for the methods you need to implement. They should return a boolean value. /** * Email is in use? * Used when registering or changing an email address to check the new one is available * * @param string $email Email Address * @param \IPS\Member|NULL $eclude Member to exclude * @return bool|NULL Boolean indicates if email is in use (TRUE means is in use and thus not registerable) or NULL if this handler does not support such an API */ public function emailIsInUse( $email, \IPS\Member $exclude=NULL ) { return NULL; } /** * Username is in use? * Used when registering or changing an username to check the new one is available * * @param string $username Username * @param \IPS\Member|NULL $eclude Member to exclude * @return bool|NULL Boolean indicates if username is in use (TRUE means is in use and thus not registerable) or NULL if this handler does not support such an API */ public function usernameIsInUse( $username, \IPS\Member $exclude=NULL ) { return NULL; } Handling Forgotten Passwords If a user uses the "Forgot Password" tool, you may want to redirect them to your site if they enter a email address which has been used, or could be used, by your Login Handler. This is especially likely for Username/Password Handlers. First, you should implement the emailIsInUse() method described above first. Then you will need to implement this method, returning an \IPS\Http\Url object: /** * Forgot Password URL * * @return \IPS\Http\Url|NULL */ public function forgotPasswordUrl() { return NULL; } Showing a Logo on Device Information Page When viewing their Recently Used Devices, Invision Community will indicate what Login Method was used to assist the user in identifying if they performed the log in. You can optionally provide a logo to display alongside the Login Method name on this screen. Simply implement the logoForDeviceInformation() method, returning an \IPS\Http\Url object: /** * Get logo to display in information about logins with this method * Returns NULL for methods where it is not necessary to indicate the method, e..g Standard * * @return \IPS\Http\Url */ public function logoForDeviceInformation() { return NULL; }
-
Syncing Data Between Your Login Handler and the Community
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. Step 1: Implement Methods For Getting Data 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; } Step 2: Show Your Login Handler in the Account Settings 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'; } Step 3: Add Syncing Options 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.
-
Login Handler Settings
You will probably need some settings for the administrator to fill in when they set up a Login Method for your Handler in the AdminCP. Some settings are already provided. You do not need to do anything with them, as Invision Community will handle the values automatically: A name for the login method (the default value will be that which is provided by the getTitle() method). Whether AdminCP logins are enabled Whether new accounts are allowed If you are using a Username/Password handler: whether the user will provide a username or an email address to log in. If you override the acpForm() method, you will need to add this back in unless you have hardcoded a value for authType(). If you are using an OAuth-based handler: settings for client ID, secret, and settings related to profile syncing. There are three methods involved: acpForm() returns an array of form elements controlling what is displayed in the form. You can also return strings which will create headers. You will override this adding your own elements. acpFormSave() performs any modification of the values you want to do before saving. If not provided, the values will be saved automatically. testSettings() tests that the settings are valid. It should return TRUE or throw a LogicException if the values are not valid. You can then access the values of these settings in your login handler code through $this->settings. Here is some sample code which you could add to the sample code provided in Creating a Button Handler which would allow the administrator to change the colour of the button: /** * 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( 'my_login_handler_customization', // NOTE: Create a language string for this 'button_color' => new \IPS\Helpers\Form\Color( 'my_login_handler_button_color', $this->buttonColor() ) // NOTE: Create a language string for this ); } /** * Get the button color * * @return string */ public function buttonColor() { return isset( $this->settings['button_color'] ) ? $this->settings['button_color'] : '#ff3399'; }
-
Creating an OAuth-Based Handler
Note: For OAuth 2.0 based logins, administrators can normally use the "Other OAuth 2.0" option when creating a Login Method and provide more details in settings. However, you might want to implement a complete Login Handler to allow ease of setup or to provide additional options and features. Note: This documentation assumes familiarity with common terms and concepts used by OAuth. To make a Login Handler which uses OAuth, first change the class you have already written to extend one of these classes instead of \IPS\Login\Handler: \IPS\Login\Handler\OAuth2 for OAuth 2.0 (all grant types supported) \IPS\Login\Handler\OAuth1 for OAuth 1.0 (only 3-legged OAuth supported flow) Implementing these classes will automatically add a number of settings to allow the administrator to provide a client ID, client secret, and to control syncing for display names and email addresses/ You will then need to implement a number of methods: For all OAuth versions: authorizationEndpoint() should return the authorization endpoint URL - note that you may want to pass an additional parameter for AdminCP or reauthentication logins to provide additional security in those contexts. authenticatedUserId() should return the user ID of the authenticated user based on the access token passed. Note that for OAuth 2.0 an access token is passed, and for OAuth 1.0 both an access token and access token secret is passed. If supported, additional methods to get additional information about the authenticated user based on the access token passed: authenticatedUserName() should return the display name - you may want to add a setting to control if this is used or what kind of name is used (as is done for most of the built-in OAuth-based login handlers). authenticatedEmail() to return the email address For OAuth 2.0 only: tokenEndpoint() should return the token endpoint URL grantType() should return the grant type to use (in most cases this will be "authorization_code"). Optional: scopesToRequest() should return an array of scopes to be requested. For OAuth 1.0 only: tokenRequestEndpoint() should return the token request endpoint URL accessTokenEndpoint() should return the access token endpoint URL If the grant type you are using is button-based (which will usually be the case) you can also use the methods described in Creating a Button Handler to control the look of the button. For an example of how to implement these methods, see one of the built-in OAuth-based login handlers.
-
Creating a Button Handler
Note: If you are creating an OAuth-based Handler, see Creating an OAuth-Based Handler. Only use this documentation for non-OAuth-based Handlers. To make a login handler where the user clicks a button (that is not OAuth-based) you will add the \IPS\Login\Handler\ButtonHandler trait and implement the methods it requires: buttonColor(), buttonIcon(), buttonText() and buttonClass() control how the button looks. authenticateButton() handles what happens when the button is clicked. You are responsible for whatever mechanism you want to use to then authenticate the user (this will almost always involve redirecting to an external service). You will then return an \IPS\Member object (including creating the account if there isn't one already and ensuring reauthentication happens if there is already an account but the user hasn't logged in with your handler before). This is a basic skeleton of a button based Login Handler. This code will process a login using a hardcoded username and email address. Obviously a real login handler wouldn't hardcode this data but would look it up in a database or through an external service. class _MyLoginHandler extends \IPS\Login\Handler { use \IPS\Login\Handler\ButtonHandler; /** * @brief Can we have multiple instances of this handler? */ public static $allowMultiple = FALSE; /** * Get title * * @return string */ public static function getTitle() { return 'my_cusom_login_handler'; // Create a langauge string for this } /** * Authenticate * * @param \IPS\Login $login The login object * @return \IPS\Member * @throws \IPS\Login\Exception */ public function authenticateButton( \IPS\Login $login ) { /* If we haven't been redirected back, redirect the user to external site */ if ( !isset( \IPS\Request::i()->success ) ) { $urlToRedirectBackTo = $destination = $login->url->setQueryString( array( '_processLogin' => $this->id, 'csrfKey' => \IPS\Session::i()->csrfKey, 'ref' => \IPS\Request::i()->ref, ) ); // NOTE: A real example would actually redirect to an external site which would authenticate // the user and then redirect back to $urlToRedirectBackTo. But since this is just an example // we'll redirect straight to it \IPS\Output::i()->redirect( $urlToRedirectBackTo->setQueryString( 'success', 1 ) ); } /* Get the user data. NOTE: Obviously a real example would look this up in some kind of database or external service or it would have been returned as the user was redirected back */ $userId = 1; // NOTE: This would be set to some kind of identifier for the user within that service. It doesn't have to be numeric, but does have to be unique. $name = 'example'; // NOTE: We will use this later to create an account if it doesn't exist. If your login handler doesn't store display names, set this to NULL (and the user will be asked to provide one) $email = 'example@example.com'; // NOTE: We will use this later to create an account if it doesn't exist. If your login handler doesn't store display names, set this to NULL (and the user will be asked to provide one) /* Find their local account if they have already logged in using this method in the past */ try { $link = \IPS\Db::i()->select( '*', 'core_login_links', array( 'token_login_method=? AND token_identifier=?', $this->id, $userId ) )->first(); $member = \IPS\Member::load( $link['token_member'] ); /* If the user never finished the linking process, or the account has been deleted, discard this access token */ if ( !$link['token_linked'] or !$member->member_id ) { \IPS\Db::i()->delete( 'core_login_links', array( 'token_login_method=? AND token_member=?', $this->id, $link['token_member'] ) ); throw new \UnderflowException; } /* ... and return the member object */ return $member; } catch ( \UnderflowException $e ) { } /* Otherwise, we need to either create one or link it to an existing one */ try { /* If the user is setting this up in the User CP, they are already logged in. Ask them to reauthenticate to link those accounts */ if ( $login->type === \IPS\Login::LOGIN_UCP ) { $exception = new \IPS\Login\Exception( 'generic_error', \IPS\Login\Exception::MERGE_SOCIAL_ACCOUNT ); $exception->handler = $this; $exception->member = $login->reauthenticateAs; throw $exception; } /* Try to create one. NOTE: Invision Community will automatically throw an exception which we catch below if $email matches an existing account, if registration is disabled, or if Spam Defense blocks the account creation */ $member = $this->createAccount( $name, $email ); /* If we're still here, a new account was created. Store something in core_login_links so that the next time this user logs in, we know they've used this method before */ \IPS\Db::i()->insert( 'core_login_links', array( 'token_login_method' => $this->id, 'token_member' => $member->member_id, 'token_identifier' => $userId, 'token_linked' => 1, ) ); /* Log something in their history so we know that this login handler created their account */ $member->logHistory( 'core', 'social_account', array( 'service' => static::getTitle(), 'handler' => $this->id, 'account_id' => $userId, 'account_name' => $name, 'linked' => TRUE, 'registered' => TRUE ) ); /* Set up syncing options. NOTE: See later steps of the documentation for more details - it is fine to just copy and paste this code */ if ( $syncOptions = $this->syncOptions( $member, TRUE ) ) { $profileSync = array(); foreach ( $syncOptions as $option ) { $profileSync[ $option ] = array( 'handler' => $this->id, 'ref' => NULL, 'error' => NULL ); } $member->profilesync = $profileSync; $member->save(); } return $member; } catch ( \IPS\Login\Exception $exception ) { /* If the account creation was rejected because there is already an account with a matching email address make a note of it in core_login_links so that after the user reauthenticates they can be set as being allowed to use this login handler in future */ if ( $exception->getCode() === \IPS\Login\Exception::MERGE_SOCIAL_ACCOUNT ) { \IPS\Db::i()->insert( 'core_login_links', array( 'token_login_method' => $this->id, 'token_member' => $exception->member->member_id, 'token_identifier' => $userId, 'token_linked' => 0, ) ); } throw $exception; } } /** * Get the button color * * @return string */ public function buttonColor() { return '#ff3399'; } /** * Get the button icon * * @return string */ public function buttonIcon() { return 'sign-in'; // A fontawesome icon } /** * Get button text * * @return string */ public function buttonText() { return 'sign_in_with_my_cusom_login_handler'; // Create a language string for this } /** * Get button CSS class * * @return string */ public function buttonClass() { return ''; } }
-
Creating a Username/Password Handler
To make a login handler where the user enters a username or email address and password you will add the \IPS\Login\Handler\UsernamePasswordHandler trait to your Login Handler class and implement the two methods it requires: authenticateUsernamePassword() is called when a user enters a username or email address and password into the login form. You need to verify if it is valid in your login handler and return an \IPS\Member object if it is (including creating the account if there isn't one already and ensuring reauthentication happens if there is already an account but the user hasn't logged in with your handler before) or throw an \IPS\Login\Exception exception if not. authenticatePasswordForMember() is called when a user is doing something sensitive and is being asked to reauthenticate by entering their password from your login handler again to confirm. This is a basic skeleton of a username/password based Login Handler. This code will process a login for the a hardcoded username/email address and password. Obviously a real login handler wouldn't hardcode this data but would look it up in a database or through an external service. class _MyLoginHandler extends \IPS\Login\Handler { use \IPS\Login\Handler\UsernamePasswordHandler; /** * @brief Can we have multiple instances of this handler? */ public static $allowMultiple = FALSE; /** * Get title * * @return string */ public static function getTitle() { return 'my_cusom_login_handler'; // Create a langauge string for this } /** * Authenticate * * @param \IPS\Login $login The login object * @param string $usernameOrEmail The username or email address provided by the user * @param string $password The plaintext password provided by the user * @return \IPS\Member * @throws \IPS\Login\Exception */ public function authenticateUsernamePassword( \IPS\Login $login, $usernameOrEmail, $password ) { /* Is this a user we can process? NOTE: Obviously a real login handler would look up $usernameOrEmail in some kind of database or external service */ $authType = $this->authType(); // NOTE: The UsernamePasswordHandler trait has automatically provided a setting in this login method which allows the administrator to choose if the user will enter a username, email address, or either if ( ( $authType & \IPS\Login::AUTH_TYPE_USERNAME and $usernameOrEmail === 'example' ) or ( $authType & \IPS\Login::AUTH_TYPE_EMAIL and $usernameOrEmail === 'example@example.com' ) ) { $userId = 1; // NOTE: This would be set to some kind of identifier for the user within that service. It doesn't have to be numeric, but does have to be unique. $name = 'example'; // NOTE: We will use this later to create an account if it doesn't exist. If your login handler doesn't store display names, set this to NULL (and the user will be asked to provide one) $email = 'example@example.com'; // NOTE: We will use this later to create an account if it doesn't exist. If your login handler doesn't store display names, set this to NULL (and the user will be asked to provide one) } else { switch ( $this->authType() ) { case \IPS\Login::AUTH_TYPE_USERNAME + \IPS\Login::AUTH_TYPE_EMAIL: $type = 'username_or_email'; break; case \IPS\Login::AUTH_TYPE_USERNAME: $type = 'username'; break; case \IPS\Login::AUTH_TYPE_EMAIL: $type = 'email_address'; break; } throw new \IPS\Login\Exception( \IPS\Member::loggedIn()->language()->addToStack( 'login_err_no_account', FALSE, array( 'sprintf' => array( \IPS\Member::loggedIn()->language()->addToStack( $type ) ) ) ), \IPS\Login\Exception::NO_ACCOUNT ); } /* Find their local account if they have already logged in using this method in the past */ $member = NULL; try { $link = \IPS\Db::i()->select( '*', 'core_login_links', array( 'token_login_method=? AND token_identifier=?', $this->id, $userId ) )->first(); $member = \IPS\Member::load( $link['token_member'] ); /* If the user never finished the linking process, or the account has been deleted, discard this access token */ if ( !$link['token_linked'] or !$member->member_id ) { \IPS\Db::i()->delete( 'core_login_links', array( 'token_login_method=? AND token_member=?', $this->id, $link['token_member'] ) ); $member = NULL; } } catch ( \UnderflowException $e ) { } /* Is the password valid? NOTE: Obviously a real login handler would actually verify the password is correct for the member. This step is done AFTER looking up their local account so that their account can be locked if they provide multiple wrong passwords */ if ( $password !== 'example' ) { throw new \IPS\Login\Exception( 'login_err_bad_password', \IPS\Login\Exception::BAD_PASSWORD, NULL, $member ); } /* If we have a local account, go ahead and return it */ if ( $member ) { return $member; } /* Otherwise, we need to either create one or link it to an existing one */ try { /* If the user is setting this up in the User CP, they are already logged in. Ask them to reauthenticate to link those accounts */ if ( $login->type === \IPS\Login::LOGIN_UCP ) { $exception = new \IPS\Login\Exception( 'generic_error', \IPS\Login\Exception::MERGE_SOCIAL_ACCOUNT ); $exception->handler = $this; $exception->member = $login->reauthenticateAs; throw $exception; } /* Try to create one. NOTE: Invision Community will automatically throw an exception which we catch below if $email matches an existing account, if registration is disabled, or if Spam Defense blocks the account creation */ $member = $this->createAccount( $name, $email ); /* If we're still here, a new account was created. Store something in core_login_links so that the next time this user logs in, we know they've used this method before */ \IPS\Db::i()->insert( 'core_login_links', array( 'token_login_method' => $this->id, 'token_member' => $member->member_id, 'token_identifier' => $userId, 'token_linked' => 1, ) ); /* Log something in their history so we know that this login handler created their account */ $member->logHistory( 'core', 'social_account', array( 'service' => static::getTitle(), 'handler' => $this->id, 'account_id' => $userId, 'account_name' => $name, 'linked' => TRUE, 'registered' => TRUE ) ); /* Set up syncing options. NOTE: See later steps of the documentation for more details - it is fine to just copy and paste this code */ if ( $syncOptions = $this->syncOptions( $member, TRUE ) ) { $profileSync = array(); foreach ( $syncOptions as $option ) { $profileSync[ $option ] = array( 'handler' => $this->id, 'ref' => NULL, 'error' => NULL ); } $member->profilesync = $profileSync; $member->save(); } return $member; } catch ( \IPS\Login\Exception $exception ) { /* If the account creation was rejected because there is already an account with a matching email address make a note of it in core_login_links so that after the user reauthenticates they can be set as being allowed to use this login handler in future */ if ( $exception->getCode() === \IPS\Login\Exception::MERGE_SOCIAL_ACCOUNT ) { \IPS\Db::i()->insert( 'core_login_links', array( 'token_login_method' => $this->id, 'token_member' => $exception->member->member_id, 'token_identifier' => $userId, 'token_linked' => 0, ) ); } throw $exception; } } /** * Authenticate * * @param \IPS\Member $member The member * @param string $password The plaintext password provided by the user * @return bool */ public function authenticatePasswordForMember( \IPS\Member $member, $password ) { // NOTE: Obviously a real login handler would actually verify the password is correct for the member. You may want to write a separate method // which can be called by both this method and authenticateUsernamePassword() so that the code isn't duplicated return $member->email === 'example@example.com' and $password === 'example'; } } Note: The \IPS\Login\Handler\UsernamePasswordHandler trait will automatically add a setting to allow the administrator to choose if the user will provide a username or email address to sign in, and this value is returned to you by the authType() method. If this does not apply to your login handler, you will need to override it. For example, if your login handler can only accept email addresses, you will need to add this code within your class: /** * ACP Settings Form * * @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(); // Remove the setting set by the UsernamePasswordHandler trait } /** * Get auth type * * @return int */ public function authType() { return \IPS\Login::AUTH_TYPE_EMAIL; }
-
Introduction to Login Methods
Note: This documentation applies to Invision Community 4.3 and higher only. For older versions, see 4.2 and below: Login Handlers. Basic functionality will be backwards compatible. Introduction Invision Community allows users to log in using a number of different Login Handlers. For example, in addition to the "Standard" login handler where the user enters their email address or display name and password that they used to register, users can also log in with their Facebook account, or using credentials provided by an external MySQL database. A Login Handler which has been set up is called a Login Method. Some Login Handlers can be used more than once (creating multiple Methods) and some cannot. For example, you can have multiple external MySQL databases but there is only one Facebook. Types and Class Structure All Login Handlers extend the abstract \IPS\Login\Handler class. Login Handlers can work in two ways: Username/Password Handlers allow the user to enter either a display name or an email address and a password (for example: the Standard handler, external MySQL database, LDAP directory). These will use the \IPS\Login\Handler\UsernamePasswordHandler trait. Button Handlers work by the user clicking a button which redirects them to an external site to authenticate (for example: Facebook, Twitter, etc.). These will use the \IPS\Login\Handler\ButtonHandler trait. Usually a Login Handler will only use one of these traits, however it can use both and change its behaviour based on how the Method is configured. There are also base classes for OAuth-based handlers (one for OAuth 1.0 and one for OAuth 2.0) which extend \IPS\Login\Handler to provide some shared code between Login Handlers which use OAuth. This is the class structure of all the Login Handlers provided in Invision Community by default: abstract \IPS\Login\Handler abstract \IPS\Login\Handler\OAuth2 uses \IPS\Login\Handler\UsernamePasswordHandler and \IPS\Login\Handler\ButtonHandler \IPS\Login\Handler\OAuth2\Facebook \IPS\Login\Handler\OAuth2\Google \IPS\Login\Handler\OAuth2\LinkedIn \IPS\Login\Handler\OAuth2\Microsoft \IPS\Login\Handler\OAuth2\Invision \IPS\Login\Handler\OAuth2\Wordpress \IPS\Login\Handler\OAuth2\Custom abstract \IPS\Login\Handler\OAuth1 \IPS\Login\Handler\OAuth1\Twitter \IPS\Login\Handler\ExternalDatabase \IPS\Login\Handler\LDAP Understanding the Flow When a user uses a Login Method they may or may not already have an account in the community's database. If the authentication is successful, one of three things will happen: If the user has logged in with that Login Method before (for example, they are logging in with Facebook and have done that on that community before), the Login Handler will return the \IPS\Member object for the member, and they will be logged straight in. If the user has not logged in with that Login Method before, the Login Handler will attempt to create an account, optionally providing (if it knows them) a display name and an email address. Invision Community will then either: If there is already an account in the database with the same email address as what the Login Handler provided, the user will be prompted to link the accounts by authenticating with another Login Method they have used before (usually this means entering their password). If there is not already an account with the same email address, or the Login Handler did not provide an email address, an account will be created (unless the administrator has configured the Login Method to not allow new accounts to be created) and the user will either be logged straight in, be prompted for more information to create their account, or be required to validate, depending on what information the Login Handler provides and what settings the administrator has chosen. Getting Started To get started with a basic skeleton Login Handler: With Developer Mode enabled, create an application you will use for your Login Handler from the Applications page in the AdminCP. Create a class for your login handler using the skeleton below. You will need to set an appropriate value for $allowMultiple (see above more information) and return the key for the language string you just created in the getTitle() method. Create a code hook on the \IPS\Login\Handler class to add the name of the class you just created to the value returned by \IPS\Login\Handler::handlerClasses(). See below for sample code. Login Handler Skeleton <?php namespace IPS\mycustomloginhandler; /* To prevent PHP errors (extending class does not exist) revealing path */ if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) ) { header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' ); exit; } class _MyCustomLoginHandler extends \IPS\Login\Handler { /** * @brief Can we have multiple instances of this handler? */ public static $allowMultiple = FALSE; /** * Get title * * @return string */ public static function getTitle() { return 'my_cusom_login_handler'; // Create a langauge string for this } } Sample Code Hook //<?php /* To prevent PHP errors (extending class does not exist) revealing path */ if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) ) { exit; } abstract class mycustomloginhandler_hook_loginHandlerHook extends _HOOK_CLASS_ { public static function handlerClasses() { $return = parent::handlerClasses(); $return[] = 'IPS\mycustomloginhandler\MyLoginHandler'; return $return; } } Next Steps Follow the instructions specific to the type of Login Handler you are creating, creating any settings you need as you do so: Username/Password Handlers Button Handlers OAuth-Based Handlers Follow instructions for adding profile syncing Follow instructions for adding any advanced features you need
-
4.2.8
This is a maintenance to release to address security issues. As we prepare for our next large release, version 4.3, we do large security audits. We decided to apply those security enhancements to the 4.2 series so you can get them now while we begin public betas of 4.3. We would like to thank @newbie LAC for his assistance.
-
Wordpress
Introduction You can link Invision Community with a Wordpress site to allow users to sign into one community using their credentials from another. First you need to decide which direction the login should happen: Allow users to log into your Invision Community with their account on a Wordpress site (Wordpress as the Server). Allow users to log into your Wordpress site with their account on an Invision Community (Invision Community as the Server). Only follow the instructions below for which direction you want. SSL is required on both Invision and word press. Option A: Wordpress as the Server 1. Setup the Wordpress Plugin First you will need to install the OAuth Server plugin for Wordpress to enable OAuth functionality on your Wordpress site. To do this, from your Wordpress Dashboard, go to Plugins > Add New, search for OAuth server. Find the miniOrange OAuth 2.0 Server/Provider by miniOrange (there are several plugins with similar names available) and click Install, and once it has installed, click Activate. Installing Wordpress Plugin After it has installed, go to the new miniOrange OAuth Server section and follow the instructions to complete the registration process. 2. Create the Client On your community, go to AdminCP -> System -> Login & Registration, click Create New to setup a new login method and choose Wordpress. Meanwhile, from your Wordpress Dashboard, go to miniOrange OAuth Server -> Add Client. Enter a name (such as the name of your community) and for the Authorized Redirect URI field enter the value shown in your AdminCP, which will be your community's URL ending with "/oauth/callback/" on the end. You should now see a screen with a Client ID and Client Secret. Wordpress Client Details 3: Set up Invision Community Back in your AdminCP, fill in the form, using the Client ID and Client Secret that you just obtained. You will also need to configure the text, color and logo to use for the button that users will click on to log in. Enterring details in Invision Community You should now be able to log into Invision Community through Wordpress! Option B: Invision Community as the Server 1. Configure Invision Community On your Invision Community, go to AdminCP -> System -> REST & OAuth and click Create New and fill out the form selecting Wordpress for Client Type. You will then see a screen with details you will need to enter into Wordpress. Client Details for Wordpress 2. Configure Wordpress Initial Setup First, from your Wordpress Dashboard, go to Settings > Permalinks. You must have the Post name option selected. If you haven't, change this and save the form. Installing the Plugin Next you will need to install the oAuth Login plugin for Wordpress to enable OAuth functionality on your Wordpress site. To do this, from your Wordpress Dashboard, go to Plugins > Add New, search for OAuth login. Find the OAuth Single Sign On - SSO (OAuth client) by miniOrange (there are several plugins with similar names available) and click Install, and once it has installed, click Activate. Installing Wordpress Plugin After it has installed, go to the new miniOrange OAuth section and follow the instructions to complete the registration process. Creating the Client You can now enter the client details in Wordpress. Enter all of the details exactly as they are shown in your Invision Community AdminCP. Please note: we have noticed that if you choose the "Invision Community" option, some versions of the plugin pre-enter the value for Get User Info Endpoint as https://<your-domain>/oauth/core/me but the correct value is either https://<your-domain/api/core/me or https://<your-domain/api/index.php?/core/me - make sure you enter the correct value shown in your AdminCP. Enterring details in Wordpress You then need to add the sign in button in your desired location. For details on how to do this, go to the Sign In Settings tab for information. You can also configure how the button appears under the Customizations tab. Once this is done, you will see the sign in button on your Wordpress site. Sign In Button on an Example Wordpress Site
-
Twitter
This integration requires access to the legacy Twitter 1.0 API. New developer accounts do not have access to this. We are working on migrating to Twitter 2.0 API, this guide will be updated when this has been completed. You can allow your members to sign in to your community using their Twitter account. To start the process, go to AdminCP -> System -> Login & Registration, click Create New to setup a new login method and choose Twitter. You will now be prompted to fill in some details which you'll need to acquire by creating an "app" with Twitter. Getting API Access Before you are able to create a twitter application, you first need to get access to the twitter API. To do this, visit https://apps.twitter.com and sign in with your twitter account. If you click on create app, it will ask you to first apply for access to the API. Click apply first of all. Once you have done this, you will be presented with the following screen, which you need to select "Doing something else" from the options. Choose Reason Once you have selected the reason, you will be asked to confirm your details. Unless you have any pressing reason to do so, you should click next at the bottom of the page. API developer Details You will now be asked a series of questions related to the use of the API on your site. Here we will be filling in the "In your words" section at the top, and the "Do you plan to display tweets............outside of twitter?" section at the bottom. All other items should have "No" selected. For the first item of "In your words" you need to enter something like the following screenshot. In your words For the last section, enter something like the below Data outside twitter Click next to complete the application. You will be asked to confirm your email address. Ensure you do this before continuing to the next section of our guide below. Creating a Twitter App 1. Go to the Twitter Application Management site and sign in with your Twitter account. 2. Click Create App and fill out the form. For the Website field, enter the URL to your community. For the Callback URLs field enter the value shown in your AdminCP, which will be your community's URL ending with "/oauth/callback/" on the end. 3. Go to the Settings tab and make sure the Allow this application to be used to Sign in with Twitter checkbox is checked. Then also provide a value for the Privacy Policy URL and the Terms of Service URL (you can create pages for these in your AdminCP under System -> Terms & Privacy Policy if you haven't already). Make sure you click Update Settings to save. Twitter Settings tab 4. Go to the Permissions tab and click Edit. Ensure that Access is set to Read and Write, and then check the Request email addresses from users checkbox (it will not be checked by default). Make sure you click Update Settings to save. Twitter Permissions tab 5. Go to the Keys and tokens tab to obtain the values you need to enter into the AdminCP. Twitter Keys and Access Tokens tab Back in your AdminCP, fill in the form, using the Consumer Key and Consumer Secret that is shown on the Keys and Access Tokens tab from Twitter. Entering details in Invision Community
-
Microsoft
You can allow your members to sign in to your community using their Microsoft account. To start the process, go to AdminCP -> System -> Login & Registration, click Create New to setup a new login method and choose Microsoft. You will now be prompted to fill in some details which you'll need to acquire by creating an "app" with Microsoft. Creating a Microsoft App 1. Go to the Microsoft Azure App registrations page and sign in with your Microsoft account. 2. Click New Registration and fill in a name (just use the name of your community). 3. Unless you have a specific need to change it, under "Supported account types" leave "Accounts in any organizational directory and personal Microsoft accounts (e.g. Skype, Xbox, Outlook.com)" selected. 4. Under the field labelled Redirect URI, leave the dropdown selected as "Web" and then enter the value shown in your AdminCP into the text field. You must then click Register at the bottom to save this. App Details Page 5. Copy the Application (client) ID shown on this page, along with password, into the AdminCP under Application Id. ACP screen 6. In the Azure App registrations page for the application you just created, click Certificates & secrets in the left hand menu. Click the New client secret button. Set the secret to Never expire, provide an optional description, and click the Add button. Creating client secret 7. Copy the value displayed on the Certificates & secrets page after adding a new client secret and paste it in to the AdminCP as the Password. 8. Finish creating the new Login Method and verify you are able to successfully log in to your community using a valid Microsoft account.
-
LinkedIn
You can allow your members to sign in to your community using their LinkedIn account. To start the process, go to AdminCP -> System -> Login & Registration, click Create New to setup a new login method and choose LinkedIn. You will now be prompted to fill in some details which you'll need to acquire by creating an "application" with LinkedIn. Creating an application with LinkedIn 1. Go to the LinkedIn Developers page and sign in with your LinkedIn account. 2. Click Create App and fill in the form. 3. You will now see your application details. Click on the Auth tab, and then under the OAuth 2.0 settings section, you will see a field labelled Redirect URLs. Click the pencil icon to edit this setting, and add the value shown in your AdminCP. You must click Update at the bottom to save this. LinkedIn Application Page 4. On the 'Products' tab, select "Sign In with LinkedIn", accept the terms, and click to add this. (You will need to await approval for this) Products Adding 5.Back in your AdminCP, fill in the form, using the Client ID and Client Secret that are shown on the LinkedIn application Auth screen. Entering details in Invision Community
-
Facebook
You can allow your members to sign in to your community using their Facebook account. In order to use facebook, your site must be on a secure https domain. You can see more information on how to switch to https after obtaining an SSL certicate from your hosting company in the following guide Basic Facebook Login To start the process, go to AdminCP -> System -> Login & Registration, click Create New to setup a new login method and choose Facebook. You will now be prompted to fill in some details which you'll need to acquire by creating an "app" with Facebook. Creating a Facebook App 1. Go to the Meta for Developers site and sign in with your Facebook account. 2. If you have not already registered with Facebook for Developers, click Register Now and follow the instructions to register 3. Click on My Apps in the top right, then select 'Create App Click to create a new app 4.Select "None" when asked for an app type App Type Selection 5. Add a name for the login and an email address, then click create app Facebook App Dashboard 6. Select "Facebook Login" from the product list 6. Select "www" and from the quickstart list 7. Add your site URL, then click continue 8. The next 3 screens can be ignored. Click on next on each until you get to the last section, then go to the next step. 9. Click on "basic"You will then be taken to the basic settings page for the new application. Here you need to add your privacy policy, and your terms of service, and logo, then save. Basic Settings 7. Go to the Products > Facebook Login > Settings tab from the left sidebar. In the Valid OAuth redirect URIs field enter the value shown in your AdminCP, which will be your community's URL ending with "/oauth/callback/" on the end. You must click Save Changes at the bottom to save this. Callback URL Set Up Invision Community Go to Settings > Basic from the left sidebar. You will see your App ID and a button to reveal the App Secret. Back in your ACP, fill in the form, using these details. App ID and Secret Now go to the front-end of your community, log out, and log in with the Sign in with Facebook button. At this point, only your own Facebook account, which you used to create the client, will work. If it works, you can continue to the next step. Entering details in Invision Community Once you have done the above, you will be able to use facebook to login to your community. Go Live IMPORTANT: - Do not go live with your product, until you have completed any application permission reviews required by facebook for Statuses, and/or social media promotion. In the top-right you will see an On/Off switch next to Status: In Development. Click the on/off switch and confirm. The switch should now indicate On and you should see Status: Live. Live Facebook App Note if you wish to enable status imports, you will need to add more permissions and get your application permissions reviewed by facebook. You should then continue below. If you only want sign in ability, you are finished at this point Optional: Enabling Status Imports Before you begin, please note, we can give advice on how to fill this in, however some details may vary based on your own uses. You will also need business verification in order to set this up. This is per facebooks guidelines. When setting up Facebook Login in your AdminCP you will have seen a Allow Status Imports? setting which mentions requiring additional setup. Using this feature means gaining access to restricted Facebook APIs, which, before you can do, you need to get approval from Facebook. 1. To get started, enable the Allow Status Imports? setting from AdminCP -> System -> Login & Registration -> Facebook. 2. On the Facebook for Developers site, go to App Review -> Permissions and Features from the left-side menu. Use the search box at the top to search for user_posts 3. select the "Request" option then select continue 4. You will now see the review, for each of the items you have chosen, there is an Add Details link. Request a review Click Add Details and fill in the form. An example of what to provide can be seen below. In addition to these information, you will need to provide a screencast of you demonstrating that process of signing in with Facebook and doing the thing you are asking Facebook for permission for (importing a status or automatically sharing to Facebook). Make sure your video shows you doing exactly the steps in the instructions you provided otherwise your review will likely be rejected. You do this by using your own account and switching your facebook application to developer mode This video needs to show your community, so we cannot provide a video for you to send to Facebook - you must record and submit it yourself. For Tell us how you're using this permission or feature enter something like the following: For Demontrate how your selected platforms will use this permission or feature? choose Web. Under this option, you will need to stated how this is used by the user. Enter the following information to show how a user logs in (please modify if your options are in different places due to theming or 3rd party items) You will then additionally need to do a screencast to show on video how the system is used. This can be uploaded to the same form. Click on save once complete 6. Click Submit For Review to submit the review and confirm the submission. You will be advised how long it will take you for Facebook to approve the submission, and should receive an email once it is complete
-
Google
You can allow your members to sign in to your community using their Google account. Applies to self-hosted customers only Important - Google requires that sites using their login API, are using a secure connection. If your site is not using SSL (HTTPS), please contact your hosting company to obtain a valid SSL certicate, then follow this guide to switch to https. https://invisioncommunity.com/4guides/advanced-options/configuration-options/using-ssl-https-r273/ To start the process, go to AdminCP -> System -> Login & Registration, click Create New to setup a new login method and choose Google. You will now be prompted to fill in some details which you'll need to acquire by creating a "project" with Google. Creating a project 1. Go to the Google APIs Console and sign in with your Google account. You may have created a project if you have enabled any other Google APIs. If you haven't, click Select a Project in the top-left corner and then create a new project. No Project Selected Project Selected 2. Go to Library and search for Google Identity. Click on "Cloud Identity" and then click on 'enable' Adding Credentials 3. Go to Credentials -> OAuth consent screen and fill out the form. 4. Go to Credentials -> Credentials. Click the Create credentials button and choose OAuth client ID from the dropdown. Create Credentials Button Choose Web application as the type, and for the Authorized redirect URIs field, enter the value shown in your AdminCP. Credentials Form Adding to the ACP You will see a screen displaying a client ID and client secret. Issued Credentials Back in your AdminCP, fill in the form, using the Client ID and Client Secret that you just obtained. Entering details in Invision Community Updating from google+ Updating from the google+ API due to the replacing of these APIs by google can be done in the following manner 1. Go to the Google APIs Console and sign in with your Google account and select your google project. 2. Select to remove the google+ API 3. Go to Library and search for Google Identity. Click on "Cloud Identity" and then click on 'enable' No further steps are required to switch to the new API as you already have credentials created from the previous setup.
-
Connect Two Invision Communities
You can link two (or more) Invision Community installations together to allow users to sign into one community using their credentials from another. All communities that you want to link will need to be running Invision Community 4.3 or higher. Server and Client You will need to decide which community is the "Server" (sometimes called the "master") which is the one whose credentials will be used for the login. All the other communities will be the "Clients" (sometimes called the "slaves"). Step One: Set up the Server On the community which will be the Server, go to AdminCP -> System -> REST & OAuth, and click the "Create New" button to create a new OAuth client. For the client type, choose "Another Invision Community". Setting up the Server After saving this form, you will see a Client Identifier and Client Secret. You will use these two values to fill out the form on the Client community. Getting the Client Identifier and Client Secret Step Two: Set up the Client Now on the community which will be the Client(s) go to AdminCP -> System -> Login & Registration, click "Create New" and choose the "Another Invision Community". You will need the Client Identifier and Client Secret from Step One. Setting up the Client Once you have saved this form, set up is complete and should be able to log in! Need more communities? If you are setting up more than two communities, run through this process for each of the Client communities.
-
4.3: AdminCP Member Profiles
Viewing and editing a member is probably one of the most frequently used features of the AdminCP. With the design unchanged for many years, and the tabbed interface starting to grow unwieldy, it was due for some love. We have not only dramatically improved the design but added many new features. New AdminCP Member Page Let's look at some of the improvements: Easy Toggle between Member and Customer View If you have Commerce installed, you can now toggle between "Member View" (which shows the screen above) and "Customer View" (which shows the current customer page in Commerce with the user's purchases, invoices, etc.). This makes it much easier to view all of a member's information in one place. If you don't have Commerce installed, the top tab bar will not show. Basic Information The pane in the top-left shows the member's basic information like name, email address and photos. You can now reposition a member's cover photo and crop the profile photo (functions previously not available in the AdminCP). To change the display name or email address, you just click and hold on the information and a textbox appears. The buttons below allow you to merge, delete, sign in as, and edit the preferences or password for the member. Basic Member Information Pane In addition, this pane lists any social networks the user is logged in with. It shows you the member's profile photo and profile name on that network (for example in this screenshot, it is showing my Facebook profile's photo and name) and for many networks you can click on this to be taken directly to their Facebook/Twitter/etc profile. You can also edit the syncing options for the method and unlink the accounts, features which weren't available previously. If you have Commerce installed, there is also an indicator if the user has an active subscription. A member with an active subscription Alerts If a member is validating, banned, flagged as a spammer, or locked, a large banner will display drawing your attention to this. For validating and banned, it will explain exactly what the status is (for example, if they haven't responded to the validation email yet versus they are awaiting admin approval, or if they have been banned manually versus are in a group without permission to access anything). A member that has been locked Other possible alerts Locations & Devices This pane shows you, on a map, all of the locations the user has been when using the community (based on their IP address) as well as the IP address they used to register and most recently. IP Address Locations While the devices tab shows the most recently used devices. Recently Used Devices Content Statistics Right in the middle of the profile you can see some statistics about the member's activity. This includes: A sparkline graph of their recent content. Their content count and reputation count (with tools to manually change or rebuild). A breakdown of the amount of content they have made across all applications. A visual indication of how much of their messenger and attachment storage they have used. If Gallery and Downloads are installed, the existing statistics overview provided by these apps are also available here. Content Statistics Warnings & Restrictions This block shows recent warnings on the account, and also highlights if any restrictions (i.e. content moderation, restricted from posting, or application-level restrictions) are being applied, which previously was difficult to see at a glance. Warnings & Restrictions Block for an account which has content moderation in effect Account Activity On the right is a pane which shows all of the latest account activity. While this was available in previous versions (called "Member History") we have made some significant improvements: The number of things that get logged has been significantly expanded. We now log photo changes, group changes, when a new device is used to login, if an account is locked (by failed logins or failed two factor authentication attempts) or unlocked, password/email/display name changes, when a user links or unlinks a social network login method, initial registration and validation, merges, being flagged/unflagged as a spammer, receiving/acknowledging/revoking a warning, restrictions being applied, two factor authentication being enabled/disabled/changed, an OAuth token being issued if Invision Community is being used as an OAuth Server, enabling/disabling receiving bulk mails, and accepting the privacy policy / terms and conditions, as well as all of the Commerce-related information that is already logged. Much more information is now shown such as who made the change (i.e. an admin, the user themselves, or if it was changed by the REST API or syncing with a social network) and how the change was made (for example, for a password change - if the user used the "Forgot Password" tool or changed it in their Account Settings) and what the data was before and after. This includes being aware of if the change was made by an admin after using the "Sign in as User" tool. You can now filter what information you are seeing to quickly find what you are looking for. Recent Account Activity Extensibility The new profile has been designed with extensibility in mind. Third party developers can easily add new blocks our even entire new tabs. Any apps/plugins which are currently adding a tab to the "Edit Member" form will retain backwards compatibility with their tab continuing to appear when clicking the "Edit Preferences" button in the basic account information pane.
-
4.3: Videos
Videos are everywhere. We shoot them on our smart phones, share them to social media, messengers and more. Up until now, the only way to share a video to Invision Community was to use a service like YouTube or Vimeo. If you uploaded a video file it would be treated like an attachment, and if the user clicked the link it would download it to their computer. In Invision Community 4.3 we've improved this. Now if you upload a video file (mp4/3gp/mov/ogg/ogv/mpg/mpeg/flv/webm/wmv/avi/m4v), it will embed similarly to an image. Uploading a video When viewing an uploaded video, if it is in a format that the user's browser and platform natively supports, it will show an embedded player. This will have all of the features supported by the operating system - for example, almost all browsers support fullscreen, and Safari supports Airplay and picture in picture. An uploaded video If the video is in a format not supported, it displays exactly as it does now - as a download link. An uploaded video in a browser without playback support for that format
-
4.3: Paid club memberships, and other club improvements
We released news of Clubs just under a year ago for Invision Community 4.2 and it has been the best received feature to date. Clubs opens up new ways to run your community by allowing members to create sub-communities away from the central forum area. Since the feature was released, we've collated an immense amount of feedback on the feature. Here's what we're improving for Invision Community 4.3. Paid Club Memberships If you have Commerce installed on your community, 4.3 adds the ability for members to create paid clubs. Users wishing to join the club will be required to pay a membership fee (which can be one off or recurring) which will be paid to the club owner, minus any commission you want to keep for the site. You can choose which groups can create paid clubs. Paid Club Settings If enabled, the club directory will show the price for membership in each club. Club directory with paid clubs The process for joining works a little differently depending on the type of club... For open clubs, the user will immediately be prompted to pay the joining fee. Once they have paid, they are added to the club as normal. For closed clubs, the user will need to request to join as normal. Once they have been accepted to join the club, they will then be able to pay the membership fee, after which they'll be added to the club. For private and read-only (a new type in 4.3, which we'll talk about below) users have to be invited to join the club Public clubs have no membership, and so cannot be paid. Joining a paid club (a closed club in this screenshot) Paid club after request to join has been accepted Paying for club membership Club leaders can also waive the membership fee, allowing certain users to join the club for free. Waiving fee when approving request to join Waiving fee when inviting members Waiving renewal fees on an existing member If a member fails to pay their renewal charge, they are moved into an "expired" state. The club leaders can see the status and renewal date for all members, and use the filter tools to just see active or expired members. Club members management Paying out membership fees works just as it does with paid files in Downloads. Users receive the amount as account credit. If enabled, they can then request a payout of this via PayPal or a manual payout method you want to use. Viewing an invoice in the AdminCP where some payment has been given to a member Viewing account credit with options to withdraw funds Club content throughout the community Currently content in clubs is only visible within the club itself. In 4.3 a new setting allows you to show the content from clubs throughout the community - for example, if a club contains a forum, that forum can show in the main forum list. Club forums showing on main forum list This is a single toggle: if enabled, all content from clubs that each user has joined will show throughout the community, appearing below the normal categories/etc in that application. New Club Type: Read Only In addition to Open, Closed, Private and Public, we have added a new club type in 4.3: read only. In a read only club, everyone can (without joining) view everything in the club, but cannot participate unless they are invited by a club leader. Following Users can now follow a club, and will then receive notifications about all new content in the club - the same as if they followed every content area in the club. List View In addition to the current grid layout of clubs, there is a new list-style. Clubs List View The admin can choose which views are available and what the default should be. AdminCP Approval You can now filter the list of clubs in the AdminCP to clubs requiring approval and approve clubs from within the AdminCP. Approving clubs in AdminCP Deleting Content Areas Club leaders can now delete content areas within their clubs. This can be useful if, for example, the leader added a club feature by mistake. Content areas can only be removed if there is no content within it, or if you have granted club leaders the ability to delete content in their clubs (since they would be able to empty it). Ability to remove features from clubs Other Minor Tweaks You can now set per-group the maximum number of clubs a member in that group can create. A member invited to join a club can now delete the invitation if they do not want to accept it (rather than just ignoring it).
-
4.3: Take payments with Apple Pay and more with Stripe and Commerce
Stripe is the most popular payment method in Commerce, allowing communities to take payments by card securely with easy setup. While there's no doubt that credit cards are still the most popular methods of making a payment, digital innovations such as Apple Pay are increasing in popularity. For 4.3 we've deepened our integration to support some of their latest features. Apple Pay & Google Pay Apple Pay allows users to pay quickly with their iPhone, iPad or Mac (with Safari and either a paired iPhone or using the MacBook Pro with Touch ID) using the card details stored on the device, authenticated with Touch ID or Face ID. Apple Pay Google Chrome (on desktop or Android devices) supports a similar feature allowing users to pay with card details stored in their Google account with Google Pay, or stored in Chrome itself. Paying with card details stored in Google Chrome Both of these features are now supported through Stripe in Invision Community 4.3. Setup is simple - for Apple Pay you simply need to verify that you own your domain by uploading a file you obtain from the Stripe dashboard, and nothing special is needed for Google Pay - and then create the payment method in the AdminCP. Stripe does not charge any additional fees for either option. Commerce will automatically hide the option if the user's device does not support either method. 3D Secure Also known as Verified by Visa, Mastercard SecureCode, and other brand names, 3D Secure is a system that is used to verify a customer's identity before purchase is completed and transfers the fraud loss liability from the merchant to the cardholder bank in case of fraudulent disputes. After the user has entered their card details, they are redirected to their bank's website and asked to provide additional verification. Our integration with Stripe in 4.3 now supports this process. A new setting allows you to choose if you want to use 3D Secure just for cards which require it (i.e. cards which would decline the payment if 3D Secure is not completed) or for all cards which optionally support it as well. Amex Express Checkout American Express cardholders can use Amex Express checkout to pay by using their American Express login rather than providing their card information. This is also now supported through Stripe in 4.3. Amex Express Checkout Alipay, Bancontact, Giropay, iDEAL, SOFORT These are popular payment processors internationally (Alipay is popular in China, Bancontact in Belgium, Giropay in Germany, iDEAL in the Netherlands, and SOFORT in several European countries). The checkout experience is similar to PayPal with the user being redirected to the appropriate site, authenticating the payment, and then being redirected back. All of these are also now supported through Stripe in 4.3. Dispute/Chargeback Handling A dispute (also known as a chargeback) occurs when one a cardholder questions your payment with their card issuer, which causes the funds, plus a fee, to immediately be taken from your account until evidence is provided that the transaction was legitimate. Anyone operating an online store knows how frustrating this experience can be. In 4.3, we've made dealing with this situation a little easier. When a dispute is created, Commerce will now mark the transaction as disputed, which will immediately revoke any benefits from the purchase (for example, if it's for a subscription that moves them into a different group, they will be placed back into their original group; if it's a Downloads file, they won't be able to download it any more; if it's for a physical item that hasn't been shipped yet, the shipping order will be placed on hold). Disputed Transaction All transactions with currently open disputes can be accessed quickly from the transaction list. The transaction page will show you the status and reason for the dispute, and links to your Stripe dashboard where you can respond. When the dispute is resolved, the transaction screen will be updated, with either the transaction being marked as refunded if the dispute is lost, or going back to paid if the dispute is won and the funds returned to you. A dispute that was lost A dispute that was won Radar Radar is Stripe's suite of fraud detection tools using machine learning and customisable rules to help detect fraudulent transactions. Stripe will automatically blocks transactions is considers highest risk already. However, for "elevated" risk transactions, while Stripe would alert you of them so you could review them, Commerce would process the transaction normally. In 4.3, Commerce will place any transactions which Radar reports as having an "elevated" risk level on hold for manual review, so you can decide whether to approve or not before the funds have been captured. In addition, the transaction details screen for Stripe transactions now provides some additional information about Stripe's checks on the transaction, including the Radar risk level, if the CVC check passed, and if the billing address provided matches the card's billing address. If a fraudulent transaction does make it through, you will now have the option to indicate this when refunding the transaction to help Stripe's anti-fraud systems learn.
-
4.3: Scaleable search and interface improvements
Search. Let's be honest, it's not the most exciting feature in the world. You ask to find things, and it shows you what it found. Simple, right? It's a lot more complex than that. After numerous tests, a few surveys and many discussions with customers, we've decided that there is no "right" or "wrong" way to search. Invision Community is used on many diverse communities and each has its own needs. The bigger the community, the more of a headache search can be when you start hitting frustrating technical limitations of the database. Happily, we've addressed all of these issues with Invision Community 4.3 and added a few extra treats. Searchable Products and Pages Products in the Store and custom Pages will now show in search results. Store product in search results More Customisable Search Experience One of the most difficult challenges with search is anticipating the scope of the search. If, for example, you're looking for something you know you've seen before, you want the search to be narrow - matching only the exact terms you provide, probably only matching against the title, in the specific area you know where the content is located. If however, you're just doing a general search about a particular subject, you want the search to be wide - matching any of the terms you enter, anywhere in the community, in both titles and content. For a while, Invision Community has had the option to choose which areas to search, defaulting to the area of the community you're in (for example, if you're in a forum, only that forum will be searched by default). We also provide a number of suggestions on the search result form (in the form of "Didn't find what you were looking for? Try searching for..." followed by a number of options) which adjust the scope of the search. In Invision Community 4.3, we have a new interface for the quick search feature which makes some of these options more visible so you're more likely to find what you're looking for on the first search. New Search UI Along these lines we have also: Changed the default "Search In" selection to "Everywhere", regardless of where the user is. Added a new setting which controls whether the "Any words" or "All words" option is checked by default. Added a new setting which allows you to adjust how much of a boost results receive for a match in the title, versus the content body, when searching both content titles and body. You can set default and/or operator. New Search Settings Elasticsearch In Invision Community 4.3 we are adding native support for Elasticsearch, a third party search engine which offers a number of benefits over searching your MySQL database: Elasticsearch, being designed and indexing data in a way optimised for search rather than data storage, is generally able to match and sort by relevancy with better accuracy than MySQL. Elasticsearch is generally faster. One user performing a search doesn't slow down other users trying to read and make posts at the same time (when searching MySQL, the data has to be "locked" from changes when the search is being performed). It scales very well with very large datasets, and runs very easily on multiple servers. Elasticsearch understands language. If for example, you search for "community", it will also return results which contain the word "communities", understanding that these are the same. Supported languages are Arabic, Armenian, Basque, Brazilian, Bulgarian, Catalan, Chinese, Czech, Danish, Dutch, English, Dinnish, Drench, Galician, German, Greek, Hindi, Hungarian, Indonesian, Irish, Italian, Japanese, Korean, Latvian, Lithuanian, Norwegian, Persian, Portuguese, Romanian, Russian, Sorani, Spanish, Swedish, Turkish, Thai. Elasticsearch supports custom functions on the scoring algorithm. In our initial implementation this has allowed us to add settings to allow you to control the time decay (allowing newer results to show higher) and author boost (allowing content posted by the user to optionally show higher in results). Unlike with MySQL, there is no minimum query length and a very small list of stop words. Elasticsearch Settings When enabled, both searches and activity streams will be retrieved from Elasticsearch. The core_search_index database table in MySQL will no longer be populated, so you will not have to store the data twice. To use Elasticsearch, you can either install it yourself on your own server, or use any of the many excellent hosted Elasticsearch options. The minimum required Elasticsearch version is 5.5. REST API Developers and those looking to integrate Invision Community features into their own sites will be pleased to learn that we've extended the REST API to accommodate searching.
-
4.3: Sign in from other sites using OAuth
The best way to convert guests into members is to make the onboarding process as simple as possible. Over the years, we've added special log in methods for Facebook, Google, LinkedIn and Microsoft. We've carefully hand coded these integrations to allow guests to sign up with just a few clicks using services they're already a member of. These services used to use proprietary methods to link with other websites, but a new standard has emerged. OAuth You may not know it, but you're probably familiar with OAuth already. If you have enabled the ability for users of your community to sign in with their Facebook, Twitter, Google, LinkedIn or Microsoft account, you may have noticed that the process for setting up each of these is quite similar. This is because they all use the OAuth protocol. In Invision Community 4.3, we are introducing several exciting new features: In addition to all of the existing social networks above, which retain their "easy setup" status, we have also added Wordpress. Users on your community can now sign in with any Wordpress site you control (you will need to install a Wordpress plugin to enable OAuth capabilities). As well as those "easy setup" options, we have also added the ability for you to allow users on your site to sign in with any OAuth 2.0 based provider. This means, for example, if your community is based in a location where other social networks are popular, if they use OAuth, you can set those up too. While the setup is a little bit more complicated, this doesn't require any custom programming - you'll just need to find out a few more pieces of information from the provider (an example is provided below). Invision Community itself can now also serve as an OAuth 2.0 server so you can set up other sites to be able to facilitate logins using credentials from your community. This works in conjunction with our REST API, allowing you to make API calls as an authenticated member, which will return just the information that user has access to. With the ability for Invision Community to serve as both an OAuth server and client, this now provides standard integration for multiple Invision Communities together, which will now replace the old IPS Connect feature. We have also taken this opportunity to make a few other minor tweaks to login, registration and account management features, especially for communities which rely heavily on non-standard login methods (more details below). Setting Up a Custom OAuth Provider For this example, I'm going to use vk.com, which is a popular social network in Europe. While Invision Community doesn't provide this as one of the "easy setup" options, it is based on OAuth 2.0 so we can use the new functionality in Invision Community 4.3 to set it up. In older versions, the list of login handlers in the AdminCP had all of the providers listed with enable/disable toggles - because now you can add as many custom handlers as you like in 4.3, it's now a list where you can add/delete options: Login Handlers List When clicking the "Create New" button, you'll see all of the different handlers Invision Community supports. Since vk.com isn't in the list, but is still OAuth 2.0-based, I'll choose the "Other OAuth 2.0" option: Choosing a Login Handler You'll now need to use the documentation provided by the site you want to integrate with to fill out this form. While no custom programming is required, the documentation is usually quite technical in nature - but you only need a few key pieces of information. We anticipate that for some of the more popular options, guides will be provided to help you find the information you need. I have created an application in vk.com's developer center and so I will copy and paste my credentials into the form: Inputting vk.com credentials I then need to find the endpoints from vk.com's documentation and input those too. Inputting vk.com endpoints Next I need to find the endpoint where I can access the user's information within their API and the parameters they are returned by. The only required piece of information is an ID, but you can also provide the parameters for accessing the display name, email address and profile photo. If display name/email address isn't available/provided, the user will be asked for this the first time they sign in. vk.com's API doesn't provide access to the email, but I can use the screen name as the display name, and they do provide access to the photo: Inputting vk.com User Information Endpoint and response parameters Finally, provide a logo and a color for the sign in button and some final settings: Inputting vk.com Logo and Button Color And now vk.com login is set up. A button will now show up on the front end which I can use to sign in. I didn't provide a way to access the email address, so on the first sign in, the user will be prompted to provide that, but the screen name and profile photo from vk.com will be used: Signing in with vk.com Using Invision Community as an OAuth Server You can also set up Invision Community itself to be an OAuth Server. This may be useful for two main reasons: If you want to integrate two communities together, or integrate with something else which supports adding custom OAuth clients. If you are a developer and want to use the REST API using OAuth for authentication rather than an API Key. You can either make requests as an authenticated user (by obtaining an access token) or using Client Credentials. The screenshots below show the full capabilities which are quite technical and mostly aimed at developers. If you will just use this feature to link two communities, don't be concerned if it looks too complicated, an easy-to-follow guide will be available to achieve that. You will set up the clients from the AdminCP: Setting up an OAuth Client When creating the OAuth Client, you can control which scopes are available, and which endpoints of the REST API they provide access to: Defining OAuth Client Scopes The login process is then the standard OAuth flow, and users have the ability to view authorisations in the account settings: Authenticating an OAuth Client The REST API has new and updated endpoints to be aware of the authenticated user: A new REST API endpoint which returns details of the currently authenticated user An updated REST API endpoint which, when called using OAuth authentication, will only return data the authenticated user has access to Other Login System Tweaks Users can now choose if they want to change their local display name or email address if it is changed by an external login method (or the administrator can choose this behaviour). If there is an issue with this (for example, it wants to change the email to one that is already taken), or profile photo syncing, this is now better communicated to the user. You can now control per-login-handler if new registrations are allowed using it. This addresses some confusion from previous versions as to if the "Allow New Registrations" setting applies to accounts being created by social network logins. The Standard login handler can be disabled if you rely totally on an alternate login method. To allow this to happen: All areas where a user is prompted to re-enter their password (some areas of the account settings) now allow reauthentication using any login handler. You can disable local registration but still allow accounts to be created by other login handlers, or redirect users to an external URL to register an account. You can also disable or redirect to an external URL for changing email address / password or the Forgot Password tool. You can now create multiple instances of the external MySQL database and LDAP login methods which have also had some other minor tweaks: The external MySQL database handler now has PHP's password_hash() function as an available option for password encryption type, and defining a custom encryption method is now much easier, done entirely in the AdminCP without needing to modify PHP files. You can now choose if changes to the local display name / email address / password is synced back to the external database / LDAP database. You can optionally show these handlers in the Account Settings pages like other login handlers to allow users with an existing account to link their accounts. You can define a Forgot Password URL for the external database which the user will be redirected to if they try to use the Forgot Password tool and that is how their account is authenticated.
-
4.3: Express yourself with Emoji
Emoji: built in to Invision Community 4.3! ? Invision Community has a long history. We remember the early days of forums, back when graphical "emoticons" or "smilies" were added. We have always shipped our products with a basic set of emoticons with the ability to add your own images and has supported emoji from mobile devices. Emoji has become a standard across mobile and desktop devices so it made sense to bring them to Invision Community fully. You can choose from 3 different styles of Emoji: The native style provided by the user's operating system (if you choose this option, users on different platforms will see different styles) Twitter style EmojiOne style Emoji Settings Once you have chosen one of these options, all of the available Emoji will show in the emoticons selector when making a post. Unlike in older versions, the entire list is scrollable (the categories drop down will jump you to the category rather than filter), you can search, and standard Emoji features like skin tone modifiers are fully supported, and of course, you can make them as big as you like. Navigating Emoji Skin Tone Modifier Make Emoji any size Autocompleting Short Codes In addition to using the selector, you can also use optionally enable standard :short_codes:. These will be autocompleted as you type. Autocompleting Short Codes You can also enable more conventional ASCII emoticons to be automatically replaced too: ASCII Short Codes Don't Worry: Custom Emoticons Aren't Going Anywhere! You can use custom emoticons either instead of, or even alongside Emoji. If you give your custom emoticons a text replacement starting and ending with : they will even show in the autocompletion alongside Emoji. Custom Emoticons Technical Details Whichever style you choose, Emoji is stored in the database as the actual Unicode characters, so you can even change the setting and all Emoji, even those in existing posts, will immediately change. If you choose to use the native style (so the Emoji will match the style provided by the operating system), the system will automatically detect which Emojis are supported and the selector will only try to show the ones the platform can render.