]> git.openstreetmap.org Git - rails.git/commitdiff
Merge remote-tracking branch 'upstream/pull/5842'
authorTom Hughes <tom@compton.nu>
Wed, 26 Mar 2025 18:23:27 +0000 (18:23 +0000)
committerTom Hughes <tom@compton.nu>
Wed, 26 Mar 2025 18:23:27 +0000 (18:23 +0000)
12 files changed:
.github/workflows/tests.yml
Gemfile
Gemfile.lock
app/controllers/confirmations_controller.rb
app/helpers/browse_helper.rb
app/views/changesets/_paging_nav.html.erb
config/locales/en.yml
config/settings.yml
db/structure.sql
lib/rich_text.rb
test/lib/rich_text_test.rb
yarn.lock

index d8a76bcb147789d9a17ac37c926c41b4ea3b56ed..b60310e7934e906c111295b2129d69255190a799 100644 (file)
@@ -45,6 +45,7 @@ jobs:
     - name: Populate database
       run: |
         sed -f script/normalise-structure db/structure.sql > db/structure.expected
+        rm -f db/structure.sql
         bundle exec rails db:migrate
         sed -f script/normalise-structure db/structure.sql > db/structure.actual
         diff -uw db/structure.expected db/structure.actual
diff --git a/Gemfile b/Gemfile
index 7155b1f624535a578706809e0f545fae1202351f..a7801fecea94b16650407258ce7079fc17440aae 100644 (file)
--- a/Gemfile
+++ b/Gemfile
@@ -14,6 +14,8 @@ gem "pg"
 gem "dartsass-sprockets"
 # Pin the dependentent sass-embedded to avoid deprecation warnings in bootstrap
 gem "sass-embedded", "~> 1.64.0"
+# Pin uri to avoid errors in dartsass-ruby
+gem "uri", "< 1.0.0"
 
 # Use Terser as compressor for JavaScript assets
 gem "terser"
@@ -64,7 +66,7 @@ gem "rack-cors"
 gem "rails-i18n", "~> 8.0.0"
 gem "rails_param"
 gem "rinku", ">= 2.0.6", :require => "rails_rinku"
-gem "strong_migrations", "< 2.0.0"
+gem "strong_migrations"
 gem "validates_email_format_of", ">= 1.5.1"
 
 # Native OSM extensions
index 91a24e8c2384da0f97b84a42ba18cf3c46cbb987..925eebe3c68b72f76aac88eb618a413ed30bb811 100644 (file)
@@ -92,17 +92,18 @@ GEM
     argon2 (2.3.2)
       ffi (~> 1.15)
       ffi-compiler (~> 1.0)
-    ast (2.4.2)
+    ast (2.4.3)
     autoprefixer-rails (10.4.19.0)
       execjs (~> 2)
     aws-eventstream (1.3.2)
-    aws-partitions (1.1068.0)
-    aws-sdk-core (3.220.1)
+    aws-partitions (1.1073.0)
+    aws-sdk-core (3.221.0)
       aws-eventstream (~> 1, >= 1.3.0)
       aws-partitions (~> 1, >= 1.992.0)
       aws-sigv4 (~> 1.9)
       base64
       jmespath (~> 1, >= 1.6.1)
+      logger
     aws-sdk-kms (1.99.0)
       aws-sdk-core (~> 3, >= 3.216.0)
       aws-sigv4 (~> 1.5)
@@ -406,7 +407,7 @@ GEM
     net-smtp (0.5.1)
       net-protocol
     nio4r (2.7.4)
-    nokogiri (1.18.5)
+    nokogiri (1.18.6)
       mini_portile2 (~> 2.8.2)
       racc (~> 1.4)
     oauth (1.1.0)
@@ -468,7 +469,7 @@ GEM
       iniparse (~> 1.4)
       rexml (>= 3.3.9)
     parallel (1.26.3)
-    parser (3.3.7.1)
+    parser (3.3.7.2)
       ast (~> 2.4.1)
       racc
     pg (1.5.9)
@@ -476,6 +477,7 @@ GEM
     pp (0.6.2)
       prettyprint
     prettyprint (0.2.0)
+    prism (1.4.0)
     progress (3.6.0)
     pstore (0.2.0)
     psych (5.2.3)
@@ -548,7 +550,7 @@ GEM
     rb-inotify (0.11.1)
       ffi (~> 1.0)
     rchardet (1.9.0)
-    rdoc (6.12.0)
+    rdoc (6.13.0)
       psych (>= 4.0.0)
     regexp_parser (2.10.0)
     reline (0.6.0)
@@ -572,8 +574,9 @@ GEM
       rubocop-ast (>= 1.38.0, < 2.0)
       ruby-progressbar (~> 1.7)
       unicode-display_width (>= 2.4.0, < 4.0)
-    rubocop-ast (1.39.0)
-      parser (>= 3.3.1.0)
+    rubocop-ast (1.43.0)
+      parser (>= 3.3.7.2)
+      prism (~> 1.4)
     rubocop-capybara (2.22.1)
       lint_roller (~> 1.1)
       rubocop (~> 1.72, >= 1.72.1)
@@ -613,7 +616,7 @@ GEM
       addressable (>= 2.3.5)
       faraday (>= 0.17.3, < 3)
     securerandom (0.4.1)
-    selenium-webdriver (4.23.0)
+    selenium-webdriver (4.30.1)
       base64 (~> 0.2)
       logger (~> 1.4)
       rexml (~> 3.2, >= 3.2.5)
@@ -641,9 +644,9 @@ GEM
       actionpack (>= 6.1)
       activesupport (>= 6.1)
       sprockets (>= 3.0.0)
-    stringio (3.1.5)
-    strong_migrations (1.8.0)
-      activerecord (>= 5.2)
+    stringio (3.1.6)
+    strong_migrations (2.2.1)
+      activerecord (>= 7)
     teaspoon (1.4.0)
       railties (>= 5.0)
     teaspoon-mocha (2.3.3)
@@ -781,12 +784,13 @@ DEPENDENCIES
   simplecov
   simplecov-lcov
   sprockets-exporters_pack
-  strong_migrations (< 2.0.0)
+  strong_migrations
   teaspoon
   teaspoon-mocha (~> 2.3.3)
   terser
   turbo-rails
   unicode-display_width
+  uri (< 1.0.0)
   validates_email_format_of (>= 1.5.1)
   vendorer
   webmock
index 27f2c949ebae500426aa1a7b76474382236faf43..cd22dc23c66d61daad90fb4e8772b192c1f87d1b 100644 (file)
@@ -118,7 +118,7 @@ class ConfirmationsController < ApplicationController
   end
 
   ##
-  # display a message about th current status of the gravatar setting
+  # display a message about the current status of the Gravatar setting
   def gravatar_status_message(user)
     if user.image_use_gravatar
       t "profiles.edit.gravatar.enabled"
index 482503e8ae66b3c48e5be261e5974c5d66aba821..5909706fb482533cc06f25e9ebb53b63e0dd47b2 100644 (file)
@@ -79,18 +79,6 @@ module BrowseHelper
     "nofollow" if object.tags.empty?
   end
 
-  def type_and_paginated_count(type, pages, selected_page = pages.current_page)
-    if pages.page_count == 1
-      t ".#{type.pluralize}",
-        :count => pages.item_count
-    else
-      t ".#{type.pluralize}_paginated",
-        :x => selected_page.first_item,
-        :y => selected_page.last_item,
-        :count => pages.item_count
-    end
-  end
-
   def sidebar_classic_pagination(pages, page_param)
     max_width_for_default_padding = 35
 
index 0587382222de27d64af44dc64dbc04ffff8c1a03..9fa23f6701f2bf438adc5ee818937561814703da 100644 (file)
@@ -1,8 +1,25 @@
-<h4 class="fs-5"><%= type_and_paginated_count(type, pages) %></h4>
-<% if pages.page_count > 1 %>
+<% if pages.page_count == 1 %>
+  <h4 class="fs-5">
+    <%= t ".#{type.pluralize}_title" %>
+    <span class="badge count-number">
+      <%= pages.item_count %>
+    </span>
+  </h4>
+<% elsif pages.page_count > 1 %>
+  <h4 class="fs-5">
+    <%= t ".#{type.pluralize}_title" %>
+    <span class="badge count-number">
+      <%= t ".range", :x => pages.current_page.first_item,
+                      :y => pages.current_page.last_item,
+                      :count => pages.item_count %>
+    </span>
+  </h4>
+
   <%= sidebar_classic_pagination(pages, "#{type}_page") do |page|
         {
-          :title => type_and_paginated_count(type, pages, page),
+          :title => t(".#{type.pluralize}_paginated", :x => page.first_item,
+                                                      :y => page.last_item,
+                                                      :count => pages.item_count),
           :data => { :turbo => "true" }
         }
       end %>
index 230d9242c5b1c28aa42a299e1ea73288c829f438..aba37e9344ea41d983f004d2fbdade2849d4a88f 100644 (file)
@@ -522,12 +522,13 @@ en:
       changesetxml: "Changeset XML"
       osmchangexml: "osmChange XML"
     paging_nav:
-      nodes: "Nodes (%{count})"
+      nodes_title: "Nodes"
       nodes_paginated: "Nodes (%{x}-%{y} of %{count})"
-      ways: "Ways (%{count})"
+      ways_title: "Ways"
       ways_paginated: "Ways (%{x}-%{y} of %{count})"
-      relations: "Relations (%{count})"
+      relations_title: "Relations"
       relations_paginated: "Relations (%{x}-%{y} of %{count})"
+      range: "%{x}-%{y} of %{count}"
     not_found_message:
       sorry: "Sorry, changeset #%{id} could not be found."
     timeout:
index 51f4444c4802edce9b6cc2c5869a8f0c4f8a33ed..b98e77c92bc9f2ebfcb31d67d4963951fbe8681c 100644 (file)
@@ -136,6 +136,10 @@ overpass_credentials: false
 graphhopper_url: "https://graphhopper.com/api/1/route"
 fossgis_osrm_url: "https://routing.openstreetmap.de/"
 fossgis_valhalla_url: "https://valhalla1.openstreetmap.de/route"
+# Main website hosts to match in linkify
+linkify_hosts: ["www.openstreetmap.org", "www.osm.org", "www.openstreetmap.com", "openstreetmap.org", "osm.org", "openstreetmap.com"]
+# Shorter host to replace main hosts
+linkify_hosts_replacement: "osm.org"
 # External authentication credentials
 #google_auth_id: ""
 #google_auth_secret: ""
index 9d3af4d001979914fd03e0f051750c40ef8c7070..48036fd8eeab3a530d1b1a22cc7f3398fe140be6 100644 (file)
@@ -9,6 +9,12 @@ SET xmloption = content;
 SET client_min_messages = warning;
 SET row_security = off;
 
+--
+-- Name: public; Type: SCHEMA; Schema: -; Owner: -
+--
+
+-- *not* creating schema, since initdb creates it
+
 
 --
 -- Name: btree_gist; Type: EXTENSION; Schema: -; Owner: -
index c20f973b12286164b56fcfc9f8eb47ac2fe0645d..d9c799611a7752f24a54dd582123449d0d8d4216 100644 (file)
@@ -76,11 +76,13 @@ module RichText
     end
 
     def linkify(text, mode = :urls)
-      if text.html_safe?
-        Rinku.auto_link(text, mode, tag_builder.tag_options(:rel => "nofollow noopener noreferrer")).html_safe
-      else
-        Rinku.auto_link(text, mode, tag_builder.tag_options(:rel => "nofollow noopener noreferrer"))
-      end
+      link_attr = tag_builder.tag_options(:rel => "nofollow noopener noreferrer")
+      Rinku.auto_link(ERB::Util.html_escape(text), mode, link_attr) do |url|
+        %r{^https?://([^/]*)(.*)$}.match(url) do |m|
+          "#{Settings.linkify_hosts_replacement}#{m[2]}" if Settings.linkify_hosts_replacement &&
+                                                            Settings.linkify_hosts&.include?(m[1])
+        end || url
+      end.html_safe
     end
   end
 
index 63c70b0996f59520e9cde2d4702c5f1a3b16f064..aa9e7008517fb0ef892b273dd0b1f1ecfdff5e84 100644 (file)
@@ -221,19 +221,62 @@ class RichTextTest < ActiveSupport::TestCase
     assert_equal 50, r.spam_score.round
   end
 
-  def test_text_to_html
-    r = RichText.new("text", "foo http://example.com/ bar")
-    assert_html r do
-      assert_select "a", 1
-      assert_select "a[href='http://example.com/']", 1
-      assert_select "a[rel='nofollow noopener noreferrer']", 1
+  def test_text_to_html_linkify
+    with_settings(:linkify_hosts => ["replace-me.example.com"], :linkify_hosts_replacement => "repl.example.com") do
+      r = RichText.new("text", "foo http://example.com/ bar")
+      assert_html r do
+        assert_dom "a", :count => 1, :text => "http://example.com/" do
+          assert_dom "> @href", "http://example.com/"
+          assert_dom "> @rel", "nofollow noopener noreferrer"
+        end
+      end
+    end
+  end
+
+  def test_text_to_html_linkify_replace
+    with_settings(:linkify_hosts => ["replace-me.example.com"], :linkify_hosts_replacement => "repl.example.com") do
+      r = RichText.new("text", "foo https://replace-me.example.com/some/path?query=te<st&limit=20>10#result12 bar")
+      assert_html r do
+        assert_dom "a", :count => 1, :text => "repl.example.com/some/path?query=te<st&limit=20>10#result12" do
+          assert_dom "> @href", "https://replace-me.example.com/some/path?query=te<st&limit=20>10#result12"
+          assert_dom "> @rel", "nofollow noopener noreferrer"
+        end
+      end
     end
+  end
+
+  def test_text_to_html_linkify_replace_other_scheme
+    with_settings(:linkify_hosts => ["replace-me.example.com"], :linkify_hosts_replacement => "repl.example.com") do
+      r = RichText.new("text", "foo ftp://replace-me.example.com/some/path?query=te<st&limit=20>10#result12 bar")
+      assert_html r do
+        assert_dom "a", :count => 1, :text => "ftp://replace-me.example.com/some/path?query=te<st&limit=20>10#result12" do
+          assert_dom "> @href", "ftp://replace-me.example.com/some/path?query=te<st&limit=20>10#result12"
+          assert_dom "> @rel", "nofollow noopener noreferrer"
+        end
+      end
+    end
+  end
 
+  def test_text_to_html_linkify_replace_undefined
+    with_settings(:linkify_hosts => ["replace-me.example.com"], :linkify_hosts_replacement => nil) do
+      r = RichText.new("text", "foo https://replace-me.example.com/some/path?query=te<st&limit=20>10#result12 bar")
+      assert_html r do
+        assert_dom "a", :count => 1, :text => "https://replace-me.example.com/some/path?query=te<st&limit=20>10#result12" do
+          assert_dom "> @href", "https://replace-me.example.com/some/path?query=te<st&limit=20>10#result12"
+          assert_dom "> @rel", "nofollow noopener noreferrer"
+        end
+      end
+    end
+  end
+
+  def test_text_to_html_email
     r = RichText.new("text", "foo example@example.com bar")
     assert_html r do
       assert_select "a", 0
     end
+  end
 
+  def test_text_to_html_escape
     r = RichText.new("text", "foo < bar & baz > qux")
     assert_html r do
       assert_select "p", "foo < bar & baz > qux"
index ed0a4d796246411a1afe4a047316a3a0e092c722..b5f8e755e1d983bce6b40a17706dda70a54a65dc 100644 (file)
--- a/yarn.lock
+++ b/yarn.lock
   integrity sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==
 
 "@types/leaflet@^1.9.0":
-  version "1.9.16"
-  resolved "https://registry.yarnpkg.com/@types/leaflet/-/leaflet-1.9.16.tgz#3e3abc103e106523cde01625057e2294f332ec3b"
-  integrity sha512-wzZoyySUxkgMZ0ihJ7IaUIblG8Rdc8AbbZKLneyn+QjYsj5q1QU7TEKYqwTr10BGSzY5LI7tJk9Ifo+mEjdFRw==
+  version "1.9.17"
+  resolved "https://registry.yarnpkg.com/@types/leaflet/-/leaflet-1.9.17.tgz#f7f12f9306029df48801cd830f66597d614a2e08"
+  integrity sha512-IJ4K6t7I3Fh5qXbQ1uwL3CFVbCi6haW9+53oLWgdKlLP7EaS21byWFJxxqOx9y8I0AP0actXSJLVMbyvxhkUTA==
   dependencies:
     "@types/geojson" "*"