This commit is contained in:
10
common.php
10
common.php
@ -286,9 +286,15 @@ function qdb_is_web_client_request(): bool {
|
|||||||
return strtolower(trim($h)) === 'web';
|
return strtolower(trim($h)) === 'web';
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Reject coach sign-in from the website; coaches use the mobile app only. */
|
/** Website or coordinator mobile app — not the counselor field app. */
|
||||||
|
function qdb_is_management_client_request(): bool {
|
||||||
|
$h = strtolower(trim($_SERVER['HTTP_X_QDB_CLIENT'] ?? ''));
|
||||||
|
return in_array($h, ['web', 'coordinator'], true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Reject coach sign-in from management clients; coaches use the field app only. */
|
||||||
function qdb_reject_coach_web_login(string $role): void {
|
function qdb_reject_coach_web_login(string $role): void {
|
||||||
if ($role === 'coach' && qdb_is_web_client_request()) {
|
if ($role === 'coach' && qdb_is_management_client_request()) {
|
||||||
json_error(
|
json_error(
|
||||||
'FORBIDDEN',
|
'FORBIDDEN',
|
||||||
'Counselor accounts can only sign in through the mobile app.',
|
'Counselor accounts can only sign in through the mobile app.',
|
||||||
|
|||||||
@ -246,7 +246,7 @@ function userRowHTML(u, myUsername) {
|
|||||||
: '—';
|
: '—';
|
||||||
const pwBadge = u.mustChangePassword == 1
|
const pwBadge = u.mustChangePassword == 1
|
||||||
? '<span class="badge badge-pending" title="Must choose a new password on next sign-in">Temporary</span>'
|
? '<span class="badge badge-pending" title="Must choose a new password on next sign-in">Temporary</span>'
|
||||||
: '<span style="color:var(--text-secondary);font-size:.8rem" title="Using a permanent password">Permanent</span>';
|
: '';
|
||||||
|
|
||||||
const canDelete = !isSelf && (
|
const canDelete = !isSelf && (
|
||||||
callerRole === 'admin' ||
|
callerRole === 'admin' ||
|
||||||
@ -408,25 +408,9 @@ function showCreateForm() {
|
|||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<fieldset class="password-mode-fieldset" style="margin-bottom:14px">
|
<p class="modal-hint" style="margin-bottom:14px">
|
||||||
<legend>Initial password</legend>
|
They must choose their own password on first sign-in.
|
||||||
<div class="password-mode-options">
|
</p>
|
||||||
<label class="password-mode-option">
|
|
||||||
<input type="radio" name="cu_pw_mode" value="temporary" checked>
|
|
||||||
<span class="password-mode-option-title">Temporary password</span>
|
|
||||||
<span class="password-mode-option-desc">
|
|
||||||
They must choose their own password on first sign-in (recommended).
|
|
||||||
</span>
|
|
||||||
</label>
|
|
||||||
<label class="password-mode-option">
|
|
||||||
<input type="radio" name="cu_pw_mode" value="permanent">
|
|
||||||
<span class="password-mode-option-title">Permanent password</span>
|
|
||||||
<span class="password-mode-option-desc">
|
|
||||||
The password above stays in effect; no forced change on first sign-in.
|
|
||||||
</span>
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
</fieldset>
|
|
||||||
|
|
||||||
<div style="display:flex;gap:8px">
|
<div style="display:flex;gap:8px">
|
||||||
<button class="btn btn-primary btn-sm" id="cu_submit">Create User</button>
|
<button class="btn btn-primary btn-sm" id="cu_submit">Create User</button>
|
||||||
@ -476,7 +460,7 @@ async function submitCreateUser() {
|
|||||||
const username = document.getElementById('cu_username').value.trim();
|
const username = document.getElementById('cu_username').value.trim();
|
||||||
const password = document.getElementById('cu_password').value;
|
const password = document.getElementById('cu_password').value;
|
||||||
const role = isSupervisor ? 'coach' : (document.getElementById('cu_role').value || 'coach');
|
const role = isSupervisor ? 'coach' : (document.getElementById('cu_role').value || 'coach');
|
||||||
const mustChange = document.querySelector('input[name="cu_pw_mode"]:checked')?.value === 'temporary' ? 1 : 0;
|
const mustChange = 1;
|
||||||
|
|
||||||
let location = '';
|
let location = '';
|
||||||
let supervisorID = '';
|
let supervisorID = '';
|
||||||
|
|||||||
@ -39,25 +39,9 @@ function ensurePasswordModal() {
|
|||||||
<h2 id="passwordModalTitle">Password</h2>
|
<h2 id="passwordModalTitle">Password</h2>
|
||||||
<p class="modal-subtitle" id="passwordModalSubtitle"></p>
|
<p class="modal-subtitle" id="passwordModalSubtitle"></p>
|
||||||
<p class="modal-hint" id="passwordModalHint"></p>
|
<p class="modal-hint" id="passwordModalHint"></p>
|
||||||
<fieldset class="password-mode-fieldset" id="passwordModalModeFieldset">
|
<p class="modal-hint" id="passwordModalTempHint" style="display:none">
|
||||||
<legend>Password type</legend>
|
They sign in with the password below, then must choose their own password before they can continue.
|
||||||
<div class="password-mode-options">
|
</p>
|
||||||
<label class="password-mode-option">
|
|
||||||
<input type="radio" name="pw_mode" value="temporary" checked>
|
|
||||||
<span class="password-mode-option-title">Temporary password</span>
|
|
||||||
<span class="password-mode-option-desc">
|
|
||||||
They sign in with the password below, then must choose their own password before they can continue. Use this when you share the password by message or in person.
|
|
||||||
</span>
|
|
||||||
</label>
|
|
||||||
<label class="password-mode-option">
|
|
||||||
<input type="radio" name="pw_mode" value="permanent">
|
|
||||||
<span class="password-mode-option-title">Permanent password</span>
|
|
||||||
<span class="password-mode-option-desc">
|
|
||||||
This becomes their regular password. They are not asked to change it when signing in, until you reset it again.
|
|
||||||
</span>
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
</fieldset>
|
|
||||||
<div class="form-group" id="passwordModalOldGroup" style="display:none">
|
<div class="form-group" id="passwordModalOldGroup" style="display:none">
|
||||||
<label for="pw_old_password">Current password</label>
|
<label for="pw_old_password">Current password</label>
|
||||||
<input type="password" id="pw_old_password" autocomplete="current-password" placeholder="Your current password">
|
<input type="password" id="pw_old_password" autocomplete="current-password" placeholder="Your current password">
|
||||||
@ -115,13 +99,9 @@ function openPasswordModal(options) {
|
|||||||
? 'You will stay signed in after saving. Use this password on your next login.'
|
? 'You will stay signed in after saving. Use this password on your next login.'
|
||||||
: signInHintForRole(role);
|
: signInHintForRole(role);
|
||||||
|
|
||||||
modal.querySelector('#passwordModalModeFieldset').style.display = isSelf ? 'none' : '';
|
modal.querySelector('#passwordModalTempHint').style.display = isSelf ? 'none' : '';
|
||||||
modal.querySelector('#passwordModalOldGroup').style.display = isSelf ? '' : 'none';
|
modal.querySelector('#passwordModalOldGroup').style.display = isSelf ? '' : 'none';
|
||||||
|
|
||||||
if (!isSelf) {
|
|
||||||
modal.querySelector('input[name="pw_mode"][value="temporary"]').checked = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
modal.querySelector('#pw_old_password').value = '';
|
modal.querySelector('#pw_old_password').value = '';
|
||||||
modal.querySelector('#pw_new_password').value = '';
|
modal.querySelector('#pw_new_password').value = '';
|
||||||
modal.querySelector('#pw_new_password_confirm').value = '';
|
modal.querySelector('#pw_new_password_confirm').value = '';
|
||||||
@ -201,27 +181,23 @@ async function submitPasswordModal() {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const mode = modal.querySelector('input[name="pw_mode"]:checked')?.value || 'temporary';
|
|
||||||
const temporary = mode === 'temporary';
|
|
||||||
const { userID, username } = modalContext;
|
const { userID, username } = modalContext;
|
||||||
|
|
||||||
const data = await apiPatch('users.php', {
|
const data = await apiPatch('users.php', {
|
||||||
userID,
|
userID,
|
||||||
password: newPassword,
|
password: newPassword,
|
||||||
mustChangePassword: temporary ? 1 : 0,
|
mustChangePassword: 1,
|
||||||
});
|
});
|
||||||
if (!data.success) throw new Error(data.error || 'Failed to reset password');
|
if (!data.success) throw new Error(data.error || 'Failed to reset password');
|
||||||
|
|
||||||
closePasswordModal();
|
closePasswordModal();
|
||||||
showToast(
|
showToast(
|
||||||
temporary
|
`Temporary password set for "${username}" — they must change it on next sign-in`,
|
||||||
? `Temporary password set for "${username}" — they must change it on next sign-in`
|
|
||||||
: `Permanent password set for "${username}"`,
|
|
||||||
'success'
|
'success'
|
||||||
);
|
);
|
||||||
|
|
||||||
document.dispatchEvent(new CustomEvent('qdb:password-reset', {
|
document.dispatchEvent(new CustomEvent('qdb:password-reset', {
|
||||||
detail: { userID, mustChangePassword: data.mustChangePassword ?? (temporary ? 1 : 0) },
|
detail: { userID, mustChangePassword: data.mustChangePassword ?? 1 },
|
||||||
}));
|
}));
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
showModalError(errEl, e.message);
|
showModalError(errEl, e.message);
|
||||||
|
|||||||
Reference in New Issue
Block a user