Add ability to mark tags as deprecated

* Deprecated tags can't be added to posts, but existing deprecated tags
  in a post won't be removed
* Only empty tags can be marked as deprecated manually
* No tags can be manually undeprecated
** These limits don't apply to admins
* Deprecating or undeprecating a tag will create a new mod action to
  prevent people from going rogue
* Added deprecate/undeprecate commands for BURs
* Deprecating a tag via BUR removes all implications to and from it as well
This commit is contained in:
nonamethanks
2022-04-08 00:51:53 +02:00
parent 98a9b2484b
commit ea76a889db
16 changed files with 194 additions and 5 deletions

View File

@@ -50,6 +50,10 @@ class BulkUpdateRequestProcessor
[:change_category, Tag.normalize_name($1), $2.downcase]
when /\Anuke (\S+)\z/i
[:nuke, $1]
when /\Adeprecate (\S+)\z/i
[:deprecate, $1]
when /\Aundeprecate (\S+)\z/i
[:undeprecate, $1]
else
[:invalid_line, line]
end
@@ -125,6 +129,18 @@ class BulkUpdateRequestProcessor
when :nuke
# okay
when :deprecate
tag = Tag.find_by_name(args[0])
if tag.is_deprecated?
errors.add(:base, "Can't deprecate #{args[0]} (tag is already deprecated)")
end
when :undeprecate
tag = Tag.find_by_name(args[0])
if !tag.is_deprecated?
errors.add(:base, "Can't undeprecate #{args[0]} (tag is not deprecated)")
end
when :invalid_line
errors.add(:base, "Invalid line: #{args[0]}")
@@ -182,6 +198,16 @@ class BulkUpdateRequestProcessor
tag = Tag.find_or_create_by_name(args[0])
tag.update!(category: Tag.categories.value_for(args[1]))
when :deprecate
tag = Tag.find_or_create_by_name(args[0])
tag.update!(is_deprecated: true)
TagImplication.active.where(consequent_name: tag.name).each { |ti| ti.reject!(User.system) }
TagImplication.active.where(antecedent_name: tag.name).each { |ti| ti.reject!(User.system) }
when :undeprecate
tag = Tag.find_or_create_by_name(args[0])
tag.update!(is_deprecated: false)
else
# should never happen
raise Error, "Unknown command: #{command}"
@@ -204,7 +230,7 @@ class BulkUpdateRequestProcessor
[args[0], args[1]]
when :mass_update
PostQuery.new(args[0]).tag_names + PostQuery.new(args[1]).tag_names
when :nuke
when :nuke, :deprecate, :undeprecate
PostQuery.new(args[0]).tag_names
when :change_category
args[0]
@@ -240,6 +266,8 @@ class BulkUpdateRequestProcessor
else
"nuke {{#{args[0]}}}"
end
when :deprecate, :undeprecate
"#{command.to_s} [[#{args[0]}]]"
when :change_category
"category [[#{args[0]}]] -> #{args[1]}"
else

View File

@@ -60,6 +60,8 @@ class ModAction < ApplicationRecord
tag_implication_create: 140,
tag_implication_update: 141, # XXX unused
tag_implication_delete: 142,
tag_deprecate: 240,
tag_undeprecate: 242,
ip_ban_create: 160,
ip_ban_delete: 162,
ip_ban_undelete: 163,

View File

@@ -411,6 +411,7 @@ class Post < ApplicationRecord
normalized_tags = Tag.convert_cosplay_tags(normalized_tags)
normalized_tags += Tag.create_for_list(Tag.automatic_tags_for(normalized_tags))
normalized_tags += TagImplication.tags_implied_by(normalized_tags).map(&:name)
normalized_tags -= added_deprecated_tags
normalized_tags = normalized_tags.compact.uniq.sort
normalized_tags = Tag.create_for_list(normalized_tags)
self.tag_string = normalized_tags.join(" ")
@@ -428,6 +429,16 @@ class Post < ApplicationRecord
tag_names - invalid_tags.map(&:name)
end
def added_deprecated_tags
added_deprecated_tags = added_tags.select(&:is_deprecated)
if added_deprecated_tags.present?
added_deprecated_tags_list = added_deprecated_tags.map { |t| "[[#{t.name}]]" }.to_sentence
warnings.add(:base, "The following tags are deprecated and could not be added: #{added_deprecated_tags_list}")
end
added_deprecated_tags.pluck(:name)
end
def remove_negated_tags(tags)
@negated_tags, tags = tags.partition {|x| x =~ /\A-/i}
@negated_tags = @negated_tags.map {|x| x[1..-1]}

View File

@@ -16,6 +16,7 @@ class Tag < ApplicationRecord
validates :category, inclusion: { in: TagCategory.category_ids }
before_create :create_character_tag_for_cosplay_tag, if: :is_cosplay_tag?
before_save :create_mod_action
after_save :update_category_cache, if: :saved_change_to_category?
after_save :update_category_post_counts, if: :saved_change_to_category?
@@ -217,6 +218,17 @@ class Tag < ApplicationRecord
end
end
concerning :DeprecationMethods do
def create_mod_action
return if CurrentUser.user == User.system
if is_deprecated_was == true and is_deprecated == false
ModAction.log("marked the tag [[#{name}]] as not deprecated", :tag_undeprecate)
elsif is_deprecated_was == false and is_deprecated == true
ModAction.log("marked the tag [[#{name}]] as deprecated", :tag_deprecate)
end
end
end
module SearchMethods
def autocorrect_matches(name)
fuzzy_name_matches(name).order_similarity(name)
@@ -265,7 +277,7 @@ class Tag < ApplicationRecord
end
def search(params)
q = search_attributes(params, :id, :created_at, :updated_at, :category, :post_count, :name, :wiki_page, :artist, :antecedent_alias, :consequent_aliases, :antecedent_implications, :consequent_implications, :dtext_links)
q = search_attributes(params, :id, :created_at, :updated_at, :is_deprecated, :category, :post_count, :name, :wiki_page, :artist, :antecedent_alias, :consequent_aliases, :antecedent_implications, :consequent_implications, :dtext_links)
if params[:fuzzy_name_matches].present?
q = q.fuzzy_name_matches(params[:fuzzy_name_matches])

View File

@@ -7,7 +7,14 @@ class TagPolicy < ApplicationPolicy
(user.is_member? && record.post_count < 50)
end
def can_change_deprecated_status?
user.is_admin? || (record.post_count == 0 && !record.is_deprecated?)
end
def permitted_attributes
[(:category if can_change_category?)].compact
[
(:category if can_change_category?),
(:is_deprecated if can_change_deprecated_status?),
].compact
end
end

View File

@@ -121,6 +121,10 @@
<%= format_text(wiki_page.body) %>
<%= render "tag_relationships/alias_and_implication_list", tag: wiki_page.tag %>
<% if wiki_page.tag&.is_deprecated? %>
<p class="fineprint">This tag is <%= link_to "deprecated", wiki_page_path("help:deprecation_notice") %> and can't be added to new posts.</p>
<% end %>
<p class="mt-4">
<%= link_to_wiki "View wiki", wiki_page.title, id: "view-wiki-link" %>
</p>
@@ -154,6 +158,10 @@
<p>There is no wiki page yet for the tag <%= link_to_wiki @post_set.tag.pretty_name %>. <%= link_to "Create new wiki page", new_wiki_page_path(wiki_page: { title: @post_set.tag.name }), rel: "nofollow" %>.</p>
<%= render "tag_relationships/alias_and_implication_list", tag: @post_set.tag %>
<% if @post_set.tag&.is_deprecated? %>
<p class="fineprint">This tag is <%= link_to "deprecated", wiki_page_path("help:deprecation_notice") %> and can't be added to new posts.</p>
<% end %>
<% end %>
</div>

View File

@@ -2,6 +2,7 @@
<%= f.input :name_or_alias_matches, label: "Name", input_html: { value: params[:search][:name_or_alias_matches], data: { autocomplete: "tag" } } %>
<%= f.input :category, label: "Category", collection: TagCategory.canonical_mapping.to_a, include_blank: true,selected: params[:search][:category] %>
<%= f.input :order, collection: [%w[Newest date], %w[Count count], %w[Name name]], include_blank: false, selected: params[:search][:order] %>
<%= f.input :is_deprecated, label: "Is deprecated?", collection: %w[yes no], include_blank: true, selected: params[:search][:is_deprecated] %>
<%= f.input :hide_empty, label: "Hide empty?", collection: %w[yes no], selected: params[:search][:hide_empty] %>
<%= f.input :has_wiki_page, label: "Has wiki?", collection: %w[yes no], include_blank: true, selected: params[:search][:has_wiki_page] %>
<%= f.input :has_artist, label: "Has artist?", collection: %w[yes no], include_blank: true, selected: params[:search][:has_artist] %>

View File

@@ -5,10 +5,20 @@
<%= edit_form_for(@tag) do |f| %>
<% if policy(@tag).can_change_category? %>
<%= f.input :category, collection: TagCategory.canonical_mapping.to_a, include_blank: false %>
<%= f.button :submit, "Submit" %>
<% else %>
<p>Create a <%= link_to "bulk update request", new_bulk_update_request_path(bulk_update_request: { script: "category #{@tag.name} -> general" }) %> to change this tag's category.</p>
<% end %>
<% if policy(@tag).can_change_deprecated_status? %>
<%= f.input :is_deprecated, :collection => [["No", "false"], ["Yes", "true"]], :include_blank => false %>
<% else %>
<% if @tag.is_deprecated? %>
<p>Create a <%= link_to "bulk update request", new_bulk_update_request_path(bulk_update_request: { script: "deprecate #{@tag.name}" }) %> to mark this tag as not deprecated.</p>
<% else %>
<p>Create a <%= link_to "bulk update request", new_bulk_update_request_path(bulk_update_request: { script: "undeprecate #{@tag.name}" }) %> to mark this tag as deprecated.</p>
<% end %>
<% end %>
<%= f.button :submit, "Submit" %>
<% end %>
</div>
</div>

View File

@@ -8,6 +8,9 @@
<ul>
<li>Count: <%= @tag.post_count %></li>
<li>Category: <%= @tag.category_name %></li>
<% if @tag.is_deprecated? %>
<li> This tag is <%= link_to "deprecated", wiki_page_path("help:deprecation_notice") %> and can't be added to new posts.</li>
<% end %>
</ul>
</div>
</div>

View File

@@ -18,6 +18,11 @@
<% end %>
<%= render "tag_relationships/alias_and_implication_list", tag: @wiki_page.tag %>
<% if @wiki_page.tag&.is_deprecated? %>
<p class="fineprint">This tag is <%= link_to "deprecated", wiki_page_path("help:deprecation_notice") %> and can't be added to new posts.</p>
<% end %>
<%= render "wiki_pages/posts", wiki_page: @wiki_page %>
<% end %>

View File

@@ -37,6 +37,10 @@
<% end %>
<%= render "tag_relationships/alias_and_implication_list", tag: @wiki_page.tag %>
<% if @wiki_page.tag&.is_deprecated? %>
<p class="fineprint">This tag is <%= link_to "deprecated", wiki_page_path("help:deprecation_notice") %> and can't be added to new posts.</p>
<% end %>
</div>
<%= render "wiki_pages/posts", wiki_page: @wiki_page %>