cartpauj / show-login
WordPress front-end login popup triggered by a URL parameter (?sl=1).
Requires
- php: >=7.4
- composer/installers: ^1.0 || ^2.0
Requires (Dev)
- dealerdirect/phpcodesniffer-composer-installer: ^1.0
- phpcompatibility/phpcompatibility-wp: ^2.1
- phpunit/phpunit: ^9.6
- squizlabs/php_codesniffer: ^3.10
- wp-cli/i18n-command: ^2.7
- wp-coding-standards/wpcs: ^3.1
- yoast/phpunit-polyfills: ^2.0 || ^3.0 || ^4.0
Suggests
None
Provides
None
Conflicts
None
Replaces
None
This package is auto-updated.
Last update: 2026-10-25 14:39:24 UTC
README
A lightweight WordPress plugin that provides a front-end login popup triggered by URL parameter. Built with modern PHP 7.4+ standards, vanilla JavaScript, and clean architecture.
Features
- Lightweight - Minimal footprint with clean, organized code structure
- Pure JavaScript - No jQuery or other libraries required
- Cache Compatible - Works perfectly with all page caching solutions (WP Rocket, LiteSpeed, etc.)
- Turnstile Integration - Automatic integration with Simple Cloudflare Turnstile plugin for CAPTCHA protection
- Two-Factor Authentication - Automatic integration with Two Factor plugin with seamless redirect flow, and with Wordfence 2FA right in the popup
- Social Login - Shows the buttons from MemberPress and WishList Member Social Login, and from Login with Google, sending people back to the page they were on
- Secure - Nonce verification, username enumeration prevention, and proper sanitization. Works with your security plugin for login limits
- Fast UX - Instant loading spinner with smooth transitions
- Accessible - WCAG compliant with proper ARIA attributes
- Extensible - Multiple hooks for custom authentication flows and customization
- Translation Ready - Every message, including the popup's JavaScript ones, can be translated. Works with Loco Translate out of the box
- Well Architected - Proper MVC separation with dedicated classes for each concern
- Multiple URL Triggers - Supports
?sl=true,?sl=1,?show_login=true, and?show_login=1
Requirements
- WordPress 5.0 or higher
- PHP 7.4 or higher
Installation
With Composer
composer require cartpauj/show-login
The package is a wordpress-plugin, so composer/installers puts it in wp-content/plugins/show-login/. Then activate it in the WordPress admin.
By hand
- Download
show-login-x.y.z.zipfrom the latest GitHub release - Upload it in Plugins → Add New → Upload Plugin (or unzip it into
/wp-content/plugins/) - Activate the plugin through the WordPress admin
- The plugin works automatically when the URL parameter is present
Usage
Basic Usage
Add any of these URL parameters to trigger the login popup for non-logged-in users:
https://example.com/page?sl=true
https://example.com/page?sl=1
https://example.com/page?show_login=true
https://example.com/page?show_login=1
All four parameter variations work identically. Parameters can be combined with other URL parameters:
https://example.com/shop/product?id=123&sl=true
https://example.com/members/content?category=premium&show_login=1
When a user successfully logs in:
- The page reloads with them logged in
- All URL parameters are preserved
- Only the popup trigger parameter is removed (e.g.,
slorshow_login)
User Experience
- User visits a page with a popup trigger parameter (e.g.,
?sl=true) - Popup appears instantly with loading spinner ("Checking login status...")
- Login status checked via AJAX (bypasses page cache)
- If logged out: Shows "You're not logged in" (1 second) → Login form appears
- If logged in: Shows "You're already logged in!" (1 second) → Popup closes
- User can close popup via:
- X button in top-right corner
- Clicking outside popup on overlay
- Pressing ESC key
- User enters credentials and submits
- AJAX authentication happens without page reload
- Errors display in popup without disruption
- On success, page reloads and user is logged in
Security Features
Login Limits (Brute-Force Protection)
Show Login doesn't limit login attempts itself. That's a job for a security plugin like Wordfence, Limit Login Attempts Reloaded, or Solid Security. The popup logs people in through WordPress's own wp_signon(), so those plugins see and block popup logins exactly like logins on wp-login.php.
Removed: The built-in rate limiter is gone, along with the
show_login_enable_rate_limiting,show_login_max_attempts,show_login_rate_limit_window, andshow_login_client_ipfilters. It trusted IP headers that visitors can fake, so it could be bypassed, and it could be used to lock real people out.
Nonce Verification
All AJAX requests are protected with WordPress nonces using check_ajax_referer().
Input Sanitization
- Username:
sanitize_text_field()+wp_unslash() - Password:
wp_unslash()only (no sanitization to preserve special characters) - Remember:
sanitize_text_field()+ boolean check - URLs:
esc_url_raw()to preserve structure
Username Enumeration Prevention
Error messages are sanitized to prevent username enumeration attacks:
- Invalid username/email and incorrect password both return: "Invalid username or password"
- Specific error codes (
invalid_username,invalid_email,incorrect_password) are replaced with generic messages - Prevents attackers from determining which usernames exist on the site
Error Message Filtering
Other errors (for example from a membership plugin) are passed through, but only simple HTML is kept: <strong>, <b>, <em>, <i>, <br>, and links. Anything else, like scripts or event handlers, is stripped. This runs before the show_login_error_message filter, so whatever your filter returns is shown as-is. Escape anything you add there.
Safe Redirects
Every redirect URL, including the one passed to the two-factor form, must stay on your site. Off-site URLs are replaced with a safe default.
Page Caching Compatibility
The plugin is fully compatible with all page caching solutions:
- JavaScript checks
?sl=trueparameter client-side before any AJAX calls - Login status checked via AJAX (always executes fresh PHP with user cookies)
- Nonces generated fresh per user - Created dynamically via AJAX, never cached
- Redirect URLs computed per request - Always accurate, never stale
- Works with WP Rocket, W3 Total Cache, Cloudflare, and all other caching plugins
- Instant loading spinner with status messages provides clear feedback:
- "Checking login status..." (during AJAX)
- "You're not logged in" (if logged out, shows 1 second before form)
- "You're already logged in!" (if logged in, shows 1 second before closing)
- The spinner stays up for at least 1 second so it doesn't flash. The browser handles this wait, so the server never sits idle
Cloudflare Turnstile Integration
Show Login includes built-in integration with the Simple Cloudflare Turnstile plugin for CAPTCHA protection.
Features
- ✅ Automatic Detection - Activates only when Turnstile plugin is installed and configured
- ✅ Cache Compatible - Turnstile scripts load dynamically via JavaScript
- ✅ All Settings Supported - Works with all Turnstile themes, sizes, and appearance modes
- ✅ Whitelist Support - Respects Turnstile's IP/user whitelist settings
- ✅ Button Disable Support - Compatible with "disable submit button" option
- ✅ Zero Configuration - No setup needed beyond installing both plugins
Setup
- Install and activate Simple Cloudflare Turnstile
- Configure your Cloudflare Turnstile Site Key and Secret Key in Settings > Turnstile
- That's it! The integration works automatically
Note: You do NOT need to enable the "Login" checkbox in Turnstile settings - Show Login popup works independently with its own integration.
Supported Settings
The integration works with ALL Turnstile settings:
- ✅ Appearance Modes: Always visible, Execute (invisible), or Interaction-only
- ✅ Themes: Light, Dark, or Auto
- ✅ Sizes: Normal, Compact, or Flexible
- ✅ Disable Submit Button: Supported in both visible and invisible modes
- ✅ Languages: All Cloudflare Turnstile languages
- ✅ Whitelist: IP and user whitelisting honored
- ✅ Custom Error Messages: Displayed in popup on validation failure
How It Works
When both plugins are active and Turnstile is configured:
- User visits page with popup trigger parameter
- Turnstile API loads dynamically (cache-compatible)
- Login popup appears with Turnstile widget between "Remember Me" and submit button
- User interaction:
- Visible mode: User completes the visible challenge, then submits
- Invisible mode: User fills form and clicks submit, verification happens automatically
- Turnstile token (
cf-turnstile-response) sent with login request - Server validates token before authentication via
cfturnstile_check() - If validation fails, user sees error message (customizable in Turnstile settings)
- If validation succeeds, normal authentication proceeds
Invisible Mode Behavior:
- No visible widget shown to user
- Verification runs automatically when submit button is clicked
- Seamless UX - users don't see CAPTCHA unless flagged as suspicious
- All validation happens server-side - no difference in backend flow
Technical Note - Single-Use Token Handling:
Cloudflare Turnstile tokens are single-use only. Show Login automatically prevents double validation by skipping the Turnstile plugin's default WordPress login check (via the cfturnstile_wp_login_checks filter) when handling Show Login AJAX requests. This ensures:
- Only ONE validation occurs per token
- Show Login popup validation works correctly
- Standard WordPress login form (
wp-login.php) continues to work with Turnstile - No conflicts between the two integrations
Filters
// Skip Turnstile validation for specific users/conditions add_filter('show_login_skip_turnstile', function($skip) { // Example: Skip for admin users testing if (current_user_can('manage_options')) { return true; } return $skip; });
Action Hooks
// Track Turnstile failures add_action('cfturnstile_show_login_failed', function($username) { error_log("Turnstile validation failed for user: {$username}"); }); // Track Turnstile successes add_action('cfturnstile_show_login_success', function($username) { error_log("Turnstile validation passed for user: {$username}"); });
Technical Details
Cache Compatibility:
- Turnstile scripts load dynamically via JavaScript when popup trigger parameter is detected
- Works with all caching plugins (WP Rocket, LiteSpeed Cache, W3 Total Cache, etc.)
- No cache variations needed for URL parameters
Validation:
- Server-side validation occurs before
wp_signon()authentication - Uses Turnstile's
cfturnstile_check()function for validation - Validates the
cf-turnstile-responsePOST parameter - Returns appropriate error messages on failure
Styling:
- Turnstile widget centered in popup
- Custom CSS classes:
.show-login-turnstile,.show-login-cf-turnstile - Respects Turnstile theme settings (light, dark, auto)
- Proper spacing and layout integration
Two-Factor Authentication Integration
Show Login includes built-in integration with the Two Factor plugin for enhanced security.
Features
- ✅ Automatic Detection - Activates only when Two Factor plugin is installed and user has 2FA enabled
- ✅ Seamless Redirect - Password verified users are redirected to standard login page for 2FA
- ✅ Clean UX - Clear messaging about 2FA requirement
- ✅ Zero Configuration - No setup needed beyond installing Two Factor plugin
How It Works
When a user with 2FA enabled attempts to login via Show Login popup:
- User enters username and password in popup
- Credentials validated (including Turnstile if active)
- If password correct but 2FA required:
- User sees message: "Redirecting to two-factor authentication..."
- Automatically redirected to
wp-login.phpwith 2FA form already displayed - No need to re-enter username/password
- User completes 2FA verification (TOTP code, email code, etc.)
- After 2FA verification, user is logged in and returned to the original page
Why This Approach?
The Two Factor plugin uses complex interactive forms (TOTP codes, backup codes, U2F keys, etc.) that aren't suitable for a simple popup. By redirecting to the standard login page, we ensure:
- Full compatibility with all 2FA methods
- Proper security (no shortcuts around 2FA)
- Clean UX (users understand they're on a secure login page)
- No maintenance burden (Two Factor plugin handles all UI)
Technical Notes
- Show Login intercepts the
wp_loginhook (priority 5) before Two Factor's handler (priority 10) - Prevents HTML output during AJAX requests
- Creates a 2FA login nonce using
Two_Factor_Core::create_login_nonce() - Returns JSON response with:
two_factor_required: trueflagredirect_urlwith pre-authenticated 2FA form URL
- URL includes:
action=validate_2fa,wp-auth-id,wp-auth-nonce,redirect_to - Clears temporary auth cookies (2FA not complete yet)
- JavaScript automatically redirects to the 2FA form
- After 2FA completion, user is redirected back to
redirect_toURL (original page)
Wordfence Two-Factor Integration
Users with Wordfence two-factor authentication can finish logging in without leaving the popup.
How It Works
- User enters username and password in the popup
- If the password is right and Wordfence wants a 2FA code, the popup shows a Two-Factor Code field with the message "Enter the code from your authenticator app."
- User enters the code from their authenticator app (or a Wordfence recovery code) and clicks Log In again
- Wordfence checks the code, and the user is logged in and stays on the same page
A wrong code shows Wordfence's own error and keeps the code field open. A wrong password shows the usual generic error, so the popup never reveals whether the password was right.
Technical Notes
- Active only when Wordfence Login Security is installed (
\WordfenceLS\Controller_WordfenceLSexists) - The code field is added with the
show_login_form_middleaction, hidden and disabled until it's needed, so it isn't sent with normal logins - When
wp_signon()fails with Wordfence'swfls_twofactor_requirederror, the response includescode_required: true - The code is sent in Wordfence's own
wfls-tokenfield, so Wordfence validates it duringwp_signon()exactly as it does onwp-login.php - Wordfence's reCAPTCHA option isn't supported in the popup
Social Login Integration
Show Login puts social login buttons at the top of the popup, above an "or" divider, when one of these is installed and set up:
| Plugin | Shown when |
|---|---|
| MemberPress Social Login, WishList Member Social Login (Caseproof) | At least one provider is enabled and verified. One button per provider. |
| Login with Google (rtCamp) | Its Client ID and Client Secret are set (in settings or with the WP_GOOGLE_LOGIN_CLIENT_ID / WP_GOOGLE_LOGIN_SECRET constants). |
Nothing to configure. If none of them is set up, the popup looks exactly as before.
After logging in, people come back to the page they were on (minus ?sl=1), just like a password login.
Good to know
- Social login leaves the page: the user goes to Google (or another provider) and comes back. It doesn't happen inside the popup.
- If social login fails (for example, the Google account isn't linked to a user on the site), the plugin shows its error on
wp-login.php, not in the popup. - The membership plugin's own login redirect rules still apply afterwards. For example, MemberPress may send a member with an expired subscription to their account page, and WishList Member's "login redirect override" setting takes priority.
- Each plugin's button stylesheet is loaded on front-end pages for logged-out visitors, because the popup is added after the page loads.
Adding your own buttons
Anything printed on the show_login_social_buttons action shows up in the same spot, with the "or" divider added automatically. You get the URL to send people back to after login:
add_action('show_login_social_buttons', function ($redirect_url) { printf( '<a class="my-sso-button" href="%s">Log in with Company SSO</a>', esc_url(add_query_arg('return', rawurlencode($redirect_url), 'https://sso.example.com/start')) ); }); // Hide a built-in integration: add_action('plugins_loaded', function () { remove_action('show_login_social_buttons', [Show_Login_Google::class, 'render_button'], 20); remove_action('show_login_social_buttons', [Show_Login_Social_Login::class, 'render_buttons'], 10); }, 30);
Translations
Show Login works with Loco Translate with no setup:
- Install and activate Loco Translate.
- Go to Loco Translate → Plugins → Show Login and click New language.
- Leave the location on Loco's default (Custom, in
wp-content/languages/loco/) and translate.
Save it there or in System (wp-content/languages/plugins/), not in the plugin's own folder. Plugin updates replace the plugin folder, so translations saved inside it would be lost.
When you save, Loco also builds the small .json files the popup's JavaScript messages need. Nothing else to do.
Good to know
- The plugin ships only the blank template,
languages/show-login.pot. It isn't on WordPress.org, so there are no automatic language packs. - Any tool that writes standard
.po/.mo(and WordPress.json) files works too. Put them inwp-content/languages/plugins/, named likeshow-login-de_DE.mo.
Developer Documentation
Hooks & Filters
Action Hooks
Form Hooks:
// Add logo/branding after the title add_action('show_login_after_title', function() { echo '<div style="text-align: center; margin: 15px 0;">'; echo '<img src="' . esc_url(get_stylesheet_directory_uri() . '/images/logo.png') . '" alt="Logo" style="max-width: 200px;">'; echo '</div>'; }); // Add custom fields at the start of the form add_action('show_login_form_start', function() { echo '<input type="hidden" name="custom_field" value="data">'; }); // Add custom fields before the submit button add_action('show_login_form_middle', function() { echo '<div class="show-login-field">'; echo '<label for="custom-field">Custom Field</label>'; echo '<input type="text" id="custom-field" name="custom_field">'; echo '</div>'; }); // Add links at the end of the form add_action('show_login_form_end', function() { echo '<p><a href="/forgot-password">Forgot Password?</a></p>'; });
Authentication Hooks:
// Before authentication attempt add_action('show_login_before_authenticate', function($username) { error_log("Login attempt for user: {$username}"); }); // After authentication attempt add_action('show_login_after_authenticate', function($user, $credentials) { if (is_wp_error($user)) { error_log("Failed login: " . $user->get_error_message()); } }, 10, 2); // On successful login add_action('show_login_success', function($user) { error_log("Successful login for user ID: {$user->ID}"); // Send notification, log analytics, etc. });
Filter Hooks
Customize Labels:
add_filter('show_login_popup_title', function($title) { return 'Welcome Back!'; }); add_filter('show_login_username_label', function($label) { return 'Email Address'; }); add_filter('show_login_password_label', function($label) { return 'Your Password'; }); add_filter('show_login_remember_label', function($label) { return 'Keep me logged in'; }); add_filter('show_login_submit_label', function($label) { return 'Sign In'; }); // Wordfence 2FA code field (only shown to users who need it) add_filter('show_login_2fa_code_label', function($label) { return 'Authenticator Code'; });
Modify Authentication:
// Modify credentials before authentication add_filter('show_login_credentials', function($credentials) { // Add custom data $credentials['custom_data'] = 'value'; return $credentials; }); // Customize error messages add_filter('show_login_error_message', function($message, $error) { if ($error->get_error_code() === 'invalid_username') { return 'The email address you entered is not registered.'; } return $message; }, 10, 2);
Redirect Customization:
add_filter('show_login_redirect_url', function($redirect_url, $current_url) { // Always redirect to a specific page after login return home_url('/dashboard'); }, 10, 2);
Button Styling:
// Change button background color add_filter('show_login_button_bg_color', function($color) { return '#e74c3c'; // Default: #0073aa }); // Change button hover background color add_filter('show_login_button_hover_bg_color', function($color) { return '#c0392b'; // Default: #005a87 }); // Change button text color add_filter('show_login_button_text_color', function($color) { return '#ffffff'; // Default: #fff });
Loading State:
// Suppress loading spinner and status messages // Popup stays hidden until AJAX confirms user is logged out add_filter('show_login_suppress_loading_state', '__return_true');
Custom Authentication Flows
For custom authentication requirements beyond the built-in Two Factor plugin integration, you can use the authentication hooks:
// Example: Custom post-authentication validation add_action('show_login_after_authenticate', function($user, $credentials) { if (is_wp_error($user)) { return; // Authentication failed, skip custom checks } // Example: Require custom condition to be met if (!user_meets_custom_requirement($user->ID)) { wp_logout(); // Log them back out wp_send_json_error([ 'message' => 'Additional authentication requirements not met.' ]); } }, 10, 2);
Customizing Styles
The plugin provides filters for button colors (see Button Styling section above). For other style customizations, use CSS specificity:
/* Change popup width */ #show-login-popup { max-width: 500px !important; } /* Change overlay opacity */ #show-login-overlay { background-color: rgba(0, 0, 0, 0.9) !important; } /* Change input border color on focus */ .show-login-field input:focus { border-color: #e74c3c !important; box-shadow: 0 0 0 1px #e74c3c !important; }
JavaScript API
The plugin exposes the showLoginData object to JavaScript:
// Available properties: showLoginData.ajaxUrl // WordPress AJAX URL showLoginData.nonce // Security nonce showLoginData.redirectUrl // URL to redirect after login
Architecture
File Structure
show-login/
├── show-login.php # Main plugin bootstrap
├── includes/
│ ├── class-show-login.php # Main controller
│ ├── class-show-login-assets.php # Asset management
│ ├── class-show-login-authenticator.php # Authentication handler
│ ├── class-show-login-popup.php # Popup rendering & AJAX check
│ ├── class-show-login-turnstile.php # Cloudflare Turnstile integration
│ ├── class-show-login-two-factor.php # Two Factor integration
│ ├── class-show-login-social-login.php # Caseproof Social Login integration
│ └── class-show-login-google.php # Login with Google (rtCamp) integration
├── assets/
│ ├── css/
│ │ └── show-login.css # Popup styles + Turnstile styles
│ └── js/
│ └── show-login.js # Front-end behavior + Turnstile loader
├── templates/
│ └── popup.php # Login popup HTML
├── languages/
│ └── show-login.pot # Translation template (built by composer make-pot)
├── loco.xml # Tells Loco Translate where strings and template live
├── README.md # Technical documentation
└── readme.txt # WordPress.org readme
Architecture
The plugin follows a clean architecture pattern with separation of concerns:
Main Controller (Show_Login)
- Coordinates all plugin components
- Initializes dependencies
- Manages WordPress hooks
- Singleton pattern for single instance
Assets Handler (Show_Login_Assets)
- Enqueues CSS and JavaScript files
- Generates dynamic button color styles
- Manages script localization
- Handles redirect URL generation
Authenticator (Show_Login_Authenticator)
- Processes AJAX login requests
- Validates credentials
- Handles authentication success/failure
- Sanitizes error messages to prevent username enumeration
Popup Handler (Show_Login_Popup)
- AJAX endpoint for login status check (cache-compatible)
- HTML rendering from template
- Returns popup data: HTML, nonce, redirect URL
- Instant feedback for logged-in users
Template (templates/popup.php)
- Semantic HTML markup
- WCAG accessibility compliant
- Action hooks for extensibility
- Filter hooks for customization
Turnstile Integration (Show_Login_Turnstile)
- Automatic detection of Turnstile plugin
- Dynamic script loading for cache compatibility
- Server-side validation before authentication
- Respects whitelist and all Turnstile settings
Authentication Flow
1. User visits page with ?sl=true (or ?sl=1, ?show_login=true, ?show_login=1)
↓
2. Page loads (may be cached HTML)
↓
3. Assets enqueued (CSS, JS on all frontend pages)
↓
4. JavaScript: Check if popup trigger parameter in URL
↓
5. If YES: Load Turnstile API dynamically (if Turnstile plugin active)
↓
6. Show loading spinner immediately
↓
7. AJAX: Check login status (show_login_check_popup)
↓
8. PHP: is_user_logged_in() → Return show=true/false + HTML/nonce/redirectUrl
↓
9. JavaScript: If logged in → Show "You're already logged in!" (1s) → Close
If logged out → Show "You're not logged in" (1s) → Show form
↓
10. Turnstile widget renders (if enabled) → User completes verification
↓
11. User submits form → AJAX request (show_login_authenticate)
↓
12. PHP: Nonce verification → Sanitization → Turnstile validation
↓
13. Before hooks fire → wp_signon() → After hooks fire
↓
14. Success: Return success JSON
Failure: Return error JSON (security plugins see it via wp_login_failed)
↓
15. JavaScript: Success → window.location.href = redirectUrl
Failure → Show error in popup
Best Practices
When to Use This Plugin
✅ Good use cases:
- Members-only content requiring login
- Seamless login experience without leaving the page
- Custom landing pages with login CTAs
- Marketing campaigns with direct login links
❌ Not recommended for:
- Primary site-wide login (use standard WordPress login)
- High-security admin access
- As a complete replacement for wp-login.php
Performance Considerations
- Assets only load when needed (logged-out users with
?sl=true) - Inline CSS/JS eliminates HTTP requests
- No database queries on every page load
Security Recommendations
- Always use HTTPS in production
- Install Simple Cloudflare Turnstile for CAPTCHA protection (see Cloudflare Turnstile Integration section)
- Install Two Factor plugin for 2FA support (see Two-Factor Authentication Integration section)
- Install a security plugin (Wordfence, Limit Login Attempts Reloaded, Solid Security, etc.) to limit login attempts. It covers the popup automatically
- Monitor failed login attempts using action hooks
- Keep WordPress and plugins updated
Troubleshooting
Popup doesn't appear
- Verify user is logged out
- Check URL has one of:
?sl=true,?sl=1,?show_login=true, or?show_login=1 - Check JavaScript console for errors
- Verify plugin is activated
Turnstile widget not showing
- Verify Simple Cloudflare Turnstile plugin is installed and activated
- Check that Turnstile keys are configured in Settings > Turnstile
- Check JavaScript console for Turnstile API loading errors
- Verify user is not whitelisted in Turnstile settings
- Note: If using invisible mode (
appearance: execute), the widget is intentionally hidden - verification happens automatically on submit
Turnstile validation always failing
- Verify both Site Key and Secret Key are correct in Turnstile settings
- Check that keys match the domain (Turnstile keys are domain-specific)
- Ensure server can make outbound HTTPS requests to
challenges.cloudflare.com - Check for JavaScript errors preventing Turnstile from loading
- Test with a different browser or incognito mode
Redirect not working after login
- Check for JavaScript errors in console
- Verify
showLoginData.redirectUrlis correct - Check for conflicting plugins that modify redirects
Developers
Everything you need to work on Show Login itself. (For hooks and filters, see Developer Documentation above.)
Releasing
Follow RELEASES.md every time. It's the step-by-step checklist for bumping the version, tagging, and checking that Packagist and GitHub picked it up.
Setup
You need PHP 7.4+, Composer, Node 20+, and a local MySQL/MariaDB server.
composer install npm install # Download WordPress + its test library into ./tmp and create a test database. # WARNING: the database named in DB_NAME is dropped and recreated. Default: show_login_tests. DB_USER=root DB_PASS=yourpassword composer tests-install # Or test against a specific WordPress version: DB_USER=root DB_PASS=yourpassword composer tests-install -- 6.9.4
Other settings: DB_HOST (default localhost, host:port works) and DB_NAME.
Running checks
| Command | What it does |
|---|---|
composer test |
PHP tests (PHPUnit + the WordPress test library) |
composer test:coverage |
PHP tests, then fails if line coverage is under 90%. Needs pcov or xdebug. |
npm test |
JavaScript tests (Jest + jsdom) |
npm run test:coverage |
JavaScript tests, fails under 90% coverage |
composer cs-check |
Coding standards (PSR-12 + WordPress security sniffs + PHP 7.4 compatibility) |
composer cs-fix |
Auto-fixes what it can |
composer make-pot |
Rebuilds the translation template languages/show-login.pot. Run it after adding or changing any user-facing text, then commit the file. |
bin/check-package.sh |
Checks that the package/zip contains only plugin files |
Test layout
tests/
├── bootstrap.php # Loads WordPress test library + the plugin
├── Framework/ # Base test cases (AjaxTestCase runs admin-ajax endpoints)
├── Fixtures/ # Stand-ins for the Two Factor and Turnstile plugins
├── TestCases/ # PHP tests: *Test.php
└── js/ # Jest tests for assets/js/show-login.js
The PHP tests never make network calls (WP_HTTP_BLOCK_EXTERNAL). Two Factor and Simple Cloudflare Turnstile are replaced by small fakes in tests/Fixtures/, so neither plugin needs to be installed.
Continuous integration
.github/workflows/tests.yml runs on every pull request (and by hand from the Actions tab):
- PHP tests: PHP 7.4, 8.2 and 8.4 against WordPress nightly, the newest release, and the newest release of the previous major (e.g. 7.1.x and 6.9.x). The WordPress versions are looked up automatically. Nightly failures are reported but don't fail the build.
- Coverage: the PHP 8.4 + newest-WordPress run and the JS run both fail under 90%.
- Coding standards, composer.json validation, and the package contents check.
- Translation template: fails if user-facing text changed but
languages/show-login.potwasn't rebuilt. Runcomposer make-potand commit. A version bump trips it too, since the template records the version.
What ships
Composer/Packagist and the GitHub release zip are both built with git archive, which skips everything marked export-ignore in .gitattributes (tests, tooling, CI config, this kind of documentation). If you add a dev-only file or folder, add it there too. bin/check-package.sh fails CI if you forget.
Contributing
This plugin is maintained by cartpauj. Issues and pull requests are welcome on GitHub.
License
GPL v2 or later. See LICENSE.
Changelog
1.3.0
- New: People who use Wordfence two-factor authentication can now log in from the popup. After their password, the popup asks for the code from their authenticator app. Before, they got a "CODE REQUIRED" error with nowhere to enter it.
- New: The
show_login_2fa_code_labelfilter changes the label on the code field.
1.2.0
- Breaking: The built-in login attempt limit is gone. It could be tricked with fake IP addresses and used to lock real people out. Use a security plugin like Wordfence, Limit Login Attempts Reloaded, or Solid Security instead. It protects the popup automatically. The
show_login_enable_rate_limiting,show_login_max_attempts,show_login_rate_limit_window, andshow_login_client_ipfilters no longer do anything. - Fix: The
show_login_redirect_urlfilter now works. Before, it never ran in the popup. - Fix: Opening the popup no longer makes your server wait a second for every visitor. The spinner still shows for at least a second, but the browser handles it.
- Fix: If the server sends back a broken reply, the spinner closes instead of getting stuck.
- Security: After two-factor login, people can only be sent to pages on your site.
- Security: Error messages from other plugins are cleaned up before the popup shows them, so they can't run scripts.
1.1.1
- Fix: The popup's status and error messages ("Checking login status...", "You're not logged in", and others) were always in English. They can now be translated.
- New: Works with Loco Translate out of the box. See Translations.
- Dev: Translation template (
languages/show-login.pot),composer make-pot, and a CI check that the template is up to date.
1.1.0
- New: Social login buttons in the popup for MemberPress and WishList Member Social Login, and Login with Google. After logging in, people return to the page they were on. See Social Login Integration.
- New:
show_login_social_buttonsaction for adding your own login buttons above the form. - New: Install with Composer (
composer require cartpauj/show-login) or download the zip from GitHub releases. - Fix: Links with special characters (like
%20) were changed when sending people back after login or two-factor. - Fix: The lockout message said "try again in 0 minutes" on sites using Redis or Memcached.
- Fix: Cloudflare's Turnstile script no longer loads on sites that don't use Turnstile.
- Fix: Translations couldn't be found because the languages folder path was wrong.
- Fix: Plugin and readme links pointed at the wrong GitHub repository.
- Dev: PHP and JavaScript test suites, CI, coding standards, and a release process (see
RELEASES.md).
1.0.0
- Initial release
- Front-end login popup with URL parameter triggers (
?sl=true,?sl=1,?show_login=true,?show_login=1) - Built-in Cloudflare Turnstile integration with dynamic script loading
- Cache-compatible architecture (works with all caching plugins)
- Rate limiting and security features
- Extensive hooks for extensibility (2FA, CAPTCHA, custom fields)
- Full translation support
- WCAG accessibility compliant