add support for user name changes

This commit is contained in:
albert 2013-03-26 01:03:42 -04:00
parent f4cc6e7d99
commit 520e97485b
13 changed files with 394 additions and 4 deletions

View File

@ -1,9 +1,9 @@
div#c-user-feedbacks {
tr.feedback-category-positive {
.feedback-category-positive {
background: #DDFFDD !important;
}
tr.feedback-category-negative {
.feedback-category-negative {
background: #FFDDDD !important;
}
}

View File

@ -0,0 +1,31 @@
#c-user-name-change-requests {
.feedback-category-positive {
background: #DDFFDD !important;
}
.feedback-category-negative {
background: #FFDDDD !important;
}
li {
margin-bottom: 1em;
}
form {
margin-bottom: 2em;
}
p {
margin: 0;
}
p.author {
font-size: 80%;
font-style: italic;
color: #AAA;
}
section {
margin-bottom: 1em;
}
}

View File

@ -0,0 +1,38 @@
class UserNameChangeRequestsController < ApplicationController
before_filter :member_only, :only => [:new, :create, :show]
before_filter :admin_only, :only => [:index, :approve, :reject]
def new
end
def create
@change_request = UserNameChangeRequest.create(
:user_id => CurrentUser.user.id,
:original_name => CurrentUser.user.name,
:status => "pending",
:change_reason => params[:reason],
:desired_name => params[:desired_name]
)
redirect_to user_name_change_request_path(@change_request), :notice => "Your request has been submitted and is pending admin review"
end
def show
@change_request = UserNameChangeRequest.find(params[:id])
end
def index
@change_requests = UserNameChangeRequest.order("id desc").paginate(params[:page])
end
def approve
@change_request = UserNameChangeRequest.find(params[:id])
@change_request.approve!
redirect_to user_name_change_request_path(@change_request), :notice => "Name change request approved"
end
def reject
@change_request = UserNameChangeRequest.find(params[:id])
@change_request.reject!(params[:reason])
redirect_to user_name_change_request_path(@change_request), :notice => "Name change request rejected"
end
end

View File

@ -3,6 +3,7 @@ class TagAlias < ActiveRecord::Base
after_save :clear_all_cache
after_destroy :clear_all_cache
before_validation :initialize_creator, :on => :create
before_validation :normalize_names
validates_presence_of :creator_id, :antecedent_name, :consequent_name
validates_uniqueness_of :antecedent_name
validate :absence_of_transitive_relation
@ -88,6 +89,11 @@ class TagAlias < ActiveRecord::Base
def is_active?
status == "active"
end
def normalize_names
self.antecedent_name = antecedent_name.mb_chars.downcase.tr(" ", "_").strip
self.consequent_name = consequent_name.downcase.tr(" ", "_").strip
end
def initialize_creator
self.creator_id = CurrentUser.user.id

View File

@ -3,6 +3,7 @@ class TagImplication < ActiveRecord::Base
after_save :update_descendant_names_for_parent
belongs_to :creator, :class_name => "User"
before_validation :initialize_creator, :on => :create
before_validation :normalize_names
validates_presence_of :creator_id, :antecedent_name, :consequent_name
validates_uniqueness_of :antecedent_name, :scope => :consequent_name
validate :absence_of_circular_relation
@ -129,6 +130,11 @@ class TagImplication < ActiveRecord::Base
end
end
end
def normalize_names
self.antecedent_name = antecedent_name.downcase.tr(" ", "_").strip
self.consequent_name = consequent_name.downcase.tr(" ", "_").strip
end
def is_pending?
status == "pending"

View File

@ -531,7 +531,7 @@ class User < ActiveRecord::Base
end
def admins
where("is_admin = TRUE")
where("level = ?", Levels::ADMIN)
end
def with_email(email)

View File

@ -0,0 +1,48 @@
class UserNameChangeRequest < ActiveRecord::Base
validates_presence_of :user_id, :original_name, :desired_name
validates_inclusion_of :status, :in => %w(pending approved rejected)
belongs_to :user
belongs_to :approver, :class_name => "User"
validate :uniqueness_of_desired_name
validates_length_of :desired_name, :within => 2..100, :on => :create
validates_format_of :desired_name, :with => /\A[^\s:]+\Z/, :on => :create, :message => "cannot have whitespace or colons"
after_create :notify_admins
def self.pending
where(:status => "pending")
end
def feedback
UserFeedback.for_user(user_id).order("id desc").all
end
def notify_admins
title = "#{original_name} is requesting a name change to #{desired_name}"
body = title + "\n\n\"See request\":/user_name_change_requests/#{id}"
User.admins.find_each do |user|
Dmail.create_split(:title => title, :body => body, :to_id => user.id)
end
end
def approve!
update_attribute(:status, "approved")
user.update_attribute(:name, desired_name)
body = "Your name change request has been approved. Be sure to log in with your new user name."
Dmail.create_split(:title => "Name change request approved", :body => body, :to_id => user_id)
end
def reject!(reason)
update_attributes(:status => "rejected", :rejection_reason => reason)
body = "Your name change request has been rejected for the following reason: #{rejection_reason}"
Dmail.create_split(:title => "Name change request rejected", :body => body, :to_id => user_id)
end
def uniqueness_of_desired_name
if User.find_by_name(desired_name)
errors.add(:desired_name, "already exists")
return false
else
return true
end
end
end

View File

@ -0,0 +1,21 @@
<h1>Name Change Request</h1>
<p>You can request a name change but it must be approved. Factors that go into consideration include your upload and update history, and your user feedback.</p>
<%= error_messages_for "change_request" %>
<%= form_tag(user_name_change_requests_path, :class => "simple_form") do %>
<div class="input">
<label for="desired_name">Desired Name</label>
<%= text_field_tag "desired_name" %>
</div>
<div class="input">
<label for="desired_name">Reason</label>
<%= text_field_tag "reason" %>
</div>
<div class="input">
<%= submit_tag %>
</div>
<% end %>

View File

@ -0,0 +1,56 @@
<div id="c-user-name-change-requests">
<h1>Name Change Request</h1>
<section>
<p><%= @change_request.original_name %> is requesting to change their name to <%= @change_request.desired_name %>.</p>
</section>
<section>
<h2>Reason</h2>
<p><%= @change_request.change_reason %></p>
</section>
<section>
<h2>Feedback</h2>
<ul>
<% @change_request.feedback.each do |feedback| %>
<li class="feedback-category-<%= feedback.category %>">
<p><%= feedback.body %></p>
<p class="author">Submitted by <%= feedback.creator.name %> <%= time_ago_in_words_tagged feedback.created_at %></p>
</li>
<% end %>
</ul>
</section>
<section>
<h2>Statistics</h2>
<ul>
<li>Level: <%= @change_request.user.level_string %></li>
<li>Uploads: <%= link_to @change_request.user.post_upload_count, posts_path("user:#{@change_request.user.name}") %></li>
<li>Updates: <%= link_to @change_request.user.post_update_count, post_versions_path(:search => {:updater_id => @change_request.user.id}) %></li>
<li>Notes: <%= link_to @change_request.user.note_update_count, note_versions_path(:search => {:updater_id => @change_request.user.id}) %></li>
<li>Favorites: <%= @change_request.user.favorite_count %></li>
</ul>
</section>
<% if CurrentUser.user.is_admin? %>
<section>
<h2>Options</h2>
<%= form_tag(approve_user_name_change_request_path(@change_request)) do %>
<%= submit_tag "Approve" %>
<% end %>
<%= form_tag(reject_user_name_change_request_path(@change_request), :class => "simple_form") do %>
<div class="input">
<label>Reason</label>
<%= text_field_tag "reason" %>
</div>
<div class="input">
<%= submit_tag "Reject" %>
</div>
<% end %>
</section>
<% end %>
</div>

View File

@ -196,6 +196,12 @@ Danbooru::Application.routes.draw do
end
end
resources :user_feedbacks
resources :user_name_change_requests do
member do
post :approve
post :reject
end
end
resources :wiki_pages do
member do
put :revert

View File

@ -0,0 +1,21 @@
class CreateUserNameChangeRequests < ActiveRecord::Migration
def up
create_table :user_name_change_requests do |t|
t.string :status, :null => false, :default => "pending"
t.integer :user_id, :null => false
t.integer :approver_id
t.string :original_name
t.string :desired_name
t.text :change_reason
t.text :rejection_reason
t.timestamps
end
add_index :user_name_change_requests, :user_id
add_index :user_name_change_requests, :original_name
end
def down
drop_table :user_name_change_requests
end
end

View File

@ -2539,6 +2539,43 @@ CREATE SEQUENCE user_feedback_id_seq
ALTER SEQUENCE user_feedback_id_seq OWNED BY user_feedback.id;
--
-- Name: user_name_change_requests; Type: TABLE; Schema: public; Owner: -; Tablespace:
--
CREATE TABLE user_name_change_requests (
id integer NOT NULL,
status character varying(255) DEFAULT 'pending'::character varying NOT NULL,
user_id integer NOT NULL,
approver_id integer,
original_name character varying(255),
desired_name character varying(255),
change_reason text,
rejection_reason text,
created_at timestamp without time zone NOT NULL,
updated_at timestamp without time zone NOT NULL
);
--
-- Name: user_name_change_requests_id_seq; Type: SEQUENCE; Schema: public; Owner: -
--
CREATE SEQUENCE user_name_change_requests_id_seq
START WITH 1
INCREMENT BY 1
NO MAXVALUE
NO MINVALUE
CACHE 1;
--
-- Name: user_name_change_requests_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: -
--
ALTER SEQUENCE user_name_change_requests_id_seq OWNED BY user_name_change_requests.id;
--
-- Name: user_password_reset_nonces; Type: TABLE; Schema: public; Owner: -; Tablespace:
--
@ -3641,6 +3678,13 @@ ALTER TABLE ONLY uploads ALTER COLUMN id SET DEFAULT nextval('uploads_id_seq'::r
ALTER TABLE ONLY user_feedback ALTER COLUMN id SET DEFAULT nextval('user_feedback_id_seq'::regclass);
--
-- Name: id; Type: DEFAULT; Schema: public; Owner: -
--
ALTER TABLE ONLY user_name_change_requests ALTER COLUMN id SET DEFAULT nextval('user_name_change_requests_id_seq'::regclass);
--
-- Name: id; Type: DEFAULT; Schema: public; Owner: -
--
@ -3941,6 +3985,14 @@ ALTER TABLE ONLY user_feedback
ADD CONSTRAINT user_feedback_pkey PRIMARY KEY (id);
--
-- Name: user_name_change_requests_pkey; Type: CONSTRAINT; Schema: public; Owner: -; Tablespace:
--
ALTER TABLE ONLY user_name_change_requests
ADD CONSTRAINT user_name_change_requests_pkey PRIMARY KEY (id);
--
-- Name: user_password_reset_nonces_pkey; Type: CONSTRAINT; Schema: public; Owner: -; Tablespace:
--
@ -6010,6 +6062,20 @@ CREATE INDEX index_user_feedback_on_creator_id ON user_feedback USING btree (cre
CREATE INDEX index_user_feedback_on_user_id ON user_feedback USING btree (user_id);
--
-- Name: index_user_name_change_requests_on_original_name; Type: INDEX; Schema: public; Owner: -; Tablespace:
--
CREATE INDEX index_user_name_change_requests_on_original_name ON user_name_change_requests USING btree (original_name);
--
-- Name: index_user_name_change_requests_on_user_id; Type: INDEX; Schema: public; Owner: -; Tablespace:
--
CREATE INDEX index_user_name_change_requests_on_user_id ON user_name_change_requests USING btree (user_id);
--
-- Name: index_users_on_email; Type: INDEX; Schema: public; Owner: -; Tablespace:
--
@ -6278,4 +6344,6 @@ INSERT INTO schema_migrations (version) VALUES ('20130322173202');
INSERT INTO schema_migrations (version) VALUES ('20130322173859');
INSERT INTO schema_migrations (version) VALUES ('20130323160259');
INSERT INTO schema_migrations (version) VALUES ('20130323160259');
INSERT INTO schema_migrations (version) VALUES ('20130326035904');

View File

@ -0,0 +1,89 @@
require 'test_helper'
class UserNameChangeRequestTest < ActiveSupport::TestCase
context "in all cases" do
setup do
@admin = FactoryGirl.create(:admin_user)
@requester = FactoryGirl.create(:user)
CurrentUser.user = @requester
end
context "approving a request" do
setup do
@change_request = UserNameChangeRequest.create(
:user_id => @requester.id,
:original_name => @requester.name,
:status => "pending",
:desired_name => "abc"
)
CurrentUser.user = @admin
end
should "create a dmail" do
assert_difference("Dmail.count", 2) do
@change_request.approve!
end
end
should "change the user's name" do
@change_request.approve!
@requester.reload
assert_equal("abc", @requester.name)
end
should "clear the user name cache" do
@change_request.approve!
assert_equal("abc", Cache.get("uin:#{@requester.id}"))
end
end
context "rejecting a request" do
setup do
@change_request = UserNameChangeRequest.create(
:user_id => @requester.id,
:original_name => @requester.name,
:status => "pending",
:desired_name => "abc"
)
CurrentUser.user = @admin
end
should "create a dmail" do
assert_difference("Dmail.count", 2) do
@change_request.reject!("msg")
end
end
should "preserve the username" do
@change_request.reject!("msg")
@requester.reload
assert_not_equal("abc", @requester.name)
end
end
context "creating a new request" do
should "send dmails to the admin" do
assert_difference("Dmail.count", 2) do
UserNameChangeRequest.create(
:user_id => @requester.id,
:original_name => @requester.name,
:status => "pending",
:desired_name => "abc"
)
end
end
should "not validate if the desired name already exists" do
assert_difference("UserNameChangeRequest.count", 0) do
req = UserNameChangeRequest.create(
:user_id => @requester.id,
:original_name => @requester.name,
:status => "pending",
:desired_name => @requester.name
)
assert_equal(["Desired name already exists"], req.errors.full_messages)
end
end
end
end
end