Fix #4877: Error when tagging favgroup:foo when post is already in favgroup:foo

Bug: If a tag edit failed because it contained a metatag that raised an
exception, then a new post version would be created even though the edit
didn't go through. This could happen if the newpool:, fav:, favgroup:,
disapproved:, status:active, or status:banned metatags failed (for
example, because of a privilege error).

Fix: Silently ignore all errors raised when applying metatags. This way
the edit will always succeed, so erroneous post versions won't be created.
This commit is contained in:
evazion
2022-05-02 14:23:01 -05:00
parent 93352b318e
commit d2502a0c40
2 changed files with 65 additions and 18 deletions

View File

@@ -546,6 +546,11 @@ class Post < ApplicationRecord
end end
end end
rescue
# XXX Silently ignore errors so that the edit doesn't fail. We can't let
# the edit fail because then it will create a new post version even if
# the edit didn't go through.
nil
end end
def apply_pre_metatags def apply_pre_metatags
@@ -709,6 +714,11 @@ class Post < ApplicationRecord
parent.update_has_children_flag if parent.present? parent.update_has_children_flag if parent.present?
Post.find(parent_id_before_last_save).update_has_children_flag if parent_id_before_last_save.present? Post.find(parent_id_before_last_save).update_has_children_flag if parent_id_before_last_save.present?
rescue
# XXX Silently ignore errors so that the edit doesn't fail. We can't let
# the edit fail because then it will create a new post version even if
# the edit didn't go through.
nil
end end
def give_favorites_to_parent def give_favorites_to_parent
@@ -790,6 +800,9 @@ class Post < ApplicationRecord
end end
concerning :VersionMethods do concerning :VersionMethods do
# XXX `create_version` must be called before `apply_post_metatags` because
# `apply_post_metatags` may update the post itself, which will clear all
# changes to the post and make saved_change_to_*? return false.
def create_version(force = false) def create_version(force = false)
if new_record? || saved_change_to_watched_attributes? || force if new_record? || saved_change_to_watched_attributes? || force
create_new_version create_new_version

View File

@@ -769,21 +769,21 @@ class PostTest < ActiveSupport::TestCase
end end
should "not allow banned users to fav" do should "not allow banned users to fav" do
assert_raises(User::PrivilegeError) do assert_no_difference("@post.favorites.count") do
as(create(:banned_user)) { @post.update(tag_string: "aaa fav:self") } as(create(:banned_user)) { @post.update(tag_string: "aaa fav:self") }
end end
assert_raises(User::PrivilegeError) do assert_no_difference("@post.favorites.count") do
as(create(:banned_user)) { @post.update(tag_string: "aaa -fav:self") } as(create(:banned_user)) { @post.update(tag_string: "aaa -fav:self") }
end end
end end
should "not allow restricted users to fav" do should "not allow restricted users to fav" do
assert_raises(User::PrivilegeError) do assert_no_difference("@post.favorites.count") do
as(create(:restricted_user)) { @post.update(tag_string: "aaa fav:self") } as(create(:restricted_user)) { @post.update(tag_string: "aaa fav:self") }
end end
assert_raises(User::PrivilegeError) do assert_no_difference("@post.favorites.count") do
as(create(:restricted_user)) { @post.update(tag_string: "aaa -fav:self") } as(create(:restricted_user)) { @post.update(tag_string: "aaa -fav:self") }
end end
end end
@@ -832,8 +832,9 @@ class PostTest < ActiveSupport::TestCase
end end
should "not approve the post if the user is doesn't have permission" do should "not approve the post if the user is doesn't have permission" do
assert_raises(User::PrivilegeError) do @post.update!(is_pending: true)
@post.update!(is_pending: true)
assert_no_difference("@post.approvals.count") do
@post.update(tag_string: "aaa status:active") @post.update(tag_string: "aaa status:active")
end end
@@ -851,7 +852,7 @@ class PostTest < ActiveSupport::TestCase
end end
should "not ban the post if the user doesn't have permission" do should "not ban the post if the user doesn't have permission" do
assert_raises(User::PrivilegeError) do assert_no_changes("@post.reload.is_banned?") do
@post.update(tag_string: "aaa status:banned") @post.update(tag_string: "aaa status:banned")
end end
@@ -870,9 +871,10 @@ class PostTest < ActiveSupport::TestCase
end end
should "not unban the post if the user doesn't have permission" do should "not unban the post if the user doesn't have permission" do
assert_raises(User::PrivilegeError) do @post.update!(is_banned: true)
@post.update!(is_banned: true)
@post.update(tag_string: "aaa status:banned") assert_no_changes("@post.reload.is_banned?") do
@post.update(tag_string: "aaa -status:banned")
end end
assert_equal(true, @post.reload.is_banned?) assert_equal(true, @post.reload.is_banned?)
@@ -894,7 +896,7 @@ class PostTest < ActiveSupport::TestCase
end end
should "not disapprove the post if the user is doesn't have permission" do should "not disapprove the post if the user is doesn't have permission" do
assert_raises(User::PrivilegeError) do assert_no_difference("@post.disapprovals.count") do
@post.update!(is_pending: true) @post.update!(is_pending: true)
@post.update(tag_string: "aaa disapproved:disinterest") @post.update(tag_string: "aaa disapproved:disinterest")
end end
@@ -903,7 +905,7 @@ class PostTest < ActiveSupport::TestCase
end end
should "not allow disapproving active posts" do should "not allow disapproving active posts" do
assert_raises(User::PrivilegeError) do assert_no_difference("@post.disapprovals.count") do
@post.update(tag_string: "aaa disapproved:disinterest") @post.update(tag_string: "aaa disapproved:disinterest")
end end
@@ -1284,25 +1286,57 @@ class PostTest < ActiveSupport::TestCase
context "that has been updated" do context "that has been updated" do
should "create a new version if it's the first version" do should "create a new version if it's the first version" do
assert_difference("PostVersion.count", 1) do assert_difference("PostVersion.count", 1) do
post = FactoryBot.create(:post) post = create(:post, tag_string: "foo")
assert_equal("foo", post.versions.last.tags)
assert_equal(["foo"], post.versions.last.added_tags)
end end
end end
should "create a new version if it's been over an hour since the last update" do should "create a new version if it's been over an hour since the last update" do
post = FactoryBot.create(:post) post = create(:post, tag_string: "foo")
travel(6.hours) do travel(6.hours) do
assert_difference("PostVersion.count", 1) do assert_difference("PostVersion.count", 1) do
post.update(tag_string: "zzz") post.update(tag_string: "bar foo")
end end
end end
assert_equal("bar foo", post.versions.last.tags)
assert_equal(["bar"], post.versions.last.added_tags)
end end
should "merge with the previous version if the updater is the same user and it's been less than an hour" do should "merge with the previous version if the updater is the same user and it's been less than an hour" do
post = FactoryBot.create(:post) post = create(:post, tag_string: "foo")
assert_difference("PostVersion.count", 0) do assert_difference("PostVersion.count", 0) do
post.update(tag_string: "zzz") post.update(tag_string: "foo bar")
end end
assert_equal("zzz", post.versions.last.tags)
assert_equal("bar foo", post.versions.last.tags)
# assert_equal(%w[bar foo], post.versions.last.added_tags)
end
should "create a new version even if adding a metatag fails" do
travel(2.hours) do
@post.update!(tag_string: "foo status:banned")
end
assert_equal(false, @post.reload.is_banned?)
assert_equal(2, @post.versions.last.version)
assert_equal("foo", @post.tag_string)
assert_equal("foo", @post.versions.last.tags)
end
should "record the changes correctly when using a metatag that modifies the post itself" do
travel(2.hours) do
@post.update!(tag_string: "foo fav:me")
end
assert_equal(1, @post.reload.score)
assert_equal(2, @post.versions.last.version)
assert_equal("foo", @post.tag_string)
assert_equal("foo", @post.versions.last.tags)
end end
should "increment the updater's post_update_count" do should "increment the updater's post_update_count" do