Merge pull request #2980 from evazion/feat-bans

Improve /bans listing
This commit is contained in:
Albert Yi
2017-04-20 14:04:10 -07:00
committed by GitHub
10 changed files with 126 additions and 13 deletions

View File

@@ -27,6 +27,9 @@ $preview_flagged_color: #F00;
$preview_sample_warning_color: hsl(0, 100%, 90%); // light red
$preview_quality_warning_color: hsl(50, 100%, 90%); // light yellow
$error_color: hsl(0, 100%, 95%); // light red
$success_color: hsl(120, 100%, 95%); // light green
@mixin border-radius($val) {
-moz-border-radius: $val;
-webkit-border-radius: $val;

View File

@@ -40,6 +40,22 @@ table.striped {
}
}
/*
* A table where one column expands to fill the screen, while the
* other columns shrink to fit their contents.
*/
table.autofit {
td, th, .col-fit {
white-space: nowrap;
padding-right: 2em;
}
.col-expand {
white-space: normal;
width: 100%;
}
}
table.search {
tr {
height: 2em;

View File

@@ -0,0 +1,19 @@
@import "../common/000_vars.scss";
#c-bans #a-index {
tr[data-expired="true"] {
background-color: $success_color !important;
&:hover {
background-color: darken($success_color, 5%) !important;
}
}
tr[data-expired="false"] {
background-color: $error_color !important;
&:hover {
background-color: darken($error_color, 5%) !important;
}
}
}

View File

@@ -11,9 +11,10 @@ class BansController < ApplicationController
end
def index
@search = Ban.search(params[:search]).order("id desc")
@bans = @search.paginate(params[:page], :limit => params[:limit])
respond_with(@bans)
@bans = Ban.search(params[:search]).paginate(params[:page], :limit => params[:limit])
respond_with(@bans) do |fmt|
fmt.html { @bans = @bans.includes(:user, :banner) }
end
end
def show

View File

@@ -81,6 +81,14 @@ module ApplicationHelper
content_tag(:time, content || datetime, :datetime => datetime, :title => time.to_formatted_s)
end
def humanized_duration(from, to)
duration = distance_of_time_in_words(from, to)
datetime = from.iso8601 + "/" + to.iso8601
title = "#{from.strftime("%Y-%m-%d %H:%M")} to #{to.strftime("%Y-%m-%d %H:%M")}"
raw content_tag(:time, duration, datetime: datetime, title: title)
end
def time_ago_in_words_tagged(time)
raw time_tag(time_ago_in_words(time) + " ago", time)
end

View File

@@ -10,13 +10,23 @@ class Ban < ActiveRecord::Base
validates_presence_of :user_id, :reason, :duration
before_validation :initialize_banner_id, :on => :create
scope :unexpired, -> { where("bans.expires_at > ?", Time.now) }
scope :expired, -> { where("bans.expires_at <= ?", Time.now) }
def self.is_banned?(user)
exists?(["user_id = ? AND expires_at > ?", user.id, Time.now])
end
def self.reason_matches(query)
if query =~ /\*/
where("lower(bans.reason) LIKE ?", query.to_escaped_for_sql_like)
else
where("bans.reason @@ plainto_tsquery(?)", query)
end
end
def self.search(params)
q = where("true")
return q if params.blank?
if params[:banner_name]
q = q.where("banner_id = (select _.id from users _ where lower(_.name) = ?)", params[:banner_name].mb_chars.downcase)
@@ -34,6 +44,22 @@ class Ban < ActiveRecord::Base
q = q.where("user_id = ?", params[:user_id].to_i)
end
if params[:reason_matches].present?
q = q.reason_matches(params[:reason_matches])
end
case params[:expired]
when "true" then q = q.expired
when "false" then q = q.unexpired
end
case params[:order]
when "expires_at_desc"
q = q.order("bans.expires_at desc")
else
q = q.order("bans.id desc")
end
q
end

View File

@@ -74,7 +74,7 @@ class UserFeedback < ActiveRecord::Base
extend SearchMethods
def initialize_creator
self.creator_id = CurrentUser.id
self.creator_id ||= CurrentUser.id
end
def user_name

View File

@@ -0,0 +1,8 @@
<%= simple_form_for(:search, method: :get, url: bans_path, defaults: { required: false }, html: { class: "inline-form" }) do |f| %>
<%= f.input :user_name, label: "User", input_html: { value: params[:search][:user_name] } %>
<%= f.input :banner_name, label: "Banner", input_html: { value: params[:search][:banner_name] } %>
<%= f.input :reason_matches, label: "Reason", hint: "Use * for wildcard", input_html: { value: params[:search][:reason_matches] } %>
<%= f.input :expired, label: "Expired?", collection: [["Yes", true], ["No", false]], include_blank: true, selected: params[:search][:expired] %>
<%= f.input :order, include_blank: false, collection: [%w[Created id_desc], %w[Expiration expires_at_desc]], selected: params[:search][:order] %>
<%= f.submit "Search" %>
<% end %>

View File

@@ -1,22 +1,34 @@
<div class="bans">
<div class="index">
<div id="c-bans">
<div id="a-index">
<h1>Bans</h1>
<table class="striped" width="100%">
<%= render "search" %>
<table class="striped autofit">
<thead>
<tr>
<th>User</th>
<th>Expires</th>
<th>Banner</th>
<th>Banned</th>
<th>Duration</th>
<th>Reason</th>
<th></th>
</tr>
</thead>
<tbody>
<% @bans.each do |ban| %>
<tr id="ban-<%= ban.id %>">
<td><%= link_to_user(ban.user) %></td>
<td><%= ban.expires_at %></td>
<td><%= format_text ban.reason, :ragel => true %></td>
<tr id="ban-<%= ban.id %>" data-expired="<%= ban.expired? %>">
<td>
<%= link_to_user(ban.user) %>
<%= link_to "»", bans_path(search: params[:search].merge(user_name: ban.user.name)) %>
</td>
<td>
<%= link_to_user(ban.banner) %>
<%= link_to "»", bans_path(search: params[:search].merge(banner_name: ban.banner.name)) %>
</td>
<td><%= time_ago_in_words_tagged(ban.created_at) %></td>
<td><%= humanized_duration(ban.created_at, ban.expires_at) %></td>
<td class="col-expand"><%= format_text ban.reason, :ragel => true %></td>
<td>
<% if CurrentUser.is_moderator? %>
<%= link_to "Edit", edit_ban_path(ban) %>

View File

@@ -165,6 +165,26 @@ class BanTest < ActiveSupport::TestCase
end
context "Searching for a ban" do
should "find a given ban" do
CurrentUser.user = FactoryGirl.create(:admin_user)
CurrentUser.ip_addr = "127.0.0.1"
user = FactoryGirl.create(:user)
ban = FactoryGirl.create(:ban, user: user)
params = {
user_name: user.name,
banner_name: ban.banner.name,
reason: ban.reason,
expired: false,
order: :id_desc
}
bans = Ban.search(params)
assert_equal(1, bans.length)
assert_equal(ban.id, bans.first.id)
end
context "by user id" do
setup do
@admin = FactoryGirl.create(:admin_user)