From 89a06ea6ef7ea481564ad41696da40601e2be3a7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Javi=20Mart=C3=ADn?= Date: Wed, 23 Jun 2021 04:36:37 +0200 Subject: [PATCH] Fix related content with custom URLs Some CONSUL installations might want to customize their URLs. For instance, Spanish institutions might want to use "/propuestas" instead of "/proposals". In that case, it would be impossible to add proposals as related content because the related contents controller assumed the name of the model was part of the URL. Using `recognize_path` instead of manually analyzing the URL solves the issue. Now that we don't call the `constantize` method on an empty string as we previously did, we can be more specific in the `rescue` block and point out that the only exception we expect is the one where users enter a route which isn't recognized. --- .../related_contents_controller.rb | 31 ++++++++++++------- spec/shared/system/relationable.rb | 27 ++++++++++++++++ 2 files changed, 46 insertions(+), 12 deletions(-) diff --git a/app/controllers/related_contents_controller.rb b/app/controllers/related_contents_controller.rb index 2acc18cff..5306fdb30 100644 --- a/app/controllers/related_contents_controller.rb +++ b/app/controllers/related_contents_controller.rb @@ -7,7 +7,8 @@ class RelatedContentsController < ApplicationController related_content = current_user.related_contents.new( parent_relationable_id: params[:relationable_id], parent_relationable_type: params[:relationable_klass], - child_relationable: related_object + child_relationable_id: child_relationable_params[:id], + child_relationable_type: child_relationable_params[:type] ) if related_content.save @@ -41,17 +42,23 @@ class RelatedContentsController < ApplicationController params[:url].start_with?(Setting["url"]) end - def related_object - if valid_url? - url = params[:url] + def child_relationable_params + @child_relationable_params ||= + if valid_url? + related_params = Rails.application.routes.recognize_path(params[:url]) - related_klass = url.scan(/\/(#{RelatedContent::RELATIONABLE_MODELS.join("|")})\//) - .flatten.map { |i| i.to_s.singularize.camelize }.join("::") - related_id = url.match(/\/(\d+)(?!.*\/\d)/)[1] - - related_klass.singularize.camelize.constantize.find_by(id: related_id) - end - rescue - nil + if RelatedContent::RELATIONABLE_MODELS.include?(related_params[:controller].split("/").last) + { + id: related_params[:id], + type: related_params[:controller].split("/").map(&:singularize).join("/").classify + } + else + {} + end + else + {} + end + rescue ActionController::RoutingError + {} end end diff --git a/spec/shared/system/relationable.rb b/spec/shared/system/relationable.rb index 3e7088f04..430e78408 100644 --- a/spec/shared/system/relationable.rb +++ b/spec/shared/system/relationable.rb @@ -90,6 +90,33 @@ shared_examples "relationable" do |relationable_model_name| expect(page).to have_content "Link not valid. You cannot relate a content to itself" end + context "custom URLs" do + before do + custom_route = proc { get "/mypath/:id" => "debates#show" } + Rails.application.routes.send(:eval_block, custom_route) + end + + after { Rails.application.reload_routes! } + + scenario "finds relationable with custom URLs" do + related = create(:debate, title: "My path is the only one I've walked") + + login_as(user) + visit relationable.url + + click_button "Add related content" + + within("#related_content") do + fill_in "Link to related content", with: "#{url}/mypath/#{related.id}" + click_button "Add" + end + + within("#related-content-list") do + expect(page).to have_content "My path is the only one I've walked" + end + end + end + scenario "related content can be scored positively" do related_content = create(:related_content, parent_relationable: relationable, child_relationable: related1, author: build(:user))