Carry next through the password change and let requesters pick a username
A forced or voluntary password change now redirects to the validated `next` target (the page the user was heading for, or the home page) instead of landing back on the account form. Login and the must-change-password middleware pass the destination along as /account?change=1&next=... and the form carries it as a hidden field. The request-access form gains a required Username field validated with the account rules and rejected when an account or another open request already holds it (case-insensitive). Migration 0009 stores it on the request; the notification email and the Users dashboard show it, and the Approve form is prefilled with it. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QVPagF6jfDv78CC5Jv2wp4
This commit is contained in:
@@ -1 +1 @@
|
||||
{% extends "layout.html" %}{% block ears %}<span>Reader account</span>{% endblock %}{% block content %}<section class="mx-auto mt-12 max-w-md px-4 sm:mt-16 sm:px-6"><p class="font-sans text-[0.72rem] uppercase tracking-[0.12em] text-muted">Reader account</p><h1 class="mt-2 text-4xl font-semibold leading-[1.1] tracking-[-0.01em]">Account</h1>{% if change_required %}<p class="notice mt-6">Choose a new password to continue. Your temporary password goes in the current-password field.</p>{% endif %}{% if !error.is_empty() %}<p class="error mt-6">{{ error }}</p>{% endif %}<form class="mt-8 grid gap-5 border-b border-rule pb-9 font-sans text-sm" method="post" action="/account/password"><label class="grid gap-1.5 font-medium">Current password <input class="min-h-11 w-full" type="password" name="current_password" autocomplete="current-password" required></label><label class="grid gap-1.5 font-medium">New password <input class="min-h-11 w-full" type="password" name="new_password" autocomplete="new-password" minlength="12" required></label><label class="grid gap-1.5 font-medium">Confirm password <input class="min-h-11 w-full" type="password" name="confirm_password" autocomplete="new-password" minlength="12" required></label><button class="btn-primary mt-1 w-full">Change password</button></form><div class="mt-7 flex flex-wrap gap-3 font-sans text-sm"><form class="m-0" method="post" action="/account/logout-all" data-confirm="Sign out everywhere?"><button class="btn">Sign out everywhere</button></form><form class="m-0" method="post" action="/logout"><button class="btn">Sign out</button></form></div></section>{% endblock %}
|
||||
{% extends "layout.html" %}{% block ears %}<span>Reader account</span>{% endblock %}{% block content %}<section class="mx-auto mt-12 max-w-md px-4 sm:mt-16 sm:px-6"><p class="font-sans text-[0.72rem] uppercase tracking-[0.12em] text-muted">Reader account</p><h1 class="mt-2 text-4xl font-semibold leading-[1.1] tracking-[-0.01em]">Account</h1>{% if change_required %}<p class="notice mt-6">Choose a new password to continue. Your temporary password goes in the current-password field.</p>{% endif %}{% if !error.is_empty() %}<p class="error mt-6">{{ error }}</p>{% endif %}<form class="mt-8 grid gap-5 border-b border-rule pb-9 font-sans text-sm" method="post" action="/account/password"><input type="hidden" name="next" value="{{ next }}"><label class="grid gap-1.5 font-medium">Current password <input class="min-h-11 w-full" type="password" name="current_password" autocomplete="current-password" required></label><label class="grid gap-1.5 font-medium">New password <input class="min-h-11 w-full" type="password" name="new_password" autocomplete="new-password" minlength="12" required></label><label class="grid gap-1.5 font-medium">Confirm password <input class="min-h-11 w-full" type="password" name="confirm_password" autocomplete="new-password" minlength="12" required></label><button class="btn-primary mt-1 w-full">Change password</button></form><div class="mt-7 flex flex-wrap gap-3 font-sans text-sm"><form class="m-0" method="post" action="/account/logout-all" data-confirm="Sign out everywhere?"><button class="btn">Sign out everywhere</button></form><form class="m-0" method="post" action="/logout"><button class="btn">Sign out</button></form></div></section>{% endblock %}
|
||||
|
||||
@@ -6,12 +6,13 @@
|
||||
<p class="muted text-sm">Approve creates a user account with a temporary password and emails it; the user must choose a new password on first sign-in. Or create accounts with <code>daily-epub users add</code> and mark the request done. Change, disable, enable, or sign out users with the <code>daily-epub users</code> CLI.</p>
|
||||
<section class="card"><h2>Access requests</h2>
|
||||
{% if requests.is_empty() %}<p class="muted">No open requests.</p>{% else %}<div class="scroll-x"><table>
|
||||
<thead><tr><th>Email</th><th>Reason or comment</th><th>Requested</th><th><span class="sr-only">Action</span></th></tr></thead>
|
||||
<thead><tr><th>Email</th><th>Username</th><th>Reason or comment</th><th>Requested</th><th><span class="sr-only">Action</span></th></tr></thead>
|
||||
<tbody>{% for request in requests %}<tr>
|
||||
<td class="font-medium text-ink"><a href="mailto:{{ request.email }}">{{ request.email }}</a></td>
|
||||
<td class="font-medium text-ink">{{ request.suggested_username }}</td>
|
||||
<td class="cell-wrap">{% if request.reason.is_empty() %}<span class="text-muted">—</span>{% else %}{{ request.reason }}{% endif %}</td>
|
||||
<td class="cell-tight text-muted">{{ request.requested }}</td>
|
||||
<td><div class="flex flex-wrap gap-2"><form class="form-inline" method="post" action="/dashboard/users/requests/{{ request.id }}/approve"><input type="text" name="username" value="{{ request.suggested_username }}" required pattern="[a-z0-9_-]+" aria-label="Username for {{ request.email }}"><button class="btn-primary" type="submit"{% if !mail_configured %} disabled title="Email is not configured"{% endif %}>Approve</button></form><form method="post" action="/dashboard/users/requests/{{ request.id }}/done"><button class="btn" type="submit">Mark done</button></form></div></td>
|
||||
<td><div class="flex flex-wrap gap-2"><form class="form-inline" method="post" action="/dashboard/users/requests/{{ request.id }}/approve"><input type="text" name="username" value="{{ request.suggested_username }}" autocomplete="username" maxlength="32" required pattern="[A-Za-z0-9._-]+" aria-label="Username for {{ request.email }}"><button class="btn-primary" type="submit"{% if !mail_configured %} disabled title="Email is not configured"{% endif %}>Approve</button></form><form method="post" action="/dashboard/users/requests/{{ request.id }}/done"><button class="btn" type="submit">Mark done</button></form></div></td>
|
||||
</tr>{% endfor %}</tbody></table></div>{% endif %}
|
||||
</section>
|
||||
{% if users.len() > 1 %}<input type="search" class="table-filter" placeholder="Filter rows on this page" aria-label="Filter rows on this page" data-table-filter>{% endif %}<div class="scroll-x tall"><table data-filter>
|
||||
|
||||
@@ -1 +1 @@
|
||||
{% extends "layout.html" %}{% block ears %}<span>Reader access</span>{% endblock %}{% block content %}<section class="mx-auto mt-12 max-w-md px-4 sm:mt-16 sm:px-6"><p class="font-sans text-[0.72rem] uppercase tracking-[0.12em] text-muted">Reader access</p><h1 class="mt-2 text-4xl font-semibold leading-[1.1] tracking-[-0.01em]">Request access</h1>{% if submitted %}<p class="notice mt-6">Thanks — requests are reviewed by hand; you'll hear back by email.</p>{% else %}<p class="mt-4 text-ink-2">Signed-in accounts can read the complete issue online, including every article's full text, and download the EPUB and XTC editions. They have no access to ratings or admin tools.</p>{% if !error.is_empty() %}<p class="error mt-6">{{ error }}</p>{% endif %}<form class="mt-8 grid gap-5 font-sans text-sm" method="post" action="/request-access"><label class="grid gap-1.5 font-medium">Email <input class="min-h-11 w-full" type="email" name="email" autocomplete="email" value="{{ email }}" maxlength="254" required></label><label class="grid gap-1.5 font-medium">Reason or comment <span class="font-normal text-muted">Optional</span><textarea class="w-full" name="reason" maxlength="2000">{{ reason }}</textarea></label><label class="sr-only" aria-hidden="true">Website <input name="website" autocomplete="off" tabindex="-1"></label><button class="btn-primary mt-1 w-full" type="submit">Request access</button></form>{% endif %}</section>{% endblock %}
|
||||
{% extends "layout.html" %}{% block ears %}<span>Reader access</span>{% endblock %}{% block content %}<section class="mx-auto mt-12 max-w-md px-4 sm:mt-16 sm:px-6"><p class="font-sans text-[0.72rem] uppercase tracking-[0.12em] text-muted">Reader access</p><h1 class="mt-2 text-4xl font-semibold leading-[1.1] tracking-[-0.01em]">Request access</h1>{% if submitted %}<p class="notice mt-6">Thanks — requests are reviewed by hand; you'll hear back by email.</p>{% else %}<p class="mt-4 text-ink-2">Signed-in accounts can read the complete issue online, including every article's full text, and download the EPUB and XTC editions. They have no access to ratings or admin tools.</p>{% if !error.is_empty() %}<p class="error mt-6">{{ error }}</p>{% endif %}<form class="mt-8 grid gap-5 font-sans text-sm" method="post" action="/request-access"><label class="grid gap-1.5 font-medium">Email <input class="min-h-11 w-full" type="email" name="email" autocomplete="email" value="{{ email }}" maxlength="254" required></label><label class="grid gap-1.5 font-medium">Username <input class="min-h-11 w-full" type="text" name="username" autocomplete="username" value="{{ username }}" maxlength="32" pattern="[A-Za-z0-9._-]+" required><span class="font-normal text-muted">1–32 characters: letters, digits, . _ -</span></label><label class="grid gap-1.5 font-medium">Reason or comment <span class="font-normal text-muted">Optional</span><textarea class="w-full" name="reason" maxlength="2000">{{ reason }}</textarea></label><label class="sr-only" aria-hidden="true">Website <input name="website" autocomplete="off" tabindex="-1"></label><button class="btn-primary mt-1 w-full" type="submit">Request access</button></form>{% endif %}</section>{% endblock %}
|
||||
|
||||
Reference in New Issue
Block a user