Support custom login pages
- Add the button (and error messages) to every wp_login_form() form via login_form_top/login_form_bottom; in button-only mode the password fields are wrapped and hidden there. - New template functions m365_login_button() and m365_login_messages(); the shortcode gains divider and messages attributes. - New setting for the custom login page URL: failed sign-ins, the fallback link and the logout redirect point there instead of wp-login.php. Must be a same-site URL. - Button-only mode now blocks every interactive password sign-in through the authenticate filter, not only wp-login.php; XML-RPC, REST, WP-CLI and cron are exempt, plus a filter for trusted exceptions. - Fallback key accepted on any page (init) instead of login_init only. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01JJxAHYdMfKPoN4koRc4Ci2
This commit is contained in:
parent
e5db0d49be
commit
701e85ab88
15 changed files with 804 additions and 300 deletions
|
|
@ -408,6 +408,33 @@ class M365_Login_Admin {
|
|||
<button type="button" class="m365-preset" data-preset='{"bg":"#2271b1","bg_hover":"#135e96","color":"#ffffff","border":"#2271b1"}'><span style="background:#2271b1"></span><?php esc_html_e( 'WordPress blue', 'm365-login' ); ?></button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="m365-card">
|
||||
<h2 class="m365-card__title"><?php esc_html_e( 'Custom login page', 'm365-login' ); ?></h2>
|
||||
<p class="m365-card__intro"><?php esc_html_e( 'Using your own login page instead of wp-login.php? Tell the plugin where it is so error messages, the fallback link and the post-logout redirect point there.', 'm365-login' ); ?></p>
|
||||
|
||||
<div class="m365-field">
|
||||
<label for="m365-custom-login"><?php esc_html_e( 'URL of your login page', 'm365-login' ); ?></label>
|
||||
<input type="url" id="m365-custom-login" name="<?php echo $field( 'custom_login_url' ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>" value="<?php echo esc_url( $s['custom_login_url'] ); ?>" class="regular-text code" placeholder="<?php echo esc_attr( home_url( '/login/' ) ); ?>" />
|
||||
<p class="description"><?php esc_html_e( 'Must be on this site. Leave empty to use wp-login.php.', 'm365-login' ); ?></p>
|
||||
</div>
|
||||
|
||||
<label class="m365-check m365-check--block">
|
||||
<input type="checkbox" name="<?php echo $field( 'inject_form' ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>" value="1" <?php checked( $s['inject_form'] ); ?> />
|
||||
<span>
|
||||
<strong><?php esc_html_e( 'Add the button to every wp_login_form() form automatically', 'm365-login' ); ?></strong>
|
||||
<em><?php esc_html_e( 'Covers themes and plugins that use the WordPress login form function. Page-builder widgets need the shortcode or the template function below.', 'm365-login' ); ?></em>
|
||||
</span>
|
||||
</label>
|
||||
|
||||
<div class="m365-field">
|
||||
<span class="m365-field__label"><?php esc_html_e( 'Manual placement', 'm365-login' ); ?></span>
|
||||
<p class="description"><?php esc_html_e( 'Shortcode (block editor, page builders):', 'm365-login' ); ?></p>
|
||||
<code>[m365_login_button redirect="/dashboard/" divider="yes"]</code>
|
||||
<p class="description"><?php esc_html_e( 'Template function (theme files):', 'm365-login' ); ?></p>
|
||||
<code><?php m365_login_button( array( 'redirect' => '/dashboard/' ) ); ?></code>
|
||||
<p class="description"><?php esc_html_e( 'Both show the error messages of the last attempt; use m365_login_messages() to place them separately.', 'm365-login' ); ?></p>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Security -->
|
||||
|
|
@ -483,7 +510,7 @@ class M365_Login_Admin {
|
|||
|
||||
<div class="m365-card">
|
||||
<h2 class="m365-card__title"><?php esc_html_e( 'Button-only mode', 'm365-login' ); ?></h2>
|
||||
<p class="m365-card__intro"><?php esc_html_e( 'Hide the username/password form and the "Lost your password?" link, and refuse password sign-ins on the login page. Application passwords, REST and XML-RPC are not affected.', 'm365-login' ); ?></p>
|
||||
<p class="m365-card__intro"><?php esc_html_e( 'Hides the username/password fields (on wp-login.php and in wp_login_form() forms) and refuses every interactive password sign-in on the site, including custom login forms. Application passwords, REST, XML-RPC and WP-CLI are not affected.', 'm365-login' ); ?></p>
|
||||
|
||||
<label class="m365-check m365-check--block">
|
||||
<input type="checkbox" name="<?php echo $field( 'button_only' ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>" value="1" <?php checked( $s['button_only'] ); ?> id="m365-button-only" />
|
||||
|
|
@ -495,7 +522,7 @@ class M365_Login_Admin {
|
|||
|
||||
<div class="m365-fallback">
|
||||
<span class="m365-field__label"><?php esc_html_e( 'Fallback link (keep it secret)', 'm365-login' ); ?></span>
|
||||
<p class="description"><?php esc_html_e( 'Opening this link shows the password form again in that browser for 30 minutes. Bookmark it somewhere safe – it is your way back in if Microsoft sign-in ever breaks.', 'm365-login' ); ?></p>
|
||||
<p class="description"><?php esc_html_e( 'Opening this link shows the password form again in that browser for 30 minutes and allows password sign-in there. Bookmark it somewhere safe – it is your way back in if Microsoft sign-in ever breaks.', 'm365-login' ); ?></p>
|
||||
<?php if ( '' !== $this->settings->fallback_url() ) : ?>
|
||||
<div class="m365-copy">
|
||||
<code id="m365-fallback-url"><?php echo esc_html( $this->settings->fallback_url() ); ?></code>
|
||||
|
|
@ -569,6 +596,7 @@ class M365_Login_Admin {
|
|||
<h2 class="m365-card__title"><?php esc_html_e( 'Shortcode', 'm365-login' ); ?></h2>
|
||||
<p><?php esc_html_e( 'Place the button on a custom login page:', 'm365-login' ); ?></p>
|
||||
<code>[m365_login_button redirect="/my-account/"]</code>
|
||||
<p class="description"><?php esc_html_e( 'More options on the Button tab under "Custom login page".', 'm365-login' ); ?></p>
|
||||
</div>
|
||||
</aside>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -51,10 +51,13 @@ class M365_Login_Auth {
|
|||
add_action( 'init', array( $this, 'maybe_handle_callback' ), 5 );
|
||||
add_filter( 'wp_login_errors', array( $this, 'login_errors' ), 10, 1 );
|
||||
|
||||
// Button-only mode.
|
||||
add_action( 'login_init', array( $this, 'maybe_accept_fallback_key' ) );
|
||||
// Button-only mode (works on wp-login.php and on custom login pages).
|
||||
add_action( 'init', array( $this, 'maybe_accept_fallback_key' ), 6 );
|
||||
// Runs after core's username/password handlers (priority 20), which would otherwise overwrite an early WP_Error.
|
||||
add_filter( 'authenticate', array( $this, 'block_password_login' ), 99, 3 );
|
||||
|
||||
// Custom login page: send people back there after logging out.
|
||||
add_filter( 'logout_redirect', array( $this, 'logout_redirect' ), 10, 3 );
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------ */
|
||||
|
|
@ -84,7 +87,7 @@ class M365_Login_Auth {
|
|||
}
|
||||
|
||||
/**
|
||||
* wp-login.php?m365_fallback=KEY → sets the fallback cookie and reloads without the key in the URL.
|
||||
* ?m365_fallback=KEY (on any page) → sets the fallback cookie and reloads the login page without the key in the URL.
|
||||
*/
|
||||
public function maybe_accept_fallback_key() {
|
||||
if ( ! $this->settings->button_only() ) {
|
||||
|
|
@ -92,7 +95,7 @@ class M365_Login_Auth {
|
|||
}
|
||||
// phpcs:ignore WordPress.Security.NonceVerification.Recommended -- the key itself is the secret.
|
||||
$given = isset( $_GET['m365_fallback'] ) ? sanitize_text_field( wp_unslash( $_GET['m365_fallback'] ) ) : '';
|
||||
if ( '' === $given ) {
|
||||
if ( '' === $given || 'on' === $given ) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -111,10 +114,26 @@ class M365_Login_Auth {
|
|||
delete_transient( $ip_key );
|
||||
$this->send_cookie( self::FALLBACK_COOKIE, $this->fallback_cookie_value(), time() + self::FALLBACK_TTL );
|
||||
nocache_headers();
|
||||
wp_safe_redirect( add_query_arg( 'm365_fallback', 'on', wp_login_url() ) );
|
||||
wp_safe_redirect( add_query_arg( 'm365_fallback', 'on', $this->settings->login_page_url() ) );
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* After logout, return to the custom login page instead of wp-login.php.
|
||||
*
|
||||
* @param string $redirect_to Requested redirect.
|
||||
* @param string $requested_redirect_to Raw requested redirect.
|
||||
* @param WP_User|WP_Error $user User.
|
||||
* @return string
|
||||
*/
|
||||
public function logout_redirect( $redirect_to, $requested_redirect_to, $user ) {
|
||||
$custom = $this->settings->custom_login_url();
|
||||
if ( '' === $custom || '' !== (string) $requested_redirect_to ) {
|
||||
return $redirect_to;
|
||||
}
|
||||
return add_query_arg( 'loggedout', 'true', $custom );
|
||||
}
|
||||
|
||||
/**
|
||||
* Refuses username/password sign-in on wp-login.php while button-only mode is active.
|
||||
*
|
||||
|
|
@ -130,13 +149,27 @@ class M365_Login_Auth {
|
|||
if ( '' === (string) $username && '' === (string) $password ) {
|
||||
return $user; // Initial form render or cookie auth, no password attempt.
|
||||
}
|
||||
// Only the interactive login form is affected: XML-RPC, REST and application passwords keep working.
|
||||
if ( ! isset( $GLOBALS['pagenow'] ) || 'wp-login.php' !== $GLOBALS['pagenow'] ) {
|
||||
// Interactive password logins only: XML-RPC, REST (application passwords), WP-CLI and cron keep working.
|
||||
if ( ( defined( 'XMLRPC_REQUEST' ) && XMLRPC_REQUEST )
|
||||
|| ( defined( 'REST_REQUEST' ) && REST_REQUEST )
|
||||
|| ( defined( 'WP_CLI' ) && WP_CLI )
|
||||
|| wp_doing_cron() ) {
|
||||
return $user;
|
||||
}
|
||||
if ( ! $user instanceof WP_User ) {
|
||||
return $user; // Already failed for another reason; keep core's message.
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows exempting a password sign-in from button-only mode (e.g. a trusted membership plugin).
|
||||
*
|
||||
* @param bool $block Whether to block. Default true.
|
||||
* @param WP_User $user Authenticated user.
|
||||
*/
|
||||
if ( ! apply_filters( 'm365_login_block_password_login', true, $user ) ) {
|
||||
return $user;
|
||||
}
|
||||
|
||||
return new WP_Error( 'm365_login_button_only', __( 'Password sign-in is disabled on this site. Please use the Microsoft button.', 'm365-login' ) );
|
||||
}
|
||||
|
||||
|
|
@ -720,45 +753,48 @@ class M365_Login_Auth {
|
|||
*/
|
||||
private function fail( $code ) {
|
||||
$this->clear_state_cookie();
|
||||
wp_safe_redirect( add_query_arg( 'm365_error', rawurlencode( $code ), wp_login_url() ) );
|
||||
nocache_headers();
|
||||
wp_safe_redirect( add_query_arg( 'm365_error', rawurlencode( $code ), $this->settings->login_page_url() ) );
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes to the PHP error log when WP_DEBUG_LOG is enabled.
|
||||
* Messages for the current request (from ?m365_error and ?m365_fallback=on), for any login page.
|
||||
*
|
||||
* @param string $message Message.
|
||||
* @return array[] Each item: array( 'type' => 'error'|'message', 'code' => string, 'text' => string ).
|
||||
*/
|
||||
private function log( $message ) {
|
||||
if ( defined( 'WP_DEBUG' ) && WP_DEBUG && defined( 'WP_DEBUG_LOG' ) && WP_DEBUG_LOG ) {
|
||||
error_log( '[M365 Login] ' . $message ); // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Maps error codes to messages on the login screen.
|
||||
*
|
||||
* @param WP_Error $errors Login errors.
|
||||
* @return WP_Error
|
||||
*/
|
||||
public function login_errors( $errors ) {
|
||||
if ( ! $errors instanceof WP_Error ) {
|
||||
$errors = new WP_Error();
|
||||
}
|
||||
|
||||
public function current_messages() {
|
||||
// phpcs:disable WordPress.Security.NonceVerification.Recommended -- read-only display of whitelisted flags.
|
||||
$code = isset( $_GET['m365_error'] ) ? sanitize_key( wp_unslash( $_GET['m365_error'] ) ) : '';
|
||||
$fallback_on = isset( $_GET['m365_fallback'] ) && 'on' === $_GET['m365_fallback'];
|
||||
// phpcs:enable WordPress.Security.NonceVerification.Recommended
|
||||
|
||||
$out = array();
|
||||
if ( $fallback_on && $this->settings->button_only() && $this->fallback_active() ) {
|
||||
$errors->add( 'm365_login_fallback_on', __( 'Password sign-in is temporarily enabled for this browser (30 minutes).', 'm365-login' ), 'message' );
|
||||
$out[] = array(
|
||||
'type' => 'message',
|
||||
'code' => 'fallback_on',
|
||||
'text' => __( 'Password sign-in is temporarily enabled for this browser (30 minutes).', 'm365-login' ),
|
||||
);
|
||||
}
|
||||
if ( '' === $code ) {
|
||||
return $errors;
|
||||
if ( '' !== $code ) {
|
||||
$messages = $this->error_messages();
|
||||
$out[] = array(
|
||||
'type' => 'access_denied' === $code ? 'message' : 'error',
|
||||
'code' => $code,
|
||||
'text' => isset( $messages[ $code ] ) ? $messages[ $code ] : $messages['provider_error'],
|
||||
);
|
||||
}
|
||||
return $out;
|
||||
}
|
||||
|
||||
$messages = array(
|
||||
/**
|
||||
* Error code → translated message map.
|
||||
*
|
||||
* @return string[]
|
||||
*/
|
||||
private function error_messages() {
|
||||
return array(
|
||||
'not_configured' => __( 'Microsoft login is not configured yet.', 'm365-login' ),
|
||||
'invalid_state' => __( 'The login request expired or was invalid. Please try again.', 'm365-login' ),
|
||||
'access_denied' => __( 'Microsoft sign-in was cancelled.', 'm365-login' ),
|
||||
|
|
@ -775,13 +811,21 @@ class M365_Login_Auth {
|
|||
'fallback_invalid' => __( 'The fallback key is not valid.', 'm365-login' ),
|
||||
'fallback_locked' => __( 'Too many attempts. Please wait 15 minutes.', 'm365-login' ),
|
||||
);
|
||||
}
|
||||
|
||||
$errors->add(
|
||||
'm365_login_' . $code,
|
||||
isset( $messages[ $code ] ) ? $messages[ $code ] : $messages['provider_error'],
|
||||
'access_denied' === $code ? 'message' : 'error'
|
||||
);
|
||||
|
||||
/**
|
||||
* Adds the current messages to the wp-login.php error object.
|
||||
*
|
||||
* @param WP_Error $errors Login errors.
|
||||
* @return WP_Error
|
||||
*/
|
||||
public function login_errors( $errors ) {
|
||||
if ( ! $errors instanceof WP_Error ) {
|
||||
$errors = new WP_Error();
|
||||
}
|
||||
foreach ( $this->current_messages() as $msg ) {
|
||||
$errors->add( 'm365_login_' . $msg['code'], $msg['text'], $msg['type'] );
|
||||
}
|
||||
return $errors;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -32,6 +32,133 @@ class M365_Login_Button {
|
|||
add_filter( 'login_message', array( $this, 'render_above' ), 20 );
|
||||
add_action( 'login_footer', array( $this, 'render_below' ) );
|
||||
add_shortcode( 'm365_login_button', array( $this, 'shortcode' ) );
|
||||
|
||||
// Custom login pages built with wp_login_form().
|
||||
add_filter( 'login_form_top', array( $this, 'form_top' ), 10, 2 );
|
||||
add_filter( 'login_form_bottom', array( $this, 'form_bottom' ), 10, 2 );
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether the plugin should hook into wp_login_form() output.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
private function inject_into_forms() {
|
||||
return $this->settings->is_configured() && (bool) $this->settings->get( 'inject_form' ) && ! is_user_logged_in();
|
||||
}
|
||||
|
||||
/**
|
||||
* wp_login_form(): messages above the fields; in button-only mode the fields are wrapped and hidden.
|
||||
*
|
||||
* @param string $content Existing content.
|
||||
* @param array $args wp_login_form() arguments.
|
||||
* @return string
|
||||
*/
|
||||
public function form_top( $content, $args = array() ) {
|
||||
if ( ! $this->inject_into_forms() ) {
|
||||
return $content;
|
||||
}
|
||||
$this->enqueue_frontend();
|
||||
$content .= $this->messages_markup();
|
||||
if ( $this->password_login_hidden() ) {
|
||||
$content .= '<div class="m365-login__hidden-fields" hidden>';
|
||||
}
|
||||
return $content;
|
||||
}
|
||||
|
||||
/**
|
||||
* wp_login_form(): closes the wrapper and appends the button.
|
||||
*
|
||||
* @param string $content Existing content.
|
||||
* @param array $args wp_login_form() arguments.
|
||||
* @return string
|
||||
*/
|
||||
public function form_bottom( $content, $args = array() ) {
|
||||
if ( ! $this->inject_into_forms() ) {
|
||||
return $content;
|
||||
}
|
||||
$hidden = $this->password_login_hidden();
|
||||
$redirect_to = isset( $args['redirect'] ) ? (string) $args['redirect'] : '';
|
||||
if ( $hidden ) {
|
||||
$content .= '</div>';
|
||||
}
|
||||
$divider = $hidden ? '' : $this->divider_markup();
|
||||
return $content . '<div class="m365-login m365-login--form">' . $divider . $this->button( $redirect_to ) . '</div>';
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether password fields should be hidden right now (button-only mode without fallback).
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
private function password_login_hidden() {
|
||||
return $this->settings->button_only() && ! M365_Login::instance()->auth->fallback_active();
|
||||
}
|
||||
|
||||
/**
|
||||
* Enqueues the stylesheet on the front end (custom login pages).
|
||||
*/
|
||||
public function enqueue_frontend() {
|
||||
wp_enqueue_style( 'm365-login', M365_LOGIN_URL . 'assets/css/login.css', array(), M365_LOGIN_VERSION );
|
||||
wp_add_inline_style( 'm365-login', $this->css_variables() );
|
||||
}
|
||||
|
||||
/**
|
||||
* Divider line markup ('' when the divider text is empty).
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
private function divider_markup() {
|
||||
$divider = (string) $this->settings->get( 'divider_text' );
|
||||
return '' === trim( $divider ) ? '' : '<div class="m365-login__divider" aria-hidden="true"><span>' . esc_html( $divider ) . '</span></div>';
|
||||
}
|
||||
|
||||
/**
|
||||
* Error / info messages for custom login pages.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function messages_markup() {
|
||||
$html = '';
|
||||
foreach ( M365_Login::instance()->auth->current_messages() as $msg ) {
|
||||
$html .= '<div class="m365-login__notice m365-login__notice--' . esc_attr( $msg['type'] ) . '" role="' . ( 'error' === $msg['type'] ? 'alert' : 'status' ) . '">' . esc_html( $msg['text'] ) . '</div>';
|
||||
}
|
||||
return $html;
|
||||
}
|
||||
|
||||
/**
|
||||
* Full markup for templates and shortcodes.
|
||||
*
|
||||
* @param array $args {
|
||||
* @type string $redirect Destination after login.
|
||||
* @type bool $show_messages Show error/info messages. Default true.
|
||||
* @type bool $show_divider Show the divider line. Default false.
|
||||
* }
|
||||
* @return string
|
||||
*/
|
||||
public function standalone( $args = array() ) {
|
||||
if ( ! $this->settings->is_configured() || is_user_logged_in() ) {
|
||||
return '';
|
||||
}
|
||||
$args = wp_parse_args(
|
||||
$args,
|
||||
array(
|
||||
'redirect' => '',
|
||||
'show_messages' => true,
|
||||
'show_divider' => false,
|
||||
)
|
||||
);
|
||||
$this->enqueue_frontend();
|
||||
|
||||
$html = '<div class="m365-login m365-login--shortcode">';
|
||||
if ( $args['show_messages'] ) {
|
||||
$html .= $this->messages_markup();
|
||||
}
|
||||
if ( $args['show_divider'] && ! $this->password_login_hidden() ) {
|
||||
$html .= $this->divider_markup();
|
||||
}
|
||||
$html .= $this->button( esc_url_raw( (string) $args['redirect'] ) );
|
||||
return $html . '</div>';
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -142,15 +269,22 @@ class M365_Login_Button {
|
|||
* @return string
|
||||
*/
|
||||
public function shortcode( $atts ) {
|
||||
if ( ! $this->settings->is_configured() || is_user_logged_in() ) {
|
||||
return '';
|
||||
}
|
||||
$atts = shortcode_atts( array( 'redirect' => '' ), $atts, 'm365_login_button' );
|
||||
|
||||
wp_enqueue_style( 'm365-login', M365_LOGIN_URL . 'assets/css/login.css', array(), M365_LOGIN_VERSION );
|
||||
wp_add_inline_style( 'm365-login', $this->css_variables() );
|
||||
|
||||
return '<div class="m365-login m365-login--shortcode">' . $this->button( esc_url_raw( $atts['redirect'] ) ) . '</div>';
|
||||
$atts = shortcode_atts(
|
||||
array(
|
||||
'redirect' => '',
|
||||
'messages' => 'yes',
|
||||
'divider' => 'no',
|
||||
),
|
||||
$atts,
|
||||
'm365_login_button'
|
||||
);
|
||||
return $this->standalone(
|
||||
array(
|
||||
'redirect' => $atts['redirect'],
|
||||
'show_messages' => 'no' !== strtolower( (string) $atts['messages'] ),
|
||||
'show_divider' => 'yes' === strtolower( (string) $atts['divider'] ),
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -163,8 +297,7 @@ class M365_Login_Button {
|
|||
// phpcs:ignore WordPress.Security.NonceVerification.Recommended -- passed through to the flow, validated there.
|
||||
$redirect_to = isset( $_REQUEST['redirect_to'] ) ? esc_url_raw( wp_unslash( $_REQUEST['redirect_to'] ) ) : '';
|
||||
|
||||
$divider = $this->form_hidden() ? '' : (string) $this->settings->get( 'divider_text' );
|
||||
$divider = '' === trim( $divider ) ? '' : '<div class="m365-login__divider" aria-hidden="true"><span>' . esc_html( $divider ) . '</span></div>';
|
||||
$divider = $this->form_hidden() ? '' : $this->divider_markup();
|
||||
|
||||
$html = '<div class="m365-login m365-login--' . esc_attr( $position ) . '" id="m365-login-block">';
|
||||
$html .= 'above' === $position ? $this->button( $redirect_to ) . $divider : $divider . $this->button( $redirect_to );
|
||||
|
|
|
|||
|
|
@ -51,6 +51,9 @@ class M365_Login_Settings {
|
|||
'button_radius' => 4,
|
||||
'button_position' => 'below',
|
||||
'divider_text' => __( 'or', 'm365-login' ),
|
||||
// Custom login pages.
|
||||
'custom_login_url' => '',
|
||||
'inject_form' => 1, // Add the button to wp_login_form() output.
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -209,7 +212,31 @@ class M365_Login_Settings {
|
|||
*/
|
||||
public function fallback_url() {
|
||||
$key = $this->fallback_key();
|
||||
return '' === $key ? '' : add_query_arg( 'm365_fallback', $key, wp_login_url() );
|
||||
return '' === $key ? '' : add_query_arg( 'm365_fallback', $key, $this->login_page_url() );
|
||||
}
|
||||
|
||||
/**
|
||||
* URL of a custom login page (same site), or empty string.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function custom_login_url() {
|
||||
$url = (string) $this->get( 'custom_login_url', '' );
|
||||
if ( '' === $url ) {
|
||||
return '';
|
||||
}
|
||||
$validated = wp_validate_redirect( $url, '' );
|
||||
return is_string( $validated ) ? $validated : '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Where users land after a failed Microsoft sign-in (custom page or wp-login.php).
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function login_page_url() {
|
||||
$custom = $this->custom_login_url();
|
||||
return '' !== $custom ? $custom : wp_login_url();
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -309,6 +336,20 @@ class M365_Login_Settings {
|
|||
}
|
||||
$out['fallback_key'] = $key;
|
||||
|
||||
// Custom login page (must be on this site).
|
||||
$custom = isset( $input['custom_login_url'] ) ? esc_url_raw( trim( wp_unslash( $input['custom_login_url'] ) ) ) : '';
|
||||
if ( '' !== $custom ) {
|
||||
if ( 0 === strpos( $custom, '/' ) ) {
|
||||
$custom = home_url( $custom );
|
||||
}
|
||||
if ( '' === wp_validate_redirect( $custom, '' ) ) {
|
||||
add_settings_error( M365_LOGIN_OPTION, 'custom_login_url', __( 'The custom login page must be a URL on this site.', 'm365-login' ) );
|
||||
$custom = $current['custom_login_url'];
|
||||
}
|
||||
}
|
||||
$out['custom_login_url'] = $custom;
|
||||
$out['inject_form'] = empty( $input['inject_form'] ) ? 0 : 1;
|
||||
|
||||
// Button.
|
||||
$text = isset( $input['button_text'] ) ? sanitize_text_field( wp_unslash( $input['button_text'] ) ) : '';
|
||||
$out['button_text'] = '' === trim( $text ) ? $defaults['button_text'] : mb_substr( $text, 0, 80 );
|
||||
|
|
|
|||
49
includes/functions.php
Normal file
49
includes/functions.php
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
<?php
|
||||
/**
|
||||
* Template functions.
|
||||
*
|
||||
* @package M365_Login
|
||||
*/
|
||||
|
||||
defined( 'ABSPATH' ) || exit;
|
||||
|
||||
if ( ! function_exists( 'm365_login_button' ) ) {
|
||||
/**
|
||||
* Outputs (or returns) the "Sign in with Microsoft" button for custom login templates.
|
||||
*
|
||||
* Usage in a theme template:
|
||||
* <?php m365_login_button( array( 'redirect' => home_url( '/dashboard/' ) ) ); ?>
|
||||
*
|
||||
* @param array $args {
|
||||
* @type string $redirect Destination after login. Default: admin dashboard.
|
||||
* @type bool $show_messages Show error/info messages from a previous attempt. Default true.
|
||||
* @type bool $show_divider Show the "or" divider above the button. Default false.
|
||||
* }
|
||||
* @param bool $display Echo (true) or return (false). Default true.
|
||||
* @return string
|
||||
*/
|
||||
function m365_login_button( $args = array(), $display = true ) {
|
||||
$plugin = M365_Login::instance();
|
||||
$html = $plugin->button->standalone( $args );
|
||||
if ( $display ) {
|
||||
echo $html; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- markup is escaped where it is built.
|
||||
}
|
||||
return $html;
|
||||
}
|
||||
}
|
||||
|
||||
if ( ! function_exists( 'm365_login_messages' ) ) {
|
||||
/**
|
||||
* Outputs (or returns) the error/info messages of the last Microsoft sign-in attempt.
|
||||
*
|
||||
* @param bool $display Echo (true) or return (false). Default true.
|
||||
* @return string
|
||||
*/
|
||||
function m365_login_messages( $display = true ) {
|
||||
$html = M365_Login::instance()->button->messages_markup();
|
||||
if ( $display ) {
|
||||
echo $html; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- markup is escaped where it is built.
|
||||
}
|
||||
return $html;
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue