Skip to content
29 changes: 14 additions & 15 deletions app/models/admin_post.rb
Original file line number Diff line number Diff line change
Expand Up @@ -18,21 +18,20 @@ class AdminPost < ApplicationRecord
has_many :admin_post_taggings
has_many :tags, through: :admin_post_taggings, source: :admin_post_tag

validates_presence_of :title
validates_length_of :title,
minimum: ArchiveConfig.TITLE_MIN,
too_short: ts("must be at least %{min} characters long.", min: ArchiveConfig.TITLE_MIN)
validates :title, presence: true,
length: { minimum: ArchiveConfig.TITLE_MIN,
maximum: ArchiveConfig.TITLE_MAX }

validates_length_of :title,
maximum: ArchiveConfig.TITLE_MAX,
too_long: ts("must be less than %{max} characters long.", max: ArchiveConfig.TITLE_MAX)
validates :content, presence: true,
length: { minimum: ArchiveConfig.CONTENT_MIN,
maximum: ArchiveConfig.CONTENT_MAX }

validates_presence_of :content
validates_length_of :content, minimum: ArchiveConfig.CONTENT_MIN,
too_short: ts("must be at least %{min} characters long.", min: ArchiveConfig.CONTENT_MIN)

validates_length_of :content, maximum: ArchiveConfig.CONTENT_MAX,
too_long: ts("cannot be more than %{max} characters long.", max: ArchiveConfig.CONTENT_MAX)
# rubocop:disable Rails/UniqueValidationWithoutIndex
validates :translated_post_id,
uniqueness: { scope: :language_id,
message: :translation_already_exists },
allow_nil: true
# rubocop:enable Rails/UniqueValidationWithoutIndex
Comment on lines +29 to +34

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Disabled this cop instead of adding a unique index on (translated_post_id, language_id). Production likely has duplicates already (that's what this issue is about), so the index would need a data cleanup first, and that felt out of scope here. The validation itself runs the same query either way and uses the existing index on translated_post_id.


validate :translated_post_must_exist

Expand Down Expand Up @@ -93,15 +92,15 @@ def tags

def translated_post_must_exist
if translated_post_id.present? && AdminPost.find_by(id: translated_post_id).nil?
errors.add(:translated_post_id, "does not exist")
errors.add(:translated_post_id, :does_not_exist)
end
end

def translated_post_language_must_differ
return if translated_post.blank?
return unless translated_post.language == language

errors.add(:translated_post_id, "cannot be same language as original post")
errors.add(:translated_post_id, :same_language_as_original)
end

def translated_post_must_be_posted_first
Expand Down
12 changes: 11 additions & 1 deletion config/locales/models/en.yml
Original file line number Diff line number Diff line change
Expand Up @@ -100,8 +100,18 @@ en:
comment_permissions:
format: "%{message}"
invalid_permissions: Comment permissions are invalid.
content:
too_long: cannot be more than %{count} characters long.
too_short: must be at least %{count} characters long.
title:
too_long: must be less than %{count} characters long.
too_short: must be at least %{count} characters long.
translated_post_id:
must_be_posted_first: cannot be posted before the original post
does_not_exist: Translated post does not exist
format: "%{message}"
must_be_posted_first: Translated post cannot be posted before the original post
same_language_as_original: Translated post cannot be same language as original post
translation_already_exists: Admin post already has a translation in this language.
block:
attributes:
blocked:
Expand Down
9 changes: 9 additions & 0 deletions features/admins/admin_post_news.feature
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,15 @@ Feature: Admin Actions to Post News
And I am logged in as "ordinaryuser"
Then I should see a translated admin post

Scenario: Make a second translation of an admin post in the same language
Given I have posted an admin post
And basic languages
And the admin post "Erste Ankuendigung" translating "Default Admin Post" to "Deutsch"
And I am logged in as a "translation" admin
When I make a translation of an admin post
Then I should see "Sorry! We couldn't save this admin post because:"
And I should see "Admin post already has a translation in this language."

Scenario: Make a translation of an admin post that doesn't exist
Given basic languages
And I am logged in as a "translation" admin
Expand Down
10 changes: 10 additions & 0 deletions features/step_definitions/admin_steps.rb
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,16 @@
FactoryBot.create(:admin_post, :draft, title: title, comment_permissions: :enable_all, tag_list: tags)
end

Given "the admin post {string} translating {string} to {string}" do |title, translated_title, lang|
admin_post = AdminPost.find_by!(title: translated_title)
language = Language.find_by!(name: lang)
FactoryBot.create(:admin_post,
title: title,
comment_permissions: :enable_all,
translated_post_id: admin_post.id,
language: language)
end

Given "the draft admin post {string} translating {string} to {string}" do |title, translated_title, lang|
admin_post = AdminPost.find_by!(title: translated_title)
language = Language.find_by!(name: lang)
Expand Down
57 changes: 56 additions & 1 deletion spec/models/admin_post_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@
drafted_post = travel_to(ArchiveConfig.ADMIN_POST_COMMENTING_EXPIRATION_DAYS.days.ago) do
create(:admin_post, :draft)
end
drafted_post.update(posted: true)
drafted_post.update!(posted: true)

AdminPost.disable_old_post_comments
expect(drafted_post.reload.disable_all_comments?).to be(false)
Expand All @@ -78,6 +78,61 @@
end
end

describe "#translated_post_id" do
let(:admin_post) { create(:admin_post) }
let(:language) { create(:language) }
let(:translation) do
build(:admin_post, translated_post: admin_post, language: language)
end
let(:message) { "Admin post already has a translation in this language." }

context "when the original post has a posted translation in the language" do
before do
create(:admin_post, translated_post: admin_post, language: language)
end

it "adds an error to the translated post" do
expect(translation).to be_invalid
expect(translation.errors[:translated_post_id]).to include(message)
end
end

context "when the original post has a draft translation in the language" do

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The issue only mentions published translations, but this implementation also blocks the case where the existing translation is still a draft. Drafts are shared between all admins, otherwise a second translation in the same language could be posted while the draft is pending.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, the issue predates the existence of drafts, but this is the behavior I'd expect now that we have them. Thanks for including drafts in test coverage!

before do
create(:admin_post, :draft,
translated_post: admin_post, language: language)
end

it "adds an error to the translated post" do
expect(translation).to be_invalid
expect(translation.errors[:translated_post_id]).to include(message)
end
end

context "when another post has a translation in that language" do
before do
other_post = create(:admin_post)
create(:admin_post, translated_post: other_post, language: language)
end

it "does not add an error" do
expect(translation).to be_valid
end
end

context "when updating the existing translation" do
let!(:existing) do
create(:admin_post, translated_post: admin_post, language: language)
end

it "does not add an error" do
existing.title = "Updated translation title"

expect(existing).to be_valid
end
end
end

describe "#set_published_at" do
before { freeze_time }

Expand Down
Loading