diff --git a/app/controllers/emails_controller.rb b/app/controllers/emails_controller.rb
index 37b385e48..2eb034ecf 100644
--- a/app/controllers/emails_controller.rb
+++ b/app/controllers/emails_controller.rb
@@ -1,8 +1,19 @@
class EmailsController < ApplicationController
respond_to :html, :xml, :json
+ def index
+ @email_addresses = authorize EmailAddress.visible(CurrentUser.user).paginated_search(params, count_pages: true)
+ @email_addresses = @email_addresses.includes(:user)
+ respond_with(@email_addresses)
+ end
+
def show
- @email_address = authorize EmailAddress.find_by_user_id!(params[:user_id])
+ if params[:user_id]
+ @email_address = authorize EmailAddress.find_by_user_id!(params[:user_id])
+ else
+ @email_address = authorize EmailAddress.find(params[:id])
+ end
+
respond_with(@email_address)
end
diff --git a/app/models/email_address.rb b/app/models/email_address.rb
index 367af7a97..9d4dd6f22 100644
--- a/app/models/email_address.rb
+++ b/app/models/email_address.rb
@@ -10,6 +10,14 @@ class EmailAddress < ApplicationRecord
validate :validate_deliverable, on: :deliverable
after_save :update_user
+ def self.visible(user)
+ if user.is_moderator?
+ where(user: User.where("level < ?", user.level)).or(where(user: user))
+ else
+ none
+ end
+ end
+
def address=(value)
self.normalized_address = EmailValidator.normalize(value) || address
super
@@ -29,6 +37,15 @@ class EmailAddress < ApplicationRecord
user.update!(is_verified: is_verified? && nondisposable?)
end
+ def self.search(params)
+ q = super
+
+ q = q.search_attributes(params, :user, :address, :normalized_address, :is_verified, :is_deliverable)
+ q = q.apply_default_order(params)
+
+ q
+ end
+
concerning :VerificationMethods do
def verifier
@verifier ||= Danbooru::MessageVerifier.new(:email_verification_key)
diff --git a/app/policies/email_address_policy.rb b/app/policies/email_address_policy.rb
index 152469125..f9ddcdfdb 100644
--- a/app/policies/email_address_policy.rb
+++ b/app/policies/email_address_policy.rb
@@ -1,4 +1,8 @@
class EmailAddressPolicy < ApplicationPolicy
+ def index?
+ user.is_moderator?
+ end
+
def show?
record.user_id == user.id || (user.is_moderator? && record.user.level < user.level)
end
diff --git a/app/views/emails/index.html.erb b/app/views/emails/index.html.erb
new file mode 100644
index 000000000..c1f7429ac
--- /dev/null
+++ b/app/views/emails/index.html.erb
@@ -0,0 +1,32 @@
+
+
+ <%= search_form_for(emails_path) do |f| %>
+ <%= f.simple_fields_for :user do |fa| %>
+ <%= fa.input :name, label: "User Name", input_html: { value: params.dig(:search, :user, :name), "data-autocomplete": "user" } %>
+ <% end %>
+
+ <%= f.input :address_ilike, label: "Address", input_html: { value: params[:search][:address] }, hint: "Use * for wildcard" %>
+ <%= f.input :is_verified, label: "Verified?", collection: %w[Yes No], selected: params[:search][:is_verified] %>
+ <%= f.submit "Search" %>
+ <% end %>
+
+ <%= table_for @email_addresses, class: "striped autofit" do |t| %>
+ <% t.column :user do |email| %>
+ <%= link_to_user email.user %>
+ <% end %>
+ <% t.column :address %>
+ <% t.column :is_verified, name: "Verified?" do |email| %>
+ <% if email.is_verified? %>
+ <%= link_to "Yes", emails_path(search: { is_verified: true }) %>
+ <% else %>
+ <%= link_to "No", emails_path(search: { is_verified: false }) %>
+ <% end %>
+ <% end %>
+ <% t.column :updated_at, name: "Updated" do |email| %>
+ <%= time_ago_in_words_tagged(email.updated_at) %>
+ <% end %>
+ <% end %>
+
+ <%= numbered_paginator(@email_addresses) %>
+
+
diff --git a/app/views/static/site_map.html.erb b/app/views/static/site_map.html.erb
index b88b8c138..4e82675d2 100644
--- a/app/views/static/site_map.html.erb
+++ b/app/views/static/site_map.html.erb
@@ -154,6 +154,10 @@
<%= link_to("Moderation Reports", moderation_reports_path) %>
<% end %>
+ <% if policy(EmailAddress).index? %>
+ <%= link_to("Email Addresses", emails_path) %>
+ <% end %>
+
<% if policy(IpAddress).index? %>
<%= link_to("IP Addresses", ip_addresses_path) %>
<% end %>
diff --git a/config/routes.rb b/config/routes.rb
index ce3a273c1..11d7e7d28 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -103,6 +103,7 @@ Rails.application.routes.draw do
end
resource :dtext_preview, :only => [:create]
resources :dtext_links, only: [:index]
+ resources :emails, only: [:index, :show]
resources :favorites, :only => [:index, :create, :destroy]
resources :favorite_groups do
member do
diff --git a/test/functional/emails_controller_test.rb b/test/functional/emails_controller_test.rb
index ec08a2c03..4da9bb9a5 100644
--- a/test/functional/emails_controller_test.rb
+++ b/test/functional/emails_controller_test.rb
@@ -10,6 +10,26 @@ class EmailsControllerTest < ActionDispatch::IntegrationTest
@restricted_user = create(:user, requires_verification: true, is_verified: false)
end
+ context "#index" do
+ should "not let regular users see emails belonging to other users" do
+ get_auth emails_path, @user
+ assert_response 403
+ end
+
+ should "let mods see emails belonging to themselves and all users below mod level" do
+ @mod1 = create(:moderator_user, email_address: build(:email_address))
+ @mod2 = create(:moderator_user, email_address: build(:email_address))
+
+ get_auth emails_path, @mod1
+
+ assert_response :success
+ assert_select "#email-address-#{@user.email_address.id}", count: 1
+ assert_select "#email-address-#{@other_user.email_address.id}", count: 1
+ assert_select "#email-address-#{@mod1.email_address.id}", count: 1
+ assert_select "#email-address-#{@mod2.email_address.id}", count: 0
+ end
+ end
+
context "#show" do
should "render" do
get_auth user_email_path(@user), @user, as: :json