diff --git a/.rubocop.yml b/.rubocop.yml index 6a4974108..b78883113 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -155,4 +155,4 @@ Style/TrailingCommaInArrayLiteral: EnforcedStyleForMultiline: consistent_comma Style/TrailingCommaInHashLiteral: - EnforcedStyleForMultiline: consistent_comma + EnforcedStyleForMultiline: consistent_comma \ No newline at end of file diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml index f26e4fa29..4ebf69619 100644 --- a/.rubocop_todo.yml +++ b/.rubocop_todo.yml @@ -498,7 +498,7 @@ Layout/SpaceInsideBlockBraces: # SupportedStylesForEmptyBraces: space, no_space Layout/SpaceInsideHashLiteralBraces: Exclude: - - 'app/controllers/admin/staff_notes_controller.rb' + - 'app/controllers/staff_notes_controller.rb' - 'app/controllers/admin/users_controller.rb' - 'app/controllers/application_controller.rb' - 'app/controllers/comment_votes_controller.rb' @@ -2416,7 +2416,7 @@ Style/StringLiterals: Exclude: - 'Gemfile' - 'Rakefile' - - 'app/controllers/admin/staff_notes_controller.rb' + - 'app/controllers/staff_notes_controller.rb' - 'app/controllers/blips_controller.rb' - 'app/controllers/comments_controller.rb' - 'app/controllers/edit_histories_controller.rb' diff --git a/Gemfile b/Gemfile index 09b10c97f..b681721c0 100644 --- a/Gemfile +++ b/Gemfile @@ -26,6 +26,7 @@ gem 'marcel' gem 'sidekiq-unique-jobs' gem 'redis' gem 'request_store' +gem "zxcvbn-ruby", require: "zxcvbn" gem "diffy" gem "rugged" diff --git a/Gemfile.lock b/Gemfile.lock index d924493e8..17e4506a4 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -376,6 +376,7 @@ GEM websocket-extensions (>= 0.1.0) websocket-extensions (0.1.5) zeitwerk (2.6.13) + zxcvbn-ruby (1.2.0) PLATFORMS ruby @@ -426,6 +427,7 @@ DEPENDENCIES streamio-ffmpeg webmock webpacker (>= 4.0.x) + zxcvbn-ruby BUNDLED WITH 2.4.10 diff --git a/app/controllers/admin/staff_notes_controller.rb b/app/controllers/admin/staff_notes_controller.rb deleted file mode 100644 index 2b5fad79f..000000000 --- a/app/controllers/admin/staff_notes_controller.rb +++ /dev/null @@ -1,41 +0,0 @@ -# frozen_string_literal: true - -module Admin - class StaffNotesController < ApplicationController - before_action :can_view_staff_notes_only - respond_to :html - - def index - @user = User.find_by(id: params[:user_id]) - @notes = StaffNote.search(search_params.merge({ user_id: params[:user_id] })).includes(:user, :creator).paginate(params[:page], limit: params[:limit]) - respond_with(@notes) - end - - def new - @user = User.find(params[:user_id]) - @staff_note = StaffNote.new(note_params) - respond_with(@note) - end - - def create - @user = User.find(params[:user_id]) - @staff_note = StaffNote.create(note_params.merge({creator: CurrentUser.user, user_id: @user.id})) - flash[:notice] = @staff_note.valid? ? "Staff Note added" : @staff_note.errors.full_messages.join("; ") - respond_with(@staff_note) do |format| - format.html do - redirect_back fallback_location: admin_staff_notes_path - end - end - end - - private - - def search_params - permit_search_params(%i[creator_id creator_name user_id user_name resolved body_matches without_system_user]) - end - - def note_params - params.fetch(:staff_note, {}).permit(%i[body]) - end - end -end diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index 490cafc3d..68d84c5f8 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -165,6 +165,7 @@ class ApplicationController < ActionController::Base def set_current_user SessionLoader.new(request).load + session.send(:load!) unless session.send(:loaded?) end def reset_current_user @@ -173,6 +174,14 @@ class ApplicationController < ActionController::Base CurrentUser.safe_mode = Danbooru.config.safe_mode? end + def requires_reauthentication + return redirect_to(new_session_path(url: request.fullpath)) if CurrentUser.user.is_anonymous? + last_authenticated_at = session[:last_authenticated_at] + if last_authenticated_at.blank? || Time.zone.parse(last_authenticated_at) < 1.hour.ago + redirect_to(confirm_password_session_path(url: request.fullpath)) + end + end + def user_access_check(method) if !CurrentUser.user.send(method) || CurrentUser.user.is_banned? || IpBan.is_banned?(CurrentUser.ip_addr) access_denied diff --git a/app/controllers/maintenance/user/api_keys_controller.rb b/app/controllers/maintenance/user/api_keys_controller.rb index f2809e1dc..f6b0e5d02 100644 --- a/app/controllers/maintenance/user/api_keys_controller.rb +++ b/app/controllers/maintenance/user/api_keys_controller.rb @@ -3,17 +3,14 @@ module Maintenance module User class ApiKeysController < ApplicationController + before_action :requires_reauthentication before_action :member_only - before_action :authenticate!, except: [:show] - rescue_from ::SessionLoader::AuthenticationFailure, with: :authentication_failed + before_action :load_apikey respond_to :html def show end - def view - end - def update @api_key.regenerate! redirect_to(user_api_key_path(CurrentUser.user), notice: "API key regenerated") @@ -24,19 +21,10 @@ module Maintenance redirect_to(CurrentUser.user) end - protected + private - def authenticate! - if ::User.authenticate(CurrentUser.user.name, params[:user][:password]) == CurrentUser.user - @api_key = CurrentUser.user.api_key || ApiKey.generate!(CurrentUser.user) - @password = params[:user][:password] - else - raise ::SessionLoader::AuthenticationFailure - end - end - - def authentication_failed - redirect_to(user_api_key_path(CurrentUser.user), notice: "Password was incorrect.") + def load_apikey + @api_key = CurrentUser.user.api_key || ApiKey.generate!(CurrentUser.user) end end end diff --git a/app/controllers/mod_actions_controller.rb b/app/controllers/mod_actions_controller.rb index b3fd2076b..c13f61348 100644 --- a/app/controllers/mod_actions_controller.rb +++ b/app/controllers/mod_actions_controller.rb @@ -5,19 +5,20 @@ class ModActionsController < ApplicationController def index @mod_actions = ModActionDecorator.decorate_collection( - ModAction.includes(:creator).search(search_params).paginate(params[:page], limit: params[:limit]), + ModAction.visible(CurrentUser.user).includes(:creator).search(search_params).paginate(params[:page], limit: params[:limit]), ) - respond_with(@mod_actions) do |format| - format.json do - render json: @mod_actions.to_json - end - end + respond_with(@mod_actions) end def show @mod_action = ModAction.find(params[:id]) + check_permission(@mod_action) respond_with(@mod_action) do |fmt| fmt.html { redirect_to mod_actions_path(search: { id: @mod_action.id }) } end end + + def check_permission(mod_action) + raise(User::PrivilegeError) unless mod_action.can_view?(CurrentUser.user) + end end diff --git a/app/controllers/sessions_controller.rb b/app/controllers/sessions_controller.rb index a95243a6f..0a62a3f34 100644 --- a/app/controllers/sessions_controller.rb +++ b/app/controllers/sessions_controller.rb @@ -6,26 +6,31 @@ class SessionsController < ApplicationController end def create + sparams = params.fetch(:session, {}).slice(:url, :name, :password, :remember) if RateLimiter.check_limit("login:#{request.remote_ip}", 15, 12.hours) DanbooruLogger.add_attributes("user.login" => "rate_limited") - return redirect_to(new_session_path, :notice => "Username/Password was incorrect") + return redirect_to(new_session_path, notice: "Username/Password was incorrect") end - session_creator = SessionCreator.new(session, cookies, params[:name], params[:password], request.remote_ip, params[:remember], request.ssl?) + session_creator = SessionCreator.new(request, session, cookies, sparams[:name], sparams[:password], sparams[:remember].to_s.truthy?) if session_creator.authenticate - url = params[:url] if params[:url] && params[:url].start_with?("/") && !params[:url].start_with?("//") + url = sparams[:url] if sparams[:url] && sparams[:url].start_with?("/") && !sparams[:url].start_with?("//") DanbooruLogger.add_attributes("user.login" => "success") - redirect_to(url || posts_path, :notice => "You are now logged in") + redirect_to(url || posts_path) else RateLimiter.hit("login:#{request.remote_ip}", 6.hours) DanbooruLogger.add_attributes("user.login" => "fail") - redirect_to(new_session_path, :notice => "Username/Password was incorrect") + redirect_back(fallback_location: new_session_path, notice: "Username/Password was incorrect") end end def destroy session.delete(:user_id) - cookies.delete :remember - redirect_to(posts_path, :notice => "You are now logged out") + cookies.delete(:remember) + session.delete(:last_authenticated_at) + redirect_to(posts_path, notice: "You are now logged out") + end + + def confirm_password end end diff --git a/app/controllers/staff_notes_controller.rb b/app/controllers/staff_notes_controller.rb new file mode 100644 index 000000000..cefaac26b --- /dev/null +++ b/app/controllers/staff_notes_controller.rb @@ -0,0 +1,77 @@ +# frozen_string_literal: true + +class StaffNotesController < ApplicationController + before_action :can_view_staff_notes_only + before_action :load_staff_note, only: %i[show edit update delete undelete] + before_action :check_edit_privilege, only: %i[update] + before_action :check_delete_privilege, only: %i[delete undelete] + respond_to :html, :json + + def index + @user = User.find_by(id: params[:user_id]) + @notes = StaffNote.search(search_params.merge({ user_id: params[:user_id] })).includes(:user, :creator).paginate(params[:page], limit: params[:limit]) + respond_with(@notes) + end + + def show + respond_with(@staff_note) + end + + def new + @user = User.find(params[:user_id]) + @staff_note = StaffNote.new(staff_note_params) + respond_with(@note) + end + + def edit + respond_with(@staff_note) + end + + def create + @user = User.find(params[:user_id]) + @staff_note = StaffNote.create(staff_note_params.merge({ user_id: @user.id })) + flash[:notice] = @staff_note.valid? ? "Staff Note added" : @staff_note.errors.full_messages.join("; ") + respond_with(@staff_note) do |format| + format.html do + redirect_back fallback_location: staff_notes_path + end + end + end + + def update + @staff_note.update(staff_note_params) + redirect_back(fallback_location: staff_notes_path) + end + + def delete + @staff_note.update(is_deleted: true) + redirect_back(fallback_location: staff_notes_path) + end + + def undelete + @staff_note.update(is_deleted: false) + redirect_back(fallback_location: staff_notes_path) + end + + private + + def search_params + permit_search_params(%i[creator_id creator_name updater_id updater_name user_id user_name body_matches without_system_user include_deleted]) + end + + def staff_note_params + params.fetch(:staff_note, {}).permit(%i[body]) + end + + def load_staff_note + @staff_note = StaffNote.find(params[:id]) + end + + def check_edit_privilege + raise User::PrivilegeError unless @staff_note.can_edit?(CurrentUser.user) + end + + def check_delete_privilege + raise User::PrivilegeError unless @staff_note.can_delete?(CurrentUser.user) + end +end diff --git a/app/decorators/mod_action_decorator.rb b/app/decorators/mod_action_decorator.rb index 45d12b184..a0b7f71c0 100644 --- a/app/decorators/mod_action_decorator.rb +++ b/app/decorators/mod_action_decorator.rb @@ -55,7 +55,7 @@ class ModActionDecorator < ApplicationDecorator when "artist_delete" "Deleted artist ##{vals['artist_id']} (#{vals['artist_name']})" when "artist_page_rename" - "Renamed artist page (\"#{vals['old_name']}\":/artists/show_or_new?name=#{vals['old_name']} -> \"#{vals['new_name']}\":/artists/show_or_new?name=#{vals['new_name']})" + "Renamed artist page (\"#{vals['old_name']}\":/artists/show_or_new?name=#{vals['old_name']} โ \"#{vals['new_name']}\":/artists/show_or_new?name=#{vals['new_name']})" when "artist_page_lock" "Locked artist page artist ##{vals['artist_page']}" when "artist_page_unlock" @@ -77,6 +77,16 @@ class ModActionDecorator < ApplicationDecorator when "avoid_posting_undelete" "Undeleted \"avoid posting ##{vals['id']}\":/avoid_postings/#{vals['id']} for [[#{vals['artist_name']}]]" + ### Staff Note ### + when "staff_note_create" + "Created \"staff note ##{vals['id']}\":/staff_notes/#{vals['id']} for #{user}\n#{vals['body']}" + when "staff_note_update" + "Updated \"staff note ##{vals['id']}\":/staff_notes/#{vals['id']} for #{user}\n#{vals['body']}" + when "staff_note_delete" + "Deleted \"staff note ##{vals['id']}\":/staff_notes/#{vals['id']} for #{user}" + when "staff_note_undelete" + "Undeleted \"staff note ##{vals['id']}\":/staff_notes/#{vals['id']} for #{user}" + ### User ### when "user_delete" @@ -276,7 +286,7 @@ class ModActionDecorator < ApplicationDecorator ### BURs ### when "mass_update" - "Mass updated [[#{vals['antecedent']}]] -> [[#{vals['consequent']}]]" + "Mass updated [[#{vals['antecedent']}]] โ [[#{vals['consequent']}]]" when "nuke_tag" "Nuked tag [[#{vals['tag_name']}]]" @@ -319,7 +329,7 @@ class ModActionDecorator < ApplicationDecorator "Edited whitelist entry" else if vals['old_pattern'] && vals['old_pattern'] != vals['pattern'] && CurrentUser.is_admin? - "Edited whitelist entry '#{vals['old_pattern']}' -> '#{vals['pattern']}'" + "Edited whitelist entry '#{vals['old_pattern']}' โ '#{vals['pattern']}'" else "Edited whitelist entry '#{CurrentUser.is_admin? ? vals['pattern'] : vals['note']}'" end diff --git a/app/indexes/post_index.rb b/app/indexes/post_index.rb index ac8d46d4a..f755adf8f 100644 --- a/app/indexes/post_index.rb +++ b/app/indexes/post_index.rb @@ -28,6 +28,7 @@ module PostIndex tag_count_general: { type: "integer" }, tag_count_artist: { type: "integer" }, + tag_count_contributor: { type: "integer" }, tag_count_character: { type: "integer" }, tag_count_copyright: { type: "integer" }, tag_count_meta: { type: "integer" }, @@ -238,37 +239,38 @@ module PostIndex tag_count: tag_count, change_seq: change_seq, - tag_count_general: tag_count_general, - tag_count_artist: tag_count_artist, - tag_count_character: tag_count_character, - tag_count_copyright: tag_count_copyright, - tag_count_meta: tag_count_meta, - tag_count_species: tag_count_species, - tag_count_invalid: tag_count_invalid, - tag_count_lore: tag_count_lore, - tag_count_people: tag_count_people, - comment_count: options[:comment_count] || comment_count, + tag_count_general: tag_count_general, + tag_count_artist: tag_count_artist, + tag_count_contributor: tag_count_contributor, + tag_count_character: tag_count_character, + tag_count_copyright: tag_count_copyright, + tag_count_meta: tag_count_meta, + tag_count_species: tag_count_species, + tag_count_invalid: tag_count_invalid, + tag_count_lore: tag_count_lore, + tag_count_people: tag_count_people, - file_size: file_size, - parent: parent_id, - pools: options[:pools] || ::Pool.where("? = ANY(post_ids)", id).pluck(:id), - sets: options[:sets] || ::PostSet.where("? = ANY(post_ids)", id).pluck(:id), - commenters: options[:commenters] || ::Comment.undeleted.where(post_id: id).pluck(:creator_id), - noters: options[:noters] || ::Note.active.where(post_id: id).pluck(:creator_id), - faves: options[:faves] || ::Favorite.where(post_id: id).pluck(:user_id), - upvotes: options[:upvotes] || ::PostVote.where(post_id: id).where("score > 0").pluck(:user_id), - downvotes: options[:downvotes] || ::PostVote.where(post_id: id).where("score < 0").pluck(:user_id), - children: options[:children] || ::Post.where(parent_id: id).pluck(:id), - notes: options[:notes] || ::Note.active.where(post_id: id).pluck(:body), - uploader: uploader_id, - approver: approver_id, - deleter: options[:deleter] || ::PostFlag.where(post_id: id, is_resolved: false, is_deletion: true).order(id: :desc).first&.creator_id, - del_reason: options[:del_reason] || ::PostFlag.where(post_id: id, is_resolved: false, is_deletion: true).order(id: :desc).first&.reason&.downcase, - width: image_width, - height: image_height, - mpixels: image_width && image_height ? (image_width.to_f * image_height / 1_000_000).round(2) : 0.0, - aspect_ratio: image_width && image_height ? (image_width.to_f / [image_height, 1].max).round(2) : 1.0, - duration: duration, + comment_count: options[:comment_count] || comment_count, + file_size: file_size, + parent: parent_id, + pools: options[:pools] || ::Pool.where("? = ANY(post_ids)", id).pluck(:id), + sets: options[:sets] || ::PostSet.where("? = ANY(post_ids)", id).pluck(:id), + commenters: options[:commenters] || ::Comment.undeleted.where(post_id: id).pluck(:creator_id), + noters: options[:noters] || ::Note.active.where(post_id: id).pluck(:creator_id), + faves: options[:faves] || ::Favorite.where(post_id: id).pluck(:user_id), + upvotes: options[:upvotes] || ::PostVote.where(post_id: id).where("score > 0").pluck(:user_id), + downvotes: options[:downvotes] || ::PostVote.where(post_id: id).where("score < 0").pluck(:user_id), + children: options[:children] || ::Post.where(parent_id: id).pluck(:id), + notes: options[:notes] || ::Note.active.where(post_id: id).pluck(:body), + uploader: uploader_id, + approver: approver_id, + deleter: options[:deleter] || ::PostFlag.where(post_id: id, is_resolved: false, is_deletion: true).order(id: :desc).first&.creator_id, + del_reason: options[:del_reason] || ::PostFlag.where(post_id: id, is_resolved: false, is_deletion: true).order(id: :desc).first&.reason&.downcase, + width: image_width, + height: image_height, + mpixels: image_width && image_height ? (image_width.to_f * image_height / 1_000_000).round(2) : 0.0, + aspect_ratio: image_width && image_height ? (image_width.to_f / [image_height, 1].max).round(2) : 1.0, + duration: duration, tags: tag_string.split(" "), md5: md5, diff --git a/app/javascript/packs/application.js b/app/javascript/packs/application.js index 1d30f168f..b04f50f6d 100644 --- a/app/javascript/packs/application.js +++ b/app/javascript/packs/application.js @@ -47,6 +47,7 @@ export { default as PostReplacement } from '../src/javascripts/post_replacement. export { default as PostVersions } from '../src/javascripts/post_versions.js'; export { default as Replacer } from '../src/javascripts/replacer.js'; export { default as Shortcuts } from '../src/javascripts/shortcuts.js'; +export { default as StaffNote } from '../src/javascripts/staff_notes.js'; export { default as Utility } from '../src/javascripts/utility.js'; export { default as TagRelationships } from '../src/javascripts/tag_relationships.js'; export { default as Takedown } from '../src/javascripts/takedowns.js'; diff --git a/app/javascript/src/javascripts/password.js b/app/javascript/src/javascripts/password.js new file mode 100644 index 000000000..158ee56a8 --- /dev/null +++ b/app/javascript/src/javascripts/password.js @@ -0,0 +1,70 @@ +import zxcvbn from "zxcvbn"; +import Page from "./utility/page"; + +let Password = {}; + +Password.init_validation = function () { + if (Page.matches("users", "new") || Page.matches("users", "create")) + Password.bootstrap_input($("#user_password"), [$("#user_name"), $("#user_email")]); + + if (Page.matches("maintenance-user-password-resets", "edit")) + Password.bootstrap_input($("#password")); + + if (Page.matches("maintenance-user-passwords", "edit")) + Password.bootstrap_input($("#user_password")); +}; + +Password.bootstrap_input = function ($password, $inputs = []) { + // Set up the UI + $password.parent().addClass("password-input"); + + const hint = $("
- Use this to remove <%= link_to "avoid_posting", wiki_page_path(id: "avoid_posting") %> and <%= link_to "conditional_dnp", wiki_page_path(id: "conditional_dnp") %> from posts of artists who got removed from the DNP list. + Use this to remove <%= link_to "avoid_posting", avoid_posting_static_path %> and <%= link_to "conditional_dnp", show_or_new_wiki_pages_path(title: "conditional_dnp") %> from posts of artists who got removed from the DNP list.
Limit of 1,000 posts at a time. diff --git a/app/views/avoid_posting_versions/_search.html.erb b/app/views/avoid_posting_versions/_search.html.erb index 65a810004..9c502e782 100644 --- a/app/views/avoid_posting_versions/_search.html.erb +++ b/app/views/avoid_posting_versions/_search.html.erb @@ -7,5 +7,5 @@ <%= f.input :artist_name, label: "Artist Name", hide_unless_value: true %> <%= f.input :artist_id, label: "Artist ID", hide_unless_value: true %> <%= f.input :any_other_name_matches, label: "Other Names", hide_unless_value: true %> - <%= f.input :is_active, label: "Active?", collection: [%w[Yes true], %w[No false], %w[Any any]] %> + <%= f.input :is_active, label: "Active?", collection: [["Yes", true], ["No", false]], include_blank: true %> <% end %> diff --git a/app/views/avoid_postings/_search.html.erb b/app/views/avoid_postings/_search.html.erb index 520e9a465..aed4a767e 100644 --- a/app/views/avoid_postings/_search.html.erb +++ b/app/views/avoid_postings/_search.html.erb @@ -14,6 +14,6 @@ <% if CurrentUser.is_staff? %> <%= f.input :staff_notes, label: "Staff Notes" %> <% end %> - <%= f.input :is_active, label: "Active?", collection: [%w[Yes true], %w[No false], %w[Any any]] %> + <%= f.input :is_active, label: "Active?", collection: [["Yes", true], ["No", false]], include_blank: true %> <%= f.input :order, collection: [["Artist Name", "artist_name"], %w[Created created_at], %w[Updated updated_at]], include_blank: true %> <% end %> diff --git a/app/views/bulk_update_requests/_secondary_links.html.erb b/app/views/bulk_update_requests/_secondary_links.html.erb index 17c092482..167e14827 100644 --- a/app/views/bulk_update_requests/_secondary_links.html.erb +++ b/app/views/bulk_update_requests/_secondary_links.html.erb @@ -1,4 +1,10 @@ <% content_for(:secondary_links) do %> <%= subnav_link_to "Listing", bulk_update_requests_path %> - <%= subnav_link_to "New", new_bulk_update_request_path %> -<% end %> + <%= subnav_link_to "Alias Listing", tag_aliases_path %> + <%= subnav_link_to "Implication Listing", tag_implications_path %> + <%= subnav_link_to "MetaSearch", meta_searches_tags_path %> + <%= subnav_link_to "New BUR", new_bulk_update_request_path %> + <%= subnav_link_to "Request implication", new_tag_implication_request_path %> + <%= subnav_link_to "Request alias", new_tag_alias_request_path %> + <%= subnav_link_to "Help", help_page_path(id: "tag_relationships#bur") %> +<% end %> \ No newline at end of file diff --git a/app/views/maintenance/user/api_keys/show.html.erb b/app/views/maintenance/user/api_keys/show.html.erb index 516321041..0fdf9dba1 100644 --- a/app/views/maintenance/user/api_keys/show.html.erb +++ b/app/views/maintenance/user/api_keys/show.html.erb @@ -1,12 +1,35 @@
You must re-enter your password to view or change your API key.
- <%= custom_form_for CurrentUser.user, url: view_user_api_key_path(CurrentUser.user), method: :post do |f| %> - <%= f.input :password, :as => :password, :input_html => {:autocomplete => "off"} %> - <%= f.button :submit, "Submit" %> - <% end %> ++ Anyone who has it has full access to your account. Don't give your API key + to third-party apps you don't trust, and don't post your API key in public places. +
+ +API Key | +Created | +Updated | +Actions | +
---|---|---|---|
<%= @api_key.key %> |
+ <%= compact_time(@api_key.created_at) %> | +<%= compact_time(@api_key.updated_at) %> | ++ <%= button_to("Regenerate", user_api_key_path(CurrentUser.user), method: :put) %> + <%= button_to("Delete", user_api_key_path(CurrentUser.user), method: :delete) %> + | +
-
API Key | -Created | -Updated | -Actions | -
---|---|---|---|
<%= @api_key.key %> |
- <%= compact_time @api_key.created_at %> | -<%= compact_time @api_key.updated_at %> | -- <%= button_to "Regenerate", user_api_key_path(CurrentUser.user), method: :put, params: { 'user[password]': @password } %> - <%= button_to "Delete", user_api_key_path(CurrentUser.user), method: :delete, params: { 'user[password]': @password } %> - | -
If you supplied an email address when signing up, <%= Danbooru.config.app_name %> can email you your login information. Password details will not be provided and will not be changed.
-If you didn't supply a valid email address, you are out of luck.
- - <%= form_tag(maintenance_user_login_reminder_path, :class => "simple_form") do %> -Invalid reset
+ <% if @nonce %> + <%= form_tag(maintenance_user_password_reset_path, :method => :put, :class => "simple_form session_form") do %> +Invalid reset
+ <% end %> +If you supplied an email address when signing up, <%= Danbooru.config.app_name %> can reset your password. You will receive an email confirming your request for a new password.
-If you didn't supply a valid email address, there is no way to recover your account.
- - <%= form_tag(maintenance_user_password_reset_path, :class => "simple_form") do %> -Nobody here but us chickens!
+Nobody here but us chickens!
<%= link_to "Go back", :back, :rel => "prev" %>
diff --git a/app/views/sessions/confirm_password.html.erb b/app/views/sessions/confirm_password.html.erb new file mode 100644 index 000000000..23a642058 --- /dev/null +++ b/app/views/sessions/confirm_password.html.erb @@ -0,0 +1,16 @@ +<% content_for(:page_title) do %> + Confirm Password +<% end %> + + +<%= render "secondary_links" %> +You must re-enter your password to continue.
+ <%= simple_form_for(:session, url: session_path) do |f| %> + <%= f.input(:url, as: :hidden, input_html: { value: params[:url] }) %> + <%= f.input(:name, as: :hidden, input_html: { value: CurrentUser.user.name }) %> + <%= f.input(:password, hint: link_to("Forgot password?", new_maintenance_user_password_reset_path), input_html: { autocomplete: "current-password" }) %> + <%= f.submit("Continue") %> + <% end %> +<%= link_to "Create ยป", new_staff_note_path(search: { user_id: user.id }), class: "expand-new-staff-note" %>
+ <%= render "staff_notes/partials/new", user: user, staff_note: StaffNote.new(user_id: user.id), hidden: true %> +An account is free and lets you keep favorites, upload artwork, and write comments.
+ <%= f.input :name, label: "Username", as: :string %> + <%= f.input :email, :required => true, as: :email %> + <%= f.input :password %> + <%= f.input :password_confirmation %> -Make sure to read the site rules before continuing.
-You must confirm your email address, so use something you can receive email with.
-This site is open to web crawlers so whatever name you choose will be public!
-This includes favorites, uploads, and comments. Almost everything is public. So don't choose a name you don't want to be associated with.
-Accounts are prefilled with the same blacklist as guests have. You can access your blacklist in your account settings.
-Please, read the site rules before making an account.
+You must confirm your email address, so you should only use one that you have access to.
+This site is open to web crawlers, meaning that almost everything is public.
+This includes your account name, favorites, uploads, and comments. Do not choose a name you don't want to be associated with.
+Accounts have the same blacklist as guests by default. You will be able to modify your blacklist in the account settings.