Files
once-campfire/app/views/users/profiles/show.html.erb
T
Jeremy Daer 44a92ff6c6 Ask for the current password to change it
Changing the password now ends every other session, so whoever holds
one could shut the owner out of their own account with a new password.
The profile asks for the current password whenever the password
changes; without it, or with a wrong one, nothing is saved and the page
says why. Other profile edits don't need it, and the admin
reset-password script still works without it.

Attempts are rate limited like signing in, 10 in 3 minutes per address,
so the current password can't be guessed here instead.
2026-10-19 12:17:14 -07:00

129 lines
5.5 KiB
ERB

<% @page_title = @user.name %>
<% content_for :nav do %>
<div class="flex-item-justify-start">
<%= link_back %>
</div>
<div class="flex-item-justify-end">
<%= form_with url: session_path, method: :delete, data: { controller: "sessions" } do %>
<%= hidden_field_tag :push_subscription_endpoint, nil, data: { sessions_target: "pushSubscriptionEndpoint" } %>
<button class="btn" data-action="sessions#logout:prevent">
<%= image_tag "logout.svg", aria: { hidden: "true" } %>
<span class="for-screen-reader">Log out</span>
</button>
<% end %>
</div>
<% end %>
<section class="panel flex flex-column gap" style="view-transition-name: avatar-<%= @user.id %>">
<%= render partial: "pwa/install_instructions" %>
<div class="align-center center avatar__form gap" data-controller="upload-preview">
<%= profile_form_with @user, class:"txt-medium" do |form| %>
<label class="btn input--file">
<%= image_tag "camera.svg", aria: { hidden: "true" }, size: 20 %>
<%= form.file_field :avatar, id: "file", class: "input", accept: "image/*",
data: { upload_preview_target: "input", action: "upload-preview#previewImage change->form#submit" } %>
<span class="for-screen-reader">Upload avatar</span>
</label>
<% end %>
<%= profile_form_with @user do |form| %>
<label class="btn avatar input--file txt-xx-large">
<%= image_tag fresh_user_avatar_path(@user), aria: { hidden: "true" }, size: 300, data: { upload_preview_target: "image" } %>
<%= form.file_field :avatar, id: "file", class: "input", accept: "image/*",
data: { upload_preview_target: "input", action: "upload-preview#previewImage change->form#submit" } %>
<span class="for-screen-reader">Avatar</span>
</label>
<% end %>
<% if @user.avatar.attached? %>
<%= button_to user_avatar_path(@user), method: :delete, class: "btn btn--negative txt-small avatar__delete-btn" do %>
<%= image_tag "minus.svg", aria: { hidden: "true" }, size: 20 %>
<span class="for-screen-reader">Delete avatar</span>
<% end %>
<% end %>
</div>
<%= profile_form_with @user do |form| %>
<div class="flex flex-column gap">
<div class="flex align-center gap">
<%= translation_button(:user_name) %>
<label class="flex align-center gap flex-item-grow input input--actor">
<%= form.text_field :name, class: "input txt-large ", autocomplete: "name", placeholder: "Enter your name",
autofocus: true, required: true, data: { "1p-ignore": true } %>
<%= image_tag "person.svg", aria: { hidden: "true" }, size: 24, class: "colorize--black" %>
</label>
</div>
<div class="flex align-center gap">
<%= translation_button(:email_address) %>
<label class="flex align-center gap flex-item-grow input input--actor">
<%= form.email_field :email_address, class: "input txt-large", value: @user.email_address, autocomplete: "username",
placeholder: "Enter your email address", required: false %>
<%= image_tag "email.svg", aria: { hidden: "true" }, size: 24, class: "colorize--black" %>
</label>
</div>
<div class="flex align-center gap">
<%= translation_button(:current_password) %>
<label class="flex align-center gap flex-item-grow input input--actor">
<%= form.password_field :password_challenge, class: "input txt-large", autocomplete: "current-password",
placeholder: "Current password", required: false, maxlength: 72 %>
<%= image_tag "password.svg", aria: { hidden: "true" }, size: 24, class: "colorize--black" %>
</label>
</div>
<div class="flex align-center gap">
<%= translation_button(:update_password) %>
<label class="flex align-center gap flex-item-grow input input--actor">
<%= form.password_field :password, class: "input txt-large", autocomplete: "new-password", placeholder: "Change password",
required: false, maxlength: 72 %>
<%= image_tag "password.svg", aria: { hidden: "true" }, size: 24, class: "colorize--black" %>
</label>
</div>
<div class="flex align-start gap">
<%= translation_button(:bio) %>
<label class="flex align--center gap flex-item--grow input input--actor">
<%= form.text_area :bio, class: "input txt-large", placeholder: "A few words about yourself…", maxlength: 200,
rows: 3, required: false %>
<%= image_tag "bio.svg", aria: { hidden: "true" }, size: 24, class: "colorize--black" %>
</label>
</div>
<%= profile_form_submit_button %>
</div>
<% end %>
<div class="margin-block pad-inline pad-block fill-shade border-radius">
<menu class="flex flex-column gap margin-none pad">
<%= render partial: "users/profiles/membership", collection: @shared_memberships %>
<% if @direct_memberships.any? && @shared_memberships.any? %>
<hr class="separator full-width" style="--border-style: solid">
<% end %>
<%= render partial: "users/profiles/membership", collection: @direct_memberships %>
</menu>
</div>
<%= render "users/profiles/transfer", user: @user %>
<% if Rails.env.development? %>
<fieldset>
<legend>
<%= image_tag "key.svg", aria: { hidden: "true" }, size: 36, class: "colorize--black" %>
</legend>
<%= link_to "Push Notifications Dev Mode", user_push_subscriptions_url, class: "btn txt-small center" %>
</fieldset>
<% end %>
</section>