fixed user tests
This commit is contained in:
@@ -1,95 +0,0 @@
|
|||||||
class Favorite
|
|
||||||
attr_accessor :attributes, :errors
|
|
||||||
|
|
||||||
def self.table_name_for(user_id)
|
|
||||||
"favorites_#{user_id.to_i % 10}"
|
|
||||||
end
|
|
||||||
|
|
||||||
def self.sql_order_clause(post_ids, posts_table_alias = "posts")
|
|
||||||
if post_ids.empty?
|
|
||||||
return "#{posts_table_alias}.id desc"
|
|
||||||
end
|
|
||||||
|
|
||||||
conditions = []
|
|
||||||
|
|
||||||
post_ids.each_with_index do |post_id, n|
|
|
||||||
conditions << "when #{post_id} then #{n}"
|
|
||||||
end
|
|
||||||
|
|
||||||
"case #{posts_table_alias}.id " + conditions.join(" ") + " end"
|
|
||||||
end
|
|
||||||
|
|
||||||
def self.create(attributes)
|
|
||||||
user_id = attributes[:user_id]
|
|
||||||
post_id = attributes[:post_id]
|
|
||||||
execute_sql("INSERT INTO #{table_name_for(user_id)} (user_id, post_id) VALUES (?, ?)", user_id, post_id)
|
|
||||||
rescue ActiveRecord::RecordNotUnique
|
|
||||||
# ignore
|
|
||||||
end
|
|
||||||
|
|
||||||
def self.count(user_id)
|
|
||||||
select_value_sql("SELECT COUNT(*) FROM #{table_name_for(user_id)}").to_i
|
|
||||||
end
|
|
||||||
|
|
||||||
def self.destroy(conditions)
|
|
||||||
if conditions[:user_id] && conditions[:post_id]
|
|
||||||
destroy_for_post_and_user(conditions[:post_id], conditions[:user_id])
|
|
||||||
elsif conditions[:user_id]
|
|
||||||
destroy_for_user(conditions[:user_id])
|
|
||||||
elsif conditions[:post_id]
|
|
||||||
destroy_for_post(conditions[:post_id])
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def self.find_post_ids(user_id, options)
|
|
||||||
limit = options[:limit] || 1 || Danbooru.config.posts_per_page
|
|
||||||
if options[:before_id]
|
|
||||||
select_values_sql("SELECT post_id FROM #{table_name_for(user_id)} WHERE id < ? ORDER BY id DESC LIMIT ?", options[:before_id], limit)
|
|
||||||
elsif options[:after_id]
|
|
||||||
select_values_sql("SELECT post_id FROM #{table_name_for(user_id)} WHERE id > ? ORDER BY id ASC LIMIT ?", options[:after_id], limit).reverse
|
|
||||||
elsif options[:offset]
|
|
||||||
select_values_sql("SELECT post_id FROM #{table_name_for(user_id)} ORDER BY id DESC LIMIT ? OFFSET ?", limit, options[:offset])
|
|
||||||
else
|
|
||||||
select_values_sql("SELECT post_id FROM #{table_name_for(user_id)} ORDER BY id DESC LIMIT ?", limit)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def self.exists?(conditions)
|
|
||||||
if conditions[:user_id] && conditions[:post_id]
|
|
||||||
select_value_sql("SELECT 1 FROM #{table_name_for(conditions[:user_id])} WHERE user_id = ? AND post_id = ?", conditions[:user_id], conditions[:post_id])
|
|
||||||
elsif conditions[:user_id]
|
|
||||||
select_value_sql("SELECT 1 FROM #{table_name_for(conditions[:user_id])} WHERE user_id = ?", conditions[:user_id])
|
|
||||||
elsif conditions[:post_id]
|
|
||||||
select_value_sql("SELECT 1 FROM #{table_name_for(conditions[:user_id])} WHERE post_id = ?", conditions[:post_id])
|
|
||||||
else
|
|
||||||
false
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def self.destroy_for_post_and_user(post_id, user_id)
|
|
||||||
execute_sql("DELETE FROM #{table_name_for(user_id)} WHERE post_id = #{post_id} AND user_id = #{user_id}")
|
|
||||||
end
|
|
||||||
|
|
||||||
def self.destroy_for_post(post)
|
|
||||||
0.upto(9) do |i|
|
|
||||||
execute_sql("DELETE FROM favorites_#{i} WHERE post_id = #{post.id}")
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def self.destroy_for_user(user)
|
|
||||||
execute_sql("DELETE FROM #{table_name_for(user)} WHERE user_id = #{user.id}")
|
|
||||||
end
|
|
||||||
|
|
||||||
def self.select_value_sql(sql, *params)
|
|
||||||
ActiveRecord::Base.select_value_sql(sql, *params)
|
|
||||||
end
|
|
||||||
|
|
||||||
def self.select_values_sql(sql, *params)
|
|
||||||
ActiveRecord::Base.select_values_sql(sql, *params)
|
|
||||||
end
|
|
||||||
|
|
||||||
def self.execute_sql(sql, *params)
|
|
||||||
ActiveRecord::Base.execute_sql(sql, *params)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
20
app/models/favorite.rb
Normal file
20
app/models/favorite.rb
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
class Favorite < ActiveRecord::Base
|
||||||
|
TABLE_COUNT = 100
|
||||||
|
validates_uniqueness_of :post_id, :scope => :user_id
|
||||||
|
|
||||||
|
def self.model_for(user_id)
|
||||||
|
mod = user_id % TABLE_COUNT
|
||||||
|
Object.const_get("Favorite#{mod}")
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.delete_post(post_id)
|
||||||
|
0.upto(TABLE_COUNT - 1) do |i|
|
||||||
|
model_for(i).destroy_all(:post_id => post_id)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
0.upto(Favorite::TABLE_COUNT - 1) do |i|
|
||||||
|
Object.const_set("Favorite#{i}", Class.new(Favorite))
|
||||||
|
Object.const_get("Favorite#{i}").set_table_name("favorites_#{i}")
|
||||||
|
end
|
||||||
@@ -373,13 +373,17 @@ class Post < ActiveRecord::Base
|
|||||||
|
|
||||||
module FavoriteMethods
|
module FavoriteMethods
|
||||||
def delete_favorites
|
def delete_favorites
|
||||||
Favorite.destroy_for_post(self)
|
Favorite.delete_post(id)
|
||||||
end
|
end
|
||||||
|
|
||||||
def favorited_by?(user_id)
|
def favorited_by?(user_id)
|
||||||
fav_string =~ /(?:\A| )fav:#{user_id}(?:\Z| )/
|
fav_string =~ /(?:\A| )fav:#{user_id}(?:\Z| )/
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def append_user_to_fav_string(user_id)
|
||||||
|
update_attribute(:fav_string, (fav_string + " fav:#{user_id}").strip)
|
||||||
|
end
|
||||||
|
|
||||||
def add_favorite(user_id)
|
def add_favorite(user_id)
|
||||||
if user_id.is_a?(ActiveRecord::Base)
|
if user_id.is_a?(ActiveRecord::Base)
|
||||||
user_id = user_id.id
|
user_id = user_id.id
|
||||||
@@ -389,10 +393,13 @@ class Post < ActiveRecord::Base
|
|||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
|
|
||||||
self.fav_string += " fav:#{user_id}"
|
append_user_to_fav_string(user_id)
|
||||||
self.fav_string.strip!
|
|
||||||
update_attribute(:fav_string, fav_string)
|
Favorite.model_for(user_id).create(:user_id => user_id, :post_id => id)
|
||||||
Favorite.create(:user_id => user_id, :post_id => id)
|
end
|
||||||
|
|
||||||
|
def delete_user_from_fav_string(user_id)
|
||||||
|
update_attribute(:fav_string, fav_string.gsub(/(?:\A| )fav:#{user_id}(?:\Z| )/, " ").strip)
|
||||||
end
|
end
|
||||||
|
|
||||||
def remove_favorite(user_id)
|
def remove_favorite(user_id)
|
||||||
@@ -400,10 +407,9 @@ class Post < ActiveRecord::Base
|
|||||||
user_id = user_id.id
|
user_id = user_id.id
|
||||||
end
|
end
|
||||||
|
|
||||||
self.fav_string.gsub!(/(?:\A| )fav:#{user_id}(?:\Z| )/, " ")
|
delete_user_from_fav_string(user_id)
|
||||||
self.fav_string.strip!
|
|
||||||
update_attribute(:fav_string, fav_string)
|
Favorite.model_for(user_id).destroy_all(:user_id => user_id, :post_id => id)
|
||||||
Favorite.destroy(:user_id => user_id, :post_id => id)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def favorited_user_ids
|
def favorited_user_ids
|
||||||
@@ -780,10 +786,6 @@ class Post < ActiveRecord::Base
|
|||||||
remove_favorite(user_id)
|
remove_favorite(user_id)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def delete_favorites
|
|
||||||
Favorite.destroy_for_post(self)
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
module DeletionMethods
|
module DeletionMethods
|
||||||
|
|||||||
@@ -118,12 +118,7 @@ class User < ActiveRecord::Base
|
|||||||
|
|
||||||
module FavoriteMethods
|
module FavoriteMethods
|
||||||
def favorites(options = {})
|
def favorites(options = {})
|
||||||
post_ids = Favorite.find_post_ids(id, options)
|
Favorite.model_for(id).where("user_id = ?", id)
|
||||||
if post_ids.any?
|
|
||||||
Post.where("id in (?)", post_ids).order(Favorite.sql_order_clause(post_ids))
|
|
||||||
else
|
|
||||||
Post.where("false")
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -212,7 +207,7 @@ class User < ActiveRecord::Base
|
|||||||
elsif created_at > 1.week.ago
|
elsif created_at > 1.week.ago
|
||||||
false
|
false
|
||||||
else
|
else
|
||||||
Comment.where("creator_id = ? and created_at > ?", id, 1.hour.ago).count <= Danbooru.config.member_comment_limit
|
Comment.where("creator_id = ? and created_at > ?", id, 1.hour.ago).count < Danbooru.config.member_comment_limit
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -1,20 +1,29 @@
|
|||||||
class CreateFavorites < ActiveRecord::Migration
|
class CreateFavorites < ActiveRecord::Migration
|
||||||
|
TABLE_COUNT = 100
|
||||||
|
|
||||||
def self.up
|
def self.up
|
||||||
(0..9).each do |number|
|
# this is a dummy table and should not be used
|
||||||
create_table "favorites_#{number}" do |t|
|
create_table "favorites" do |t|
|
||||||
t.column :post_id, :integer
|
t.column :user_id, :integer
|
||||||
|
t.column :post_id, :integer
|
||||||
|
end
|
||||||
|
|
||||||
|
0.upto(TABLE_COUNT - 1) do |i|
|
||||||
|
create_table "favorites_#{i}" do |t|
|
||||||
t.column :user_id, :integer
|
t.column :user_id, :integer
|
||||||
|
t.column :post_id, :integer
|
||||||
end
|
end
|
||||||
|
|
||||||
add_index "favorites_#{number}", :post_id
|
add_index "favorites_#{i}", :user_id
|
||||||
add_index "favorites_#{number}", :user_id
|
add_index "favorites_#{i}", :post_id
|
||||||
add_index "favorites_#{number}", [:post_id, :user_id], :unique => true
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.down
|
def self.down
|
||||||
(0..9).each do |number|
|
drop_table "favorites"
|
||||||
drop_table "favorites_#{number}"
|
|
||||||
|
0.upto(TABLE_COUNT - 1) do |i|
|
||||||
|
drop_table "favorites_#{i}"
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ class FavoriteTest < ActiveSupport::TestCase
|
|||||||
CurrentUser.user = user
|
CurrentUser.user = user
|
||||||
CurrentUser.ip_addr = "127.0.0.1"
|
CurrentUser.ip_addr = "127.0.0.1"
|
||||||
MEMCACHE.flush_all
|
MEMCACHE.flush_all
|
||||||
|
Favorite # need to force loading the favorite model
|
||||||
end
|
end
|
||||||
|
|
||||||
teardown do
|
teardown do
|
||||||
@@ -23,30 +24,26 @@ class FavoriteTest < ActiveSupport::TestCase
|
|||||||
p1.add_favorite(user1)
|
p1.add_favorite(user1)
|
||||||
p2.add_favorite(user1)
|
p2.add_favorite(user1)
|
||||||
p1.add_favorite(user2)
|
p1.add_favorite(user2)
|
||||||
|
|
||||||
favorites = user1.favorite_posts
|
|
||||||
assert_equal(2, favorites.size)
|
|
||||||
assert_equal(p2.id, favorites[0].id)
|
|
||||||
assert_equal(p1.id, favorites[1].id)
|
|
||||||
|
|
||||||
favorites = user2.favorite_posts
|
favorites = user1.favorites.order("id desc")
|
||||||
assert_equal(1, favorites.size)
|
assert_equal(2, favorites.count)
|
||||||
assert_equal(p1.id, favorites[0].id)
|
assert_equal(p2.id, favorites[0].post_id)
|
||||||
|
assert_equal(p1.id, favorites[1].post_id)
|
||||||
|
|
||||||
|
favorites = user2.favorites.order("id desc")
|
||||||
|
assert_equal(1, favorites.count)
|
||||||
|
assert_equal(p1.id, favorites[0].post_id)
|
||||||
end
|
end
|
||||||
|
|
||||||
should "filter before a given id" do
|
should "not allow duplicates" do
|
||||||
user1 = Factory.create(:user)
|
user1 = Factory.create(:user)
|
||||||
p1 = Factory.create(:post)
|
p1 = Factory.create(:post)
|
||||||
p2 = Factory.create(:post)
|
p2 = Factory.create(:post)
|
||||||
p3 = Factory.create(:post)
|
|
||||||
p1.add_favorite(user1)
|
p1.add_favorite(user1)
|
||||||
p2.add_favorite(user1)
|
p1.add_favorite(user1)
|
||||||
p3.add_favorite(user1)
|
|
||||||
favorites = user1.favorite_posts
|
assert_equal(1, user1.favorites.count)
|
||||||
favorites = user1.favorite_posts(:before_id => favorites.first.favorite_id)
|
|
||||||
assert_equal(2, favorites.count)
|
|
||||||
assert_equal(p2.id, favorites[0].id)
|
|
||||||
assert_equal(p1.id, favorites[1].id)
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -85,8 +85,8 @@ class PostTest < ActiveSupport::TestCase
|
|||||||
c1.add_favorite(user)
|
c1.add_favorite(user)
|
||||||
c1.delete!
|
c1.delete!
|
||||||
p1.reload
|
p1.reload
|
||||||
assert(!Favorite.exists?(:post_id => c1.id, :user_id => user.id))
|
assert(!Favorite.model_for(user.id).exists?(:post_id => c1.id, :user_id => user.id))
|
||||||
assert(Favorite.exists?(:post_id => p1.id, :user_id => user.id))
|
assert(Favorite.model_for(user.id).exists?(:post_id => p1.id, :user_id => user.id))
|
||||||
end
|
end
|
||||||
|
|
||||||
should "update the parent's has_children flag" do
|
should "update the parent's has_children flag" do
|
||||||
@@ -335,22 +335,22 @@ class PostTest < ActiveSupport::TestCase
|
|||||||
post.add_favorite(user)
|
post.add_favorite(user)
|
||||||
post.reload
|
post.reload
|
||||||
assert_equal("fav:#{user.id}", post.fav_string)
|
assert_equal("fav:#{user.id}", post.fav_string)
|
||||||
assert(Favorite.exists?(:user_id => user.id, :post_id => post.id))
|
assert(Favorite.model_for(user.id).exists?(:user_id => user.id, :post_id => post.id))
|
||||||
|
|
||||||
post.add_favorite(user)
|
post.add_favorite(user)
|
||||||
post.reload
|
post.reload
|
||||||
assert_equal("fav:#{user.id}", post.fav_string)
|
assert_equal("fav:#{user.id}", post.fav_string)
|
||||||
assert(Favorite.exists?(:user_id => user.id, :post_id => post.id))
|
assert(Favorite.model_for(user.id).exists?(:user_id => user.id, :post_id => post.id))
|
||||||
|
|
||||||
post.remove_favorite(user)
|
post.remove_favorite(user)
|
||||||
post.reload
|
post.reload
|
||||||
assert_equal("", post.fav_string)
|
assert_equal("", post.fav_string)
|
||||||
assert(!Favorite.exists?(:user_id => user.id, :post_id => post.id))
|
assert(!Favorite.model_for(user.id).exists?(:user_id => user.id, :post_id => post.id))
|
||||||
|
|
||||||
post.remove_favorite(user)
|
post.remove_favorite(user)
|
||||||
post.reload
|
post.reload
|
||||||
assert_equal("", post.fav_string)
|
assert_equal("", post.fav_string)
|
||||||
assert(!Favorite.exists?(:user_id => user.id, :post_id => post.id))
|
assert(!Favorite.model_for(user.id).exists?(:user_id => user.id, :post_id => post.id))
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -1,19 +1,19 @@
|
|||||||
require_relative '../test_helper'
|
require_relative '../test_helper'
|
||||||
|
|
||||||
class UserTest < ActiveSupport::TestCase
|
class UserTest < ActiveSupport::TestCase
|
||||||
setup do
|
|
||||||
user = Factory.create(:user)
|
|
||||||
CurrentUser.user = user
|
|
||||||
CurrentUser.ip_addr = "127.0.0.1"
|
|
||||||
MEMCACHE.flush_all
|
|
||||||
end
|
|
||||||
|
|
||||||
teardown do
|
|
||||||
CurrentUser.user = nil
|
|
||||||
CurrentUser.ip_addr = nil
|
|
||||||
end
|
|
||||||
|
|
||||||
context "A user" do
|
context "A user" do
|
||||||
|
setup do
|
||||||
|
@user = Factory.create(:user)
|
||||||
|
CurrentUser.user = @user
|
||||||
|
CurrentUser.ip_addr = "127.0.0.1"
|
||||||
|
MEMCACHE.flush_all
|
||||||
|
end
|
||||||
|
|
||||||
|
teardown do
|
||||||
|
CurrentUser.user = nil
|
||||||
|
CurrentUser.ip_addr = nil
|
||||||
|
end
|
||||||
|
|
||||||
should "not validate if the originating ip address is banned" do
|
should "not validate if the originating ip address is banned" do
|
||||||
Factory.create(:ip_ban)
|
Factory.create(:ip_ban)
|
||||||
user = Factory.build(:user)
|
user = Factory.build(:user)
|
||||||
@@ -23,59 +23,55 @@ class UserTest < ActiveSupport::TestCase
|
|||||||
end
|
end
|
||||||
|
|
||||||
should "limit post uploads" do
|
should "limit post uploads" do
|
||||||
user = Factory.create(:user)
|
assert(!@user.can_upload?)
|
||||||
assert(!user.can_upload?)
|
@user.update_attribute(:is_contributor, true)
|
||||||
user.update_attribute(:is_contributor, true)
|
assert(@user.can_upload?)
|
||||||
assert(user.can_upload?)
|
@user.update_attribute(:is_contributor, false)
|
||||||
user.update_attribute(:is_contributor, false)
|
|
||||||
|
|
||||||
40.times do
|
40.times do
|
||||||
Factory.create(:post, :uploader => user, :is_deleted => true)
|
Factory.create(:post, :uploader => @user, :is_deleted => true)
|
||||||
end
|
end
|
||||||
|
|
||||||
assert(!user.can_upload?)
|
assert(!@user.can_upload?)
|
||||||
end
|
end
|
||||||
|
|
||||||
should "limit comment votes" do
|
should "limit comment votes" do
|
||||||
user = Factory.create(:user)
|
assert(@user.can_comment_vote?)
|
||||||
assert(user.can_comment_vote?)
|
10.times do
|
||||||
12.times do
|
|
||||||
comment = Factory.create(:comment)
|
comment = Factory.create(:comment)
|
||||||
Factory.create(:comment_vote, :user => user, :comment_id => comment.id)
|
Factory.create(:comment_vote, :comment_id => comment.id)
|
||||||
end
|
end
|
||||||
assert(!user.can_comment_vote?)
|
|
||||||
|
assert(!@user.can_comment_vote?)
|
||||||
CommentVote.update_all("created_at = '1990-01-01'")
|
CommentVote.update_all("created_at = '1990-01-01'")
|
||||||
assert(user.can_comment_vote?)
|
assert(@user.can_comment_vote?)
|
||||||
end
|
end
|
||||||
|
|
||||||
should "limit comments" do
|
should "limit comments" do
|
||||||
user = Factory.create(:user)
|
assert(!@user.can_comment?)
|
||||||
assert(!user.can_comment?)
|
@user.update_attribute(:is_privileged, true)
|
||||||
user.update_attribute(:is_privileged, true)
|
assert(@user.can_comment?)
|
||||||
assert(user.can_comment?)
|
@user.update_attribute(:is_privileged, false)
|
||||||
user.update_attribute(:is_privileged, false)
|
@user.update_attribute(:created_at, 1.year.ago)
|
||||||
user.update_attribute(:created_at, 1.year.ago)
|
assert(@user.can_comment?)
|
||||||
assert(user.can_comment?)
|
(Danbooru.config.member_comment_limit).times do
|
||||||
(Danbooru.config.member_comment_limit + 1).times do
|
Factory.create(:comment)
|
||||||
Factory.create(:comment, :creator => user)
|
|
||||||
end
|
end
|
||||||
assert(!user.can_comment?)
|
assert(!@user.can_comment?)
|
||||||
end
|
end
|
||||||
|
|
||||||
should "verify" do
|
should "verify" do
|
||||||
user = Factory.create(:user)
|
assert(@user.is_verified?)
|
||||||
assert(user.is_verified?)
|
@user = Factory.create(:user)
|
||||||
user = Factory.create(:user)
|
@user.generate_email_verification_key
|
||||||
user.generate_email_verification_key
|
@user.save
|
||||||
user.save
|
assert(!@user.is_verified?)
|
||||||
assert(!user.is_verified?)
|
assert_raise(User::Error) {@user.verify!("bbb")}
|
||||||
assert_raise(User::Error) {user.verify!("bbb")}
|
assert_nothing_raised {@user.verify!(@user.email_verification_key)}
|
||||||
assert_nothing_raised {user.verify!(user.email_verification_key)}
|
assert(@user.is_verified?)
|
||||||
assert(user.is_verified?)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
should "authenticate" do
|
should "authenticate" do
|
||||||
@user = Factory.create(:user)
|
|
||||||
assert(User.authenticate(@user.name, "password"), "Authentication should have succeeded")
|
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, "password2"), "Authentication should not have succeeded")
|
||||||
assert(User.authenticate_hash(@user.name, @user.password_hash), "Authentication should have succeeded")
|
assert(User.authenticate_hash(@user.name, @user.password_hash), "Authentication should have succeeded")
|
||||||
|
|||||||
Reference in New Issue
Block a user