forked from e621ng/e621ng
Merge branch 'master' into master2
This commit is contained in:
commit
01d9e47d0a
@ -155,4 +155,4 @@ Style/TrailingCommaInArrayLiteral:
|
||||
EnforcedStyleForMultiline: consistent_comma
|
||||
|
||||
Style/TrailingCommaInHashLiteral:
|
||||
EnforcedStyleForMultiline: consistent_comma
|
||||
EnforcedStyleForMultiline: consistent_comma
|
@ -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'
|
||||
|
1
Gemfile
1
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"
|
||||
|
@ -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
|
||||
|
@ -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
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
77
app/controllers/staff_notes_controller.rb
Normal file
77
app/controllers/staff_notes_controller.rb
Normal file
@ -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
|
@ -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
|
||||
|
@ -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,
|
||||
|
@ -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';
|
||||
|
70
app/javascript/src/javascripts/password.js
Normal file
70
app/javascript/src/javascripts/password.js
Normal file
@ -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 = $("<div>")
|
||||
.addClass("password-feedback")
|
||||
.insertAfter($password);
|
||||
const display = $("<div>")
|
||||
.addClass("password-strength")
|
||||
.insertAfter($password);
|
||||
const progress = $("<div>")
|
||||
.addClass("password-progress")
|
||||
.css("width", 0)
|
||||
.appendTo(display);
|
||||
|
||||
// Listen to secondary input changes
|
||||
let extraData = getExtraData();
|
||||
for (const one of $inputs)
|
||||
one.on("input", () => {
|
||||
extraData = getExtraData();
|
||||
});
|
||||
|
||||
// Listen to main input changes
|
||||
$password.on("input", () => {
|
||||
const analysis = zxcvbn($password.val() + "", extraData);
|
||||
|
||||
progress.css("width", ((analysis.score * 25) + 10) + "%");
|
||||
hint.html("");
|
||||
if (analysis.feedback.warning)
|
||||
$("<span>")
|
||||
.text(analysis.feedback.warning)
|
||||
.addClass("password-warning")
|
||||
.appendTo(hint);
|
||||
for (const one of analysis.feedback.suggestions)
|
||||
$("<span>")
|
||||
.text(one)
|
||||
.appendTo(hint);
|
||||
});
|
||||
|
||||
function getExtraData () {
|
||||
const output = [];
|
||||
for (const one of $inputs) {
|
||||
const val = one.val() + "";
|
||||
if (val) output.push(one.val() + "");
|
||||
}
|
||||
return output;
|
||||
}
|
||||
};
|
||||
|
||||
$(() => {
|
||||
Password.init_validation();
|
||||
});
|
||||
|
||||
export default Password;
|
25
app/javascript/src/javascripts/staff_notes.js
Normal file
25
app/javascript/src/javascripts/staff_notes.js
Normal file
@ -0,0 +1,25 @@
|
||||
const StaffNote = {};
|
||||
|
||||
StaffNote.initialize_all = function () {
|
||||
$(".expand-new-staff-note").on("click", StaffNote.show_new_note_form);
|
||||
$(".edit-staff-note-link").on("click", StaffNote.show_edit_form);
|
||||
};
|
||||
|
||||
StaffNote.show_new_note_form = function (e) {
|
||||
e.preventDefault();
|
||||
$(e.target).hide();
|
||||
var $form = $(e.target).closest("div.new-staff-note").find("form");
|
||||
$form.show();
|
||||
$form[0].scrollIntoView(false);
|
||||
};
|
||||
|
||||
StaffNote.show_edit_form = function (e) {
|
||||
e.preventDefault();
|
||||
$(this).closest(".staff-note").find(".edit_staff_note").show();
|
||||
};
|
||||
|
||||
$(document).ready(function () {
|
||||
StaffNote.initialize_all();
|
||||
});
|
||||
|
||||
export default StaffNote;
|
@ -11,6 +11,7 @@
|
||||
Related:
|
||||
<a href="#" @click.prevent="findRelated()">Tags</a> |
|
||||
<a href="#" @click.prevent="findRelated(1)">Artists</a> |
|
||||
<a href="#" @click.prevent="findRelated(2)">Contributors</a> |
|
||||
<a href="#" @click.prevent="findRelated(3)">Copyrights</a> |
|
||||
<a href="#" @click.prevent="findRelated(4)">Characters</a> |
|
||||
<a href="#" @click.prevent="findRelated(9)">People</a> |
|
||||
|
@ -28,7 +28,7 @@
|
||||
<template v-if="normalMode">
|
||||
<div class="flex-grid border-bottom">
|
||||
<div class="col">
|
||||
<label class="section-label" for="names">Artists</label>
|
||||
<label class="section-label" for="names">Artists and Contributors</label>
|
||||
<div><a href="/help/tags#catchange">How do I tag an artist?</a></div>
|
||||
<div>Please don't use <a href="/wiki_pages/anonymous_artist">anonymous_artist</a> or <a href="/wiki_pages/unknown_artist">unknown_artist</a> tags unless they fall under those definitions on the wiki.</div>
|
||||
</div>
|
||||
@ -348,7 +348,7 @@
|
||||
// Import tags from query parameters
|
||||
const fillTags = function() {
|
||||
const queryList = ["tags-artist", "tags-character", "tags-species", "tags-content"];
|
||||
|
||||
|
||||
if(params.has("tags"))
|
||||
self.importTags(params.get("tags"), "other");
|
||||
|
||||
|
@ -54,7 +54,6 @@
|
||||
@import "specific/iqdb_queries.scss";
|
||||
@import "specific/keyboard_shortcuts.scss";
|
||||
@import "specific/lockdown.scss";
|
||||
@import "specific/maintenance.scss";
|
||||
@import "specific/meta_searches.scss";
|
||||
@import "specific/moderator_dashboard.scss";
|
||||
@import "specific/news_updates.scss";
|
||||
|
@ -156,6 +156,7 @@ $user-home-greeting-color: gold;
|
||||
$tag-categories: (
|
||||
"0": "general",
|
||||
"1": "artist",
|
||||
"2": "contributor",
|
||||
"3": "copyright",
|
||||
"4": "character",
|
||||
"5": "species",
|
||||
@ -168,6 +169,7 @@ $tag-categories: (
|
||||
$tag-categories-short: (
|
||||
"gen": "general",
|
||||
"art": "artist",
|
||||
"cont": "contributor",
|
||||
"copy": "copyright",
|
||||
"char": "character",
|
||||
"spec": "species",
|
||||
|
@ -24,6 +24,10 @@ section.posts-container {
|
||||
h2.posts-container-header {
|
||||
grid-column: -1 / 1;
|
||||
}
|
||||
|
||||
.no-results {
|
||||
grid-column: -1 / 1;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -1,5 +0,0 @@
|
||||
div#c-maintenance-user-login-reminders {
|
||||
div#a-new {
|
||||
width: 50em;
|
||||
}
|
||||
}
|
@ -1,8 +1,6 @@
|
||||
|
||||
|
||||
div#c-sessions {
|
||||
div#a-new {
|
||||
label#remember-label {
|
||||
label[for=session_remember] {
|
||||
display: inline;
|
||||
font-weight: normal;
|
||||
font-style: italic;
|
||||
|
@ -1,11 +1,15 @@
|
||||
.staff-note-list {
|
||||
.staff-note {
|
||||
margin-bottom: $padding-050;
|
||||
}
|
||||
|
||||
// This is getting overwritten through DText styling on user profiles,
|
||||
// because the whole list is wrapped in a collapsable section.
|
||||
h4.author-name {
|
||||
font-size: $h4-size;
|
||||
}
|
||||
}
|
||||
|
||||
.staff-note {
|
||||
margin-bottom: $padding-050;
|
||||
|
||||
&[data-is-deleted="true"] {
|
||||
background: $background-article-hidden;
|
||||
}
|
||||
}
|
||||
|
@ -223,13 +223,83 @@ div#c-users {
|
||||
color: themed("color-link-active");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
div#a-new {
|
||||
max-width: 60em;
|
||||
// User signup and login
|
||||
#c-users #a-new,
|
||||
#c-sessions #a-new,
|
||||
#c-maintenance-user-password-resets #a-new,
|
||||
#c-maintenance-user-login-reminders #a-new {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(auto, 360px));
|
||||
gap: 1em;
|
||||
|
||||
p {
|
||||
font-size: 1.2em;
|
||||
line-height: 1.4em;
|
||||
margin-bottom: 1em;
|
||||
}
|
||||
|
||||
.simple_form.session_form {
|
||||
box-sizing: border-box;
|
||||
max-width: 360px;
|
||||
margin: 0;
|
||||
|
||||
h1 {
|
||||
margin-bottom: 0.5em;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
div.input {
|
||||
input[type="text"], input[type="email"], input[type="password"], select {
|
||||
width: 100%;
|
||||
max-width: unset;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.session_info {
|
||||
display: flex;
|
||||
flex-flow: column;
|
||||
justify-content: center;
|
||||
box-sizing: border-box;
|
||||
max-width: 360px;
|
||||
padding: 0.5rem;
|
||||
border-radius: 3px;
|
||||
background-color: themed("color-section");
|
||||
|
||||
h3 { margin-bottom: 1em; }
|
||||
}
|
||||
|
||||
// Password validation
|
||||
.password-input {
|
||||
input[type="password"] {
|
||||
border-radius: 3px 3px 0 0;
|
||||
}
|
||||
|
||||
.password-strength {
|
||||
width: 100%;
|
||||
height: 0.25rem;
|
||||
border-radius: 0 0 3px 3px;
|
||||
|
||||
background: white;
|
||||
overflow: hidden;
|
||||
margin: 0;
|
||||
|
||||
.password-progress {
|
||||
background: linear-gradient(to right, palette("text-red") 0%, palette("text-yellow") 25%, palette("text-green") 100%);
|
||||
background-size: 360px 100%;
|
||||
|
||||
height: 100%;
|
||||
transition: width 1s ease-in-out;
|
||||
}
|
||||
}
|
||||
|
||||
.password-feedback {
|
||||
display: flex;
|
||||
flex-flow: column;
|
||||
padding-left: 1em;
|
||||
margin-top: 0.5em;
|
||||
|
||||
span { display: list-item; }
|
||||
.password-warning { font-weight: bold; }
|
||||
}
|
||||
}
|
||||
|
@ -83,6 +83,9 @@ body {
|
||||
--color-tag-artist: #f2ac08;
|
||||
--color-tag-artist-alt: #fbd67f;
|
||||
|
||||
--color-tag-contributor: #c0c0c0;
|
||||
--color-tag-contributor-alt: #71706e;
|
||||
|
||||
--color-tag-copyright: #d0d;
|
||||
--color-tag-copyright-alt: #ff5eff;
|
||||
|
||||
|
@ -325,7 +325,7 @@ class ElasticPostQueryBuilder < ElasticQueryBuilder
|
||||
order.push({id: :desc})
|
||||
end
|
||||
|
||||
if !CurrentUser.user.is_staff? && Security::Lockdown.hide_pending_posts_for > 0
|
||||
if !CurrentUser.user.nil? && !CurrentUser.user.is_staff? && Security::Lockdown.hide_pending_posts_for > 0
|
||||
should = [
|
||||
{
|
||||
range: {
|
||||
|
@ -1,16 +1,15 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class SessionCreator
|
||||
attr_reader :session, :cookies, :name, :password, :ip_addr, :remember, :secure
|
||||
attr_reader :request, :session, :cookies, :name, :password, :remember
|
||||
|
||||
def initialize(session, cookies, name, password, ip_addr, remember = false, secure = false)
|
||||
def initialize(request, session, cookies, name, password, remember = false)
|
||||
@request = request
|
||||
@session = session
|
||||
@cookies = cookies
|
||||
@name = name
|
||||
@password = password
|
||||
@ip_addr = ip_addr
|
||||
@remember = remember
|
||||
@secure = secure
|
||||
end
|
||||
|
||||
def authenticate
|
||||
@ -18,12 +17,13 @@ class SessionCreator
|
||||
user = User.find_by_name(name)
|
||||
|
||||
session[:user_id] = user.id
|
||||
session[:last_authenticated_at] = Time.now.utc.to_s
|
||||
session[:ph] = user.password_token
|
||||
user.update_column(:last_ip_addr, ip_addr) unless user.is_blocked?
|
||||
user.update_column(:last_ip_addr, request.remote_ip) unless user.is_blocked?
|
||||
|
||||
if remember
|
||||
verifier = ActiveSupport::MessageVerifier.new(Danbooru.config.remember_key, serializer: JSON, digest: "SHA256")
|
||||
cookies.encrypted[:remember] = {value: verifier.generate("#{user.id}:#{user.password_token}", purpose: "rbr", expires_in: 14.days), expires: Time.now + 14.days, httponly: true, same_site: :lax, secure: Rails.env.production?}
|
||||
cookies.encrypted[:remember] = { value: verifier.generate("#{user.id}:#{user.password_token}", purpose: "rbr", expires_in: 14.days), expires: Time.now + 14.days, httponly: true, same_site: :lax, secure: Rails.env.production? }
|
||||
end
|
||||
return true
|
||||
else
|
||||
|
@ -6,6 +6,9 @@ class TagCategory
|
||||
"gen" => 0,
|
||||
"artist" => 1,
|
||||
"art" => 1,
|
||||
"contributor" => 2,
|
||||
"contrib" => 2,
|
||||
"cont" => 2,
|
||||
"copyright" => 3,
|
||||
"copy" => 3,
|
||||
"co" => 3,
|
||||
@ -27,6 +30,7 @@ class TagCategory
|
||||
CANONICAL_MAPPING = {
|
||||
"General" => 0,
|
||||
"Artist" => 1,
|
||||
"Contributor" => 2,
|
||||
"Copyright" => 3,
|
||||
"Character" => 4,
|
||||
"Species" => 5,
|
||||
@ -39,6 +43,7 @@ class TagCategory
|
||||
REVERSE_MAPPING = {
|
||||
0 => "general",
|
||||
1 => "artist",
|
||||
2 => "contributor",
|
||||
3 => "copyright",
|
||||
4 => "character",
|
||||
5 => "species",
|
||||
@ -51,6 +56,7 @@ class TagCategory
|
||||
SHORT_NAME_MAPPING = {
|
||||
"gen" => "general",
|
||||
"art" => "artist",
|
||||
"cont" => "contributor",
|
||||
"copy" => "copyright",
|
||||
"char" => "character",
|
||||
"spec" => "species",
|
||||
@ -63,6 +69,7 @@ class TagCategory
|
||||
HEADER_MAPPING = {
|
||||
"general" => "General",
|
||||
"artist" => "Artists",
|
||||
"contributor" => "Contributors",
|
||||
"copyright" => "Copyrights",
|
||||
"character" => "Characters",
|
||||
"species" => "Species",
|
||||
@ -75,6 +82,7 @@ class TagCategory
|
||||
ADMIN_ONLY_MAPPING = {
|
||||
"general" => false,
|
||||
"artist" => false,
|
||||
"contributor" => false,
|
||||
"copyright" => false,
|
||||
"character" => false,
|
||||
"species" => false,
|
||||
@ -105,13 +113,13 @@ class TagCategory
|
||||
},
|
||||
}.freeze
|
||||
|
||||
CATEGORIES = %w[general species character copyright artist invalid lore meta people].freeze
|
||||
CATEGORIES = %w[general species character copyright artist contributor invalid lore meta people].freeze
|
||||
CATEGORY_IDS = CANONICAL_MAPPING.values
|
||||
|
||||
SHORT_NAME_LIST = SHORT_NAME_MAPPING.keys
|
||||
HUMANIZED_LIST = %w[character copyright artist].freeze
|
||||
SPLIT_HEADER_LIST = %w[invalid artist copyright character people species general meta lore].freeze
|
||||
CATEGORIZED_LIST = %w[invalid artist copyright character people species meta general lore].freeze
|
||||
SPLIT_HEADER_LIST = %w[invalid artist contributor copyright character people species general meta lore].freeze
|
||||
CATEGORIZED_LIST = %w[invalid artist contributor copyright character people species meta general lore].freeze
|
||||
|
||||
SHORT_NAME_REGEX = SHORT_NAME_LIST.join("|").freeze
|
||||
ALL_NAMES_REGEX = MAPPING.keys.join("|").freeze
|
||||
|
@ -16,6 +16,10 @@ class ModAction < ApplicationRecord
|
||||
avoid_posting_delete: %i[id artist_name],
|
||||
avoid_posting_undelete: %i[id artist_name],
|
||||
avoid_posting_destroy: %i[id artist_name],
|
||||
staff_note_create: %i[id user_id body],
|
||||
staff_note_update: %i[id user_id body old_body],
|
||||
staff_note_delete: %i[id user_id],
|
||||
staff_note_undelete: %i[id user_id],
|
||||
blip_delete: %i[blip_id user_id],
|
||||
blip_hide: %i[blip_id user_id],
|
||||
blip_unhide: %i[blip_id user_id],
|
||||
@ -91,18 +95,38 @@ class ModAction < ApplicationRecord
|
||||
takedown_process: %i[takedown_id],
|
||||
}.freeze
|
||||
|
||||
ProtectedActionKeys = %w[staff_note_create staff_note_update staff_note_delete staff_note_undelete ip_ban_create ip_ban_delete].freeze
|
||||
|
||||
KnownActionKeys = KnownActions.keys.freeze
|
||||
|
||||
def self.search(params)
|
||||
q = super
|
||||
|
||||
q = q.where_user(:creator_id, :creator, params)
|
||||
|
||||
if params[:action].present?
|
||||
q = q.where("action = ?", params[:action])
|
||||
module SearchMethods
|
||||
def visible(user)
|
||||
if user.is_staff?
|
||||
all
|
||||
else
|
||||
where.not(action: ProtectedActionKeys)
|
||||
end
|
||||
end
|
||||
|
||||
q.apply_basic_order(params)
|
||||
def search(params)
|
||||
q = super
|
||||
|
||||
q = q.where_user(:creator_id, :creator, params)
|
||||
|
||||
if params[:action].present?
|
||||
q = q.where("action = ?", params[:action])
|
||||
end
|
||||
|
||||
q.apply_basic_order(params)
|
||||
end
|
||||
end
|
||||
|
||||
def can_view?(user)
|
||||
if user.is_staff?
|
||||
true
|
||||
else
|
||||
ProtectedActionKeys.exclude?(action)
|
||||
end
|
||||
end
|
||||
|
||||
def values
|
||||
@ -131,7 +155,7 @@ class ModAction < ApplicationRecord
|
||||
end
|
||||
|
||||
def hidden_attributes
|
||||
super + [:values]
|
||||
super + %i[values values_old]
|
||||
end
|
||||
|
||||
def method_attributes
|
||||
@ -145,4 +169,6 @@ class ModAction < ApplicationRecord
|
||||
def initialize_creator
|
||||
self.creator_id = CurrentUser.id
|
||||
end
|
||||
|
||||
extend SearchMethods
|
||||
end
|
||||
|
@ -1,8 +1,33 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class StaffNote < ApplicationRecord
|
||||
belongs_to :creator, class_name: "User"
|
||||
belongs_to_creator
|
||||
belongs_to_updater
|
||||
belongs_to :user
|
||||
after_create :log_create
|
||||
after_update :log_update
|
||||
|
||||
scope :active, -> { where(is_deleted: false) }
|
||||
|
||||
module LogMethods
|
||||
def log_create
|
||||
ModAction.log(:staff_note_create, { id: id, user_id: user_id, body: body })
|
||||
end
|
||||
|
||||
def log_update
|
||||
if saved_change_to_body?
|
||||
ModAction.log(:staff_note_update, { id: id, user_id: user_id, body: body, old_body: body_before_last_save })
|
||||
end
|
||||
|
||||
if saved_change_to_is_deleted?
|
||||
if is_deleted?
|
||||
ModAction.log(:staff_note_delete, { id: id, user_id: user_id })
|
||||
else
|
||||
ModAction.log(:staff_note_undelete, { id: id, user_id: user_id })
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
module SearchMethods
|
||||
def search(params)
|
||||
@ -12,28 +37,45 @@ class StaffNote < ApplicationRecord
|
||||
q = q.attribute_matches(:body, params[:body_matches])
|
||||
q = q.where_user(:user_id, :user, params)
|
||||
q = q.where_user(:creator_id, :creator, params)
|
||||
q = q.where_user(:updater_id, :updater, params)
|
||||
|
||||
if params[:without_system_user]&.truthy?
|
||||
q = q.where.not(creator: User.system)
|
||||
end
|
||||
|
||||
if params[:is_deleted].present?
|
||||
q = q.attribute_matches(:is_deleted, params[:is_deleted])
|
||||
elsif !params[:include_deleted]&.truthy?
|
||||
q = q.active
|
||||
end
|
||||
|
||||
q.apply_basic_order(params)
|
||||
end
|
||||
|
||||
def default_order
|
||||
order("resolved asc, id desc")
|
||||
order("id desc")
|
||||
end
|
||||
end
|
||||
|
||||
include LogMethods
|
||||
extend SearchMethods
|
||||
|
||||
def resolve!
|
||||
self.resolved = true
|
||||
save
|
||||
def user_name
|
||||
User.id_to_name(user_id)
|
||||
end
|
||||
|
||||
def unresolve!
|
||||
self.resolved = false
|
||||
save
|
||||
def user_name=(name)
|
||||
self.user_id = User.name_to_id(name)
|
||||
end
|
||||
|
||||
def can_edit?(user)
|
||||
return false unless user.is_staff?
|
||||
user.id == creator_id || user.is_admin?
|
||||
end
|
||||
|
||||
def can_delete?(user)
|
||||
return false unless user.is_staff?
|
||||
return true if creator_id == user.id || user.is_admin?
|
||||
user_id != user.id
|
||||
end
|
||||
end
|
||||
|
@ -1,5 +1,7 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require "zxcvbn"
|
||||
|
||||
class User < ApplicationRecord
|
||||
class Error < Exception ; end
|
||||
class PrivilegeError < Exception
|
||||
@ -71,7 +73,8 @@ class User < ApplicationRecord
|
||||
validates :per_page, inclusion: { :in => 1..320 }
|
||||
validates :comment_threshold, presence: true
|
||||
validates :comment_threshold, numericality: { only_integer: true, less_than: 50_000, greater_than: -50_000 }
|
||||
validates :password, length: { :minimum => 6, :if => ->(rec) { rec.new_record? || rec.password.present? || rec.old_password.present? } }
|
||||
validates :password, length: { minimum: 8, if: ->(rec) { rec.new_record? || rec.password.present? || rec.old_password.present? } }
|
||||
validate :password_is_secure, if: ->(rec) { rec.new_record? || rec.password.present? || rec.old_password.present? }
|
||||
validates :password, confirmation: true
|
||||
validates :password_confirmation, presence: { if: ->(rec) { rec.new_record? || rec.old_password.present? } }
|
||||
validate :validate_ip_addr_is_not_banned, :on => :create
|
||||
@ -108,7 +111,7 @@ class User < ApplicationRecord
|
||||
has_many :post_sets, -> { order(name: :asc) }, foreign_key: :creator_id
|
||||
has_many :post_versions
|
||||
has_many :post_votes
|
||||
has_many :staff_notes, -> { order("staff_notes.id desc") }
|
||||
has_many :staff_notes, -> { active.order("staff_notes.id desc") }
|
||||
has_many :user_name_change_requests, -> { order(id: :asc) }
|
||||
|
||||
belongs_to :avatar, class_name: 'Post', optional: true
|
||||
@ -227,6 +230,16 @@ class User < ApplicationRecord
|
||||
def upgrade_password(pass)
|
||||
self.update_columns(password_hash: '', bcrypt_password_hash: User.bcrypt(pass))
|
||||
end
|
||||
|
||||
def password_is_secure
|
||||
analysis = Zxcvbn.test(password, [name, email])
|
||||
return unless analysis.score < 2
|
||||
if analysis.feedback.warning
|
||||
errors.add(:password, "is insecure: #{analysis.feedback.warning}")
|
||||
else
|
||||
errors.add(:password, "is insecure")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
module AuthenticationMethods
|
||||
|
@ -1,13 +0,0 @@
|
||||
<% if CurrentUser.can_view_staff_notes? %>
|
||||
<div class="staff-notes-section styled-dtext">
|
||||
<details>
|
||||
<summary>Staff Notes (<%= user.staff_notes.count %>)</summary>
|
||||
<div>
|
||||
<h4><%= link_to "Staff Notes", user_staff_notes_path(user_id: user.id) %></h4>
|
||||
<%= render "admin/staff_notes/partials/list_of_notes", staff_notes: user.staff_notes.limit(15), show_receiver_name: false %>
|
||||
<h4>New Staff Note</h4>
|
||||
<%= render "admin/staff_notes/partials/new", user: user, staff_note: StaffNote.new(user_id: user.id) %>
|
||||
</div>
|
||||
</details>
|
||||
</div>
|
||||
<% end %>
|
@ -1,3 +0,0 @@
|
||||
<div id="p-staff-notes-list" class="staff-note-list">
|
||||
<%= render partial: "admin/staff_notes/partials/staff_note", collection: staff_notes, locals: { show_receiver_name: show_receiver_name } %>
|
||||
</div>
|
@ -1,6 +0,0 @@
|
||||
<%= error_messages_for :staff_note %>
|
||||
|
||||
<%= custom_form_for(staff_note, url: user_staff_notes_path(user_id: user.id), method: :post) do |f| %>
|
||||
<%= f.input :body, as: :dtext, label: false, allow_color: true %>
|
||||
<%= f.button :submit, "Submit" %>
|
||||
<% end %>
|
@ -1,19 +0,0 @@
|
||||
<article class="staff-note comment-post-grid" id="staff-note-<%= staff_note.id %>">
|
||||
<div class="author-info">
|
||||
<div class="name-rank">
|
||||
<h4 class="author-name"><%= link_to_user staff_note.creator %></h4>
|
||||
<%= staff_note.creator.level_string %>
|
||||
</div>
|
||||
<div class="post-time">
|
||||
<%= link_to time_ago_in_words_tagged(staff_note.created_at), user_staff_notes_path(user_id: staff_note.user_id, anchor: "staff-note-#{staff_note.id}") %>
|
||||
</div>
|
||||
</div>
|
||||
<div class="content">
|
||||
<% if show_receiver_name %>
|
||||
<h4>On <%= link_to_user staff_note.user %></h4>
|
||||
<% end %>
|
||||
<div class="body dtext-container">
|
||||
<%= format_text(staff_note.body, allow_color: true) %>
|
||||
</div>
|
||||
</div>
|
||||
</article>
|
@ -2,7 +2,7 @@
|
||||
<div id="a-new">
|
||||
<h1>Remove stuck DNP tags</h1>
|
||||
<p>
|
||||
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.
|
||||
</p>
|
||||
<p>
|
||||
Limit of 1,000 posts at a time.
|
||||
|
@ -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 %>
|
||||
|
@ -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 %>
|
||||
|
@ -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 %>
|
@ -1,12 +1,35 @@
|
||||
<div id="c-maintenance-user-api-keys">
|
||||
<div id="a-show">
|
||||
<h1>API Key</h1>
|
||||
<p>You must re-enter your password to view or change your API key.</p>
|
||||
|
||||
<%= 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 %>
|
||||
<h2><b>Your API key is like your password</b></h2>
|
||||
<p>
|
||||
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.
|
||||
</p>
|
||||
|
||||
<table class="striped">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>API Key</th>
|
||||
<th>Created</th>
|
||||
<th>Updated</th>
|
||||
<th>Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
|
||||
<tbody>
|
||||
<tr id="api-key-<%= @api_key.id %>">
|
||||
<td id="api-key"><code><%= @api_key.key %></code></td>
|
||||
<td id="api-key-created"><%= compact_time(@api_key.created_at) %></td>
|
||||
<td id="api-key-updated"><%= compact_time(@api_key.updated_at) %></td>
|
||||
<td>
|
||||
<%= button_to("Regenerate", user_api_key_path(CurrentUser.user), method: :put) %>
|
||||
<%= button_to("Delete", user_api_key_path(CurrentUser.user), method: :delete) %>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
@ -1,38 +0,0 @@
|
||||
<div id="c-maintenance-user-api-keys">
|
||||
<div id="a-view">
|
||||
<h1>API Key</h1>
|
||||
|
||||
<p>
|
||||
<h2><b>Your API key is like your password</b></h2>
|
||||
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.
|
||||
</p>
|
||||
|
||||
<table class="striped">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>API Key</th>
|
||||
<th>Created</th>
|
||||
<th>Updated</th>
|
||||
<th>Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
|
||||
<tbody>
|
||||
<tr>
|
||||
<td id="api-key"><code><%= @api_key.key %></code></td>
|
||||
<td id="api-key-created"><%= compact_time @api_key.created_at %></td>
|
||||
<td id="api-key-updated"><%= compact_time @api_key.updated_at %></td>
|
||||
<td>
|
||||
<%= 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 } %>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<% content_for(:page_title) do %>
|
||||
API Key
|
||||
<% end %>
|
@ -1,21 +1,21 @@
|
||||
<div id="c-maintenance-user-login-reminders">
|
||||
<div id="a-new">
|
||||
<div id="c-maintenance-user-login-reminders"><div id="a-new">
|
||||
|
||||
<%= form_tag(maintenance_user_login_reminder_path, :class => "simple_form session_form") do %>
|
||||
<h1>Login Reminder</h1>
|
||||
|
||||
<div class="input email required">
|
||||
<label for="user_email" class="required">Email</label>
|
||||
<%= email_field(:user, :email) %>
|
||||
</div>
|
||||
|
||||
<%= submit_tag "Submit" %>
|
||||
<% end %>
|
||||
|
||||
<section class="session_info">
|
||||
<p>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.</p>
|
||||
|
||||
<p>If you didn't supply a valid email address, you are out of luck.</p>
|
||||
|
||||
<%= form_tag(maintenance_user_login_reminder_path, :class => "simple_form") do %>
|
||||
<div class="input email required">
|
||||
<label for="user_email" class="required">Email</label>
|
||||
<%= email_field(:user, :email) %>
|
||||
</div>
|
||||
|
||||
<%= submit_tag "Submit" %>
|
||||
<% end %>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</div></div>
|
||||
|
||||
<%= render "sessions/secondary_links" %>
|
||||
|
||||
|
@ -1,26 +1,28 @@
|
||||
<div id="c-maintenance-user-password-resets">
|
||||
<div id="a-edit">
|
||||
<h1>Reset Password</h1>
|
||||
<div id="c-maintenance-user-password-resets"><div id="a-edit">
|
||||
|
||||
<% if @nonce %>
|
||||
<%= form_tag(maintenance_user_password_reset_path, :method => :put) do %>
|
||||
<%= hidden_field_tag :uid, params[:uid] %>
|
||||
<%= hidden_field_tag :key, params[:key] %>
|
||||
<div class="input">
|
||||
<label for="password">Password</label>
|
||||
<%= password_field_tag :password %>
|
||||
</div>
|
||||
<div class="input">
|
||||
<label for="password_confirm">Confirm Password</label>
|
||||
<%= password_field_tag :password_confirm %>
|
||||
</div>
|
||||
<%= submit_tag "Reset" %>
|
||||
<% end %>
|
||||
<% else %>
|
||||
<p>Invalid reset</p>
|
||||
<% if @nonce %>
|
||||
<%= form_tag(maintenance_user_password_reset_path, :method => :put, :class => "simple_form session_form") do %>
|
||||
<h1>Reset Password</h1>
|
||||
|
||||
<%= hidden_field_tag :uid, params[:uid] %>
|
||||
<%= hidden_field_tag :key, params[:key] %>
|
||||
|
||||
<div class="input">
|
||||
<label for="password">Password</label>
|
||||
<%= password_field_tag :password %>
|
||||
</div>
|
||||
|
||||
<div class="input">
|
||||
<label for="password_confirm">Confirm Password</label>
|
||||
<%= password_field_tag :password_confirm %>
|
||||
</div>
|
||||
|
||||
<%= submit_tag "Reset" %>
|
||||
<% end %>
|
||||
</div>
|
||||
</div>
|
||||
<% else %>
|
||||
<p>Invalid reset</p>
|
||||
<% end %>
|
||||
</div></div>
|
||||
|
||||
<%= render "sessions/secondary_links" %>
|
||||
|
||||
|
@ -1,20 +1,19 @@
|
||||
<div id="c-maintenance-user-password-resets">
|
||||
<div id="a-new">
|
||||
<div id="c-maintenance-user-password-resets"><div id="a-new">
|
||||
|
||||
<%= form_tag(maintenance_user_password_reset_path, :class => "simple_form session_form") do %>
|
||||
<h1>Reset Password</h1>
|
||||
|
||||
|
||||
<div class="input email required">
|
||||
<label for="nonce_email" class="required">Email</label>
|
||||
<%= text_field_tag :email %>
|
||||
</div>
|
||||
<%= submit_tag "Submit" %>
|
||||
<% end %>
|
||||
<section class="session_info">
|
||||
<p>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.</p>
|
||||
|
||||
<p>If you didn't supply a valid email address, there is no way to recover your account.</p>
|
||||
|
||||
<%= form_tag(maintenance_user_password_reset_path, :class => "simple_form") do %>
|
||||
<div class="input email required">
|
||||
<label for="nonce_email" class="required">Email</label>
|
||||
<%= text_field_tag :email %>
|
||||
</div>
|
||||
<%= submit_tag "Submit" %>
|
||||
<% end %>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</div></div>
|
||||
|
||||
<%= render "sessions/secondary_links" %>
|
||||
|
||||
|
@ -1,15 +1,12 @@
|
||||
<div id="c-maintenance-user-passwords">
|
||||
<div id="a-edit">
|
||||
<div id="c-maintenance-user-passwords"><div id="a-edit">
|
||||
<%= custom_form_for(@user, html: { class: "session_form" }) do |f| %>
|
||||
<h1>Change Password</h1>
|
||||
|
||||
<%= custom_form_for @user do |f| %>
|
||||
<%= f.input :old_password, :as => :password, :input_html => {:autocomplete => "off"} %>
|
||||
<%= f.input :password, :label => "New password", :input_html => {:autocomplete => "off"} %>
|
||||
<%= f.input :password_confirmation %>
|
||||
<%= f.button :submit, "Submit" %>
|
||||
<% end %>
|
||||
</div>
|
||||
</div>
|
||||
<%= f.input :old_password, :as => :password, :input_html => {:autocomplete => "off"} %>
|
||||
<%= f.input :password, :label => "New password", :input_html => {:autocomplete => "off"} %>
|
||||
<%= f.input :password_confirmation %>
|
||||
<%= f.button :submit, "Submit" %>
|
||||
<% end %>
|
||||
</div></div>
|
||||
|
||||
<% content_for(:page_title) do %>
|
||||
Change Password
|
||||
|
@ -1,4 +1,4 @@
|
||||
<%= form_search(path: mod_actions_path) do |f| %>
|
||||
<%= f.user :creator %>
|
||||
<%= f.input :action, label: "Action", collection: ModAction::KnownActionKeys.map {|k| [k.to_s.capitalize.tr("_"," "), k.to_s]}, include_blank: true %>
|
||||
<%= f.input :action, label: "Action", collection: ModAction::KnownActionKeys.reject { |k| !CurrentUser.is_staff? && ModAction::ProtectedActionKeys.include?(k) }.map { |k| [k.to_s.capitalize.tr("_", " "), k.to_s] }, include_blank: true %>
|
||||
<% end %>
|
||||
|
@ -1,3 +1,3 @@
|
||||
<p>Nobody here but us chickens!</p>
|
||||
<p class="no-results">Nobody here but us chickens!</p>
|
||||
|
||||
<p class="paginator"><%= link_to "Go back", :back, :rel => "prev" %></p>
|
||||
|
16
app/views/sessions/confirm_password.html.erb
Normal file
16
app/views/sessions/confirm_password.html.erb
Normal file
@ -0,0 +1,16 @@
|
||||
<% content_for(:page_title) do %>
|
||||
Confirm Password
|
||||
<% end %>
|
||||
|
||||
|
||||
<%= render "secondary_links" %>
|
||||
<div id="c-sessions"><div id="a-confirm-password">
|
||||
<h1>Confirm password</h1>
|
||||
<p>You must re-enter your password to continue.</p>
|
||||
<%= 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 %>
|
||||
</div></div>
|
@ -1,38 +1,24 @@
|
||||
<div id="c-sessions">
|
||||
<div id="a-new">
|
||||
<section>
|
||||
<h1>Sign in</h1>
|
||||
<%= form_tag(session_path, :class => "simple_form") do %>
|
||||
<%= hidden_field_tag "url", params[:url] %>
|
||||
<div id="c-sessions"><div id="a-new">
|
||||
<%= simple_form_for(:session, url: session_path, html: { class: "session_form" }) do |f| %>
|
||||
<h1>Sign in</h1>
|
||||
<%= f.input(:url, as: :hidden, input_html: { value: params[:url] }) %>
|
||||
<%= f.input(:name, label: "Username") %>
|
||||
<%= f.input(:password) %>
|
||||
<%= f.input(:remember, as: :boolean, input_html: { checked: "checked" }) %>
|
||||
<%= f.submit("Continue") %>
|
||||
<% end %>
|
||||
|
||||
<div class="input">
|
||||
<label for="name">Username</label>
|
||||
<%= text_field_tag :name %>
|
||||
</div>
|
||||
<section class="session_info">
|
||||
<h3>
|
||||
Don't have an account?<br />
|
||||
<%= link_to("Sign up here.", new_user_path) %>
|
||||
</h3>
|
||||
<br />
|
||||
|
||||
<div class="input">
|
||||
<label for="password">Password</label>
|
||||
<%= password_field_tag :password %>
|
||||
</div>
|
||||
|
||||
<div class="input">
|
||||
<%= check_box_tag :remember, "1", true %>
|
||||
<label for="remember" id="remember-label">Remember</label>
|
||||
</div>
|
||||
|
||||
<div class="input">
|
||||
<%= submit_tag "Submit", :data => { :disable_with => "Signing in..." } %>
|
||||
</div>
|
||||
<% end %>
|
||||
</section>
|
||||
|
||||
<section class="box-section">
|
||||
<h2><%= link_to "Don't have an account? Sign up here.", new_user_path() %></h2>
|
||||
<h2><%= link_to "Reset Password", new_maintenance_user_password_reset_path %></h2>
|
||||
<h2><%= link_to "Login Reminder", new_maintenance_user_login_reminder_path %></h2>
|
||||
</section>
|
||||
</div>
|
||||
</div>
|
||||
<h3><%= link_to("Reset Password", new_maintenance_user_password_reset_path) %></h3>
|
||||
<h3><%= link_to("Login Reminder", new_maintenance_user_login_reminder_path) %></h3>
|
||||
</section>
|
||||
</div></div>
|
||||
|
||||
<% content_for(:page_title) do %>
|
||||
Sign in
|
||||
|
@ -1,6 +1,8 @@
|
||||
<%= form_search path: admin_staff_notes_path, always_display: true do |f| %>
|
||||
<%= form_search path: staff_notes_path do |f| %>
|
||||
<%= f.user :creator %>
|
||||
<%= f.user :updater %>
|
||||
<%= f.user :user %>
|
||||
<%= f.input :body_matches, label: "Body" %>
|
||||
<%= f.input :without_system_user, as: :boolean, label: "Hide automated?" %>
|
||||
<%= f.input :include_deleted, as: :boolean, label: "Show deleted?" %>
|
||||
<% end %>
|
4
app/views/staff_notes/_secondary_links.html.erb
Normal file
4
app/views/staff_notes/_secondary_links.html.erb
Normal file
@ -0,0 +1,4 @@
|
||||
<% content_for(:secondary_links) do %>
|
||||
<%= subnav_link_to "Listing", staff_notes_path %>
|
||||
<%= subnav_link_to "Search", search_staff_notes_path %>
|
||||
<% end %>
|
13
app/views/staff_notes/edit.html.erb
Normal file
13
app/views/staff_notes/edit.html.erb
Normal file
@ -0,0 +1,13 @@
|
||||
<div id="c-staff-notes">
|
||||
<div id="a-edit">
|
||||
<h1>Edit Staff Note For <%= link_to_user @staff_note.user %></h1>
|
||||
|
||||
<%= render "staff_notes/partials/edit", staff_note: @staff_note %>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<%= render "secondary_links" %>
|
||||
|
||||
<% content_for(:page_title) do %>
|
||||
Edit Staff Note
|
||||
<% end %>
|
@ -7,12 +7,12 @@
|
||||
<% end %>
|
||||
<%= render "search" %>
|
||||
|
||||
<%= render "admin/staff_notes/partials/list_of_notes", staff_notes: @notes, show_receiver_name: @user.blank? %>
|
||||
<%= render "staff_notes/partials/list_of_notes", staff_notes: @notes, show_receiver_name: @user.blank? %>
|
||||
<%= numbered_paginator(@notes) %>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<%= render "users/secondary_links" %>
|
||||
<%= render "secondary_links" %>
|
||||
|
||||
<% content_for(:page_title) do %>
|
||||
<%= @user ? "#{@user.name} - Staff Notes" : "Staff Notes" %>
|
@ -2,11 +2,11 @@
|
||||
<div id="a-new">
|
||||
<h1>New Staff Note for <%= link_to_user(@user) %></h1>
|
||||
|
||||
<%= render "admin/staff_notes/partials/new", staff_note: @staff_note, user: @user %>
|
||||
<%= render "staff_notes/partials/new", staff_note: @staff_note, user: @user %>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<%= render "users/secondary_links" %>
|
||||
<%= render "secondary_links" %>
|
||||
|
||||
<% content_for(:page_title) do %>
|
||||
New Staff Note
|
6
app/views/staff_notes/partials/_edit.html.erb
Normal file
6
app/views/staff_notes/partials/_edit.html.erb
Normal file
@ -0,0 +1,6 @@
|
||||
<%= error_messages_for :staff_note %>
|
||||
|
||||
<%= custom_form_for(staff_note, html: { style: ("display: none;" if local_assigns[:hidden]) }, url: staff_note_path(staff_note, user_id: staff_note.user_id), method: :patch) do |f| %>
|
||||
<%= f.input :body, as: :dtext, label: false %>
|
||||
<%= f.button :submit, "Submit" %>
|
||||
<% end %>
|
15
app/views/staff_notes/partials/_for_user.html.erb
Normal file
15
app/views/staff_notes/partials/_for_user.html.erb
Normal file
@ -0,0 +1,15 @@
|
||||
<% if CurrentUser.can_view_staff_notes? %>
|
||||
<div class="staff-notes-section styled-dtext">
|
||||
<details>
|
||||
<summary>Staff Notes (<%= user.staff_notes.count %>)</summary>
|
||||
<div>
|
||||
<h4><%= link_to "Staff Notes", staff_notes_path(search: { user_id: user.id }) %></h4>
|
||||
<%= render "staff_notes/partials/list_of_notes", staff_notes: user.staff_notes.limit(15), show_receiver_name: false %>
|
||||
<div class="new-staff-note">
|
||||
<p><%= link_to "Create »", new_staff_note_path(search: { user_id: user.id }), class: "expand-new-staff-note" %></p>
|
||||
<%= render "staff_notes/partials/new", user: user, staff_note: StaffNote.new(user_id: user.id), hidden: true %>
|
||||
</div>
|
||||
</div>
|
||||
</details>
|
||||
</div>
|
||||
<% end %>
|
3
app/views/staff_notes/partials/_list_of_notes.html.erb
Normal file
3
app/views/staff_notes/partials/_list_of_notes.html.erb
Normal file
@ -0,0 +1,3 @@
|
||||
<div id="p-staff-notes-list" class="staff-note-list">
|
||||
<%= render partial: "staff_notes/partials/staff_note", collection: staff_notes, locals: { show_receiver_name: show_receiver_name } %>
|
||||
</div>
|
6
app/views/staff_notes/partials/_new.html.erb
Normal file
6
app/views/staff_notes/partials/_new.html.erb
Normal file
@ -0,0 +1,6 @@
|
||||
<%= error_messages_for :staff_note %>
|
||||
|
||||
<%= custom_form_for(staff_note, html: { style: ("display: none;" if local_assigns[:hidden]) }, url: staff_notes_path(user_id: user.id), method: :post) do |f| %>
|
||||
<%= f.input :body, as: :dtext, label: false, allow_color: true %>
|
||||
<%= f.button :submit, "Submit" %>
|
||||
<% end %>
|
39
app/views/staff_notes/partials/_staff_note.html.erb
Normal file
39
app/views/staff_notes/partials/_staff_note.html.erb
Normal file
@ -0,0 +1,39 @@
|
||||
<article class="staff-note comment-post-grid" data-is-deleted="<%= staff_note.is_deleted %>" id="staff-note-<%= staff_note.id %>">
|
||||
<div class="author-info">
|
||||
<div class="name-rank">
|
||||
<h4 class="author-name"><%= link_to_user staff_note.creator %></h4>
|
||||
<%= staff_note.creator.level_string %>
|
||||
</div>
|
||||
<div class="post-time">
|
||||
<%= link_to time_ago_in_words_tagged(staff_note.created_at), staff_notes_path(search: { user_id: staff_note.user_id }, anchor: "staff-note-#{staff_note.id}") %>
|
||||
</div>
|
||||
</div>
|
||||
<div class="content">
|
||||
<% if show_receiver_name %>
|
||||
<h4>On <%= link_to_user staff_note.user %></h4>
|
||||
<% end %>
|
||||
<div class="body dtext-container">
|
||||
<%= format_text(staff_note.body, allow_color: true) %>
|
||||
</div>
|
||||
|
||||
<%= render "application/update_notice", record: staff_note %>
|
||||
<div class="content-menu">
|
||||
<menu>
|
||||
<% if staff_note.can_edit?(CurrentUser.user) %>
|
||||
<li><%= link_to "Edit", edit_staff_note_path(staff_note), id: "edit-staff-note-link-#{staff_note.id}", class: "edit-staff-note-link" %></li>
|
||||
<% end %>
|
||||
<% if staff_note.can_delete?(CurrentUser.user) %>
|
||||
<% if staff_note.is_deleted? %>
|
||||
<li><%= link_to "Undelete", undelete_staff_note_path(staff_note), method: :put %></li>
|
||||
<% else %>
|
||||
<li><%= link_to "Delete", delete_staff_note_path(staff_note), method: :put %></li>
|
||||
<% end %>
|
||||
<% end %>
|
||||
</menu>
|
||||
</div>
|
||||
|
||||
<% if staff_note.can_edit?(CurrentUser.user) %>
|
||||
<%= render "staff_notes/partials/edit", staff_note: staff_note, hidden: true %>
|
||||
<% end %>
|
||||
</div>
|
||||
</article>
|
13
app/views/staff_notes/search.html.erb
Normal file
13
app/views/staff_notes/search.html.erb
Normal file
@ -0,0 +1,13 @@
|
||||
<div id="c-staff-notes">
|
||||
<div id="a-search">
|
||||
<h1>Search Staff Notes</h1>
|
||||
|
||||
<%= render "search" %>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<%= render "secondary_links" %>
|
||||
|
||||
<% content_for(:page_title) do %>
|
||||
Search Staff Notes
|
||||
<% end %>
|
13
app/views/staff_notes/show.html.erb
Normal file
13
app/views/staff_notes/show.html.erb
Normal file
@ -0,0 +1,13 @@
|
||||
<div id="c-staff-notes">
|
||||
<div id="a-show">
|
||||
<h1>Staff Note For <%= link_to_user @staff_note.user %></h1>
|
||||
|
||||
<%= render "staff_notes/partials/staff_note", staff_note: @staff_note, show_receiver_name: true %>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<%= render "secondary_links" %>
|
||||
|
||||
<% content_for(:page_title) do %>
|
||||
Staff Note - <%= @staff_note.user_name %>
|
||||
<% end %>
|
@ -119,6 +119,9 @@
|
||||
<li><%= link_to("Stuck DNP tags", new_admin_stuck_dnp_path) %></li>
|
||||
<li><%= link_to("Destroyed Posts", admin_destroyed_posts_path) %></li>
|
||||
<% end %>
|
||||
<% if CurrentUser.is_staff? %>
|
||||
<li><%= link_to("Staff Notes", staff_notes_path) %></li>
|
||||
<% end %>
|
||||
<li><%= link_to("Upload Whitelist", upload_whitelists_path) %></li>
|
||||
<li><%= link_to("Mod Actions", mod_actions_path) %></li>
|
||||
<li><%= link_to("Bulk Update Requests", bulk_update_requests_path) %></li>
|
||||
|
@ -87,6 +87,7 @@
|
||||
["Tag count", "total_tags"],
|
||||
["General tag count", "general_tags", "total_tags"],
|
||||
["Artist tag count", "artist_tags", "total_tags"],
|
||||
["Contributor tag count", "contributor_tags", "total_tags"],
|
||||
["Character tag count", "character_tags", "total_tags"],
|
||||
["Copyright tag count", "copyright_tags", "total_tags"],
|
||||
["Species tag count", "species_tags", "total_tags"],
|
||||
|
@ -1,7 +1,9 @@
|
||||
<% content_for(:secondary_links) do %>
|
||||
<%= subnav_link_to "Listing", tag_aliases_path %>
|
||||
<%= subnav_link_to "Implication Listing", tag_implications_path %>
|
||||
<%= subnav_link_to "BUR Listing", bulk_update_requests_path %>
|
||||
<%= subnav_link_to "MetaSearch", meta_searches_tags_path %>
|
||||
<%= subnav_link_to "Request implication", new_tag_implication_request_path %>
|
||||
<%= subnav_link_to "Request alias", new_tag_alias_request_path %>
|
||||
<%= subnav_link_to "Request bulk update", new_bulk_update_request_path %>
|
||||
<%= subnav_link_to "Help", help_page_path(id: "tag_aliases") %>
|
||||
|
@ -1,8 +1,10 @@
|
||||
<% content_for(:secondary_links) do %>
|
||||
<%= subnav_link_to "Listing", tag_implications_path %>
|
||||
<%= subnav_link_to "Alias Listing", tag_aliases_path %>
|
||||
<%= subnav_link_to "BUR Listing", bulk_update_requests_path %>
|
||||
<%= subnav_link_to "MetaSearch", meta_searches_tags_path %>
|
||||
<%= subnav_link_to "Request implication", new_tag_implication_request_path %>
|
||||
<%= subnav_link_to "Request alias", new_tag_alias_request_path %>
|
||||
<%= subnav_link_to "Request bulk update", new_bulk_update_request_path %>
|
||||
<%= subnav_link_to "Help", help_page_path(id: "tag_implications") %>
|
||||
<% end %>
|
||||
|
@ -1,34 +1,30 @@
|
||||
<div id="c-users">
|
||||
<div id="a-new">
|
||||
<div id="c-users"><div id="a-new">
|
||||
<%= custom_form_for(@user, html: { id: "signup-form", class: "session_form" }) do |f| %>
|
||||
<h1>Sign Up</h1>
|
||||
|
||||
<p>An account is <strong>free</strong> and lets you keep favorites, upload artwork, and write comments.</p>
|
||||
<%= f.input :name, label: "Username", as: :string %>
|
||||
<%= f.input :email, :required => true, as: :email %>
|
||||
<%= f.input :password %>
|
||||
<%= f.input :password_confirmation %>
|
||||
|
||||
<div class="box-section background-red">
|
||||
<p>Make sure to read the <a href="/wiki_pages/e621:rules">site rules</a> before continuing.</p>
|
||||
<p>You must confirm your email address, so use something you can receive email with.</p>
|
||||
<p>This site is open to web crawlers so whatever name you choose will be public!</p>
|
||||
<p>This includes favorites, uploads, and comments. Almost everything is public. So don't choose a name you don't want to be associated with.</p>
|
||||
<p>Accounts are prefilled with the same blacklist as guests have. You can access your blacklist in your account settings.</p>
|
||||
</div>
|
||||
<%= f.input :time_zone, include_blank: false %>
|
||||
|
||||
<div id="p3">
|
||||
<%= custom_form_for(@user, html: {id: "signup-form"}) do |f| %>
|
||||
<%= f.input :name, :as => :string %>
|
||||
<%= f.input :email, :required => true, :as => :email %>
|
||||
<%= f.input :password %>
|
||||
<%= f.input :password_confirmation %>
|
||||
|
||||
<%= f.input :time_zone, :include_blank => false %>
|
||||
|
||||
<% if Danbooru.config.enable_recaptcha? %>
|
||||
<%= recaptcha_tags theme: 'dark', nonce: content_security_policy_nonce %>
|
||||
<% end %>
|
||||
<%= f.submit "Sign up", :data => { :disable_with => "Signing up..." } %>
|
||||
<% end %>
|
||||
</div>
|
||||
<% if Danbooru.config.enable_recaptcha? %>
|
||||
<%= recaptcha_tags theme: "dark", nonce: content_security_policy_nonce %>
|
||||
<% end %>
|
||||
<%= f.submit "Sign up", :data => { :disable_with => "Signing up..." } %>
|
||||
<% end %>
|
||||
<section class="session_info">
|
||||
<h3>
|
||||
Already have an account? <%= link_to "Sign In.", new_session_path %>
|
||||
</h3>
|
||||
<p>Please, read the <a href="/wiki_pages/e621:rules">site rules</a> before making an account.</p>
|
||||
<p>You must confirm your email address, so you should only use one that you have access to.</p>
|
||||
<p>This site is open to web crawlers, meaning that almost everything is public.</p>
|
||||
<p>This includes your account name, favorites, uploads, and comments. Do not choose a name you don't want to be associated with.</p>
|
||||
<p>Accounts have the same blacklist as guests by default. You will be able to modify your blacklist in the account settings.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div></div>
|
||||
|
||||
<%= render "secondary_links" %>
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
<div id="c-users">
|
||||
<div id="a-show">
|
||||
<%= render "statistics", :presenter => @presenter, :user => @user %>
|
||||
<%= render "admin/staff_notes/partials/for_user", user: @user %>
|
||||
<%= render "staff_notes/partials/for_user", user: @user %>
|
||||
<%= render "posts/partials/common/inline_blacklist" %>
|
||||
<%= render "post_summary", presenter: @presenter, user: @user %>
|
||||
<%= render "about", presenter: @presenter, user: @user %>
|
||||
|
@ -25,7 +25,6 @@ Rails.application.routes.draw do
|
||||
resource :reowner, controller: 'reowner', only: [:new, :create]
|
||||
resource :stuck_dnp, controller: "stuck_dnp", only: %i[new create]
|
||||
resources :destroyed_posts, only: %i[index show update]
|
||||
resources :staff_notes, only: [:index]
|
||||
end
|
||||
|
||||
namespace :security do
|
||||
@ -95,6 +94,16 @@ Rails.application.routes.draw do
|
||||
|
||||
resources :avoid_posting_versions, only: %i[index]
|
||||
|
||||
resources :staff_notes, except: %i[destroy] do
|
||||
collection do
|
||||
get :search
|
||||
end
|
||||
member do
|
||||
put :delete
|
||||
put :undelete
|
||||
end
|
||||
end
|
||||
|
||||
resources :tickets, except: %i[destroy] do
|
||||
member do
|
||||
post :claim
|
||||
@ -267,7 +276,9 @@ Rails.application.routes.draw do
|
||||
end
|
||||
resource :related_tag, :only => [:show, :update]
|
||||
match "related_tag/bulk", to: "related_tags#bulk", via: [:get, :post]
|
||||
resource :session, only: [:new, :create, :destroy]
|
||||
resource :session, only: %i[new create destroy] do
|
||||
get :confirm_password, on: :collection
|
||||
end
|
||||
resources :stats, only: [:index]
|
||||
resources :tags, constraints: id_name_constraint do
|
||||
resource :correction, :only => [:new, :create, :show], :controller => "tag_corrections"
|
||||
@ -291,10 +302,7 @@ Rails.application.routes.draw do
|
||||
resources :uploads
|
||||
resources :users do
|
||||
resource :password, :only => [:edit], :controller => "maintenance/user/passwords"
|
||||
resource :api_key, :only => [:show, :view, :update, :destroy], :controller => "maintenance/user/api_keys" do
|
||||
post :view
|
||||
end
|
||||
resources :staff_notes, only: [:index, :new, :create], controller: "admin/staff_notes"
|
||||
resource :api_key, only: %i[show update destroy], controller: "maintenance/user/api_keys"
|
||||
|
||||
collection do
|
||||
get :home
|
||||
|
7
db/fixes/122_1_add_contributor_category_index.rb
Executable file
7
db/fixes/122_1_add_contributor_category_index.rb
Executable file
@ -0,0 +1,7 @@
|
||||
#!/usr/bin/env ruby
|
||||
# frozen_string_literal: true
|
||||
|
||||
require File.expand_path(File.join(File.dirname(__FILE__), "..", "..", "config", "environment"))
|
||||
|
||||
client = Post.document_store.client
|
||||
client.indices.put_mapping(index: Post.document_store.index_name, body: { properties: { tag_count_contributor: { type: "integer" } } })
|
9
db/fixes/122_2_add_contributor_category_data.rb
Executable file
9
db/fixes/122_2_add_contributor_category_data.rb
Executable file
@ -0,0 +1,9 @@
|
||||
#!/usr/bin/env ruby
|
||||
# frozen_string_literal: true
|
||||
|
||||
require File.expand_path(File.join(File.dirname(__FILE__), "..", "..", "config", "environment"))
|
||||
|
||||
client = Post.document_store.client
|
||||
Post.find_in_batches(batch_size: 10_000) do |posts|
|
||||
client.bulk(body: posts.map { |post| { update: { _index: Post.document_store.index_name, _id: post.id, data: { doc: { tag_count_contributor: 0 } } } } }, refresh: true)
|
||||
end
|
13
db/migrate/20240205174652_soft_deletable_staff_notes.rb
Normal file
13
db/migrate/20240205174652_soft_deletable_staff_notes.rb
Normal file
@ -0,0 +1,13 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class SoftDeletableStaffNotes < ActiveRecord::Migration[7.1]
|
||||
def up
|
||||
add_column :staff_notes, :is_deleted, :boolean, null: false, default: false
|
||||
|
||||
add_reference :staff_notes, :updater, foreign_key: { to_table: :users }, null: true
|
||||
execute("UPDATE staff_notes SET updater_id = creator_id")
|
||||
change_column_null :staff_notes, :updater_id, false
|
||||
|
||||
remove_column :staff_notes, :resolved, :boolean, null: false, default: false
|
||||
end
|
||||
end
|
9
db/migrate/20241114055212_add_contributor_category.rb
Normal file
9
db/migrate/20241114055212_add_contributor_category.rb
Normal file
@ -0,0 +1,9 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class AddContributorCategory < ActiveRecord::Migration[7.1]
|
||||
def change
|
||||
Post.without_timeout do
|
||||
add_column(:posts, :tag_count_contributor, :integer, default: 0, null: false)
|
||||
end
|
||||
end
|
||||
end
|
@ -37,7 +37,7 @@ POSTVOTES = presets[:postvotes]
|
||||
POOLS = presets[:pools]
|
||||
|
||||
DISTRIBUTION = ENV.fetch("DISTRIBUTION", 10).to_i
|
||||
DEFAULT_PASSWORD = ENV.fetch("PASSWORD", "qwerty")
|
||||
DEFAULT_PASSWORD = ENV.fetch("PASSWORD", "hexerade")
|
||||
|
||||
CurrentUser.user = User.system
|
||||
|
||||
|
@ -9,8 +9,8 @@ require "tempfile"
|
||||
|
||||
admin = User.find_or_create_by!(name: "admin") do |user|
|
||||
user.created_at = 2.weeks.ago
|
||||
user.password = "qwerty"
|
||||
user.password_confirmation = "qwerty"
|
||||
user.password = "hexerade"
|
||||
user.password_confirmation = "hexerade"
|
||||
user.password_hash = ""
|
||||
user.email = "admin@e621.local"
|
||||
user.can_upload_free = true
|
||||
@ -38,7 +38,7 @@ def api_request(path)
|
||||
end
|
||||
|
||||
def import_mascots
|
||||
api_request("/mascots.json?limit=1").each do |mascot|
|
||||
api_request("/mascots.json?limit=3").each do |mascot|
|
||||
puts mascot["url_path"]
|
||||
Mascot.create!(
|
||||
creator: CurrentUser.user,
|
||||
@ -48,7 +48,7 @@ def import_mascots
|
||||
artist_url: mascot["artist_url"],
|
||||
artist_name: mascot["artist_name"],
|
||||
available_on_string: Danbooru.config.app_name,
|
||||
active: mascot["active"],
|
||||
active: true,
|
||||
)
|
||||
end
|
||||
end
|
||||
|
@ -1685,7 +1685,8 @@ CREATE TABLE public.posts (
|
||||
duration numeric,
|
||||
is_comment_disabled boolean DEFAULT false NOT NULL,
|
||||
is_comment_locked boolean DEFAULT false NOT NULL,
|
||||
transcript text DEFAULT ''::text NOT NULL
|
||||
transcript text DEFAULT ''::text NOT NULL,
|
||||
tag_count_contributor integer DEFAULT 0 NOT NULL
|
||||
);
|
||||
|
||||
|
||||
@ -1781,7 +1782,8 @@ CREATE TABLE public.staff_notes (
|
||||
user_id bigint NOT NULL,
|
||||
creator_id integer NOT NULL,
|
||||
body character varying,
|
||||
resolved boolean DEFAULT false NOT NULL
|
||||
is_deleted boolean DEFAULT false NOT NULL,
|
||||
updater_id bigint NOT NULL
|
||||
);
|
||||
|
||||
|
||||
@ -4230,6 +4232,13 @@ CREATE INDEX index_staff_audit_logs_on_user_id ON public.staff_audit_logs USING
|
||||
CREATE INDEX index_staff_notes_on_creator_id ON public.staff_notes USING btree (creator_id);
|
||||
|
||||
|
||||
--
|
||||
-- Name: index_staff_notes_on_updater_id; Type: INDEX; Schema: public; Owner: -
|
||||
--
|
||||
|
||||
CREATE INDEX index_staff_notes_on_updater_id ON public.staff_notes USING btree (updater_id);
|
||||
|
||||
|
||||
--
|
||||
-- Name: index_staff_notes_on_user_id; Type: INDEX; Schema: public; Owner: -
|
||||
--
|
||||
@ -4671,6 +4680,14 @@ ALTER TABLE ONLY public.avoid_postings
|
||||
ADD CONSTRAINT fk_rails_d45cc0f1a1 FOREIGN KEY (creator_id) REFERENCES public.users(id);
|
||||
|
||||
|
||||
--
|
||||
-- Name: staff_notes fk_rails_eaa7223eea; Type: FK CONSTRAINT; Schema: public; Owner: -
|
||||
--
|
||||
|
||||
ALTER TABLE ONLY public.staff_notes
|
||||
ADD CONSTRAINT fk_rails_eaa7223eea FOREIGN KEY (updater_id) REFERENCES public.users(id);
|
||||
|
||||
|
||||
--
|
||||
-- PostgreSQL database dump complete
|
||||
--
|
||||
@ -4678,6 +4695,7 @@ ALTER TABLE ONLY public.avoid_postings
|
||||
SET search_path TO "$user", public;
|
||||
|
||||
INSERT INTO "schema_migrations" (version) VALUES
|
||||
('20241114055212'),
|
||||
('20241110171706'),
|
||||
('20241110171006'),
|
||||
('20241110170952'),
|
||||
@ -4693,6 +4711,7 @@ INSERT INTO "schema_migrations" (version) VALUES
|
||||
('20240726170041'),
|
||||
('20240709134926'),
|
||||
('20240706061122'),
|
||||
('20240205174652'),
|
||||
('20240103002049'),
|
||||
('20240103002040'),
|
||||
('20240101042716'),
|
||||
|
@ -21,7 +21,8 @@
|
||||
"vue": "^3.1.0",
|
||||
"vue-loader": "^17.4.2",
|
||||
"webpack": "^5.51.1",
|
||||
"zingtouch": "^1.0.6"
|
||||
"zingtouch": "^1.0.6",
|
||||
"zxcvbn": "^4.4.2"
|
||||
},
|
||||
"version": "0.1.0",
|
||||
"devDependencies": {
|
||||
|
@ -5,14 +5,14 @@ FactoryBot.define do
|
||||
sequence :name do |n|
|
||||
"user#{n}"
|
||||
end
|
||||
password { "password" }
|
||||
password_confirmation { "password" }
|
||||
password { "6cQE!wbA" }
|
||||
password_confirmation { "6cQE!wbA" }
|
||||
sequence(:email) { |n| "user_email_#{n}@example.com" }
|
||||
default_image_size { "large" }
|
||||
base_upload_limit { 10 }
|
||||
level { 20 }
|
||||
created_at {Time.now}
|
||||
last_logged_in_at {Time.now}
|
||||
created_at { Time.now }
|
||||
last_logged_in_at { Time.now }
|
||||
|
||||
factory(:banned_user) do
|
||||
transient { ban_duration { 3 } }
|
||||
|
@ -36,7 +36,7 @@ class ApplicationControllerTest < ActionDispatch::IntegrationTest
|
||||
|
||||
context "on api authentication" do
|
||||
setup do
|
||||
@user = create(:user, password: "password")
|
||||
@user = create(:user, password: "6cQE!wbA")
|
||||
@api_key = ApiKey.generate!(@user)
|
||||
|
||||
ActionController::Base.allow_forgery_protection = true
|
||||
@ -108,7 +108,7 @@ class ApplicationControllerTest < ActionDispatch::IntegrationTest
|
||||
token = css_select("form input[name=authenticity_token]").first["value"]
|
||||
|
||||
# login
|
||||
post session_path, params: { authenticity_token: token, name: @user.name, password: "password" }
|
||||
post session_path, params: { authenticity_token: token, session: { name: @user.name, password: "6cQE!wbA" } }
|
||||
assert_redirected_to posts_path
|
||||
|
||||
# try to submit a form with cookies but without the csrf token
|
||||
@ -122,9 +122,9 @@ class ApplicationControllerTest < ActionDispatch::IntegrationTest
|
||||
|
||||
context "on session cookie authentication" do
|
||||
should "succeed" do
|
||||
user = create(:user, password: "password")
|
||||
user = create(:user, password: "6cQE!wbA")
|
||||
|
||||
post session_path, params: { name: user.name, password: "password" }
|
||||
post session_path, params: { session: { name: user.name, password: "6cQE!wbA" } }
|
||||
get edit_user_path(user)
|
||||
|
||||
assert_response :success
|
||||
|
@ -7,62 +7,43 @@ module Maintenance
|
||||
class ApiKeysControllerTest < ActionDispatch::IntegrationTest
|
||||
context "An api keys controller" do
|
||||
setup do
|
||||
@user = create(:privileged_user, :password => "password")
|
||||
ApiKey.generate!(@user)
|
||||
@user = create(:privileged_user, password: "6cQE!wbA")
|
||||
@api_key = ApiKey.generate!(@user)
|
||||
end
|
||||
|
||||
context "#show" do
|
||||
should "render" do
|
||||
get_auth maintenance_user_api_key_path, @user, params: {user_id: @user.id}
|
||||
context "show action" do
|
||||
should "let a user see their own API keys" do
|
||||
get_auth maintenance_user_api_key_path(@user.id), @user
|
||||
assert_response :success
|
||||
assert_select "#api-key-#{@api_key.id}", count: 1
|
||||
end
|
||||
|
||||
should "not let a user see API keys belonging to other users" do
|
||||
get_auth maintenance_user_api_key_path(@user.id), create(:user)
|
||||
assert_response :success
|
||||
assert_select "#api-key-#{@api_key.id}", count: 0
|
||||
end
|
||||
|
||||
should "redirect to the confirm password page if the user hasn't recently authenticated" do
|
||||
post session_path, params: { session: { name: @user.name, password: @user.password } }
|
||||
travel_to 2.hours.from_now do
|
||||
get maintenance_user_api_key_path(@user.id)
|
||||
end
|
||||
assert_redirected_to confirm_password_session_path(url: maintenance_user_api_key_path(@user.id))
|
||||
end
|
||||
end
|
||||
|
||||
context "#view" do
|
||||
context "with a correct password" do
|
||||
should "succeed" do
|
||||
post_auth view_maintenance_user_api_key_path(user_id: @user.id), @user, params: {user: {password: "password"}}
|
||||
assert_response :success
|
||||
end
|
||||
|
||||
# hard to test this in integrationtest
|
||||
# context "if the user doesn't already have an api key" do
|
||||
# setup do
|
||||
# ::User.any_instance.stubs(:api_key).returns(nil)
|
||||
# cookies[:user_name] = @user.name
|
||||
# cookies[:password_hash] = @user.bcrypt_cookie_password_hash
|
||||
# end
|
||||
|
||||
# should "generate one" do
|
||||
# ApiKey.expects(:generate!)
|
||||
|
||||
# assert_difference("ApiKey.count", 1) do
|
||||
# post view_maintenance_user_api_key_path(user_id: @user.id), params: {user: {password: "password"}}
|
||||
# end
|
||||
|
||||
# assert_not_nil(@user.reload.api_key)
|
||||
# end
|
||||
# end
|
||||
|
||||
should "not generate another API key if the user already has one" do
|
||||
assert_difference("ApiKey.count", 0) do
|
||||
post_auth view_maintenance_user_api_key_path(user_id: @user.id), @user, params: {user: {password: "password"}}
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context "#update" do
|
||||
context "update action" do
|
||||
should "regenerate the API key" do
|
||||
old_key = @user.api_key
|
||||
put_auth maintenance_user_api_key_path, @user, params: {user_id: @user.id, user: {password: "password"}}
|
||||
put_auth maintenance_user_api_key_path, @user
|
||||
assert_not_equal(old_key.key, @user.reload.api_key.key)
|
||||
end
|
||||
end
|
||||
|
||||
context "#destroy" do
|
||||
context "destroy action" do
|
||||
should "delete the API key" do
|
||||
delete_auth maintenance_user_api_key_path, @user, params: {user_id: @user.id, user: {password: "password"}}
|
||||
delete_auth maintenance_user_api_key_path, @user
|
||||
assert_nil(@user.reload.api_key)
|
||||
end
|
||||
end
|
||||
|
@ -19,7 +19,7 @@ module Maintenance
|
||||
|
||||
context "#destroy" do
|
||||
should "render" do
|
||||
delete_auth maintenance_user_deletion_path, @user, params: { password: "password" }
|
||||
delete_auth maintenance_user_deletion_path, @user, params: { password: "6cQE!wbA" }
|
||||
assert_redirected_to(posts_path)
|
||||
end
|
||||
end
|
||||
|
@ -21,7 +21,7 @@ module Maintenance
|
||||
context "#create" do
|
||||
context "with the correct password" do
|
||||
should "work" do
|
||||
post_auth maintenance_user_email_change_path, @user, params: { email_change: { password: "password", email: "abc@ogres.net" } }
|
||||
post_auth maintenance_user_email_change_path, @user, params: { email_change: { password: "6cQE!wbA", email: "abc@ogres.net" } }
|
||||
assert_redirected_to(home_users_path)
|
||||
@user.reload
|
||||
assert_equal("abc@ogres.net", @user.email)
|
||||
@ -37,7 +37,7 @@ module Maintenance
|
||||
end
|
||||
|
||||
should "not work with an invalid email" do
|
||||
post_auth maintenance_user_email_change_path, @user, params: { email_change: { password: "password", email: "" } }
|
||||
post_auth maintenance_user_email_change_path, @user, params: { email_change: { password: "6cQE!wbA", email: "" } }
|
||||
@user.reload
|
||||
assert_not_equal("", @user.email)
|
||||
assert_match(/Email can't be blank/, flash[:notice])
|
||||
@ -45,7 +45,7 @@ module Maintenance
|
||||
|
||||
should "work with a valid email when the users current email is invalid" do
|
||||
@user = create(:user, email: "")
|
||||
post_auth maintenance_user_email_change_path, @user, params: { email_change: { password: "password", email: "abc@ogres.net" } }
|
||||
post_auth maintenance_user_email_change_path, @user, params: { email_change: { password: "6cQE!wbA", email: "abc@ogres.net" } }
|
||||
@user.reload
|
||||
assert_equal("abc@ogres.net", @user.email)
|
||||
end
|
||||
|
@ -15,7 +15,7 @@ class SessionsControllerTest < ActionDispatch::IntegrationTest
|
||||
should "create a new session" do
|
||||
user = create(:user)
|
||||
|
||||
post session_path, params: { name: user.name, password: "password" }
|
||||
post session_path, params: { session: { name: user.name, password: "6cQE!wbA" } }
|
||||
user.reload
|
||||
|
||||
assert_redirected_to(posts_path)
|
||||
@ -33,8 +33,8 @@ class SessionsControllerTest < ActionDispatch::IntegrationTest
|
||||
end
|
||||
|
||||
should "fail when provided an invalid password" do
|
||||
user = create(:user, password: "xxxxxx", password_confirmation: "xxxxxx")
|
||||
post session_path, params: { name: user.name, password: "yyy" }
|
||||
user = create(:user, password: "6cQE!wbA", password_confirmation: "6cQE!wbA")
|
||||
post session_path, params: { session: { name: user.name, password: "yyy" } }
|
||||
|
||||
assert_nil(session[:user_id])
|
||||
assert_equal("Username/Password was incorrect", flash[:notice])
|
||||
@ -45,7 +45,7 @@ class SessionsControllerTest < ActionDispatch::IntegrationTest
|
||||
should "clear the session" do
|
||||
user = create(:user)
|
||||
|
||||
post session_path, params: { name: user.name, password: "password" }
|
||||
post session_path, params: { session: { name: user.name, password: "6cQE!wbA" } }
|
||||
assert_not_nil(session[:user_id])
|
||||
|
||||
delete_auth(session_path, user)
|
||||
|
@ -93,7 +93,7 @@ class UserFeedbacksControllerTest < ActionDispatch::IntegrationTest
|
||||
end
|
||||
|
||||
should "delete a feedback" do
|
||||
assert_difference({ "UserFeedback.count" => -1, "ModAction.count" => 1 }) do
|
||||
assert_difference({ "UserFeedback.count" => -1, "ModAction.count" => 2 }) do
|
||||
delete_auth user_feedback_path(@user_feedback), @critic
|
||||
end
|
||||
end
|
||||
@ -101,7 +101,7 @@ class UserFeedbacksControllerTest < ActionDispatch::IntegrationTest
|
||||
context "by a moderator" do
|
||||
should "allow destroying feedbacks they created" do
|
||||
as(@mod) { @user_feedback = create(:user_feedback, user: @user) }
|
||||
assert_difference({ "UserFeedback.count" => -1, "ModAction.count" => 1 }) do
|
||||
assert_difference({ "UserFeedback.count" => -1, "ModAction.count" => 2 }) do
|
||||
delete_auth user_feedback_path(@user_feedback), @mod
|
||||
end
|
||||
end
|
||||
@ -126,13 +126,13 @@ class UserFeedbacksControllerTest < ActionDispatch::IntegrationTest
|
||||
context "by an admin" do
|
||||
should "allow destroying feedbacks they created" do
|
||||
as(@admin) { @user_feedback = create(:user_feedback, user: @user) }
|
||||
assert_difference({ "UserFeedback.count" => -1, "ModAction.count" => 1 }) do
|
||||
assert_difference({ "UserFeedback.count" => -1, "ModAction.count" => 2 }) do
|
||||
delete_auth user_feedback_path(@user_feedback), @admin
|
||||
end
|
||||
end
|
||||
|
||||
should "allow destroying feedbacks they did not create" do
|
||||
assert_difference({ "UserFeedback.count" => -1, "ModAction.count" => 1 }) do
|
||||
assert_difference({ "UserFeedback.count" => -1, "ModAction.count" => 2 }) do
|
||||
delete_auth user_feedback_path(@user_feedback, format: :json), @admin
|
||||
end
|
||||
end
|
||||
|
@ -76,7 +76,7 @@ class UsersControllerTest < ActionDispatch::IntegrationTest
|
||||
context "create action" do
|
||||
should "create a user" do
|
||||
assert_difference(-> { User.count }, 1) do
|
||||
post users_path, params: { user: { name: "xxx", password: "xxxxx1", password_confirmation: "xxxxx1" } }
|
||||
post users_path, params: { user: { name: "xxx", password: "nePD.3L4", password_confirmation: "nePD.3L4" } }
|
||||
end
|
||||
created_user = User.find(session[:user_id])
|
||||
assert_equal("xxx", created_user.name)
|
||||
|
@ -95,7 +95,7 @@ end
|
||||
|
||||
class ActionDispatch::IntegrationTest
|
||||
def method_authenticated(method_name, url, user, options)
|
||||
post session_path, params: { name: user.name, password: user.password }
|
||||
post session_path, params: { session: { name: user.name, password: user.password } }
|
||||
self.send(method_name, url, **options)
|
||||
end
|
||||
|
||||
|
@ -22,7 +22,7 @@ class UserDeletionTest < ActiveSupport::TestCase
|
||||
setup do
|
||||
@user = create(:admin_user)
|
||||
CurrentUser.user = @user
|
||||
@deletion = UserDeletion.new(@user, "password")
|
||||
@deletion = UserDeletion.new(@user, "6cQE!wbA")
|
||||
end
|
||||
|
||||
should "fail" do
|
||||
@ -43,7 +43,7 @@ class UserDeletionTest < ActiveSupport::TestCase
|
||||
|
||||
@user.update(email: "ted@danbooru.com")
|
||||
|
||||
@deletion = UserDeletion.new(@user, "password")
|
||||
@deletion = UserDeletion.new(@user, "6cQE!wbA")
|
||||
with_inline_jobs { @deletion.delete! }
|
||||
@user.reload
|
||||
end
|
||||
@ -58,7 +58,7 @@ class UserDeletionTest < ActiveSupport::TestCase
|
||||
|
||||
should "reset the password" do
|
||||
assert_raises(BCrypt::Errors::InvalidHash) do
|
||||
User.authenticate(@user.name, "password")
|
||||
User.authenticate(@user.name, "6cQE!wbA")
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -116,8 +116,8 @@ class UserTest < ActiveSupport::TestCase
|
||||
end
|
||||
|
||||
should "authenticate" do
|
||||
assert(User.authenticate(@user.name, "password"), "Authentication should have succeeded")
|
||||
assert(!User.authenticate(@user.name, "password2"), "Authentication should not have succeeded")
|
||||
assert(User.authenticate(@user.name, "6cQE!wbA"), "Authentication should have succeeded")
|
||||
assert_not(User.authenticate(@user.name, "password2"), "Authentication should not have succeeded")
|
||||
end
|
||||
|
||||
should "normalize its level" do
|
||||
@ -209,8 +209,8 @@ class UserTest < ActiveSupport::TestCase
|
||||
|
||||
should "fail if the confirmation does not match" do
|
||||
@user = create(:user)
|
||||
@user.password = "zugzug6"
|
||||
@user.password_confirmation = "zugzug5"
|
||||
@user.password = "6cQE!wbA"
|
||||
@user.password_confirmation = "7cQE!wbA"
|
||||
@user.save
|
||||
assert_equal(["Password confirmation doesn't match Password"], @user.errors.full_messages)
|
||||
end
|
||||
@ -220,7 +220,15 @@ class UserTest < ActiveSupport::TestCase
|
||||
@user.password = "x5"
|
||||
@user.password_confirmation = "x5"
|
||||
@user.save
|
||||
assert_equal(["Password is too short (minimum is 6 characters)"], @user.errors.full_messages)
|
||||
assert_equal(["Password is too short (minimum is 8 characters)", "Password is insecure"], @user.errors.full_messages)
|
||||
end
|
||||
|
||||
should "not be insecure" do
|
||||
@user = create(:user)
|
||||
@user.password = "qwerty123"
|
||||
@user.password_confirmation = "qwerty123"
|
||||
@user.save
|
||||
assert_equal(["Password is insecure: This is similar to a commonly used password"], @user.errors.full_messages)
|
||||
end
|
||||
|
||||
# should "not change the password if the password and old password are blank" do
|
||||
|
@ -3716,3 +3716,8 @@ zingtouch@^1.0.6:
|
||||
version "1.0.6"
|
||||
resolved "https://registry.yarnpkg.com/zingtouch/-/zingtouch-1.0.6.tgz#456cf2b0a69f91a5ffbd8a83b18033c671f1096d"
|
||||
integrity sha512-S7jcR7cSRy28VmQBO0Tq7ZJV4pzfvvrTU9FrrL0K1QPpfBal9wm0oKhoCuifc+PPCq+hQMTJr5E9XKUQDm00VA==
|
||||
|
||||
zxcvbn@^4.4.2:
|
||||
version "4.4.2"
|
||||
resolved "https://registry.yarnpkg.com/zxcvbn/-/zxcvbn-4.4.2.tgz#28ec17cf09743edcab056ddd8b1b06262cc73c30"
|
||||
integrity sha512-Bq0B+ixT/DMyG8kgX2xWcI5jUvCwqrMxSFam7m0lAf78nf04hv6lNCsyLYdyYTrCVMqNDY/206K7eExYCeSyUQ==
|
||||
|
Loading…
Reference in New Issue
Block a user