[TikiWiki-commits] [Git][tikiwiki/tiki][master] [FIX][UX] Login Module: improve UX when switching user
"Adrien Mbuya Maloba \(@adrienmaloba\) via TikiWiki-cvs" <[email protected]>
| Newsgroups | gmane.comp.cms.tiki.cvs |
|---|---|
| Message-ID | <6a16dfc689f00_381931c479940@gitlab-sidekiq-low-urgency-cpu-bound-v2-5944c7446f-2hpf5.mail> |
Adrien Mbuya Maloba pushed to branch master at Tiki Wiki CMS Groupware / Tiki
Commits:
3b6c69ae by Landry Bitege at 2026-05-27T14:54:40+03:00
[FIX][UX] Login Module: improve UX when switching user
---
* [FIX][UX] Login Module: improve UX when switching user
See merge request tikiwiki/tiki!10184
- - - - -
4 changed files:
- lib/smarty_tiki/FunctionHandler/UserSelector.php
- modules/mod-func-login_box.php
- templates/modules/mod-login_box.tpl
- tiki-login.php
Changes:
=====================================
lib/smarty_tiki/FunctionHandler/UserSelector.php
=====================================
@@ -57,7 +57,9 @@ class UserSelector extends Base
'noneLabel' => 'None',
'realnames' => 'y',
'class' => 'form-control',
- 'lazyload' => 'false'
+ 'lazyload' => 'false',
+ 'noneSelectable' => 'y',
+ 'exclude' => '',
];
$params = array_merge($defaults, $params);
@@ -154,6 +156,14 @@ class UserSelector extends Base
}
}
+ if (! empty($params['exclude'])) {
+ $excludeUsers = is_array($params['exclude']) ? $params['exclude'] : explode(',', $params['exclude']);
+ $excludeUsers = array_map('trim', $excludeUsers);
+ foreach ($excludeUsers as $excludeUser) {
+ unset($users[$excludeUser]);
+ }
+ }
+
if ($params['realnames'] === 'y') {
$dupes = [];
foreach (array_count_values($users) as $usr => $c) {
@@ -192,7 +202,12 @@ class UserSelector extends Base
$ret .= '<select name="' . $params['name'] . '" id="' . $params['id'] . '"' . $sz . $ed . $mt . ' style="' . $params['style'] . '" class="form-control">';
if ($params['allowNone'] === 'y') {
- $ret .= '<option value=""' . (empty($params['user']) ? ' selected="selected"' : '') . ' >' . tra($params['noneLabel']) . '</option>';
+ $noneOptionAttributes = (empty($params['user']) ? ' selected="selected"' : '');
+ if ($params['multiple'] !== 'true' && $params['noneSelectable'] !== 'y') {
+ $noneOptionAttributes .= ' disabled="disabled" hidden';
+ }
+
+ $ret .= '<option value=""' . $noneOptionAttributes . ' >' . tra($params['noneLabel']) . '</option>';
}
foreach ($users as $usr => $usersname) {
$selected = isset($params['select']) && ($params['select'] === $usr || (is_array($params['select']) && in_array($usr, $params['select'])));
=====================================
modules/mod-func-login_box.php
=====================================
@@ -61,7 +61,7 @@ function module_login_box_info()
],
'allowNone' => [
'name' => tra('Allow None'),
- 'description' => tra('Allow user selection field to be empty (without any users selected) when enabled.') . ' (y/n)',
+ 'description' => tra('Show a non-selectable placeholder in the Switch User dropdown. Set to "n" to hide it entirely.') . ' (y/n)',
'filter' => 'alpha',
],
'show_user_avatar' => [
=====================================
templates/modules/mod-login_box.tpl
=====================================
@@ -13,6 +13,7 @@ function capLock(e, el){
}
{/jq}
{jq}
+var currentLoggedInUser = "{{$user|escape:'javascript'}}";
$(document).ready(function () {
var twoFAType = "{{$prefs.twoFactorAuthType}}";
var is2FAEnabled = "{{$prefs.twoFactorAuth}}";
@@ -74,16 +75,6 @@ $(document).ready(function () {
});
}
- function displayFeedback(type, message) {
- const feedbackClass = type === "success" ? "alert-success" : "alert-danger";
- $("#login_feeback_{{$module_logo_instance}}").html(
- `<div class="alert ${feedbackClass} alert-dismissible">
- ${message}
- <button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
- </div>`
- );
- }
-
function getTwoFactorSecretGoogle2FA(username) {
return new Promise((resolve, reject) => {
$.ajax({
@@ -204,14 +195,66 @@ $(document).ready(function () {
}
});
});
-$("#switchbox-{{$module_logo_instance}} .submit").on("click", function () {
- if ($("#login-switchuser_{{$module_logo_instance}}").val()) {
- confirmPopup('{tr}Switch user?{/tr}')
- return true;
- } else {
- $("#login-switchuser_{{$module_logo_instance}}").trigger("focus");
+function displayFeedback(type, message) {
+ const feedbackClass = type === "success" ? "alert-success" : "alert-danger";
+ $("#login_feeback_{{$module_logo_instance}}").html(
+ `<div class="alert ${feedbackClass} alert-dismissible">
+ ${message}
+ <button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
+ </div>`
+ );
+}
+var switchUserSelector_{{$module_logo_instance}} = $("#login-switchuser_{{$module_logo_instance}}");
+
+function showSwitchConfirm_{{$module_logo_instance}}(message, $form) {
+ var $modal = $(".footer-modal:not(.show)").first();
+ if (! $modal.length) {
+ if (window.confirm(message)) {
+ $form.append('<input type="hidden" name="confirmForm" value="y">');
+ $form[0].submit();
+ }
+ return;
+ }
+ $modal.find(".modal-content").html(
+ '<div class="modal-header">' +
+ '<h5 class="modal-title">' + message + '</h5>' +
+ '<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="' + tr("Close") + '"></button>' +
+ '</div>' +
+ '<div class="modal-footer">' +
+ '<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">' + tr("Cancel") + '</button>' +
+ '<button type="button" class="btn btn-primary lbox-confirm-ok">' + tr("OK") + '</button>' +
+ '</div>'
+ );
+ var bsModal = bootstrap.Modal.getOrCreateInstance($modal[0]);
+ $modal.find(".lbox-confirm-ok").one("click", function () {
+ bsModal.hide();
+ $form.append('<input type="hidden" name="confirmForm" value="y">');
+ $form[0].submit();
+ });
+ bsModal.show();
+}
+
+$("#switchbox-{{$module_logo_instance}} .submit").on("click", function (event) {
+ event.preventDefault();
+ var selectedUser = (switchUserSelector_{{$module_logo_instance}}.val() || '').trim();
+
+ if (! selectedUser) {
+ displayFeedback("error", tr("Please select a user to switch to."));
+ switchUserSelector_{{$module_logo_instance}}.trigger("focus");
return false;
}
+
+ var msg = tr("Switch user from \"%0\" to \"%1\"?")
+ .replace("%0", currentLoggedInUser)
+ .replace("%1", selectedUser);
+ showSwitchConfirm_{{$module_logo_instance}}(msg, $(this).closest("form"));
+ return false;
+});
+
+$("#revertbox-{{$module_logo_instance}} button[type=submit]").on("click", function (event) {
+ event.preventDefault();
+ showSwitchConfirm_{{$module_logo_instance}}(tr("Return to main user?"), $(this).closest("form"));
+ return false;
});
$('label[for="login-remember-module_{{$module_logo_instance}}"]').on('click', function(event) {
event.stopPropagation();
@@ -232,7 +275,7 @@ $('label[for="login-remember-module_{{$module_logo_instance}}"]').on('click', fu
{button href="tiki-logout.php" _text="{tr}Log out{/tr}"}
</div>
{if !empty($login_module.can_revert)}
- <form action="{$login_module.login_url|escape}" method="post">
+ <form action="{$login_module.login_url|escape}" method="post" id="revertbox-{$module_logo_instance}">
{ticket}
<fieldset>
<legend class="fs-5">{tr}Return to Main User{/tr}</legend>
@@ -243,7 +286,6 @@ $('label[for="login-remember-module_{{$module_logo_instance}}"]').on('click', fu
type="submit"
class="btn btn-primary"
name="actsu"
- onclick="confirmPopup('{tr}Return to main user?{/tr}')"
>
{tr}Switch{/tr}
</button>
@@ -288,7 +330,18 @@ $('label[for="login-remember-module_{{$module_logo_instance}}"]').on('click', fu
{if $prefs.feature_help eq 'y'}
{help url="Switch+User" desc="{tr}Help{/tr}" desc="{tr}Switch User:{/tr}{tr}Select a username and click 'Switch'.<br>Useful for testing permissions.{/tr}"}
{/if}
- {user_selector groupIds=$module_params.groups id="login-switchuser_"|cat:$module_logo_instance name='username' user='' editable=$login_module.can_switch_user class='form-control' allowNone=$module_params.allowNone}
+ {user_selector
+ groupIds=$module_params.groups
+ id="login-switchuser_"|cat:$module_logo_instance
+ name='username'
+ user=''
+ editable=$login_module.can_switch_user
+ class='form-control'
+ allowNone=$module_params.allowNone|default:'y'
+ noneSelectable='n'
+ noneLabel='Select user to switch to'
+ exclude=$user
+ }
</div>
<div class="text-center">
<button
=====================================
tiki-login.php
=====================================
@@ -123,6 +123,25 @@ if (isset($_REQUEST['su']) && $access->checkCsrf(true)) {
if ($loginlib->isSwitched() && $_REQUEST['su'] == 'revert') {
$loginlib->revertSwitch();
} else {
+ $canSwitchUser = false;
+ $perms = Perms::get();
+ if ($perms->admin) {
+ $canSwitchUser = true;
+ } else {
+ $previous_username = $_SESSION[$user_cookie_site . '_previous'] ?? null;
+ if ($previous_username) {
+ $permsContext = new Perms_Context($previous_username);
+ $permsPrevious = Perms::get();
+ $canSwitchUser = (bool) $permsPrevious->admin;
+ unset($permsContext);
+ }
+ }
+
+ if (! $canSwitchUser) {
+ Feedback::errorAndDie(tra('You do not have permission to switch users.'), \Laminas\Http\Response::STATUS_CODE_403);
+ }
+
+ $fromUser = $user;
if (empty($_REQUEST['username'])) {
Feedback::errorAndDie(tra('Username field cannot be empty. Please go back and try again.'), \Laminas\Http\Response::STATUS_CODE_409);
}
@@ -133,9 +152,23 @@ if (isset($_REQUEST['su']) && $access->checkCsrf(true)) {
}
}
if ($userlib->user_exists($_REQUEST['username'])) {
- $loginlib->switchUser($_REQUEST['username']);
+ $targetUser = $userlib->get_user_real_case($_REQUEST['username']);
+ if (strcasecmp($targetUser, $fromUser) === 0) {
+ Feedback::error(tr('You are already logged in as "%0". Please select a different user.', $targetUser));
+ $access->redirect($_SESSION['loginfrom']);
+ }
+
+ $previousUser = $_SESSION[$user_cookie_site . '_previous'] ?? null;
+ if ($loginlib->isSwitched() && $previousUser && strcasecmp($targetUser, $previousUser) === 0) {
+ $loginlib->revertSwitch();
+ } else {
+ $loginlib->switchUser($targetUser);
+ }
+
+ Feedback::success(tr('Switched user from "%0" to "%1".', $fromUser, $targetUser));
$access->redirect();
}
+ Feedback::error(tr('User "%0" not found', $_REQUEST['username']));
$_SESSION["keep_login_box_visible"] = isset($_REQUEST["keep_login_box_visible"]) ? 'y' : 'n';
}
$access->redirect($_SESSION['loginfrom']);
View it on GitLab: https://gitlab.com/tikiwiki/tiki/-/commit/3b6c69ae004d76b91d7c7fd76607b0df43173a1b
--
View it on GitLab: https://gitlab.com/tikiwiki/tiki/-/commit/3b6c69ae004d76b91d7c7fd76607b0df43173a1b
You're receiving this email because of your account on gitlab.com. Manage all notifications: https://gitlab.com/-/profile/notifications | Help: https://gitlab.com/help
_______________________________________________
TikiWiki-cvs mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/tikiwiki-cvs