bans: change expires_at field to duration.

Changes:

* Change the `expires_at` field to `duration`.
* Make moderators choose from a fixed set of standard ban lengths,
  instead of allowing arbitrary ban lengths.
* List `duration` in seconds in the /bans.json API.
* Dump bans to BigQuery.

Note that some old bans have a negative duration. This is because their
expiration date was before their creation date, which is because in 2013
bans were migrated to Danbooru 2 and the original ban creation dates
were lost.
This commit is contained in:
evazion
2021-03-10 17:20:52 -06:00
parent 791b8c61f6
commit 81fe68d392
16 changed files with 90 additions and 86 deletions

View File

@@ -1,5 +1,5 @@
class BansController < ApplicationController
respond_to :html, :xml, :json
respond_to :html, :xml, :json, :js
def new
@ban = authorize Ban.new(permitted_attributes(Ban))

View File

@@ -21,7 +21,7 @@ class BigqueryExportService
Rails.application.eager_load!
models = ApplicationRecord.descendants.sort_by(&:name)
models -= [Ban, Favorite, IpAddress, TagRelationship, ArtistVersion, ArtistCommentaryVersion, NoteVersion, PoolVersion, PostVersion, WikiPageVersion]
models -= [Favorite, IpAddress, TagRelationship, ArtistVersion, ArtistCommentaryVersion, NoteVersion, PoolVersion, PostVersion, WikiPageVersion]
models
end

View File

@@ -212,7 +212,7 @@ module Searchable
search_text_attribute(name, params)
when :boolean
search_boolean_attribute(name, params)
when :integer, :float, :datetime
when :integer, :float, :datetime, :interval
search_numeric_attribute(name, params, type: type)
when :inet
search_inet_attribute(name, params)
@@ -221,7 +221,7 @@ module Searchable
when :array
search_array_attribute(name, subtype, params)
else
raise NotImplementedError, "unhandled attribute type: #{name}"
raise NotImplementedError, "unhandled attribute type: #{name} (#{type})"
end
end

View File

@@ -740,6 +740,9 @@ class PostQueryBuilder
when :age
DurationParser.parse(object).ago
when :interval
DurationParser.parse(object)
when :ratio
object =~ /\A(\d+(?:\.\d+)?):(\d+(?:\.\d+)?)\Z/i
@@ -765,6 +768,9 @@ class PostQueryBuilder
end
(size * conversion_factor).to_i
else
raise NotImplementedError, "unrecognized type #{type} for #{object}"
end
end

View File

@@ -1,4 +1,6 @@
class Ban < ApplicationRecord
attribute :duration, :interval
after_create :create_feedback
after_create :update_user_on_create
after_create :create_ban_mod_action
@@ -7,20 +9,14 @@ class Ban < ApplicationRecord
belongs_to :user
belongs_to :banner, :class_name => "User"
validates_presence_of :reason, :duration
validates :reason, presence: true
validate :user, :validate_user_is_bannable, on: :create
scope :unexpired, -> { where("bans.expires_at > ?", Time.now) }
scope :expired, -> { where("bans.expires_at <= ?", Time.now) }
attr_reader :duration
def self.is_banned?(user)
exists?(["user_id = ? AND expires_at > ?", user.id, Time.now])
end
scope :unexpired, -> { where("bans.created_at + bans.duration > ?", Time.now) }
scope :expired, -> { where("bans.created_at + bans.duration <= ?", Time.now) }
def self.search(params)
q = search_attributes(params, :id, :created_at, :updated_at, :expires_at, :reason, :user, :banner)
q = search_attributes(params, :id, :created_at, :updated_at, :duration, :reason, :user, :banner)
q = q.text_attribute_matches(:reason, params[:reason_matches])
q = q.expired if params[:expired].to_s.truthy?
@@ -28,7 +24,7 @@ class Ban < ApplicationRecord
case params[:order]
when "expires_at_desc"
q = q.order("bans.expires_at desc")
q = q.order(Arel.sql("bans.created_at + bans.duration DESC"))
else
q = q.apply_default_order(params)
end
@@ -62,9 +58,8 @@ class Ban < ApplicationRecord
self.user = User.find_by_name(username)
end
def duration=(dur)
self.expires_at = dur.to_i.days.from_now
@duration = dur
def expires_at
created_at + duration
end
def humanized_duration

View File

@@ -9,11 +9,11 @@ class BanPolicy < ApplicationPolicy
alias_method :destroy?, :bannable?
def permitted_attributes_for_create
[:reason, :duration, :expires_at, :user_id, :user_name]
[:reason, :duration, :user_id, :user_name]
end
def permitted_attributes_for_update
[:reason, :duration, :expires_at]
[:reason, :duration]
end
def html_data_attributes

View File

@@ -1,6 +1,9 @@
<%= edit_form_for(ban) do |f| %>
<%= f.input :user_name, :as => :string, :input_html => { data: { autocomplete: "user" } } %>
<%= f.input :duration, :hint => "in days" %>
<% if ban.new_record? %>
<%= f.input :user_name, as: :string, input_html: { "data-autocomplete": "user" } %>
<% end %>
<%= f.input :duration, collection: [["1 day", 1.day.iso8601], ["3 days", 3.days.iso8601], ["1 week", 1.week.iso8601], ["1 month", 1.month.iso8601], ["3 months", 3.months.iso8601], ["6 months", 6.months.iso8601], ["1 year", 1.year.iso8601], ["forever", 100.years.iso8601]] %>
<%= f.input :reason %>
<%= f.button :submit, :value => "Ban" %>
<%= f.button :submit %>
<% end %>

View File

@@ -1 +1 @@
$("#ban-<%= @ban.id %>").remove();
location.reload();

View File

@@ -2,11 +2,7 @@
<div id="a-edit">
<h1>Edit Ban</h1>
<%= edit_form_for(@ban) do |f| %>
<%= f.input :duration, :hint => "in days" %>
<%= f.input :reason %>
<%= f.button :submit, :value => "Ban" %>
<% end %>
<%= render "form", ban: @ban %>
</div>
</div>

View File

@@ -9,14 +9,14 @@
<%= link_to_user(ban.user) %>
<%= link_to "»", bans_path(search: search_params.merge(user_name: ban.user.name)) %>
<% end %>
<% t.column "Duration" do |ban| %>
<%= humanized_duration(ban.created_at, ban.expires_at) %>
<% end %>
<% t.column "Reason", td: {class: "col-expand"} do |ban| %>
<div class="prose">
<%= format_text ban.reason %>
</div>
<% end %>
<% t.column "Duration" do |ban| %>
<%= humanized_duration(ban.created_at, ban.expires_at) %>
<% end %>
<% t.column "Banner" do |ban| %>
<%= link_to_user ban.banner %>
<%= link_to "»", bans_path(search: { banner_name: ban.banner.name }) %>

View File

@@ -5,10 +5,10 @@
<div id="a-show">
<h1>Show Ban</h1>
<ul style="margin-bottom: 1em;">
<li><strong>User</strong>: <%= link_to_user(@ban.user) %></li>
<li><strong>Expires</strong>: <%= compact_time @ban.expires_at %></li>
<li><strong>User</strong> <%= link_to_user(@ban.user) %></li>
<li><strong>Duration</strong> <%= humanized_duration(@ban.created_at, @ban.expires_at) %></li>
<li>
<strong>Reason</strong>:
<strong>Reason</strong>
<div class="prose">
<%= format_text @ban.reason %>
</div>