diff --git a/app/models/budget/investment.rb b/app/models/budget/investment.rb
index 1ef54653e..1a3516199 100644
--- a/app/models/budget/investment.rb
+++ b/app/models/budget/investment.rb
@@ -28,6 +28,10 @@ class Budget
extend DownloadSettings::BudgetInvestmentCsv
+ translates :title, touch: true
+ translates :description, touch: true
+ include Globalizable
+
belongs_to :author, -> { with_hidden }, class_name: "User", foreign_key: "author_id"
belongs_to :heading
belongs_to :group
@@ -48,15 +52,13 @@ class Budget
delegate :name, :email, to: :author, prefix: true
- validates :title, presence: true
+ validates_translation :title, presence: true, length: { in: 4..Budget::Investment.title_max_length }
+ validates_translation :description, presence: true, length: { maximum: Budget::Investment.description_max_length }
+
validates :author, presence: true
- validates :description, presence: true
validates :heading_id, presence: true
validates :unfeasibility_explanation, presence: { if: :unfeasibility_explanation_required? }
validates :price, presence: { if: :price_required? }
-
- validates :title, length: { in: 4..Budget::Investment.title_max_length }
- validates :description, length: { maximum: Budget::Investment.description_max_length }
validates :terms_of_service, acceptance: { allow_nil: false }, on: :create
scope :sort_by_confidence_score, -> { reorder(confidence_score: :desc, id: :desc) }
@@ -64,7 +66,6 @@ class Budget
scope :sort_by_price, -> { reorder(price: :desc, confidence_score: :desc, id: :desc) }
scope :sort_by_id, -> { order("id DESC") }
- scope :sort_by_title, -> { order("title ASC") }
scope :sort_by_supports, -> { order("cached_votes_up DESC") }
scope :valuation_open, -> { where(valuation_finished: false) }
@@ -117,6 +118,10 @@ class Budget
budget_investment_path(budget, self)
end
+ def self.sort_by_title
+ all.sort_by(&:title)
+ end
+
def self.filter_params(params)
params.permit(%i[heading_id group_id administrator_id tag_name valuator_id])
end
@@ -187,7 +192,7 @@ class Budget
if title_or_id =~ /^[0-9]+$/
results.where(id: title_or_id)
else
- results.where("title ILIKE ?", "%#{title_or_id}%")
+ results.with_translations(I18n.locale).where("budget_investment_translations.title ILIKE ?", "%#{title_or_id}%")
end
end
diff --git a/db/migrate/20181214094002_add_budget_investment_translations.rb b/db/migrate/20181214094002_add_budget_investment_translations.rb
new file mode 100644
index 000000000..a2da96021
--- /dev/null
+++ b/db/migrate/20181214094002_add_budget_investment_translations.rb
@@ -0,0 +1,15 @@
+class AddBudgetInvestmentTranslations < ActiveRecord::Migration[4.2]
+ def self.up
+ Budget::Investment.create_translation_table!(
+ {
+ title: :string,
+ description: :text
+ },
+ { migrate_data: true }
+ )
+ end
+
+ def self.down
+ Budget::Investment.drop_translation_table!
+ end
+end
diff --git a/db/schema.rb b/db/schema.rb
index fc97b55bb..977c14f95 100644
--- a/db/schema.rb
+++ b/db/schema.rb
@@ -234,6 +234,17 @@ ActiveRecord::Schema.define(version: 20190607160900) do
t.index ["hidden_at"], name: "index_budget_investment_statuses_on_hidden_at", using: :btree
end
+ create_table "budget_investment_translations", force: :cascade do |t|
+ t.integer "budget_investment_id", null: false
+ t.string "locale", null: false
+ t.datetime "created_at", null: false
+ t.datetime "updated_at", null: false
+ t.string "title"
+ t.text "description"
+ t.index ["budget_investment_id"], name: "index_budget_investment_translations_on_budget_investment_id", using: :btree
+ t.index ["locale"], name: "index_budget_investment_translations_on_locale", using: :btree
+ end
+
create_table "budget_investments", force: :cascade do |t|
t.integer "author_id"
t.integer "administrator_id"
diff --git a/spec/models/budget/investment_spec.rb b/spec/models/budget/investment_spec.rb
index 439938eff..e021f8f04 100644
--- a/spec/models/budget/investment_spec.rb
+++ b/spec/models/budget/investment_spec.rb
@@ -5,6 +5,8 @@ describe Budget::Investment do
describe "Concerns" do
it_behaves_like "notifiable"
+ it_behaves_like "globalizable", :budget_investment
+ it_behaves_like "acts as imageable", :budget_investment_image
end
it "is valid" do
@@ -33,12 +35,38 @@ describe Budget::Investment do
end
end
- it_behaves_like "acts as imageable", "budget_investment_image"
+ describe "#description" do
+ it "is sanitized" do
+ investment.description = ""
- it "sanitizes description" do
- investment.description = ""
- investment.valid?
- expect(investment.description).to eq("alert('danger');")
+ investment.valid?
+
+ expect(investment.description).to eq("alert('danger');")
+ end
+
+ it "is sanitized using globalize accessors" do
+ investment.description_en = ""
+
+ investment.valid?
+
+ expect(investment.description_en).to eq("alert('danger');")
+ end
+
+ it "is html_safe" do
+ investment.description = ""
+
+ investment.valid?
+
+ expect(investment.description).to be_html_safe
+ end
+
+ it "is html_safe using globalize accessors" do
+ investment.description_en = ""
+
+ investment.valid?
+
+ expect(investment.description_en).to be_html_safe
+ end
end
it "set correct group and budget ids" do
@@ -549,6 +577,20 @@ describe Budget::Investment do
expect(described_class.unselected.sort).to eq [unselected_undecided_investment, unselected_feasible_investment].sort
end
end
+
+ describe "sort_by_title" do
+ it "should take into consideration title fallbacks when there is no
+ translation for current locale" do
+ english_investment = create(:budget_investment, :selected, title: "English title")
+ spanish_investment = Globalize.with_locale(:es) do
+ I18n.with_locale(:es) do
+ create(:budget_investment, :selected, title: "Título en español")
+ end
+ end
+
+ expect(described_class.sort_by_title).to eq [english_investment, spanish_investment]
+ end
+ end
end
describe "apply_filters_and_search" do