diff --git a/.github/workflows/danger.yml b/.github/workflows/danger.yml
index 4d1fcddcb22..1e782e46c62 100644
--- a/.github/workflows/danger.yml
+++ b/.github/workflows/danger.yml
@@ -18,7 +18,7 @@ jobs:
with:
fetch-depth: 0
- name: Setup ruby
- uses: ruby/setup-ruby@14594264cd68ce8a2345dd349bc3d138a4ef85c8 # v1.327.0
+ uses: ruby/setup-ruby@e81a8fa391b11a595c1b7ed84177cc0fe02faf83 # v1.328.0
with:
ruby-version: 3.3
bundler-cache: true
diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml
index 2d881f5157b..7222e5528e1 100644
--- a/.github/workflows/lint.yml
+++ b/.github/workflows/lint.yml
@@ -19,7 +19,7 @@ jobs:
- name: Check out code
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- name: Setup ruby
- uses: ruby/setup-ruby@14594264cd68ce8a2345dd349bc3d138a4ef85c8 # v1.327.0
+ uses: ruby/setup-ruby@e81a8fa391b11a595c1b7ed84177cc0fe02faf83 # v1.328.0
with:
ruby-version: ${{ env.ruby }}
bundler-cache: true
@@ -33,7 +33,7 @@ jobs:
- name: Check out code
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- name: Setup ruby
- uses: ruby/setup-ruby@14594264cd68ce8a2345dd349bc3d138a4ef85c8 # v1.327.0
+ uses: ruby/setup-ruby@e81a8fa391b11a595c1b7ed84177cc0fe02faf83 # v1.328.0
with:
ruby-version: ${{ env.ruby }}
bundler-cache: true
@@ -47,14 +47,14 @@ jobs:
- name: Check out code
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- name: Setup ruby
- uses: ruby/setup-ruby@14594264cd68ce8a2345dd349bc3d138a4ef85c8 # v1.327.0
+ uses: ruby/setup-ruby@e81a8fa391b11a595c1b7ed84177cc0fe02faf83 # v1.328.0
with:
ruby-version: ${{ env.ruby }}
bundler-cache: true
- name: Run herb parser
run: bundle exec herb analyze app/
- name: Cache node modules
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
+ uses: actions/setup-node@949feb2413d6458794dcd2491c4babbbce0c15c1 # v7.1.0
with:
cache: yarn
- name: Install node modules
@@ -71,12 +71,12 @@ jobs:
- name: Check out code
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- name: Setup ruby
- uses: ruby/setup-ruby@14594264cd68ce8a2345dd349bc3d138a4ef85c8 # v1.327.0
+ uses: ruby/setup-ruby@e81a8fa391b11a595c1b7ed84177cc0fe02faf83 # v1.328.0
with:
ruby-version: ${{ env.ruby }}
bundler-cache: true
- name: Cache node modules
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
+ uses: actions/setup-node@949feb2413d6458794dcd2491c4babbbce0c15c1 # v7.1.0
with:
cache: yarn
- name: Install node modules
@@ -97,7 +97,7 @@ jobs:
- name: Check out code
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- name: Setup ruby
- uses: ruby/setup-ruby@14594264cd68ce8a2345dd349bc3d138a4ef85c8 # v1.327.0
+ uses: ruby/setup-ruby@e81a8fa391b11a595c1b7ed84177cc0fe02faf83 # v1.328.0
with:
ruby-version: ${{ env.ruby }}
bundler-cache: true
@@ -113,7 +113,7 @@ jobs:
- name: Check out code
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- name: Setup ruby
- uses: ruby/setup-ruby@14594264cd68ce8a2345dd349bc3d138a4ef85c8 # v1.327.0
+ uses: ruby/setup-ruby@e81a8fa391b11a595c1b7ed84177cc0fe02faf83 # v1.328.0
with:
ruby-version: ${{ env.ruby }}
bundler-cache: true
@@ -122,7 +122,7 @@ jobs:
cp config/github.database.yml config/database.yml
cp config/example.storage.yml config/storage.yml
- name: Cache node modules
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
+ uses: actions/setup-node@949feb2413d6458794dcd2491c4babbbce0c15c1 # v7.1.0
with:
cache: yarn
- name: Install node modules
@@ -151,7 +151,7 @@ jobs:
- name: Check out code
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- name: Setup ruby
- uses: ruby/setup-ruby@14594264cd68ce8a2345dd349bc3d138a4ef85c8 # v1.327.0
+ uses: ruby/setup-ruby@e81a8fa391b11a595c1b7ed84177cc0fe02faf83 # v1.328.0
with:
ruby-version: ${{ env.ruby }}
bundler-cache: true
@@ -160,7 +160,7 @@ jobs:
cp config/github.database.yml config/database.yml
cp config/example.storage.yml config/storage.yml
- name: Cache node modules
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
+ uses: actions/setup-node@949feb2413d6458794dcd2491c4babbbce0c15c1 # v7.1.0
with:
cache: yarn
- name: Install node modules
diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml
index d9a5f9e64ce..8a9c168deaa 100644
--- a/.github/workflows/tests.yml
+++ b/.github/workflows/tests.yml
@@ -25,13 +25,13 @@ jobs:
- name: Checkout source
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- name: Setup ruby
- uses: ruby/setup-ruby@14594264cd68ce8a2345dd349bc3d138a4ef85c8 # v1.327.0
+ uses: ruby/setup-ruby@e81a8fa391b11a595c1b7ed84177cc0fe02faf83 # v1.328.0
with:
ruby-version: ${{ matrix.ruby }}
rubygems: 3.4.10
bundler-cache: true
- name: Cache node modules
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
+ uses: actions/setup-node@949feb2413d6458794dcd2491c4babbbce0c15c1 # v7.1.0
with:
cache: yarn
- name: Install packages
@@ -76,7 +76,7 @@ jobs:
env:
DISPLAY: ":99"
- name: Upload screenshots
- uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
+ uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
if: failure()
with:
name: screenshots
diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml
index 6c94be970d7..48254c6cbe5 100644
--- a/.rubocop_todo.yml
+++ b/.rubocop_todo.yml
@@ -57,7 +57,7 @@ Metrics/BlockNesting:
# Offense count: 23
# Configuration parameters: CountComments, CountAsOne.
Metrics/ClassLength:
- Max: 352
+ Max: 353
# Offense count: 72
# Configuration parameters: AllowedMethods, AllowedPatterns.
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 6b23d890119..f323de1a308 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -64,7 +64,7 @@ you do communicate, early and often, so that you can get feedback quickly.
This will help you get buy-in from the maintainers, which will translate into
less waste for everyone and a much easier time getting your code merged.
-Bug fixes should be more straighforward than new features, but the same
+Bug fixes should be more straightforward than new features, but the same
guidance applies. If it turns out to be more complex than initially expected,
stop for a moment and seek feedback, be it in an issue or in a draft PR.
diff --git a/FAQ.md b/FAQ.md
index b55b26b2825..c5a59f84312 100644
--- a/FAQ.md
+++ b/FAQ.md
@@ -7,8 +7,8 @@ promote a large OpenStreetMap focused conference or host a worldwide donation
drive. This is a great way to reach a lot of people!
1. Please review the Operations Working Group's [Banner Policy](https://operations.osmfoundation.org/policies/banner/) to know whether your event qualifies for a front-page banner.
-2. Create the banner image. The image needs to:
- * be exactly 350px wide and at most 350px tall,
+2. Create the banner image in two sizes for 1× and 2× resolution. The images need to:
+ * be exactly 350px wide and at most 350px tall, or double that for 2×,
* be in PNG format,
* have nothing important in the top-right 60x60px corner of the banner which has a close 'X' button overlayed, and
* have sufficient visual contrast with the colours #cccccc and #999999 so the 'X' can be seen.
diff --git a/Gemfile b/Gemfile
index 85e2cc01309..8a3cc7dc0bc 100644
--- a/Gemfile
+++ b/Gemfile
@@ -12,10 +12,8 @@ gem "pg"
# Use SCSS for stylesheets
gem "dartsass-sprockets"
-# Pin the dependent 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"
+# Don't allow upgrade to 3.0.0 as some deprecations will become errors
+gem "sass-embedded", "< 3.0.0"
# Use Terser as compressor for JavaScript assets
gem "terser"
@@ -53,6 +51,7 @@ gem "benchmark"
gem "actionpack-page_caching"
gem "activerecord-import"
gem "active_record_union"
+gem "active_storage_validations"
gem "bootstrap"
gem "bootstrap_form"
gem "cancancan"
diff --git a/Gemfile.lock b/Gemfile.lock
index 04c49eb463b..df42bd26c6f 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -69,6 +69,12 @@ GEM
rails-html-sanitizer (~> 1.6)
active_record_union (1.4.0)
activerecord (>= 6.0)
+ active_storage_validations (4.1.1)
+ activejob (>= 7.0.1)
+ activemodel (>= 7.0.1)
+ activestorage (>= 7.0.1)
+ activesupport (>= 7.0.1)
+ marcel (>= 1.0.3)
activejob (8.1.4)
activesupport (= 8.1.4)
globalid (>= 0.3.6)
@@ -119,10 +125,10 @@ GEM
attr_required (1.0.2)
auth-sanitizer (0.2.3)
version_gem (~> 1.1, >= 1.1.14)
- autoprefixer-rails (10.4.21.0)
+ autoprefixer-rails (10.6.1.0)
execjs (~> 2)
aws-eventstream (1.4.0)
- aws-partitions (1.1291.0)
+ aws-partitions (1.1296.0)
aws-sdk-core (3.257.0)
aws-eventstream (~> 1, >= 1.3.0)
aws-partitions (~> 1, >= 1.992.0)
@@ -135,7 +141,7 @@ GEM
aws-sdk-kms (1.132.0)
aws-sdk-core (~> 3, >= 3.256.0)
aws-sigv4 (~> 1.5)
- aws-sdk-s3 (1.232.2)
+ aws-sdk-s3 (1.233.2)
aws-sdk-core (~> 3, >= 3.256.0)
aws-sdk-kms (~> 1)
aws-sigv4 (~> 1.5)
@@ -165,7 +171,7 @@ GEM
bootstrap_form (5.6.1)
actionpack (>= 7.2)
activemodel (>= 7.2)
- brakeman (8.0.6)
+ brakeman (8.1.0)
racc
brotli (0.8.0)
builder (3.3.0)
@@ -201,7 +207,7 @@ GEM
bigdecimal
rexml
crass (1.0.7)
- dalli (5.1.1)
+ dalli (5.2.2)
logger
danger (9.6.1)
base64 (~> 0.2)
@@ -221,17 +227,15 @@ GEM
danger-plugin-api (~> 1.0)
danger-plugin-api (1.0.0)
danger (> 2.0)
- dartsass-ruby (3.0.2)
- sass-embedded (~> 1.54, < 1.67)
- dartsass-sprockets (3.0.0)
- dartsass-ruby (~> 3.0)
+ dartsass-sprockets (3.2.1)
railties (>= 4.0.0)
+ sassc-embedded (~> 1.80.1)
sprockets (> 3.0)
sprockets-rails
tilt
- database_consistency (3.0.13)
+ database_consistency (3.0.15)
activerecord (>= 3.2)
- date (3.5.1)
+ date (3.6.0)
debug (1.11.1)
irb (~> 1.10)
reline (>= 0.3.8)
@@ -244,7 +248,7 @@ GEM
delayed_job_active_record (4.1.11)
activerecord (>= 3.0, < 9.0)
delayed_job (>= 3.0, < 5)
- digest (3.2.1)
+ digest (3.3.0)
doorkeeper (5.9.9)
railties (>= 5)
doorkeeper-i18n (5.2.9)
@@ -333,9 +337,11 @@ GEM
glob (0.5.0)
globalid (1.4.0)
activesupport (>= 6.1)
- google-protobuf (3.25.8)
- googleapis-common-protos-types (1.20.0)
- google-protobuf (>= 3.18, < 5.a)
+ google-protobuf (4.36.2)
+ bigdecimal
+ rake (~> 13.3)
+ googleapis-common-protos-types (1.23.0)
+ google-protobuf (~> 4.26)
hashdiff (1.2.1)
hashie (5.1.0)
logger
@@ -371,7 +377,7 @@ GEM
image_optim (~> 0.24)
railties
sprockets
- image_processing (2.1.0)
+ image_processing (2.2.0)
image_size (3.6.0)
in_threads (1.6.0)
iniparse (1.5.0)
@@ -392,7 +398,7 @@ GEM
rails-dom-testing (>= 1, < 3)
railties (>= 4.2.0)
thor (>= 0.14, < 2.0)
- json (3.0.2)
+ json (3.1.0)
json-jwt (1.17.2)
activesupport (>= 4.2)
aes_key_wrap
@@ -410,7 +416,7 @@ GEM
libv8-node (24.12.0.1)
libxml-ruby (6.0.0)
lint_roller (1.1.0)
- listen (3.10.0)
+ listen (3.10.1)
logger
rb-fsevent (~> 0.10, >= 0.10.3)
rb-inotify (~> 0.9, >= 0.9.10)
@@ -441,7 +447,8 @@ GEM
multi_xml (0.9.1)
bigdecimal (>= 3.1, < 5)
nap (1.1.0)
- net-http (0.9.1)
+ net-http (0.10.0)
+ net-protocol (>= 0.3.0)
uri (>= 0.11.1)
net-imap (0.6.7)
date
@@ -456,8 +463,8 @@ GEM
nokogiri (1.19.4)
mini_portile2 (~> 2.8.2)
racc (~> 1.4)
- noticed (3.0.0)
- rails (>= 6.1.0)
+ noticed (3.1.0)
+ rails (>= 7.1.0)
oauth (1.1.8)
anonymous_loader (~> 0.1, >= 0.1.3)
auth-sanitizer (~> 0.2, >= 0.2.3)
@@ -518,10 +525,14 @@ GEM
omniauth-rails_csrf_protection (2.0.1)
actionpack (>= 4.2)
omniauth (~> 2.0)
+<<<<<<< HEAD
omniauth_openid_connect (0.8.0)
omniauth (>= 1.9, < 3)
openid_connect (~> 2.2)
open3 (0.2.1)
+=======
+ open3 (0.3.0)
+>>>>>>> upstream-tip
open4 (1.3.4)
openid_connect (2.5.0)
activemodel
@@ -538,26 +549,27 @@ GEM
openstreetmap-deadlock_retry (1.3.1)
opentelemetry-api (1.11.1)
logger
- opentelemetry-common (0.25.1)
+ opentelemetry-common (0.25.2)
opentelemetry-api (~> 1.0)
- opentelemetry-exporter-otlp (0.36.0)
+ opentelemetry-exporter-otlp (0.37.0)
google-protobuf (>= 3.18)
googleapis-common-protos-types (~> 1.3)
opentelemetry-api (~> 1.1)
opentelemetry-common (~> 0.20)
- opentelemetry-exporter-otlp-common
+ opentelemetry-exporter-otlp-common (~> 0.2)
opentelemetry-sdk (~> 1.10)
opentelemetry-semantic_conventions
- opentelemetry-exporter-otlp-common (0.2.0)
+ opentelemetry-exporter-otlp-common (0.3.0)
google-protobuf (>= 3.18)
googleapis-common-protos-types (~> 1.3)
opentelemetry-api (~> 1.1)
+ opentelemetry-common (~> 0.20)
opentelemetry-helpers-mysql (0.6.0)
opentelemetry-api (~> 1.7)
opentelemetry-common (~> 0.21)
opentelemetry-helpers-sql (0.4.0)
opentelemetry-api (~> 1.7)
- opentelemetry-helpers-sql-processor (0.5.0)
+ opentelemetry-helpers-sql-processor (0.5.1)
opentelemetry-api (~> 1.0)
opentelemetry-common (~> 0.21)
opentelemetry-instrumentation-action_mailer (0.8.1)
@@ -576,7 +588,7 @@ GEM
opentelemetry-instrumentation-active_support (~> 0.10)
opentelemetry-instrumentation-active_support (0.12.1)
opentelemetry-instrumentation-base (~> 0.25)
- opentelemetry-instrumentation-all (0.96.0)
+ opentelemetry-instrumentation-all (0.98.0)
opentelemetry-instrumentation-active_model_serializers (~> 0.25.0)
opentelemetry-instrumentation-anthropic (~> 0.5.0)
opentelemetry-instrumentation-aws_lambda (~> 0.7.0)
@@ -596,10 +608,11 @@ GEM
opentelemetry-instrumentation-http_client (~> 0.29.0)
opentelemetry-instrumentation-httpx (~> 0.8.0)
opentelemetry-instrumentation-koala (~> 0.24.0)
- opentelemetry-instrumentation-lmdb (~> 0.26.0)
+ opentelemetry-instrumentation-lmdb (~> 0.27.0)
opentelemetry-instrumentation-mongo (~> 0.26.0)
opentelemetry-instrumentation-mysql2 (~> 0.34.0)
opentelemetry-instrumentation-net_http (~> 0.29.0)
+ opentelemetry-instrumentation-openai (~> 0.1.0)
opentelemetry-instrumentation-pg (~> 0.37.0)
opentelemetry-instrumentation-que (~> 0.13.0)
opentelemetry-instrumentation-racecar (~> 0.7.0)
@@ -616,13 +629,15 @@ GEM
opentelemetry-instrumentation-trilogy (~> 0.69.0)
opentelemetry-instrumentation-anthropic (0.5.0)
opentelemetry-instrumentation-base (~> 0.25)
- opentelemetry-instrumentation-aws_lambda (0.7.0)
+ opentelemetry-instrumentation-aws_lambda (0.7.1)
opentelemetry-instrumentation-base (~> 0.25)
opentelemetry-instrumentation-aws_sdk (0.13.0)
opentelemetry-instrumentation-base (~> 0.25)
- opentelemetry-instrumentation-base (0.26.1)
+ opentelemetry-instrumentation-base (0.27.0)
opentelemetry-api (~> 1.7)
opentelemetry-common (~> 0.21)
+ opentelemetry-logs-api (~> 0.6)
+ opentelemetry-metrics-api (~> 0.9)
opentelemetry-registry (~> 0.1)
opentelemetry-instrumentation-bunny (0.25.0)
opentelemetry-instrumentation-base (~> 0.25)
@@ -638,7 +653,7 @@ GEM
opentelemetry-instrumentation-base (~> 0.25)
opentelemetry-instrumentation-faraday (0.33.0)
opentelemetry-instrumentation-base (~> 0.25)
- opentelemetry-instrumentation-grape (0.7.1)
+ opentelemetry-instrumentation-grape (0.7.2)
opentelemetry-instrumentation-rack (~> 0.29)
opentelemetry-instrumentation-graphql (0.32.0)
opentelemetry-instrumentation-base (~> 0.25)
@@ -654,26 +669,28 @@ GEM
opentelemetry-instrumentation-base (~> 0.25)
opentelemetry-instrumentation-koala (0.24.0)
opentelemetry-instrumentation-base (~> 0.25)
- opentelemetry-instrumentation-lmdb (0.26.1)
+ opentelemetry-instrumentation-lmdb (0.27.0)
opentelemetry-instrumentation-base (~> 0.25)
opentelemetry-instrumentation-mongo (0.26.0)
opentelemetry-instrumentation-base (~> 0.25)
- opentelemetry-instrumentation-mysql2 (0.34.0)
+ opentelemetry-instrumentation-mysql2 (0.34.1)
opentelemetry-helpers-mysql
opentelemetry-helpers-sql
- opentelemetry-helpers-sql-processor
+ opentelemetry-helpers-sql-processor (>= 0.5.1, < 1.0)
opentelemetry-instrumentation-base (~> 0.25)
- opentelemetry-instrumentation-net_http (0.29.1)
+ opentelemetry-instrumentation-net_http (0.29.2)
opentelemetry-instrumentation-base (~> 0.25)
- opentelemetry-instrumentation-pg (0.37.0)
+ opentelemetry-instrumentation-openai (0.1.0)
+ opentelemetry-instrumentation-base (~> 0.27)
+ opentelemetry-instrumentation-pg (0.37.1)
opentelemetry-helpers-sql
- opentelemetry-helpers-sql-processor
+ opentelemetry-helpers-sql-processor (>= 0.5.1, < 1.0)
opentelemetry-instrumentation-base (~> 0.25)
opentelemetry-instrumentation-que (0.13.0)
opentelemetry-instrumentation-base (~> 0.25)
opentelemetry-instrumentation-racecar (0.7.0)
opentelemetry-instrumentation-base (~> 0.25)
- opentelemetry-instrumentation-rack (0.31.1)
+ opentelemetry-instrumentation-rack (0.31.2)
opentelemetry-instrumentation-base (~> 0.25)
opentelemetry-instrumentation-rails (0.42.0)
opentelemetry-instrumentation-action_mailer (~> 0.7)
@@ -698,14 +715,18 @@ GEM
opentelemetry-instrumentation-base (~> 0.25)
opentelemetry-instrumentation-sidekiq (0.29.0)
opentelemetry-instrumentation-base (~> 0.25)
- opentelemetry-instrumentation-sinatra (0.30.0)
+ opentelemetry-instrumentation-sinatra (0.30.1)
opentelemetry-instrumentation-rack (~> 0.29)
- opentelemetry-instrumentation-trilogy (0.69.0)
+ opentelemetry-instrumentation-trilogy (0.69.1)
opentelemetry-helpers-mysql
opentelemetry-helpers-sql
- opentelemetry-helpers-sql-processor
+ opentelemetry-helpers-sql-processor (>= 0.5.1, < 1.0)
opentelemetry-instrumentation-base (~> 0.25)
opentelemetry-semantic_conventions (>= 1.8.0)
+ opentelemetry-logs-api (0.7.0)
+ opentelemetry-api (~> 1.0)
+ opentelemetry-metrics-api (0.10.0)
+ opentelemetry-api (~> 1.0)
opentelemetry-registry (0.6.3)
opentelemetry-api (~> 1.1)
opentelemetry-sdk (1.13.1)
@@ -721,17 +742,17 @@ GEM
childprocess (>= 0.6.3, < 6)
iniparse (~> 1.4)
rexml (>= 3.4.2)
- parallel (2.2.0)
+ parallel (2.3.0)
parser (3.3.12.0)
ast (~> 2.4.1)
racc
- pg (1.6.3)
- pg_query (6.2.2)
- google-protobuf (>= 3.25.3)
+ pg (1.7.0)
+ pg_query (6.2.5)
+ google-protobuf (>= 4.34.0)
popper_js (2.11.8)
pp (0.6.4)
prettyprint
- prettyprint (0.2.0)
+ prettyprint (0.2.1)
prism (1.9.0)
process_executer (4.1.1)
track_open_instances (~> 0.1)
@@ -821,7 +842,7 @@ GEM
prism (>= 1.6.0)
rbs (>= 4.0.0)
tsort
- regexp_parser (2.13.0)
+ regexp_parser (2.13.1)
reline (0.7.0)
io-console (~> 0.5)
rexml (3.4.4)
@@ -835,14 +856,14 @@ GEM
strscan (~> 3.1)
rtlcss (0.2.1)
mini_racer (>= 0.6.3)
- rubocop (1.91.0)
+ rubocop (1.92.0)
json (>= 2.3)
language_server-protocol (~> 3.17.0.2)
lint_roller (~> 1.1.0)
parallel (>= 1.10)
parser (>= 3.3.0.2)
rainbow (>= 2.2.2, < 4.0)
- regexp_parser (>= 2.9.3, < 3.0)
+ regexp_parser (>= 2.11.3, < 3.0)
rubocop-ast (>= 1.49.0, < 2.0)
ruby-progressbar (~> 1.7)
unicode-display_width (>= 2.4.0, < 4.0)
@@ -855,9 +876,9 @@ GEM
rubocop-factory_bot (2.28.0)
lint_roller (~> 1.1)
rubocop (~> 1.72, >= 1.72.1)
- rubocop-minitest (0.40.0)
+ rubocop-minitest (0.41.0)
lint_roller (~> 1.1)
- rubocop (>= 1.75.0, < 2.0)
+ rubocop (>= 1.89.0, < 2.0)
rubocop-ast (>= 1.38.0, < 2.0)
rubocop-performance (1.27.0)
lint_roller (~> 1.1)
@@ -880,26 +901,27 @@ GEM
sanitize (7.0.0)
crass (~> 1.0.2)
nokogiri (>= 1.16.8)
- sass-embedded (1.64.2)
- google-protobuf (~> 3.23)
- rake (>= 13.0.0)
+ sass-embedded (1.105.1)
+ google-protobuf (~> 4.31)
+ rake (~> 13.3)
+ sassc-embedded (1.80.10)
+ sass-embedded (~> 1.80)
sawyer (0.9.3)
addressable (>= 2.3.5)
faraday (>= 0.17.3, < 3)
securerandom (0.4.1)
- selenium-webdriver (4.49.0)
+ selenium-webdriver (4.51.0)
base64 (~> 0.2)
logger (~> 1.4)
- rexml (~> 3.2, >= 3.2.5)
rubyzip (>= 1.2.2, < 4.0)
websocket (~> 1.0)
- sentry-delayed_job (7.0.0)
+ sentry-delayed_job (7.1.0)
delayed_job (>= 4.0)
- sentry-ruby (~> 7.0.0)
- sentry-rails (7.0.0)
+ sentry-ruby (~> 7.1.0)
+ sentry-rails (7.1.0)
railties (>= 5.2.0)
- sentry-ruby (~> 7.0.0)
- sentry-ruby (7.0.0)
+ sentry-ruby (~> 7.1.0)
+ sentry-ruby (7.1.0)
bigdecimal
concurrent-ruby (~> 1.0, >= 1.0.2)
logger
@@ -950,7 +972,7 @@ GEM
unicode-display_width (3.3.0)
unicode-emoji (~> 4.3)
unicode-emoji (4.3.0)
- uri (0.13.3)
+ uri (1.1.2)
useragent (0.16.11)
validate_url (1.0.15)
activemodel (>= 3.0.0)
@@ -960,11 +982,15 @@ GEM
simpleidn
vendorer (0.2.0)
version_gem (1.1.15)
+<<<<<<< HEAD
webfinger (2.1.3)
activesupport
faraday (~> 2.0)
faraday-follow_redirects
webmock (3.26.4)
+=======
+ webmock (3.29.0)
+>>>>>>> upstream-tip
addressable (>= 2.8.0)
crack (>= 0.3.2)
hashdiff (>= 0.4.0, < 2.0.0)
@@ -985,6 +1011,7 @@ DEPENDENCIES
aasm
actionpack-page_caching
active_record_union
+ active_storage_validations
activerecord-import
activerecord-postgis
addressable
@@ -1086,7 +1113,7 @@ DEPENDENCIES
rubocop-rake
ruby-vips
sanitize
- sass-embedded (~> 1.64.0)
+ sass-embedded (< 3.0.0)
selenium-webdriver
sentry-delayed_job
sentry-rails
@@ -1101,11 +1128,10 @@ DEPENDENCIES
timeout
turbo-rails
unicode-display_width
- uri (< 1.0.0)
validates_email_format_of
vendorer
webmock
webrick
BUNDLED WITH
- 4.0.3
+ 4.0.20
diff --git a/app/assets/javascripts/index_modules/changeset.js b/app/assets/javascripts/index_modules/changeset.js
index d2bfd56a7a6..c9d126abeca 100644
--- a/app/assets/javascripts/index_modules/changeset.js
+++ b/app/assets/javascripts/index_modules/changeset.js
@@ -1,6 +1,5 @@
export default function (map) {
- const page = {},
- content = $("#sidebar_content");
+ const content = $("#sidebar_content");
content.on("turbo:before-frame-render", "turbo-frame", function () {
$(this).find(".numbered_pagination").trigger("numbered_pagination:disable");
@@ -10,15 +9,17 @@ export default function (map) {
$(this).find(".numbered_pagination").trigger("numbered_pagination:enable");
});
- page.load = function (path) {
- OSM.loadSidebarContent(path)
- .then(page.init);
- };
+ return {
+ load(path) {
+ OSM.loadSidebarContent(path)
+ .then(this.init);
+ },
- page.init = function () {
- const changesetData = content.find("[data-changeset]").data("changeset");
- changesetData.type = "changeset";
+ init() {
+ const changesetData = content.find("[data-changeset]").data("changeset");
+ changesetData.type = "changeset";
+<<<<<<< HEAD
const hashParams = OSM.parseHash();
initialize();
map.addObject(changesetData, function (bounds) {
@@ -32,60 +33,63 @@ export default function (map) {
addOpenHistoricalMapTimeSlider(map);
};
+=======
+ const hashParams = OSM.parseHash();
+ content.find("button[data-method][data-url]").on("click", e => {
+ e.preventDefault();
+ const { method, url } = $(e.target).data();
+ const data = new URLSearchParams();
+>>>>>>> upstream-tip
- function updateChangeset(method, url, include_data) {
- const data = new URLSearchParams();
-
- content.find("#comment-error").prop("hidden", true);
- content.find("button[data-method][data-url]").prop("disabled", true);
+ content.find("#comment-error").prop("hidden", true);
+ content.find("button[data-method][data-url]").prop("disabled", true);
- if (include_data) {
- data.set("text", content.find("textarea").val());
- }
+ if (e.target.name === "comment") {
+ data.set("text", content.find("textarea").val());
+ }
- fetch(url, {
- method: method,
- headers: { ...OSM.oauth },
- body: data
- })
- .then(response => {
- if (response.ok) return response;
- return response.text().then(text => {
- throw new Error(text);
- });
- })
- .then(() => OSM.loadSidebarContent(location.pathname))
- .then(page.init)
- .catch(error => {
- content.find("button[data-method][data-url]").prop("disabled", false);
- content.find("#comment-error")
- .text(error.message)
- .prop("hidden", false)
- .get(0).scrollIntoView({ block: "nearest" });
+ fetch(url, {
+ method: method,
+ headers: { ...OSM.oauth },
+ body: data
+ })
+ .then(response => {
+ if (response.ok) return response;
+ return response.text().then(text => {
+ throw new Error(text);
+ });
+ })
+ .then(() => OSM.loadSidebarContent(location.pathname))
+ .then(this.init)
+ .catch(error => {
+ content.find("button[data-method][data-url]").prop("disabled", false);
+ content.find("#comment-error")
+ .text(error.message)
+ .prop("hidden", false)
+ .get(0).scrollIntoView({ block: "nearest" });
+ });
});
- }
-
- function initialize() {
- content.find("button[data-method][data-url]").on("click", function (e) {
- e.preventDefault();
- const data = $(e.target).data();
- const include_data = e.target.name === "comment";
- updateChangeset(data.method, data.url, include_data);
- });
- content.find("textarea").on("input", function (e) {
- const form = e.target.form,
- disabled = $(e.target).val() === "";
- form.comment.disabled = disabled;
- });
+ content.find("textarea").on("input", function (e) {
+ const form = e.target.form,
+ disabled = $(e.target).val() === "";
+ form.comment.disabled = disabled;
+ });
- content.find("textarea").val("").trigger("input");
- }
+ content.find("textarea").val("").trigger("input");
+ map.addObject(changesetData, function (bounds) {
+ if (!hashParams.center && bounds.isValid()) {
+ OSM.router.withoutMoveListener(function () {
+ map.fitBounds(bounds);
+ });
+ }
+ });
+ $(".numbered_pagination").trigger("numbered_pagination:enable");
+ },
- page.unload = function () {
- map.removeObject();
- $(".numbered_pagination").trigger("numbered_pagination:disable");
+ unload() {
+ map.removeObject();
+ $(".numbered_pagination").trigger("numbered_pagination:disable");
+ }
};
-
- return page;
}
diff --git a/app/assets/javascripts/index_modules/directions.js b/app/assets/javascripts/index_modules/directions.js
index 3c663321d68..a3b43a543cb 100644
--- a/app/assets/javascripts/index_modules/directions.js
+++ b/app/assets/javascripts/index_modules/directions.js
@@ -241,8 +241,6 @@ export default function (map) {
endpoints[1].enableListeners();
}
- const page = {};
-
function sidebarLoaded() {
if ($("#directions_route").length) {
sidebarReadyPromise = null;
@@ -253,37 +251,43 @@ export default function (map) {
return sidebarReadyPromise ??= OSM.loadSidebarContent("/directions");
}
- page.load = page.init = function () {
- initializeFromParams();
+ return {
+ load() { this.init(); },
- $(".search_form").hide();
- $(".directions_form").show();
+ init() {
+ initializeFromParams();
- sidebarLoaded().then(enableListeners);
+ $(".search_form").hide();
+ $(".directions_form").show();
+<<<<<<< HEAD
map.setSidebarOverlaid(!endpoints[0].latlng || !endpoints[1].latlng);
addOpenHistoricalMapTimeSlider(map);
- };
+=======
+ sidebarLoaded().then(enableListeners);
- page.unload = function () {
- $(".search_form").show();
- $(".directions_form").hide();
+ map.setSidebarOverlaid(!endpoints[0].latlng || !endpoints[1].latlng);
+ },
- $("#sidebar .sidebar-close-controls button").off("click", closeButtonListener);
- $("#map").off("dragend dragover drop");
- map.off("locationfound", sendStartingLocation);
+ unload() {
+ $(".search_form").show();
+ $(".directions_form").hide();
- endpoints[0].disableListeners();
- endpoints[1].disableListeners();
+ $("#sidebar .sidebar-close-controls button").off("click", closeButtonListener);
+ $("#map").off("dragend dragover drop");
+ map.off("locationfound", sendStartingLocation);
- endpoints[0].clearValue();
- endpoints[1].clearValue();
+ endpoints[0].disableListeners();
+ endpoints[1].disableListeners();
- routeOutput.remove();
+ endpoints[0].clearValue();
+ endpoints[1].clearValue();
- sidebarReadyPromise = null;
- };
+ routeOutput.remove();
- return page;
+ sidebarReadyPromise = null;
+ }
+>>>>>>> upstream-tip
+ };
}
diff --git a/app/assets/javascripts/index_modules/element.js b/app/assets/javascripts/index_modules/element.js
index 2bba3bb5283..1cc2b3ec587 100644
--- a/app/assets/javascripts/index_modules/element.js
+++ b/app/assets/javascripts/index_modules/element.js
@@ -5,36 +5,36 @@ const isOfExpectedLanguage = ({ language }) => languagesToRequest[0].startsWith(
export function element(type) {
return function () {
- const page = {};
-
- page.load = function (path, id, version) {
- OSM.loadSidebarContent(path)
- .then(() => page.init(path, id, version, true));
- };
-
- page.init = function (path, id, version, keepViewport) {
- page._addObject(type, id, version, keepViewport);
- $(".numbered_pagination").trigger("numbered_pagination:enable");
- abortController = new AbortController();
- };
-
- page.unload = function () {
- page._removeObject();
- $(".numbered_pagination").trigger("numbered_pagination:disable");
- abortController?.abort();
+ return {
+ load(path, id, version) {
+ OSM.loadSidebarContent(path)
+ .then(() => this.init(path, id, version, true));
+ },
+
+ init(path, id, version, keepViewport) {
+ this._addObject(type, id, version, keepViewport);
+ $(".numbered_pagination").trigger("numbered_pagination:enable");
+ abortController = new AbortController();
+ },
+
+ unload() {
+ this._removeObject();
+ $(".numbered_pagination").trigger("numbered_pagination:disable");
+ abortController?.abort();
+ },
+
+ _addObject() {},
+ _removeObject() {}
};
-
- page._addObject = function () {};
- page._removeObject = function () {};
-
- return page;
};
};
export function mappedElement(type) {
return function (map) {
- const page = element(type)(map);
+ return {
+ ...element(type)(map),
+<<<<<<< HEAD
page._addObject = function (type, id, version, keepViewport) {
const hashParams = OSM.parseHash();
map.addObject({ type: type, id: parseInt(id, 10), version: version && parseInt(version, 10) }, function (bounds) {
@@ -48,13 +48,24 @@ export function mappedElement(type) {
addOpenHistoricalMapTimeSlider(map);
addOpenHistoricalMapInspector();
+=======
+ _addObject(type, id, version, keepViewport) {
+ const hashParams = OSM.parseHash();
+ map.addObject({ type: type, id: parseInt(id, 10), version: version && parseInt(version, 10) }, function (bounds) {
+ if (!hashParams.center && bounds.isValid() &&
+ (!keepViewport || !map.getBounds().contains(bounds))) {
+ OSM.router.withoutMoveListener(function () {
+ map.fitBounds(bounds);
+ });
+ }
+ });
+ },
+
+ _removeObject() {
+ map.removeObject();
+ }
+>>>>>>> upstream-tip
};
-
- page._removeObject = function () {
- map.removeObject();
- };
-
- return page;
};
};
@@ -65,6 +76,10 @@ function previewWikidataValue($btn) {
const items = $btn.data("qids");
if (!items?.length) return;
$btn.prop("disabled", true);
+ const fetchOptions = {
+ headers: { "Api-User-Agent": "OSM-TagPreview (https://github.com/openstreetmap/openstreetmap-website)" },
+ signal: abortController?.signal
+ };
fetch(OSM.WIKIDATA_API_URL + "?" + new URLSearchParams({
action: "wbgetentities",
format: "json",
@@ -74,10 +89,7 @@ function previewWikidataValue($btn) {
languages: languagesToRequest.join("|"),
languagefallback: 1,
sitefilter: wikisToRequest.join("|")
- }), {
- headers: { "Api-User-Agent": "OSM-TagPreview (https://github.com/openstreetmap/openstreetmap-website)" },
- signal: abortController?.signal
- })
+ }), fetchOptions)
.then(response => response.ok ? response.json() : Promise.reject(response))
.then(({ entities }) => {
if (!entities) return Promise.reject(entities);
@@ -88,7 +100,7 @@ function previewWikidataValue($btn) {
.filter(qid => entities[qid])
.map(qid => getLocalizedResponse(entities[qid]))
.filter(data => data.label || data.icon || data.description || data.article)
- .map(data => renderWikidataResponse(data, $btn.siblings(`a[href*="wikidata.org/entity/${data.qid}"]`)))
+ .map(data => renderWikidataResponse(data, $btn.siblings(`a[href*="wikidata.org/entity/${data.qid}"]`), fetchOptions))
);
})
.catch(() => $btn.prop("disabled", false));
@@ -115,20 +127,22 @@ function getLocalizedResponse(entity) {
return data;
}
-function renderWikidataResponse({ icon, label, article, description }, $link) {
+function renderWikidataResponse({ icon, label, article, description }, $link, fetchOptions) {
const localeName = new Intl.DisplayNames(OSM.preferred_languages, { type: "language" });
const cell = $("
")
.attr("colspan", 2)
.addClass("bg-body-tertiary");
if (icon && OSM.WIKIMEDIA_COMMONS_URL) {
- let src = OSM.WIKIMEDIA_COMMONS_URL + "Special:Redirect/file/" + encodeURIComponent(icon) + "?mobileaction=toggle_view_desktop";
- if (!icon.endsWith(".svg")) src += "&width=128";
- $("")
- .attr("href", OSM.WIKIMEDIA_COMMONS_URL + "File:" + encodeURIComponent(icon) + `?uselang=${OSM.i18n.locale}`)
- .append($(" ").attr({ src, height: "32" }))
- .addClass("float-end mb-1 ms-2")
- .appendTo(cell);
+ fetchCommonsThumbnail(icon, fetchOptions)
+ .then(imageAttributes => {
+ $("")
+ .attr("href", OSM.WIKIMEDIA_COMMONS_URL + "/wiki/File:" + encodeURIComponent(icon) + `?uselang=${OSM.i18n.locale}`)
+ .append($(" ").attr(imageAttributes))
+ .addClass("float-end mb-1 ms-2")
+ .prependTo(cell);
+ })
+ .catch(() => {});
}
if (label) {
const link = $link.clone()
@@ -168,6 +182,7 @@ function renderWikidataResponse({ icon, label, article, description }, $link) {
return $("").append(cell);
}
+<<<<<<< HEAD
// add the enhanced inspector (class comes from the external ohm-inspector
// script loaded in layouts/_head.html.erb)
function addOpenHistoricalMapInspector() {
@@ -182,4 +197,36 @@ function addOpenHistoricalMapInspector() {
apiBaseUrl: "/api" // no trailing /
});
inspector.selectFeatureFromUrl();
+=======
+function fetchCommonsThumbnail(filename, fetchOptions) {
+ const isVectorImage = filename.toLowerCase().endsWith(".svg");
+ return fetch(OSM.WIKIMEDIA_COMMONS_URL + "/w/api.php?" + new URLSearchParams({
+ action: "query",
+ format: "json",
+ origin: "*",
+ prop: "imageinfo",
+ titles: "File:" + filename,
+ iiprop: "url|thumburls",
+ iiurlheight: "32"
+ }), fetchOptions)
+ .then(response => response.ok ? response.json() : Promise.reject(response))
+ .then(({ query }) => {
+ const page = Object.values(query.pages)[0];
+ const imageInfo = page.imageinfo?.[0];
+ const imageUrl = isVectorImage ? imageInfo?.url : imageInfo?.thumburl;
+ if (!imageUrl) return Promise.reject(page);
+
+ const imageAttributes = { src: imageUrl, height: "32", alt: filename };
+ if (!isVectorImage) {
+ const thumbUrls = Object.entries(imageInfo.thumburls ?? {});
+ if (thumbUrls.length) {
+ imageAttributes.srcset = thumbUrls
+ .map(([width, thumbnail]) => `${thumbnail.url} ${width}w`)
+ .join(", ");
+ imageAttributes.sizes = `${imageInfo.thumbwidth}px`;
+ }
+ }
+ return imageAttributes;
+ });
+>>>>>>> upstream-tip
}
diff --git a/app/assets/javascripts/index_modules/export.js b/app/assets/javascripts/index_modules/export.js
index a83060aa69e..9f14eab2e78 100644
--- a/app/assets/javascripts/index_modules/export.js
+++ b/app/assets/javascripts/index_modules/export.js
@@ -1,8 +1,6 @@
//= require download_util
export default function (map) {
- const page = {};
-
const locationFilter = new L.LocationFilter({
enableButton: false,
adjustButton: false
@@ -79,52 +77,57 @@ export default function (map) {
if (getBounds().getSize() > OSM.MAX_REQUEST_AREA) e.preventDefault();
}
- page.load = function (path) {
- OSM.loadSidebarContent(path)
- .then(page.init);
- };
-
- page.init = function () {
- map
- .addLayer(locationFilter)
- .on("moveend", update);
-
- $("#maxlat, #minlon, #maxlon, #minlat").change(boundsChanged);
- $("#drag_box").click(enableFilter);
- $(".export_form").on("submit", checkSubmit);
-
- document.querySelector(".export_form")
- .addEventListener("turbo:submit-end", OSM.getTurboBlobHandler("map.osm"));
-
- document.querySelector(".export_form")
- .addEventListener("turbo:before-fetch-response", OSM.turboHtmlResponseHandler);
-
- document.querySelector(".export_form")
- .addEventListener("turbo:before-fetch-request", function (event) {
- event.detail.fetchOptions.headers.Accept = "application/xml";
+ return {
+ load(path) {
+ OSM.loadSidebarContent(path)
+ .then(this.init);
+ },
+
+ init() {
+ map
+ .addLayer(locationFilter)
+ .on("moveend", update);
+
+ $("#maxlat, #minlon, #maxlon, #minlat").change(boundsChanged);
+ $("#drag_box").click(enableFilter);
+ $(".export_form").on("submit", checkSubmit);
+
+ document.querySelector(".export_form")
+ .addEventListener("turbo:submit-end", OSM.getTurboBlobHandler("map.osm"));
+
+ document.querySelector(".export_form")
+ .addEventListener("turbo:before-fetch-response", OSM.turboHtmlResponseHandler);
+
+ document.querySelector(".export_form")
+ .addEventListener("turbo:before-fetch-request", function (event) {
+ event.detail.fetchOptions.headers.Accept = "application/xml";
+ });
+
+ $("#export_overpass").on("click", async function (event) {
+ event.preventDefault();
+ const downloadUrl = $(this).attr("href");
+ const confirmed = await showConfirmationModal();
+ if (confirmed) {
+ window.location.href = downloadUrl;
+ }
});
- $("#export_overpass").on("click", async function (event) {
- event.preventDefault();
- const downloadUrl = $(this).attr("href");
- const confirmed = await showConfirmationModal();
- if (confirmed) {
- window.location.href = downloadUrl;
- }
- });
+ update();
+ return map.getState();
+ },
+<<<<<<< HEAD
update();
addOpenHistoricalMapTimeSlider(map);
return map.getState();
+=======
+ unload() {
+ map
+ .removeLayer(locationFilter)
+ .off("moveend", update);
+ }
+>>>>>>> upstream-tip
};
-
- page.unload = function () {
- map
- .removeLayer(locationFilter)
- .off("moveend", update);
- };
-
- return page;
}
diff --git a/app/assets/javascripts/index_modules/history.js b/app/assets/javascripts/index_modules/history.js
index 7722b909dfb..b05515543d4 100644
--- a/app/assets/javascripts/index_modules/history.js
+++ b/app/assets/javascripts/index_modules/history.js
@@ -2,8 +2,6 @@
/* global ChangesetsLayer */
export default function (map) {
- const page = {};
-
$("#sidebar_content")
.on("click", ".changeset_more a", loadMoreChangesets)
.on("mouseover", "[data-changeset]", function () {
@@ -287,11 +285,13 @@ export default function (map) {
}
}
- page.load = function (path) {
- OSM.loadSidebarContent(path)
- .then(page.init);
- };
+ return {
+ load(path) {
+ OSM.loadSidebarContent(path)
+ .then(this.init);
+ },
+<<<<<<< HEAD
page.init = function () {
map.addLayer(changesetsLayer);
map.on("moveend", moveEndListener);
@@ -300,13 +300,20 @@ export default function (map) {
addOpenHistoricalMapTimeSlider(map);
};
-
- page.unload = function () {
- map.removeLayer(changesetsLayer);
- map.off("moveend", moveEndListener);
- map.off("zoomend", zoomEndListener);
- disableChangesetIntersectionObserver();
+=======
+ init() {
+ map.addLayer(changesetsLayer);
+ map.on("moveend", moveEndListener);
+ map.on("zoomend", zoomEndListener);
+ loadFirstChangesets();
+ },
+>>>>>>> upstream-tip
+
+ unload() {
+ map.removeLayer(changesetsLayer);
+ map.off("moveend", moveEndListener);
+ map.off("zoomend", zoomEndListener);
+ disableChangesetIntersectionObserver();
+ }
};
-
- return page;
}
diff --git a/app/assets/javascripts/index_modules/home.js b/app/assets/javascripts/index_modules/home.js
index 3a1d43896a6..66c69789612 100644
--- a/app/assets/javascripts/index_modules/home.js
+++ b/app/assets/javascripts/index_modules/home.js
@@ -6,8 +6,6 @@ export default function (map) {
marker = null;
}
- const page = {};
-
function show(panToHome) {
map.setSidebarOverlaid(true);
clearMarker();
@@ -31,18 +29,18 @@ export default function (map) {
}
}
- page.load = function () {
- show(true);
- };
+ return {
+ load() {
+ show(true);
+ },
- page.init = function () {
- show(!OSM.parseHash(location.hash).center);
- };
+ init() {
+ show(!OSM.parseHash(location.hash).center);
+ },
- page.unload = function () {
- clearMarker();
- $("#browse_status").empty();
+ unload() {
+ clearMarker();
+ $("#browse_status").empty();
+ }
};
-
- return page;
}
diff --git a/app/assets/javascripts/index_modules/index.js b/app/assets/javascripts/index_modules/index.js
index 7e3ba55b41b..c1a1819d6d7 100644
--- a/app/assets/javascripts/index_modules/index.js
+++ b/app/assets/javascripts/index_modules/index.js
@@ -1,21 +1,23 @@
export default function (map) {
- const page = {};
+ return {
+ load() {
+ map.setSidebarOverlaid(true);
+ document.title = OSM.i18n.t("layouts.project_name.title");
+ },
- page.load = function () {
- map.setSidebarOverlaid(true);
- document.title = OSM.i18n.t("layouts.project_name.title");
- };
-
- page.init = function () {
- const params = new URLSearchParams(location.search);
- if (params.has("query")) {
- $("#sidebar .search_form input[name=query]").value(params.get("query"));
+ init() {
+ const params = new URLSearchParams(location.search);
+ if (params.has("query")) {
+ $("#sidebar .search_form input[name=query]").value(params.get("query"));
+ }
+ return map.getState();
}
+<<<<<<< HEAD
addOpenHistoricalMapTimeSlider(map);
return map.getState();
+=======
+>>>>>>> upstream-tip
};
-
- return page;
};
diff --git a/app/assets/javascripts/index_modules/new_note.js b/app/assets/javascripts/index_modules/new_note.js
index e7fb7929c0b..4d556e71cc2 100644
--- a/app/assets/javascripts/index_modules/new_note.js
+++ b/app/assets/javascripts/index_modules/new_note.js
@@ -1,7 +1,6 @@
export default function (map) {
const noteLayer = map.noteLayer,
content = $("#sidebar_content"),
- page = {},
control = $(".control-note"),
addNoteButton = control.find(".control-button");
let newNoteMarker,
@@ -98,24 +97,82 @@ export default function (map) {
if (newNoteMarker) newNoteMarker.setOpacity(zoomedOut ? 0.5 : 0.9);
}
- page.load = function (path) {
- OSM.loadSidebarContent(path)
- .then(() => page.init(path));
- };
+ return {
+ load(path) {
+ OSM.loadSidebarContent(path)
+ .then(() => this.init(path));
+ },
+
+ init(path) {
+ control.addClass("active");
+
+ map.addLayer(noteLayer);
- page.init = function (path) {
- control.addClass("active");
+ const params = new URLSearchParams(path.substring(path.indexOf("?")));
+ let markerLatlng;
- map.addLayer(noteLayer);
+ if (params.has("lat") && params.has("lon")) {
+ markerLatlng = { lat: params.get("lat"), lng: params.get("lon") };
+ } else {
+ markerLatlng = map.getCenter();
+ }
- const params = new URLSearchParams(path.substring(path.indexOf("?")));
- let markerLatlng;
+ map.panInside(markerLatlng, {
+ padding: [50, 50]
+ });
+
+ addNewNoteMarker(markerLatlng);
+
+ content.find("textarea")
+ .on("input", updateControls)
+ .attr("readonly", "readonly") // avoid virtual keyboard popping up on focus
+ .trigger("focus")
+ .removeAttr("readonly");
+
+ content.find("input[type=submit]").on("click", function (e) {
+ const location = newNoteMarker.getLatLng().wrap();
+ const text = content.find("textarea").val();
+
+ errorPanel = content.find(".new-note-error");
+ errorPanel.addClass("d-none");
+ errorPanelDetail = errorPanel.find(".new-note-error-detail");
+
+ e.preventDefault();
+ $(this).prop("disabled", true);
+ newNoteMarker.options.draggable = false;
+ newNoteMarker.dragging.disable();
+
+ createNote(location, text)
+ .then(feature => {
+ if (typeof OSM.user === "undefined") {
+ const anonymousNotesCount = Number(OSM.cookies.get("_osm_anonymous_notes_count")) || 0;
+ OSM.cookies.set("_osm_anonymous_notes_count", anonymousNotesCount + 1, { expires: 14 });
+ }
+ content.find("textarea").val("");
+ addCreatedNoteMarker(feature);
+ OSM.router.route("/note/" + feature.properties.id);
+ })
+ .catch(err => {
+ errorPanel.removeClass("d-none");
+ errorPanelDetail.text(err.message || err);
+ updateControls();
+ });
+ });
- if (params.has("lat") && params.has("lon")) {
- markerLatlng = { lat: params.get("lat"), lng: params.get("lon") };
- } else {
- markerLatlng = map.getCenter();
+ map.on("click", moveNewNoteMarkerToClick);
+ addNoteButton.on("disabled enabled", updateControls);
+ updateControls();
+
+ return map.getState();
+ },
+
+ unload() {
+ map.off("click", moveNewNoteMarkerToClick);
+ addNoteButton.off("disabled enabled", updateControls);
+ removeNewNoteMarker();
+ control.removeClass("active");
}
+<<<<<<< HEAD
map.panInside(markerLatlng, {
padding: [50, 50]
@@ -166,14 +223,7 @@ export default function (map) {
addOpenHistoricalMapTimeSlider(map);
return map.getState();
+=======
+>>>>>>> upstream-tip
};
-
- page.unload = function () {
- map.off("click", moveNewNoteMarkerToClick);
- addNoteButton.off("disabled enabled", updateControls);
- removeNewNoteMarker();
- control.removeClass("active");
- };
-
- return page;
}
diff --git a/app/assets/javascripts/index_modules/note.js b/app/assets/javascripts/index_modules/note.js
index f8f37058e41..7e54d09fe21 100644
--- a/app/assets/javascripts/index_modules/note.js
+++ b/app/assets/javascripts/index_modules/note.js
@@ -1,4 +1,5 @@
export default function (map) {
+<<<<<<< HEAD
const content = $("#sidebar_content"),
page = {};
@@ -15,6 +16,9 @@ export default function (map) {
initialize(path, id);
addOpenHistoricalMapTimeSlider(map);
};
+=======
+ const content = $("#sidebar_content");
+>>>>>>> upstream-tip
function initialize(path, id, skipMoveToNote) {
content.find("button[name]").on("click", function (e) {
@@ -92,9 +96,22 @@ export default function (map) {
}
}
- page.unload = function () {
- map.removeObject();
- };
+ return {
+ load(path, id) {
+ OSM.loadSidebarContent(path).then(function () {
+ const data = $(".details").data();
+ if (!data) return;
+ const [lat, lng] = data.coordinates.split(",").map(parseFloat);
+ initialize(path, id, map.getBounds().contains({ lat, lng }));
+ });
+ },
+
+ init(path, id) {
+ initialize(path, id);
+ },
- return page;
+ unload() {
+ map.removeObject();
+ }
+ };
}
diff --git a/app/assets/javascripts/index_modules/query.js b/app/assets/javascripts/index_modules/query.js
index a7ca4485bfc..54acd208b6b 100644
--- a/app/assets/javascripts/index_modules/query.js
+++ b/app/assets/javascripts/index_modules/query.js
@@ -298,23 +298,26 @@ export default function (map) {
runQuery(isin, $("#query-isin"), true, (feature1, feature2) => size(feature1.bounds) - size(feature2.bounds), jsDateFilter);
}
- const page = {};
-
- page.load = function (path) {
- OSM.loadSidebarContent(path)
- .then(() => page.init(path, true));
- };
-
- page.init = function (path, noCentre) {
- const params = new URLSearchParams(path.substring(path.indexOf("?"))),
- latlng = L.latLng(params.get("lat"), params.get("lon"));
+ return {
+ load(path) {
+ OSM.loadSidebarContent(path)
+ .then(() => this.init(path, true));
+ },
+
+ init(path, noCentre) {
+ const params = new URLSearchParams(path.substring(path.indexOf("?"))),
+ latlng = L.latLng(params.get("lat"), params.get("lon"));
+
+ if (!location.hash && !noCentre && !map.getBounds().contains(latlng)) {
+ OSM.router.withoutMoveListener(function () {
+ map.setView(latlng, 15);
+ });
+ }
- if (!location.hash && !noCentre && !map.getBounds().contains(latlng)) {
- OSM.router.withoutMoveListener(function () {
- map.setView(latlng, 15);
- });
- }
+ queryOverpass([params.get("lat"), params.get("lon")]);
+ },
+<<<<<<< HEAD
queryOverpass([params.get("lat"), params.get("lon")]);
addOpenHistoricalMapTimeSlider(map);
@@ -323,8 +326,12 @@ export default function (map) {
page.unload = function (sameController) {
if (!sameController) {
$("#sidebar_content .query-results a.selected").each(hideResultGeometry);
+=======
+ unload(sameController) {
+ if (!sameController) {
+ $("#sidebar_content .query-results a.selected").each(hideResultGeometry);
+ }
+>>>>>>> upstream-tip
}
};
-
- return page;
}
diff --git a/app/assets/javascripts/index_modules/search.js b/app/assets/javascripts/index_modules/search.js
index 26ad7ca2647..e28fe273864 100644
--- a/app/assets/javascripts/index_modules/search.js
+++ b/app/assets/javascripts/index_modules/search.js
@@ -84,43 +84,45 @@ export default function (map) {
e.stopPropagation();
}
- const page = {};
-
- page.load = function (path) {
- const params = new URLSearchParams(path.substring(path.indexOf("?")));
- if (params.has("query")) {
- $(".search_form input[name=query]").val(params.get("query"));
- } else if (params.has("lat") && params.has("lon")) {
- $(".search_form input[name=query]").val(params.get("lat") + ", " + params.get("lon"));
- }
- OSM.loadSidebarContent(path)
- .then(page.init);
- };
-
- page.init = function () {
- $(".search_results_entry[data-href]").each(function (index) {
- const entry = $(this);
- fetchReplace(this.dataset, entry.children().first())
- .then(() => {
- // go to first result of first geocoder
- if (index === 0) {
- const firstResult = entry.find("*[data-lat][data-lon]:first").first();
- if (firstResult.length) {
- panToSearchResult(firstResult.data());
+ return {
+ load(path) {
+ const params = new URLSearchParams(path.substring(path.indexOf("?")));
+ if (params.has("query")) {
+ $(".search_form input[name=query]").val(params.get("query"));
+ } else if (params.has("lat") && params.has("lon")) {
+ $(".search_form input[name=query]").val(params.get("lat") + ", " + params.get("lon"));
+ }
+ OSM.loadSidebarContent(path)
+ .then(this.init);
+ },
+
+ init() {
+ $(".search_results_entry[data-href]").each(function (index) {
+ const entry = $(this);
+ fetchReplace(this.dataset, entry.children().first())
+ .then(() => {
+ // go to first result of first geocoder
+ if (index === 0) {
+ const firstResult = entry.find("*[data-lat][data-lon]:first").first();
+ if (firstResult.length) {
+ panToSearchResult(firstResult.data());
+ }
}
- }
- });
- });
+ });
+ });
+<<<<<<< HEAD
addOpenHistoricalMapTimeSlider(map);
return map.getState();
- };
+=======
+ return map.getState();
+ },
- page.unload = function () {
- markers.clearLayers();
- processedResults = 0;
+ unload() {
+ markers.clearLayers();
+ processedResults = 0;
+ }
+>>>>>>> upstream-tip
};
-
- return page;
}
diff --git a/app/assets/javascripts/user.js b/app/assets/javascripts/user.js
index f02dc05fa17..da5b8e78984 100644
--- a/app/assets/javascripts/user.js
+++ b/app/assets/javascripts/user.js
@@ -2,6 +2,7 @@
//= require ./home_location_name-endpoint
$(function () {
+ const defaultHomeZoom = 11;
let map, marker, deleted_lat, deleted_lon, deleted_home_name, homeLocationNameGeocoder, savedLat, savedLon;
if ($("#social_links").length) {
@@ -111,7 +112,7 @@ $(function () {
const lat = $("#home_lat").val(),
lon = $("#home_lon").val();
- map.flyTo({ center: [lon, lat], zoom: OSM.MapLibre.defaultHomeZoom });
+ map.flyTo({ center: [lon, lat], zoom: defaultHomeZoom });
});
$("#home_delete").click(function () {
diff --git a/app/assets/stylesheets/maplibre-gl-all.scss b/app/assets/stylesheets/maplibre-gl-all.scss
index 742061d6222..a33c5364b31 100644
--- a/app/assets/stylesheets/maplibre-gl-all.scss
+++ b/app/assets/stylesheets/maplibre-gl-all.scss
@@ -51,6 +51,9 @@
}
.maplibregl-map .maplibregl-canvas-container {
+ // Keep marker z-indexes below sibling popups and map controls.
+ isolation: isolate;
+
.maplibregl-user-location-accuracy-circle {
background-color: $green;
opacity: 0.2 !important;
diff --git a/app/controllers/api/changesets_controller.rb b/app/controllers/api/changesets_controller.rb
index 6cc673810f2..ed5dba358d1 100644
--- a/app/controllers/api/changesets_controller.rb
+++ b/app/controllers/api/changesets_controller.rb
@@ -94,7 +94,7 @@ def create
# changesets are not (yet?) versioned, so we don't have to deal with
# history tables here. changesets are locked to a single user, however.
#
- # after succesful update, returns the XML of the changeset.
+ # after successful update, returns the XML of the changeset.
def update
@changeset = Changeset.find(params.expect(:id))
new_changeset = Changeset.from_xml(request.raw_post)
diff --git a/app/controllers/api/maps_controller.rb b/app/controllers/api/maps_controller.rb
index 019d9af664b..0784667d179 100644
--- a/app/controllers/api/maps_controller.rb
+++ b/app/controllers/api/maps_controller.rb
@@ -46,9 +46,9 @@ def show
else
way_nodes = WayNode.where(:node_id => node_ids)
way_ids = way_nodes.collect { |way_node| way_node.id[0] }
- ways = Way.preload(:nodes, :element_tags, :changeset => :user).find(way_ids)
+ ways = Way.preload(:way_nodes, :element_tags, :changeset => :user).find(way_ids)
- list_of_way_nodes = ways.flat_map { |way| way.way_nodes.map(&:node_id) }
+ list_of_way_nodes = ways.flat_map(&:nds)
end
# - [0] in case some thing links to node 0 which doesn't exist. Shouldn't actually ever happen but it does. FIXME: file a ticket for this
diff --git a/app/controllers/api/nodes/ways_controller.rb b/app/controllers/api/nodes/ways_controller.rb
index ad7b667fab1..9d4e66f589b 100644
--- a/app/controllers/api/nodes/ways_controller.rb
+++ b/app/controllers/api/nodes/ways_controller.rb
@@ -14,7 +14,7 @@ class WaysController < ApiController
def index
@ways = Way
.visible
- .includes(:nodes, :changeset => :user)
+ .includes(:way_nodes, :changeset => :user)
.where(:id => WayNode.where(
:node_id => params[:node_id]
).select(:way_id))
diff --git a/app/controllers/api/notes_controller.rb b/app/controllers/api/notes_controller.rb
index ffc749ab2ac..0ead52933cd 100644
--- a/app/controllers/api/notes_controller.rb
+++ b/app/controllers/api/notes_controller.rb
@@ -32,7 +32,7 @@ def index
# Check that the boundaries are valid
bbox.check_boundaries
- # Check the the bounding box is not too big
+ # Check that the bounding box is not too big
bbox.check_size(Settings.max_note_request_area)
@min_lon = bbox.min_lon
@min_lat = bbox.min_lat
diff --git a/app/controllers/api/ways_controller.rb b/app/controllers/api/ways_controller.rb
index d1989be66b4..62cbeb63fbb 100644
--- a/app/controllers/api/ways_controller.rb
+++ b/app/controllers/api/ways_controller.rb
@@ -18,7 +18,7 @@ def index
raise OSM::APIBadUserInput, "No ways were given to search for" if ids.empty?
- @ways = Way.includes(:element_tags, :changeset => :user, :nodes => { :changeset => :user }).find(ids)
+ @ways = Way.includes(:way_nodes, :element_tags, :changeset => :user).find(ids)
# Render the result
respond_to do |format|
@@ -29,7 +29,7 @@ def index
def show
@way = Way
- @way = @way.includes(:nodes, :element_tags)
+ @way = @way.includes(:way_nodes, :element_tags)
@way = @way.includes(:nodes => [:element_tags, { :changeset => :user }]) if params[:full]
@way = @way.find(params.expect(:id))
diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb
index 3e6fa3a0af7..784c6c14db2 100644
--- a/app/controllers/application_controller.rb
+++ b/app/controllers/application_controller.rb
@@ -253,9 +253,10 @@ def map_layout
policy = request.content_security_policy.clone
policy.connect_src(*policy.connect_src, "http://127.0.0.1:8111", "https://vector.openstreetmap.org", "https://api.maptiler.com",
"https://tile.thunderforest.com", "https://render.openstreetmap.org", Settings.nominatim_url, Settings.overpass_url,
- Settings.fossgis_osrm_url, Settings.graphhopper_url, Settings.fossgis_valhalla_url, Settings.wikidata_api_url)
+ Settings.fossgis_osrm_url, Settings.graphhopper_url, Settings.fossgis_valhalla_url, Settings.wikidata_api_url,
+ Settings.wikimedia_commons_url)
policy.form_action(*policy.form_action, "render.openstreetmap.org", "tile.thunderforest.com")
- policy.img_src(*policy.img_src, Settings.wikimedia_commons_url, "upload.wikimedia.org")
+ policy.img_src(*policy.img_src, Settings.wikimedia_commons_url, "upload.wikimedia.org", "thumb.wikimedia.org")
policy.script_src(*policy.script_src, :wasm_unsafe_eval)
policy.style_src(*policy.style_src, :unsafe_inline)
diff --git a/app/controllers/concerns/nominatim_methods.rb b/app/controllers/concerns/nominatim_methods.rb
index 3fd7cfe2465..cb5de2c9f94 100644
--- a/app/controllers/concerns/nominatim_methods.rb
+++ b/app/controllers/concerns/nominatim_methods.rb
@@ -46,10 +46,12 @@ def nominatim_reverse_query_url(format: nil)
parameters = {
"format" => format,
"lat" => lat,
- "lon" => lon,
- "zoom" => zoom
+ "lon" => lon
}
+ # add any zoom; a blank value is sent as "zoom=" and Nominatim rejects it
+ parameters["zoom"] = zoom if zoom.present?
+
# build url
nominatim_url("reverse", parameters)
end
diff --git a/app/controllers/confirmations_controller.rb b/app/controllers/confirmations_controller.rb
index 7dd799d313c..22cc7291c0a 100644
--- a/app/controllers/confirmations_controller.rb
+++ b/app/controllers/confirmations_controller.rb
@@ -72,25 +72,25 @@ def confirm_resend
def confirm_email
if request.post?
- self.current_user = User.find_by_token_for(:new_email, params[:confirm_string])
-
- if current_user&.new_email?
- current_user.email = current_user.new_email
- current_user.new_email = nil
- current_user.email_valid = true
- gravatar_enabled = current_user.gravatar_enable!
- if current_user.save
+ user = User.find_by_token_for(:new_email, params[:confirm_string])
+
+ if user&.new_email?
+ user.email = user.new_email
+ user.new_email = nil
+ user.email_valid = true
+ gravatar_enabled = user.gravatar_enable!
+ if user.save
flash[:notice] = if gravatar_enabled
- "#{t('.success')} #{gravatar_status_message(current_user)}"
+ "#{t('.success')} #{gravatar_status_message(user)}"
else
t(".success")
end
else
- flash[:errors] = current_user.errors
+ flash[:errors] = user.errors
end
- session[:user] = current_user.id
- session[:fingerprint] = current_user.fingerprint
- elsif current_user
+ session[:user] = user.id
+ session[:fingerprint] = user.fingerprint
+ elsif user
flash[:error] = t ".failure"
else
flash[:error] = t ".unknown_token"
diff --git a/app/controllers/passwords_controller.rb b/app/controllers/passwords_controller.rb
index aa300b4925b..3e740742cac 100644
--- a/app/controllers/passwords_controller.rb
+++ b/app/controllers/passwords_controller.rb
@@ -21,9 +21,9 @@ def edit
@title = t ".title"
if params[:token]
- self.current_user = User.find_by_token_for(:password_reset, params[:token])
+ @user = User.find_by_token_for(:password_reset, params[:token])
- if current_user.nil?
+ if @user.nil?
flash[:error] = t ".flash token bad"
redirect_to :action => "new"
end
@@ -52,19 +52,19 @@ def create
def update
if params[:token]
- self.current_user = User.find_by_token_for(:password_reset, params[:token])
+ @user = User.find_by_token_for(:password_reset, params[:token])
- if current_user
+ if @user
if params[:user]
- current_user.pass_crypt = params[:user][:pass_crypt]
- current_user.pass_crypt_confirmation = params[:user][:pass_crypt_confirmation]
- current_user.activate if current_user.may_activate?
- current_user.email_valid = true
+ @user.pass_crypt = params[:user][:pass_crypt]
+ @user.pass_crypt_confirmation = params[:user][:pass_crypt_confirmation]
+ @user.activate if @user.may_activate?
+ @user.email_valid = true
- if current_user.save
- session[:fingerprint] = current_user.fingerprint
+ if @user.save
+ session[:fingerprint] = @user.fingerprint
flash[:notice] = t ".flash changed"
- successful_login(current_user)
+ successful_login(@user)
else
render :edit
end
diff --git a/app/controllers/profiles/images_controller.rb b/app/controllers/profiles/images_controller.rb
index 911b31e0449..b79a4a26d20 100644
--- a/app/controllers/profiles/images_controller.rb
+++ b/app/controllers/profiles/images_controller.rb
@@ -17,7 +17,11 @@ def update_profile
current_user.image_use_gravatar = true
end
- current_user.save
+ saved_user = current_user.save
+
+ current_user.attachment_changes.delete("avatar") unless saved_user
+
+ saved_user
end
end
end
diff --git a/app/controllers/users/lists_controller.rb b/app/controllers/users/lists_controller.rb
index 44d40b7dbeb..ba70028b0cb 100644
--- a/app/controllers/users/lists_controller.rb
+++ b/app/controllers/users/lists_controller.rb
@@ -35,8 +35,8 @@ def show
def update
ids = params.fetch(:user, {}).keys.collect(&:to_i)
- User.where(:id => ids).each(&:confirm!) if params[:confirm]
- User.where(:id => ids).each(&:suspend_if_possible!) if params[:suspend]
+ User.where(:id => ids).preload(:avatar_attachment).each(&:confirm!) if params[:confirm]
+ User.where(:id => ids).preload(:avatar_attachment).each(&:suspend_if_possible!) if params[:suspend]
redirect_to url_for(params.permit(:status, :username, :ip, :edits, :before, :after))
end
diff --git a/app/helpers/user_helper.rb b/app/helpers/user_helper.rb
index f27772892b0..37575295f47 100644
--- a/app/helpers/user_helper.rb
+++ b/app/helpers/user_helper.rb
@@ -3,43 +3,18 @@
module UserHelper
# User images
- def user_image(user, options = {})
- options[:class] ||= "user_image border border-secondary-subtle bg-body"
- options[:alt] ||= ""
+ USER_IMAGE_SIZES = { :user_thumbnail_tiny => 25, :user_thumbnail => 50, :user_image => 100 }.freeze
- if user.image_use_gravatar
- user_gravatar_tag(user, options)
- elsif user.avatar.attached?
- user_avatar_variant_tag(user, { :resize_to_limit => [100, 100] }, options)
- else
- image_tag "avatar.svg", options.merge(:width => 100, :height => 100)
- end
+ def user_image(user, options = {})
+ user_image_tag(user, :user_image, options)
end
def user_thumbnail(user, options = {})
- options[:class] ||= "user_thumbnail border border-secondary-subtle bg-body"
- options[:alt] ||= ""
-
- if user.image_use_gravatar
- user_gravatar_tag(user, options.merge(:size => 50))
- elsif user.avatar.attached?
- user_avatar_variant_tag(user, { :resize_to_limit => [50, 50] }, options)
- else
- image_tag "avatar.svg", options.merge(:width => 50, :height => 50)
- end
+ user_image_tag(user, :user_thumbnail, options)
end
def user_thumbnail_tiny(user, options = {})
- options[:class] ||= "user_thumbnail_tiny border border-secondary-subtle bg-body"
- options[:alt] ||= ""
-
- if user.image_use_gravatar
- user_gravatar_tag(user, options.merge(:size => 50))
- elsif user.avatar.attached?
- user_avatar_variant_tag(user, { :resize_to_limit => [50, 50] }, options)
- else
- image_tag "avatar.svg", options.merge(:width => 50, :height => 50)
- end
+ user_image_tag(user, :user_thumbnail_tiny, options)
end
def user_image_url(user)
@@ -54,6 +29,21 @@ def user_image_url(user)
private
+ def user_image_tag(user, size, options = {})
+ options = { :class => "border border-secondary-subtle bg-body", :alt => "" }.merge(options)
+ options[:class] = "#{size} " + options[:class]
+
+ size = USER_IMAGE_SIZES[size]
+
+ if user.image_use_gravatar
+ user_gravatar_tag(user, options.merge(:size => size))
+ elsif user.avatar.attached?
+ user_avatar_variant_tag(user, { :resize_to_limit => [size, size] }, options)
+ else
+ image_tag "avatar.svg", options.merge(:width => size, :height => size)
+ end
+ end
+
# Local avatar support
def user_avatar_variant_tag(user, variant_options, options)
if user.avatar.variable?
@@ -92,7 +82,7 @@ def user_gravatar_url(user, options = {})
def user_gravatar_tag(user, options = {})
url = user_gravatar_url(user, options)
- options[:height] = options[:width] = options.delete(:size) || 100
+ options[:height] = options[:width] = options.delete(:size)
image_tag url, options
end
end
diff --git a/app/models/relation.rb b/app/models/relation.rb
index 32a0bb5f7c4..dac6e3eb876 100644
--- a/app/models/relation.rb
+++ b/app/models/relation.rb
@@ -207,7 +207,7 @@ def create_with_history(user)
def preconditions_ok?(good_members = [])
raise OSM::APITooManyRelationMembersError.new(id, members.length, Settings.max_number_of_relation_members) if members.length > Settings.max_number_of_relation_members
- # These are hastables that store an id in the index of all
+ # These are hashtables that store an id in the index of all
# the nodes/way/relations that have already been added.
# If the member is valid and visible then we add it to the
# relevant hash table, with the value true as a cache.
diff --git a/app/models/user.rb b/app/models/user.rb
index 520450c7ef5..4386a794409 100644
--- a/app/models/user.rb
+++ b/app/models/user.rb
@@ -124,8 +124,8 @@ class User < ApplicationRecord
validates :preferred_editor, :inclusion => Editors::ALL_EDITORS, :allow_nil => true
validates :auth_uid, :unless => proc { |u| u.auth_provider.nil? },
:uniqueness => { :scope => :auth_provider }
- validates :avatar, :if => proc { |u| u.attachment_changes["avatar"] },
- :image => true
+ validates :avatar, :image => { :if => proc { |u| u.attachment_changes["avatar"] } },
+ :processable_file => true
validates :description, :length => 0..65536
validates_email_format_of :email, :if => proc { |u| u.email_changed? }
diff --git a/app/notifiers/gpx_import_success_notifier.rb b/app/notifiers/gpx_import_success_notifier.rb
index b0beda515be..1bd80e89c11 100644
--- a/app/notifiers/gpx_import_success_notifier.rb
+++ b/app/notifiers/gpx_import_success_notifier.rb
@@ -8,6 +8,6 @@ class GpxImportSuccessNotifier < ApplicationNotifier
deliver_by :email do |config|
config.mailer = "UserMailer"
config.method = "gpx_success"
- config.if = -> { recipient.notification_preferences.gpx_import_success.include?("email") }
+ config.if = -> { record && recipient.notification_preferences.gpx_import_success.include?("email") }
end
end
diff --git a/app/notifiers/new_follower_notifier.rb b/app/notifiers/new_follower_notifier.rb
index 12c9e0a691b..139925250ca 100644
--- a/app/notifiers/new_follower_notifier.rb
+++ b/app/notifiers/new_follower_notifier.rb
@@ -8,6 +8,6 @@ class NewFollowerNotifier < ApplicationNotifier
deliver_by :email do |config|
config.mailer = "UserMailer"
config.method = "follow_notification"
- config.if = -> { recipient.notification_preferences.new_follower.include?("email") }
+ config.if = -> { record && recipient.notification_preferences.new_follower.include?("email") }
end
end
diff --git a/app/views/api/ways/_way.json.jbuilder b/app/views/api/ways/_way.json.jbuilder
index 27813f769f4..8b3786cfc04 100644
--- a/app/views/api/ways/_way.json.jbuilder
+++ b/app/views/api/ways/_way.json.jbuilder
@@ -10,6 +10,6 @@ json.uid way.changeset.user_id
json.visible way.visible unless way.visible
-json.nodes way.nodes.ids unless way.nodes.ids.empty?
+json.nodes way.nds unless way.nds.empty?
json.tags way.tags unless way.tags.empty?
diff --git a/app/views/api/ways/_way.xml.builder b/app/views/api/ways/_way.xml.builder
index 73e4cf9a043..c4a560b5b6e 100644
--- a/app/views/api/ways/_way.xml.builder
+++ b/app/views/api/ways/_way.xml.builder
@@ -11,8 +11,8 @@ attrs = {
}
xml.way(attrs) do |w|
- way.nodes.each do |n|
- w.nd(:ref => n.id)
+ way.nds.each do |n|
+ w.nd(:ref => n)
end
way.tags.each do |k, v|
diff --git a/app/views/layouts/_header.html.erb b/app/views/layouts/_header.html.erb
index 07f7388c589..8e2c277f20a 100644
--- a/app/views/layouts/_header.html.erb
+++ b/app/views/layouts/_header.html.erb
@@ -49,7 +49,7 @@
<% if current_user && current_user.id %>
|