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