From: Tom Hughes Date: Sun, 2 Feb 2025 12:44:27 +0000 (+0000) Subject: Merge remote-tracking branch 'upstream/pull/5064' X-Git-Tag: live X-Git-Url: https://git.openstreetmap.org./rails.git/commitdiff_plain/8f7f02b025f59db466fdb22ea3686cb6c5adc400?hp=147b3f05d83e79d270a26ee03aa2ef7b231945a1 Merge remote-tracking branch 'upstream/pull/5064' --- diff --git a/.erb-lint.yml b/.erb_lint.yml similarity index 93% rename from .erb-lint.yml rename to .erb_lint.yml index 0c729d38f..dc9b39c5c 100644 --- a/.erb-lint.yml +++ b/.erb_lint.yml @@ -20,8 +20,6 @@ linters: Enabled: false Naming/FileName: Enabled: false - Rails/OutputSafety: - Enabled: false Style/FrozenStringLiteralComment: Enabled: false SelfClosingTag: diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md new file mode 100644 index 000000000..b5f38b76b --- /dev/null +++ b/.github/pull_request_template.md @@ -0,0 +1,13 @@ + + +### Description + + +### How has this been tested? + diff --git a/.github/workflows/danger.yml b/.github/workflows/danger.yml new file mode 100644 index 000000000..6da5e7164 --- /dev/null +++ b/.github/workflows/danger.yml @@ -0,0 +1,35 @@ +name: Danger + +on: + pull_request_target: + types: [opened, synchronize] + +permissions: + pull-requests: write + statuses: write + +jobs: + danger: + runs-on: ubuntu-22.04 + steps: + - name: Check out code + uses: actions/checkout@v4 + with: + fetch-depth: 0 + - name: Setup ruby + uses: ruby/setup-ruby@v1 + with: + ruby-version: 3.1 + rubygems: 3.4.10 + bundler-cache: true + - name: Create base branch + run: | + git fetch ${{ github.event.pull_request.base.repo.clone_url }} ${{ github.event.pull_request.base.sha }}:danger_base + - name: Create head branch + run: | + git fetch ${{ github.event.pull_request.head.repo.clone_url }} ${{ github.event.pull_request.head.sha }}:danger_head + - name: Danger + env: + DANGER_GITHUB_BEARER_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + bundle exec danger --verbose diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index 9fc132014..06f6ff841 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -7,7 +7,7 @@ concurrency: cancel-in-progress: true env: os: ubuntu-22.04 - ruby: '3.0' + ruby: '3.1' jobs: rubocop: name: RuboCop @@ -36,7 +36,7 @@ jobs: rubygems: 3.4.10 bundler-cache: true - name: Run erblint - run: bundle exec erblint . + run: bundle exec erb_lint . eslint: name: ESLint runs-on: ubuntu-22.04 diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 46ab75482..bfeb3d33a 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -10,8 +10,8 @@ jobs: name: Ubuntu ${{ matrix.ubuntu }}, Ruby ${{ matrix.ruby }} strategy: matrix: - ubuntu: [20.04, 22.04] - ruby: ['3.0', '3.1', '3.2', '3.3'] + ubuntu: [22.04, 24.04] + ruby: ['3.1', '3.2', '3.3'] runs-on: ubuntu-${{ matrix.ubuntu }} env: RAILS_ENV: test @@ -58,10 +58,14 @@ jobs: run: bundle exec bin/yarn install - name: Compile assets run: bundle exec rails assets:precompile + - name: Create tmp/pids directory + run: mkdir -p tmp/pids - name: Run tests run: bundle exec rails test:all + - name: Run javascript tests + run: bundle exec teaspoon - name: Report completion to Coveralls - uses: coverallsapp/github-action@v2.3.0 + uses: coverallsapp/github-action@v2.3.6 with: github-token: ${{ secrets.github_token }} flag-name: ubuntu-${{ matrix.ubuntu }}-ruby-${{ matrix.ruby }} @@ -73,7 +77,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Report completion to Coveralls - uses: coverallsapp/github-action@v2.3.0 + uses: coverallsapp/github-action@v2.3.6 with: github-token: ${{ secrets.github_token }} parallel-finished: true diff --git a/.rubocop.yml b/.rubocop.yml index c0f0c1fa0..68a7ca003 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -7,9 +7,10 @@ require: - rubocop-performance - rubocop-rails - rubocop-rake + - ./.rubocop/specific_action_names.rb AllCops: - TargetRubyVersion: 3.0 + TargetRubyVersion: 3.1 NewCops: enable Exclude: - 'vendor/**/*' @@ -70,7 +71,7 @@ Rails/ReflectionClassName: Rails/SkipsModelValidations: Exclude: - 'db/migrate/*.rb' - - 'app/controllers/users_controller.rb' + - 'app/controllers/users/lists_controller.rb' Style/Documentation: Enabled: false @@ -110,3 +111,41 @@ Style/StringLiterals: Style/SymbolArray: EnforcedStyle: brackets + +Rails/SpecificActionNames: + Description: Use only specific action names. + Enabled: true + ActionNames: + - index + - show + - new + - edit + - create + - update + - destroy + Include: + - app/controllers/**/*.rb + Exclude: + # This is a todo list, but is currently too long for `rubocop --auto-gen-config` + - 'app/controllers/api/changeset_comments_controller.rb' + - 'app/controllers/api/changesets_controller.rb' + - 'app/controllers/api/nodes_controller.rb' + - 'app/controllers/api/notes_controller.rb' + - 'app/controllers/api/old_elements_controller.rb' + - 'app/controllers/api/relations_controller.rb' + - 'app/controllers/api/user_preferences_controller.rb' + - 'app/controllers/api/users_controller.rb' + - 'app/controllers/api/ways_controller.rb' + - 'app/controllers/browse_controller.rb' + - 'app/controllers/changesets_controller.rb' + - 'app/controllers/confirmations_controller.rb' + - 'app/controllers/diary_comments_controller.rb' + - 'app/controllers/diary_entries_controller.rb' + - 'app/controllers/directions_controller.rb' + - 'app/controllers/errors_controller.rb' + - 'app/controllers/export_controller.rb' + - 'app/controllers/geocoder_controller.rb' + - 'app/controllers/issues_controller.rb' + - 'app/controllers/site_controller.rb' + - 'app/controllers/traces_controller.rb' + - 'app/controllers/users_controller.rb' diff --git a/.rubocop/specific_action_names.rb b/.rubocop/specific_action_names.rb new file mode 100644 index 000000000..e78790bac --- /dev/null +++ b/.rubocop/specific_action_names.rb @@ -0,0 +1,69 @@ +# frozen_string_literal: true + +module RuboCop + module Cop + module Rails + # Use only specific action names. + # + # It is good practice to separate controller classes rather than adding more actions as needed. + # By default, the 7 CRUD action names are specified that are generated by the Rails scaffold. + # + # @example + # # bad + # class UsersController < ApplicationController + # def articles + # end + # end + # + # # good + # class UserArticlesController < ApplicationController + # def index + # end + # end + class SpecificActionNames < Base + include VisibilityHelp + + MSG = "Use only specific action names." + + # @param node [RuboCop::AST::DefNode] + # @return [void] + def on_def(node) + return unless bad?(node) + + add_offense( + node.location.name, + :message => format( + "Use only specific action names (%s).", + :action_names => configured_action_names.join(", ") + ) + ) + end + + private + + # @param node [RuboCop::AST::DefNode] + # @return [Boolean] + def action?(node) + node_visibility(node) == :public + end + + # @param node [RuboCop::AST::DefNode] + # @return [Boolean] + def bad?(node) + action?(node) && !configured_action_name?(node) + end + + # @param node [RuboCop::AST::DefNode] + # @return [Boolean] + def configured_action_name?(node) + configured_action_names.include?(node.method_name.to_s) + end + + # @return [Array] + def configured_action_names + cop_config["ActionNames"] + end + end + end + end +end diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml index dc1092360..3d7cea500 100644 --- a/.rubocop_todo.yml +++ b/.rubocop_todo.yml @@ -20,12 +20,16 @@ require: FactoryBot/ExcessiveCreateList: Exclude: - 'test/controllers/api/changeset_comments_controller_test.rb' - - 'test/controllers/api/messages_controller_test.rb' + - 'test/controllers/api/messages/inboxes_controller_test.rb' + - 'test/controllers/api/messages/outboxes_controller_test.rb' - 'test/controllers/changesets_controller_test.rb' - 'test/controllers/diary_entries_controller_test.rb' - 'test/controllers/notes_controller_test.rb' - 'test/controllers/traces_controller_test.rb' - 'test/controllers/user_blocks_controller_test.rb' + - 'test/controllers/users/issued_blocks_controller_test.rb' + - 'test/controllers/users/received_blocks_controller_test.rb' + - 'test/system/users_test.rb' # Offense count: 635 # This cop supports safe autocorrection (--autocorrect). @@ -48,7 +52,6 @@ Lint/AssignmentInCondition: - 'app/helpers/application_helper.rb' - 'app/helpers/browse_tags_helper.rb' - 'app/mailers/user_mailer.rb' - - 'app/models/client_application.rb' - 'lib/nominatim.rb' - 'lib/osm.rb' - 'script/deliver-message' @@ -103,20 +106,6 @@ Minitest/EmptyLineBeforeAssertionMethods: Minitest/MultipleAssertions: Max: 60 -# Offense count: 10 -# This cop supports unsafe autocorrection (--autocorrect-all). -Rails/ActionControllerFlashBeforeRender: - Exclude: - - 'app/controllers/application_controller.rb' - - 'app/controllers/confirmations_controller.rb' - - 'app/controllers/friendships_controller.rb' - - 'app/controllers/issue_comments_controller.rb' - - 'app/controllers/messages_controller.rb' - - 'app/controllers/passwords_controller.rb' - - 'app/controllers/traces_controller.rb' - - 'app/controllers/user_blocks_controller.rb' - - 'app/controllers/users_controller.rb' - # Offense count: 2 # Configuration parameters: Include. # Include: app/models/**/*.rb @@ -139,7 +128,6 @@ Rails/InverseOf: Exclude: - 'app/models/changeset.rb' - 'app/models/diary_entry.rb' - - 'app/models/friendship.rb' - 'app/models/issue.rb' - 'app/models/message.rb' - 'app/models/note.rb' @@ -168,6 +156,7 @@ Rails/NotNullColumn: Rails/OutputSafety: Exclude: - 'app/helpers/application_helper.rb' + - 'app/helpers/user_mailer_helper.rb' - 'lib/rich_text.rb' - 'test/helpers/application_helper_test.rb' diff --git a/CONFIGURE.md b/CONFIGURE.md index dcc8ae2ac..29d1daad8 100644 --- a/CONFIGURE.md +++ b/CONFIGURE.md @@ -76,7 +76,7 @@ For iD, do the following: An example excerpt from settings.local.yml: -``` +```yaml # Default editor default_editor: "id" # OAuth 2 Client ID for iD @@ -99,7 +99,7 @@ To allow [Notes](https://wiki.openstreetmap.org/wiki/Notes) and changeset discus An example excerpt from settings.local.yml: -``` +```yaml # OAuth 2 Client ID for the web site oauth_application: "SGm8QJ6tmoPXEaUPIZzLUmm1iujltYZVWCp9hvGsqXg" # OAuth 2 Client Secret for the web site diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 383e793b3..aea8b30b9 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,6 +1,15 @@ +# Contributing + * https://www.ruby-lang.org/ - The homepage of Ruby which has more links and some great tutorials. * https://rubyonrails.org/ - The homepage of Rails, also has links and tutorials. +## Assigning Issues + +We don't assign issues to individual contributors. You are welcome to work on any +issue, and there's no need to ask first. + +For more details see [our FAQ](FAQ.md)] + ## Coding style We use [Rubocop](https://github.com/rubocop-hq/rubocop) (for ruby files) @@ -11,7 +20,7 @@ development to check that your code matches our guidelines: ``` bundle exec rubocop bundle exec rails eslint -bundle exec erblint . +bundle exec erb_lint . ``` You can also install hooks to have git run checks automatically when @@ -43,6 +52,12 @@ You can run the existing test suite with: bundle exec rails test:all ``` +You can run javascript tests with: + +``` +bundle exec teaspoon +``` + You can view test coverage statistics by browsing the `coverage` directory. The tests are automatically run on Pull Requests and other commits via github @@ -77,15 +92,6 @@ only submit changes to the `en.yml` file. The other files are updated via [Translatewiki](https://translatewiki.net/wiki/Translating:OpenStreetMap) and should not be included in your pull request. -### Nominatim prefixes - -I18n keys under the `geocoder.search_osm_nominatim` keyspace are managed by the -Nominatim maintainers. From time to time they run stats over the Nominatim -database, and update the list of available keys manually. - -Adding or removing keys to this list is therefore discouraged, but contributions -to the descriptive texts are welcome. - ### Copyright attribution The list of attributions on the /copyright page is managed by the [OSMF Licensing diff --git a/DOCKER.md b/DOCKER.md index f6caa6988..b93bf6d50 100644 --- a/DOCKER.md +++ b/DOCKER.md @@ -24,27 +24,37 @@ Use [Docker Engine](https://docs.docker.com/engine/install/ubuntu/) with the [do The first step is to fork/clone the repo to your local machine: - git clone https://github.com/openstreetmap/openstreetmap-website.git +``` +git clone https://github.com/openstreetmap/openstreetmap-website.git +``` Now change working directory to the `openstreetmap-website`: - cd openstreetmap-website +``` +cd openstreetmap-website +``` ## Initial Setup ### Storage - cp config/example.storage.yml config/storage.yml +``` +cp config/example.storage.yml config/storage.yml +``` ### Database - cp config/docker.database.yml config/database.yml +``` +cp config/docker.database.yml config/database.yml +``` ## Prepare local settings file This is a workaround. [See issues/2185 for details](https://github.com/openstreetmap/openstreetmap-website/issues/2185#issuecomment-508676026). - touch config/settings.local.yml +``` +touch config/settings.local.yml +``` **Windows users:** `touch` is not an availible command in Windows so just create a `settings.local.yml` file in the `config` directory, or if you have WSL you can run `wsl touch config/settings.local.yml`. @@ -52,13 +62,17 @@ This is a workaround. [See issues/2185 for details](https://github.com/openstree To build local Docker images run from the root directory of the repository: - docker compose build +``` +docker compose build +``` If this is your first time running or you have removed cache this will take some time to complete. Once the Docker images have finished building you can launch the images as containers. To launch the app run: - docker compose up -d +``` +docker compose up -d +``` This will launch one Docker container for each 'service' specified in `docker-compose.yml` and run them in the background. There are two options for inspecting the logs of these running containers: @@ -69,17 +83,29 @@ This will launch one Docker container for each 'service' specified in `docker-co Run the Rails database migrations: - docker compose run --rm web bundle exec rails db:migrate +``` +docker compose run --rm web bundle exec rails db:migrate +``` ### Tests Prepare the test database: - docker compose run --rm web bundle exec rails db:test:prepare +``` +docker compose run --rm web bundle exec rails db:test:prepare +``` Run the test suite: - docker compose run --rm web bundle exec rails test:all +``` +docker compose run --rm web bundle exec rails test:all +``` + +If you encounter errors about missing assets, precompile the assets: + +``` +docker compose run --rm web bundle exec rake assets:precompile +``` ### Loading an OSM extract @@ -87,31 +113,37 @@ This installation comes with no geographic data loaded. You can either create ne For example, let's download the District of Columbia from Geofabrik or [any other region](https://download.geofabrik.de): - wget https://download.geofabrik.de/north-america/us/district-of-columbia-latest.osm.pbf +``` +wget https://download.geofabrik.de/north-america/us/district-of-columbia-latest.osm.pbf +``` You can now use Docker to load this extract into your local Docker-based OSM instance: - docker compose run --rm web osmosis \ - -verbose \ - --read-pbf district-of-columbia-latest.osm.pbf \ - --log-progress \ - --write-apidb \ - host="db" \ - database="openstreetmap" \ - user="openstreetmap" \ - validateSchemaVersion="no" +``` +docker compose run --rm web osmosis \ + -verbose \ + --read-pbf district-of-columbia-latest.osm.pbf \ + --log-progress \ + --write-apidb \ + host="db" \ + database="openstreetmap" \ + user="openstreetmap" \ + validateSchemaVersion="no" +``` **Windows users:** Powershell uses `` ` `` and CMD uses `^` at the end of each line, e.g.: - docker compose run --rm web osmosis ` - -verbose ` - --read-pbf district-of-columbia-latest.osm.pbf ` - --log-progress ` - --write-apidb ` - host="db" ` - database="openstreetmap" ` - user="openstreetmap" ` - validateSchemaVersion="no" +``` +docker compose run --rm web osmosis ` + -verbose ` + --read-pbf district-of-columbia-latest.osm.pbf ` + --log-progress ` + --write-apidb ` + host="db" ` + database="openstreetmap" ` + user="openstreetmap" ` + validateSchemaVersion="no" +``` Once you have data loaded for Washington, DC you should be able to navigate to [`http://localhost:3000/#map=12/38.8938/-77.0146`](http://localhost:3000/#map=12/38.8938/-77.0146) to begin working with your local instance. @@ -123,12 +155,18 @@ See [`CONFIGURE.md`](CONFIGURE.md) for information on how to manage users and en If you want to get into a web container and run specific commands you can fire up a throwaway container to run bash in via: - docker compose run --rm web bash +``` +docker compose run --rm web bash +``` Alternatively, if you want to use the already-running `web` container then you can `exec` into it via: - docker compose exec web bash +``` +docker compose exec web bash +``` Similarly, if you want to `exec` in the db container use: - docker compose exec db bash +``` +docker compose exec db bash +``` diff --git a/Dangerfile b/Dangerfile new file mode 100644 index 000000000..6e2aeced8 --- /dev/null +++ b/Dangerfile @@ -0,0 +1,42 @@ +# Get PR number +pr_number = github.pr_json["number"] + +# Report if number of changed lines is > 500 +if git.lines_of_code > 500 + warn("Number of updated lines of code is too large to be in one PR. Perhaps it should be separated into two or more?") + auto_label.set(pr_number, "big-pr", "FBCA04") +else + auto_label.remove("big-pr") +end + +# Get list of translation files (except en.yml) which are modified +modified_yml_files = git.modified_files.select do |file| + file.start_with?("config/locales") && File.extname(file) == ".yml" && File.basename(file) != "en.yml" +end + +# Report if some translation file (except en.yml) is modified +if modified_yml_files.empty? + auto_label.remove("inappropriate-translations") +else + modified_files_str = modified_yml_files.map { |file| "`#{file}`" }.join(", ") + warn("The following YAML files other than `en.yml` have been modified: #{modified_files_str}. Only `en.yml` is allowed to be changed. Translations are updated via Translatewiki, see CONTRIBUTING.md.") + auto_label.set(pr_number, "inappropriate-translations", "B60205") +end + +# Report if there are merge-commits in PR +if git.commits.any? { |c| c.parents.count > 1 } + warn("Merge commits are found in PR. Please rebase to get rid of the merge commits in this PR, see CONTRIBUTING.md.") + auto_label.set(pr_number, "merge-commits", "D93F0B") +else + auto_label.remove("merge-commits") +end + +# Check if Gemfile is modified but Gemfile.lock is not +gemfile_modified = git.modified_files.include?("Gemfile") +gemfile_lock_modified = git.modified_files.include?("Gemfile.lock") +if gemfile_modified && !gemfile_lock_modified + warn("Gemfile was updated, but Gemfile.lock wasn't updated. Usually, when Gemfile is updated, you should run `bundle install` to update Gemfile.lock.") + auto_label.set(pr_number, "gemfile-lock-outdated", "F9D0C4") +else + auto_label.remove("gemfile-lock-outdated") +end diff --git a/Dockerfile b/Dockerfile index 1c5638275..dae25be3f 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,51 +1,39 @@ -FROM ubuntu:22.04 +FROM debian:bookworm ENV DEBIAN_FRONTEND=noninteractive # Install system packages then clean up to minimize image size RUN apt-get update \ - && apt-get install --no-install-recommends -y \ - build-essential \ - curl \ - default-jre-headless \ - file \ - git-core \ - gpg-agent \ - libarchive-dev \ - libffi-dev \ - libgd-dev \ - libpq-dev \ - libsasl2-dev \ - libvips-dev \ - libxml2-dev \ - libxslt1-dev \ - libyaml-dev \ - locales \ - postgresql-client \ - ruby \ - ruby-dev \ - ruby-bundler \ - software-properties-common \ - tzdata \ - unzip - -# Install Node.js 18 and npm -RUN curl -fsSL https://deb.nodesource.com/setup_18.x | bash - \ - && apt-get install -y nodejs + && apt-get install --no-install-recommends -y \ + build-essential \ + curl \ + default-jre-headless \ + file \ + git-core \ + gpg-agent \ + libarchive-dev \ + libffi-dev \ + libgd-dev \ + libpq-dev \ + libsasl2-dev \ + libvips-dev \ + libxml2-dev \ + libxslt1-dev \ + libyaml-dev \ + locales \ + postgresql-client \ + ruby-dev \ + ruby-bundler \ + tzdata \ + unzip \ + nodejs \ + npm \ + osmosis \ + ca-certificates \ + firefox-esr # Install yarn globally -RUN npm install --global yarn \ - # We can't use snap packages for firefox inside a container, so we need to get firefox+geckodriver elsewhere - && add-apt-repository -y ppa:mozillateam/ppa \ - && echo "Package: *\nPin: release o=LP-PPA-mozillateam\nPin-Priority: 1001" > /etc/apt/preferences.d/mozilla-firefox \ - && apt-get install --no-install-recommends -y \ - firefox-geckodriver \ - && apt-get clean \ - && rm -rf /var/lib/apt/lists/* - -# Install compatible Osmosis to help users import sample data in a new instance -RUN curl -OL https://github.com/openstreetmap/osmosis/releases/download/0.47.2/osmosis-0.47.2.tgz \ - && tar -C /usr/local -xzf osmosis-0.47.2.tgz +RUN npm install --global yarn ENV DEBIAN_FRONTEND=dialog diff --git a/FAQ.md b/FAQ.md index d4ac1fc9f..e53c8dddb 100644 --- a/FAQ.md +++ b/FAQ.md @@ -1,3 +1,5 @@ +# Frequently Asked Questions + ## How do I create a banner to promote my OpenStreetMap event? We occasionally display banner images on the main page of [openstreetmap.org](https://www.openstreetmap.org/) to @@ -23,3 +25,13 @@ drive. This is a great way to reach a lot of people! See [PR #1296](https://github.com/openstreetmap/openstreetmap-website/pull/1296) as an example. + +## Why don't you assign issues? + +We don't assign issues to volunteers for several reasons. The main reasons are that it discourages other volunteers from working on the issue, and the process turns into an unproductive administrative overhead for our team. + +There's no need to ask for an issue to be assigned before anyone starts working on it. Everyone is welcome to work on any issue at any time. + +In our experience, most people who ask for an issue to be assigned to them never create a pull request. So we would need to keep track of the assigned issues, and remember to unassign them a week or two into the future, when it is likely that they will not be making a PR. Assigned developers might feel bad if they perceive that we're unhappy with their progress, further discouraging them from contributing. Or we will get drawn into discussions about needing more time, or re-assigning them again, or so on. So it is best not to assign in the first place. + +The risk that two people are both genuinely working on the same task in the same hour or two is vanishingly remote, and doesn't outweigh the downsides described above. A better approach is to encourage people to simply work on the task and create a pull request, at which point everyone knows that they are actually working on the issue and not just planning/hoping/wishing to do so. diff --git a/Gemfile b/Gemfile index 27f295eb6..2765b1ae7 100644 --- a/Gemfile +++ b/Gemfile @@ -1,7 +1,7 @@ source "https://rubygems.org" # Require rails -gem "rails", "~> 7.1.0" +gem "rails", "~> 7.2.0" gem "turbo-rails" # Require json for multi_json @@ -55,6 +55,7 @@ gem "bootstrap_form", "~> 5.0" gem "cancancan" gem "config" gem "delayed_job_active_record" +gem "dry-schema", "< 1.14.0" # see https://github.com/openstreetmap/openstreetmap-website/issues/5482 gem "dry-validation" gem "frozen_record" gem "http_accept_language", "~> 2.1.1" @@ -129,9 +130,6 @@ gem "ffi-libarchive" gem "gd2-ffij", ">= 0.4.0" gem "marcel" -# Used for browser detection -gem "browser", "< 6" # for ruby 3.0 support - # Used for S3 object storage gem "aws-sdk-s3" @@ -141,13 +139,15 @@ gem "image_processing" # Used to validate widths gem "unicode-display_width" -# Keep ruby 3.0 compatibility -gem "multi_xml", "~> 0.6.0" +# Lock some modules to old versions for ruby 3.1 support +gem "zeitwerk", "< 2.7" # Gems useful for development group :development do gem "better_errors" gem "binding_of_caller" + gem "danger" + gem "danger-auto_label" gem "debug_inspector" gem "i18n-tasks" gem "listen" @@ -181,6 +181,8 @@ end group :development, :test do gem "annotate" + gem "teaspoon" + gem "teaspoon-mocha", "~> 2.3.3" # See https://guides.rubyonrails.org/debugging_rails_applications.html#debugging-with-the-debug-gem gem "debug", :require => "debug/prelude" diff --git a/Gemfile.lock b/Gemfile.lock index afc6463b3..761beefa3 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -3,86 +3,83 @@ GEM specs: aasm (5.5.0) concurrent-ruby (~> 1.0) - actioncable (7.1.4) - actionpack (= 7.1.4) - activesupport (= 7.1.4) + actioncable (7.2.2.1) + actionpack (= 7.2.2.1) + activesupport (= 7.2.2.1) nio4r (~> 2.0) websocket-driver (>= 0.6.1) zeitwerk (~> 2.6) - actionmailbox (7.1.4) - actionpack (= 7.1.4) - activejob (= 7.1.4) - activerecord (= 7.1.4) - activestorage (= 7.1.4) - activesupport (= 7.1.4) - mail (>= 2.7.1) - net-imap - net-pop - net-smtp - actionmailer (7.1.4) - actionpack (= 7.1.4) - actionview (= 7.1.4) - activejob (= 7.1.4) - activesupport (= 7.1.4) - mail (~> 2.5, >= 2.5.4) - net-imap - net-pop - net-smtp + actionmailbox (7.2.2.1) + actionpack (= 7.2.2.1) + activejob (= 7.2.2.1) + activerecord (= 7.2.2.1) + activestorage (= 7.2.2.1) + activesupport (= 7.2.2.1) + mail (>= 2.8.0) + actionmailer (7.2.2.1) + actionpack (= 7.2.2.1) + actionview (= 7.2.2.1) + activejob (= 7.2.2.1) + activesupport (= 7.2.2.1) + mail (>= 2.8.0) rails-dom-testing (~> 2.2) - actionpack (7.1.4) - actionview (= 7.1.4) - activesupport (= 7.1.4) + actionpack (7.2.2.1) + actionview (= 7.2.2.1) + activesupport (= 7.2.2.1) nokogiri (>= 1.8.5) racc - rack (>= 2.2.4) + rack (>= 2.2.4, < 3.2) rack-session (>= 1.0.1) rack-test (>= 0.6.3) rails-dom-testing (~> 2.2) rails-html-sanitizer (~> 1.6) + useragent (~> 0.16) actionpack-page_caching (1.2.4) actionpack (>= 4.0.0) - actiontext (7.1.4) - actionpack (= 7.1.4) - activerecord (= 7.1.4) - activestorage (= 7.1.4) - activesupport (= 7.1.4) + actiontext (7.2.2.1) + actionpack (= 7.2.2.1) + activerecord (= 7.2.2.1) + activestorage (= 7.2.2.1) + activesupport (= 7.2.2.1) globalid (>= 0.6.0) nokogiri (>= 1.8.5) - actionview (7.1.4) - activesupport (= 7.1.4) + actionview (7.2.2.1) + activesupport (= 7.2.2.1) builder (~> 3.1) erubi (~> 1.11) rails-dom-testing (~> 2.2) rails-html-sanitizer (~> 1.6) active_record_union (1.3.0) activerecord (>= 4.0) - activejob (7.1.4) - activesupport (= 7.1.4) + activejob (7.2.2.1) + activesupport (= 7.2.2.1) globalid (>= 0.3.6) - activemodel (7.1.4) - activesupport (= 7.1.4) - activerecord (7.1.4) - activemodel (= 7.1.4) - activesupport (= 7.1.4) + activemodel (7.2.2.1) + activesupport (= 7.2.2.1) + activerecord (7.2.2.1) + activemodel (= 7.2.2.1) + activesupport (= 7.2.2.1) timeout (>= 0.4.0) - activerecord-import (1.8.0) + activerecord-import (2.0.0) activerecord (>= 4.2) - activestorage (7.1.4) - actionpack (= 7.1.4) - activejob (= 7.1.4) - activerecord (= 7.1.4) - activesupport (= 7.1.4) + activestorage (7.2.2.1) + actionpack (= 7.2.2.1) + activejob (= 7.2.2.1) + activerecord (= 7.2.2.1) + activesupport (= 7.2.2.1) marcel (~> 1.0) - activesupport (7.1.4) + activesupport (7.2.2.1) base64 + benchmark (>= 0.3) bigdecimal - concurrent-ruby (~> 1.0, >= 1.0.2) + concurrent-ruby (~> 1.0, >= 1.3.1) connection_pool (>= 2.2.5) drb i18n (>= 1.6, < 2) + logger (>= 1.4.2) minitest (>= 5.1) - mutex_m - tzinfo (~> 2.0) + securerandom (>= 0.3) + tzinfo (~> 2.0, >= 2.0.5) addressable (2.8.7) public_suffix (>= 2.0.2, < 7.0) annotate (3.2.0) @@ -95,22 +92,23 @@ GEM autoprefixer-rails (10.4.19.0) execjs (~> 2) aws-eventstream (1.3.0) - aws-partitions (1.970.0) - aws-sdk-core (3.202.2) + aws-partitions (1.1043.0) + aws-sdk-core (3.217.0) aws-eventstream (~> 1, >= 1.3.0) - aws-partitions (~> 1, >= 1.651.0) + aws-partitions (~> 1, >= 1.992.0) aws-sigv4 (~> 1.9) jmespath (~> 1, >= 1.6.1) - aws-sdk-kms (1.88.0) - aws-sdk-core (~> 3, >= 3.201.0) + aws-sdk-kms (1.97.0) + aws-sdk-core (~> 3, >= 3.216.0) aws-sigv4 (~> 1.5) - aws-sdk-s3 (1.159.0) - aws-sdk-core (~> 3, >= 3.201.0) + aws-sdk-s3 (1.178.0) + aws-sdk-core (~> 3, >= 3.216.0) aws-sdk-kms (~> 1) aws-sigv4 (~> 1.5) - aws-sigv4 (1.9.1) + aws-sigv4 (1.11.0) aws-eventstream (~> 1, >= 1.0.2) base64 (0.2.0) + benchmark (0.4.0) better_errors (2.10.1) erubi (>= 1.0.0) rack (>= 0.9.0) @@ -122,7 +120,7 @@ GEM erubi (~> 1.4) parser (>= 2.4) smart_properties - bigdecimal (3.1.8) + bigdecimal (3.1.9) binding_of_caller (1.0.1) debug_inspector (>= 1.2.0) bootsnap (1.18.4) @@ -133,16 +131,15 @@ GEM bootstrap_form (5.4.0) actionpack (>= 6.1) activemodel (>= 6.1) - brakeman (6.2.1) + brakeman (7.0.0) racc brotli (0.6.0) - browser (5.3.1) builder (3.3.0) bzip2-ffi (1.1.1) ffi (~> 1.0) cancancan (3.6.1) - canonical-rails (0.2.15) - actionview (>= 4.1, <= 7.2) + canonical-rails (0.2.17) + actionview (>= 4.1, < 8.1) capybara (3.40.0) addressable matrix @@ -154,15 +151,42 @@ GEM xpath (~> 3.2) childprocess (5.1.0) logger (~> 1.5) - concurrent-ruby (1.3.4) - config (5.5.1) + claide (1.1.0) + claide-plugins (0.9.2) + cork + nap + open4 (~> 1.3) + colored2 (3.1.2) + concurrent-ruby (1.3.5) + config (5.5.2) deep_merge (~> 1.2, >= 1.2.1) - connection_pool (2.4.1) + ostruct + connection_pool (2.5.0) + cork (0.3.0) + colored2 (~> 3.1) crack (1.0.0) bigdecimal rexml crass (1.0.6) dalli (3.2.8) + danger (9.5.1) + base64 (~> 0.2) + claide (~> 1.0) + claide-plugins (>= 0.9.2) + colored2 (~> 3.1) + cork (~> 0.1) + faraday (>= 0.9.0, < 3.0) + faraday-http-cache (~> 2.0) + git (~> 1.13) + kramdown (~> 2.3) + kramdown-parser-gfm (~> 1.0) + octokit (>= 4.0) + pstore (~> 0.1) + terminal-table (>= 1, < 4) + danger-auto_label (1.3.1) + 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) @@ -171,37 +195,39 @@ GEM sprockets (> 3.0) sprockets-rails tilt - date (3.3.4) - debug (1.9.2) + date (3.4.1) + debug (1.10.0) irb (~> 1.10) reline (>= 0.3.8) debug_inspector (1.2.0) deep_merge (1.2.2) - delayed_job (4.1.12) - activesupport (>= 3.0, < 8.0) - delayed_job_active_record (4.1.10) - activerecord (>= 3.0, < 8.0) + delayed_job (4.1.13) + activesupport (>= 3.0, < 9.0) + delayed_job_active_record (4.1.11) + activerecord (>= 3.0, < 9.0) delayed_job (>= 3.0, < 5) docile (1.4.1) - doorkeeper (5.7.1) + doorkeeper (5.8.1) railties (>= 5) doorkeeper-i18n (5.2.7) doorkeeper (>= 5.2) - doorkeeper-openid_connect (1.8.9) - doorkeeper (>= 5.5, < 5.8) + doorkeeper-openid_connect (1.8.10) + doorkeeper (>= 5.5, < 5.9) jwt (>= 2.5) drb (2.2.1) - dry-configurable (1.2.0) - dry-core (~> 1.0, < 2) + dry-configurable (1.3.0) + dry-core (~> 1.1) zeitwerk (~> 2.6) - dry-core (1.0.1) + dry-core (1.1.0) concurrent-ruby (~> 1.0) + logger zeitwerk (~> 2.6) - dry-inflector (1.1.0) - dry-initializer (3.1.1) - dry-logic (1.5.0) + dry-inflector (1.2.0) + dry-initializer (3.2.0) + dry-logic (1.6.0) + bigdecimal concurrent-ruby (~> 1.0) - dry-core (~> 1.0, < 2) + dry-core (~> 1.1) zeitwerk (~> 2.6) dry-schema (1.13.4) concurrent-ruby (~> 1.0) @@ -211,7 +237,7 @@ GEM dry-logic (>= 1.4, < 2) dry-types (>= 1.7, < 2) zeitwerk (~> 2.6) - dry-types (1.7.2) + dry-types (1.8.1) bigdecimal (~> 3.0) concurrent-ruby (~> 1.0) dry-core (~> 1.0) @@ -224,27 +250,30 @@ GEM dry-initializer (~> 3.0) dry-schema (>= 1.12, < 2) zeitwerk (~> 2.6) - erb_lint (0.6.0) + erb_lint (0.9.0) activesupport better_html (>= 2.0.1) parser (>= 2.7.1.4) rainbow rubocop (>= 1) smart_properties - erubi (1.13.0) - execjs (2.9.1) - exifr (1.4.0) - factory_bot (6.4.6) + erubi (1.13.1) + execjs (2.10.0) + exifr (1.4.1) + factory_bot (6.5.0) activesupport (>= 5.0.0) - factory_bot_rails (6.4.3) - factory_bot (~> 6.4) + factory_bot_rails (6.4.4) + factory_bot (~> 6.5) railties (>= 5.0.0) - faraday (2.11.0) - faraday-net_http (>= 2.0, < 3.4) + faraday (2.12.2) + faraday-net_http (>= 2.0, < 3.5) + json logger - faraday-net_http (3.3.0) - net-http - ffi (1.17.0) + faraday-http-cache (2.5.1) + faraday (>= 0.8) + faraday-net_http (3.4.0) + net-http (>= 0.5.0) + ffi (1.17.1) ffi-compiler (1.3.2) ffi (>= 1.15.5) rake @@ -256,16 +285,19 @@ GEM fspath (3.1.2) gd2-ffij (0.4.0) ffi (>= 1.0.0) + git (1.19.1) + addressable (~> 2.8) + rchardet (~> 1.8) globalid (1.2.1) activesupport (>= 6.1) - google-protobuf (3.25.4) - hashdiff (1.1.1) + google-protobuf (3.25.6) + hashdiff (1.1.2) hashie (5.0.0) - highline (3.1.1) + highline (3.1.2) reline htmlentities (4.3.4) http_accept_language (2.1.1) - i18n (1.14.5) + i18n (1.14.7) concurrent-ruby (~> 1.0) i18n-js (3.9.2) i18n (>= 0.6.6) @@ -279,7 +311,7 @@ GEM rails-i18n rainbow (>= 2.2.2, < 4.0) terminal-table (>= 1.5.1) - image_optim (0.31.3) + image_optim (0.31.4) exifr (~> 1.2, >= 1.2.2) fspath (~> 3.0) image_size (>= 1.5, < 4) @@ -295,11 +327,12 @@ GEM image_size (3.4.0) in_threads (1.6.0) iniparse (1.5.0) - io-console (0.7.2) - irb (1.14.0) + io-console (0.8.0) + irb (1.15.1) + pp (>= 0.6.0) rdoc (>= 4.0.0) reline (>= 0.4.2) - jbuilder (2.12.0) + jbuilder (2.13.0) actionview (>= 5.0.0) activesupport (>= 5.0.0) jmespath (1.6.2) @@ -307,23 +340,25 @@ GEM rails-dom-testing (>= 1, < 3) railties (>= 4.2.0) thor (>= 0.14, < 2.0) - json (2.7.2) - jwt (2.8.2) + json (2.9.1) + jwt (2.10.1) base64 kgio (2.11.4) - kramdown (2.4.0) - rexml - language_server-protocol (3.17.0.3) + kramdown (2.5.1) + rexml (>= 3.3.9) + kramdown-parser-gfm (1.1.0) + kramdown (~> 2.0) + language_server-protocol (3.17.0.4) libv8-node (18.19.0.0) libxml-ruby (5.0.3) listen (3.9.0) rb-fsevent (~> 0.10, >= 0.10.3) rb-inotify (~> 0.9, >= 0.9.10) - logger (1.6.1) + logger (1.6.5) logstasher (2.1.5) activesupport (>= 5.2) request_store - loofah (2.22.0) + loofah (2.24.0) crass (~> 1.0.2) nokogiri (>= 1.12.0) mail (2.8.1) @@ -336,19 +371,20 @@ GEM maxminddb (0.1.22) mini_magick (4.13.2) mini_mime (1.1.5) - mini_portile2 (2.8.7) + mini_portile2 (2.8.8) mini_racer (0.9.0) libv8-node (~> 18.19.0.0) - minitest (5.25.1) + minitest (5.25.4) minitest-focus (1.4.0) minitest (>= 4, < 6) - msgpack (1.7.2) + msgpack (1.7.5) multi_json (1.15.0) - multi_xml (0.6.0) - mutex_m (0.2.0) - net-http (0.4.1) + multi_xml (0.7.1) + bigdecimal (~> 3.1) + nap (1.1.0) + net-http (0.6.0) uri - net-imap (0.4.15) + net-imap (0.5.5) date net-protocol net-pop (0.1.2) @@ -357,8 +393,8 @@ GEM timeout net-smtp (0.5.0) net-protocol - nio4r (2.7.3) - nokogiri (1.16.7) + nio4r (2.7.4) + nokogiri (1.18.2) mini_portile2 (~> 2.8.2) racc (~> 1.4) oauth (1.1.0) @@ -374,6 +410,9 @@ GEM rack (>= 1.2, < 4) snaky_hash (~> 2.0) version_gem (~> 1.1) + octokit (9.2.0) + faraday (>= 1, < 3) + sawyer (~> 0.9) omniauth (2.0.4) hashie (>= 3.4.6) rack (>= 1.6.2, < 3) @@ -384,8 +423,8 @@ GEM omniauth-github (2.0.1) omniauth (~> 2.0) omniauth-oauth2 (~> 1.8) - omniauth-google-oauth2 (1.1.3) - jwt (>= 2.0) + omniauth-google-oauth2 (1.2.1) + jwt (>= 2.9.2) oauth2 (~> 2.0) omniauth (~> 2.0) omniauth-oauth2 (~> 1.8) @@ -409,26 +448,33 @@ GEM omniauth-rails_csrf_protection (1.0.2) actionpack (>= 4.2) omniauth (~> 2.0) + open4 (1.3.4) openstreetmap-deadlock_retry (1.3.1) - overcommit (0.64.0) + ostruct (0.6.1) + overcommit (0.65.0) childprocess (>= 0.6.3, < 6) iniparse (~> 1.4) - rexml (~> 3.2) + rexml (>= 3.3.9) parallel (1.26.3) - parser (3.3.4.2) + parser (3.3.7.0) ast (~> 2.4.1) racc - pg (1.5.7) + pg (1.5.9) popper_js (2.11.8) + pp (0.6.2) + prettyprint + prettyprint (0.2.0) progress (3.6.0) - psych (5.1.2) + pstore (0.1.4) + psych (5.2.3) + date stringio public_suffix (6.0.1) - puma (5.6.8) + puma (5.6.9) nio4r (~> 2.0) quad_tile (1.0.1) racc (1.8.1) - rack (2.2.9) + rack (2.2.10) rack-cors (2.0.2) rack (>= 2.0.0) rack-openid (1.4.2) @@ -439,26 +485,26 @@ GEM rack (~> 2.2, >= 2.2.4) rack-session (1.0.2) rack (< 3) - rack-test (2.1.0) + rack-test (2.2.0) rack (>= 1.3) rack-uri_sanitizer (0.0.2) - rackup (1.0.0) + rackup (1.0.1) rack (< 3) webrick - rails (7.1.4) - actioncable (= 7.1.4) - actionmailbox (= 7.1.4) - actionmailer (= 7.1.4) - actionpack (= 7.1.4) - actiontext (= 7.1.4) - actionview (= 7.1.4) - activejob (= 7.1.4) - activemodel (= 7.1.4) - activerecord (= 7.1.4) - activestorage (= 7.1.4) - activesupport (= 7.1.4) + rails (7.2.2.1) + actioncable (= 7.2.2.1) + actionmailbox (= 7.2.2.1) + actionmailer (= 7.2.2.1) + actionpack (= 7.2.2.1) + actiontext (= 7.2.2.1) + actionview (= 7.2.2.1) + activejob (= 7.2.2.1) + activemodel (= 7.2.2.1) + activerecord (= 7.2.2.1) + activestorage (= 7.2.2.1) + activesupport (= 7.2.2.1) bundler (>= 1.15.0) - railties (= 7.1.4) + railties (= 7.2.2.1) rails-controller-testing (1.0.5) actionpack (>= 5.0.1.rc1) actionview (>= 5.0.1.rc1) @@ -467,19 +513,19 @@ GEM activesupport (>= 5.0.0) minitest nokogiri (>= 1.6) - rails-html-sanitizer (1.6.0) + rails-html-sanitizer (1.6.2) loofah (~> 2.21) - nokogiri (~> 1.14) - rails-i18n (7.0.9) + nokogiri (>= 1.15.7, != 1.16.7, != 1.16.6, != 1.16.5, != 1.16.4, != 1.16.3, != 1.16.2, != 1.16.1, != 1.16.0.rc1, != 1.16.0) + rails-i18n (7.0.10) i18n (>= 0.7, < 2) railties (>= 6.0.0, < 8) rails_param (1.3.1) actionpack (>= 3.2.0) activesupport (>= 3.2.0) - railties (7.1.4) - actionpack (= 7.1.4) - activesupport (= 7.1.4) - irb + railties (7.2.2.1) + actionpack (= 7.2.2.1) + activesupport (= 7.2.2.1) + irb (~> 1.13) rackup (>= 1.0.0) rake (>= 12.2) thor (~> 1.0, >= 1.2.2) @@ -489,31 +535,31 @@ GEM rb-fsevent (0.11.2) rb-inotify (0.11.1) ffi (~> 1.0) - rdoc (6.7.0) + rchardet (1.9.0) + rdoc (6.11.0) psych (>= 4.0.0) - regexp_parser (2.9.2) - reline (0.5.9) + regexp_parser (2.10.0) + reline (0.6.0) io-console (~> 0.5) request_store (1.7.0) rack (>= 1.4) - rexml (3.3.6) - strscan + rexml (3.4.0) rinku (2.0.6) rotp (6.3.0) - rouge (4.3.0) + rouge (4.5.1) rtlcss (0.2.1) mini_racer (>= 0.6.3) - rubocop (1.66.0) + rubocop (1.71.0) json (~> 2.3) language_server-protocol (>= 3.17.0) parallel (~> 1.10) parser (>= 3.3.0.2) rainbow (>= 2.2.2, < 4.0) - regexp_parser (>= 2.4, < 3.0) - rubocop-ast (>= 1.32.1, < 2.0) + regexp_parser (>= 2.9.3, < 3.0) + rubocop-ast (>= 1.36.2, < 2.0) ruby-progressbar (~> 1.7) - unicode-display_width (>= 2.4.0, < 3.0) - rubocop-ast (1.32.2) + unicode-display_width (>= 2.4.0, < 4.0) + rubocop-ast (1.38.0) parser (>= 3.3.1.0) rubocop-capybara (2.21.0) rubocop (~> 1.41) @@ -522,10 +568,10 @@ GEM rubocop-minitest (0.36.0) rubocop (>= 1.61, < 2.0) rubocop-ast (>= 1.31.1, < 2.0) - rubocop-performance (1.21.1) + rubocop-performance (1.23.1) rubocop (>= 1.48.1, < 2.0) rubocop-ast (>= 1.31.1, < 2.0) - rubocop-rails (2.26.0) + rubocop-rails (2.29.1) activesupport (>= 4.2.0) rack (>= 1.1) rubocop (>= 1.52.0, < 2.0) @@ -537,13 +583,17 @@ GEM ruby-vips (2.2.2) ffi (~> 1.12) logger - rubyzip (2.3.2) - sanitize (6.1.3) + rubyzip (2.4.1) + sanitize (7.0.0) crass (~> 1.0.2) - nokogiri (>= 1.12.0) + nokogiri (>= 1.16.8) sass-embedded (1.64.2) google-protobuf (~> 3.23) rake (>= 13.0.0) + sawyer (0.9.2) + addressable (>= 2.3.5) + faraday (>= 0.17.3, < 3) + securerandom (0.4.1) selenium-webdriver (4.23.0) base64 (~> 0.2) logger (~> 1.4) @@ -554,7 +604,7 @@ GEM docile (~> 1.1) simplecov-html (~> 0.11) simplecov_json_formatter (~> 0.1) - simplecov-html (0.12.3) + simplecov-html (0.13.1) simplecov-lcov (0.8.0) simplecov_json_formatter (0.1.4) simpleidn (0.2.3) @@ -572,37 +622,41 @@ GEM actionpack (>= 6.1) activesupport (>= 6.1) sprockets (>= 3.0.0) - stringio (3.1.1) + stringio (3.1.2) strong_migrations (1.8.0) activerecord (>= 5.2) - strscan (3.1.0) + teaspoon (1.4.0) + railties (>= 5.0) + teaspoon-mocha (2.3.3) + teaspoon (>= 1.0.0) terminal-table (3.0.2) unicode-display_width (>= 1.1.1, < 3) - terser (1.2.3) + terser (1.2.5) execjs (>= 0.3.0, < 3) thor (1.3.2) - tilt (2.4.0) - timeout (0.4.1) - turbo-rails (2.0.6) + tilt (2.6.0) + timeout (0.4.3) + turbo-rails (2.0.11) actionpack (>= 6.0.0) - activejob (>= 6.0.0) railties (>= 6.0.0) tzinfo (2.0.6) concurrent-ruby (~> 1.0) - unicode-display_width (2.5.0) + unicode-display_width (2.6.0) uri (0.13.1) + useragent (0.16.11) validates_email_format_of (1.8.2) i18n (>= 0.8.0) simpleidn vendorer (0.2.0) version_gem (1.1.4) - webmock (3.23.1) + webmock (3.24.0) addressable (>= 2.8.0) crack (>= 0.3.2) hashdiff (>= 0.4.0, < 2.0.0) - webrick (1.8.1) + webrick (1.9.1) websocket (1.2.11) - websocket-driver (0.7.6) + websocket-driver (0.7.7) + base64 websocket-extensions (>= 0.1.0) websocket-extensions (0.1.5) xpath (3.2.0) @@ -628,7 +682,6 @@ DEPENDENCIES bootstrap (~> 5.3.2) bootstrap_form (~> 5.0) brakeman - browser (< 6) bzip2-ffi cancancan canonical-rails @@ -636,6 +689,8 @@ DEPENDENCIES config connection_pool dalli + danger + danger-auto_label dartsass-sprockets debug debug_inspector @@ -643,6 +698,7 @@ DEPENDENCIES doorkeeper doorkeeper-i18n doorkeeper-openid_connect + dry-schema (< 1.14.0) dry-validation erb_lint factory_bot_rails @@ -672,7 +728,6 @@ DEPENDENCIES minitest (~> 5.1) minitest-focus multi_json - multi_xml (~> 0.6.0) omniauth (~> 2.0.2) omniauth-facebook omniauth-github @@ -688,7 +743,7 @@ DEPENDENCIES quad_tile (~> 1.0.1) rack-cors rack-uri_sanitizer - rails (~> 7.1.0) + rails (~> 7.2.0) rails-controller-testing rails-i18n (~> 7.0.0) rails_param @@ -709,12 +764,15 @@ DEPENDENCIES simplecov-lcov sprockets-exporters_pack strong_migrations (< 2.0.0) + teaspoon + teaspoon-mocha (~> 2.3.3) terser turbo-rails unicode-display_width validates_email_format_of (>= 1.5.1) vendorer webmock + zeitwerk (< 2.7) BUNDLED WITH - 2.4.19 + 2.5.22 diff --git a/INSTALL.md b/INSTALL.md index c8e28a62f..8667fb512 100644 --- a/INSTALL.md +++ b/INSTALL.md @@ -9,7 +9,7 @@ are two alternatives which make it easier to get a consistent development enviro * **Vagrant** This installs the software into a virtual machine. For Vagrant instructions see [VAGRANT.md](VAGRANT.md). * **Docker** This installs the software using containerization. For Docker instructions see [DOCKER.md](DOCKER.md). -These instructions are based on Ubuntu 22.04 LTS, which is the platform used by the OSMF servers. +These instructions are based on Ubuntu 24.04 LTS, which is the platform used by the OSMF servers. The instructions also work, with only minor amendments, for all other current Ubuntu releases, Fedora and MacOSX We don't recommend attempting to develop or deploy this software on Windows. Some Ruby gems may not be supported. If you need to use Windows the easiest solutions in order are [Docker](DOCKER.md), [Vagrant](VAGRANT.md), and Ubuntu in a virtual machine. @@ -22,12 +22,12 @@ of packages required before you can get the various gems installed. ## Minimum requirements -* Ruby 3.0+ +* Ruby 3.1+ * PostgreSQL 13+ * Bundler (see note below about [developer Ruby setup](#rbenv)) * Javascript Runtime -These can be installed on Ubuntu 22.04 or later with: +These can be installed on Ubuntu 24.04 or later with: ``` sudo apt-get update diff --git a/Vagrantfile b/Vagrantfile index 7895d3b86..617bd7b4d 100644 --- a/Vagrantfile +++ b/Vagrantfile @@ -2,9 +2,11 @@ # vi: set ft=ruby : Vagrant.configure("2") do |config| - # use official ubuntu image for virtualbox + # use official debian image + config.vm.box = "debian/bookworm64" + + # configure virtualbox provider config.vm.provider "virtualbox" do |vb, override| - override.vm.box = "ubuntu/jammy64" override.vm.synced_folder ".", "/srv/openstreetmap-website" vb.customize ["modifyvm", :id, "--memory", "4096"] vb.customize ["modifyvm", :id, "--cpus", "2"] @@ -14,16 +16,16 @@ Vagrant.configure("2") do |config| # Use sshfs sharing if available, otherwise NFS sharing sharing_type = Vagrant.has_plugin?("vagrant-sshfs") ? "sshfs" : "nfs" - # use third party image and sshfs or NFS sharing for lxc + # configure lxc provider config.vm.provider "lxc" do |_, override| - override.vm.box = "generic/ubuntu2204" override.vm.synced_folder ".", "/srv/openstreetmap-website", :type => sharing_type end - # use third party image and sshfs or NFS sharing for libvirt - config.vm.provider "libvirt" do |_, override| - override.vm.box = "generic/ubuntu2204" + # configure libvirt provider + config.vm.provider "libvirt" do |libvirt, override| override.vm.synced_folder ".", "/srv/openstreetmap-website", :type => sharing_type + libvirt.memory = 4096 + libvirt.cpus = 2 end # configure shared package cache if possible diff --git a/app/abilities/ability.rb b/app/abilities/ability.rb index d6584f364..3116bc5cd 100644 --- a/app/abilities/ability.rb +++ b/app/abilities/ability.rb @@ -5,59 +5,59 @@ class Ability def initialize(user) can :query, :browse - can :show, [Node, Way, Relation] - can [:index, :show], [OldNode, OldWay, OldRelation] - can [:show, :new], Note + can :read, [Node, Way, Relation, OldNode, OldWay, OldRelation] + can [:show, :create], Note can :search, :direction can [:index, :permalink, :edit, :help, :fixthemap, :offline, :export, :about, :communities, :preview, :copyright, :key, :id], :site can [:finish, :embed], :export can [:search, :search_latlon, :search_osm_nominatim, :search_osm_nominatim_reverse], :geocoder if Settings.status != "database_offline" - can [:index, :feed, :show], Changeset - can :show, ChangesetComment + can [:read, :feed], Changeset + can :read, ChangesetComment can [:confirm, :confirm_resend, :confirm_email], :confirmation - can [:index, :rss, :show], DiaryEntry - can :index, DiaryComment + can [:read, :rss], DiaryEntry + can :read, DiaryComment can [:index], Note - can [:new, :create, :update], :password - can [:index, :show], Redaction - can [:new, :create, :destroy], :session - can [:index, :show, :data, :georss], Trace - can [:terms, :new, :create, :save, :suspended, :show, :auth_success, :auth_failure], User - can [:index, :show, :blocks_on, :blocks_by], UserBlock + can [:create, :update], :password + can :read, Redaction + can [:create, :destroy], :session + can [:read, :data], Trace + can [:read, :create, :suspended, :auth_success, :auth_failure], User + can :read, UserBlock end if user&.active? can :welcome, :site - can [:show], :deletion + can :read, [:deletion, :account_terms] if Settings.status != "database_offline" - can [:subscribe, :unsubscribe], Changeset - can [:index, :new, :create, :show, :update, :destroy], :oauth2_application - can [:index, :destroy], :oauth2_authorized_application - can [:new, :show, :create, :destroy], :oauth2_authorization + can [:read, :create, :destroy], :changeset_subscription + can [:read, :create, :update, :destroy], :oauth2_application + can [:read, :destroy], :oauth2_authorized_application + can [:read, :create, :destroy], :oauth2_authorization can [:update, :destroy], :account - can [:show], :dashboard - can [:new, :create, :subscribe, :unsubscribe], DiaryEntry + can :update, :account_terms + can :read, :dashboard + can [:create, :subscribe, :unsubscribe], DiaryEntry can :update, DiaryEntry, :user => user can [:create], DiaryComment - can [:make_friend, :remove_friend], Friendship - can [:new, :create, :reply, :show, :inbox, :outbox, :muted, :mark, :unmute, :destroy], Message + can [:show, :create, :destroy], Follow + can [:read, :create, :destroy], Message can [:close, :reopen], Note - can [:show, :update], :preference + can [:read, :update], :preference can :update, :profile - can [:new, :create], Report - can [:mine, :new, :create, :update, :destroy], Trace + can :create, Report + can [:mine, :create, :update, :destroy], Trace can [:account, :go_public], User - can [:index, :create, :destroy], UserMute + can [:read, :create, :destroy], UserMute if user.moderator? can [:hide, :unhide], [DiaryEntry, DiaryComment] - can [:index, :show, :resolve, :ignore, :reopen], Issue + can [:read, :resolve, :ignore, :reopen], Issue can :create, IssueComment - can [:new, :create, :update, :destroy], Redaction - can [:new, :create, :revoke_all], UserBlock + can [:create, :update, :destroy], Redaction + can [:create, :destroy], UserBlock can :update, UserBlock, :creator => user can :update, UserBlock, :revoker => user can :update, UserBlock, :active? => true @@ -65,10 +65,12 @@ class Ability if user.administrator? can [:hide, :unhide], [DiaryEntry, DiaryComment] - can [:index, :show, :resolve, :ignore, :reopen], Issue + can [:read, :resolve, :ignore, :reopen], Issue can :create, IssueComment - can [:set_status, :destroy, :index], User - can [:grant, :revoke], UserRole + + can [:update], :user_status + can [:read, :update], :users_list + can [:create, :destroy], UserRole end end end diff --git a/app/abilities/api_ability.rb b/app/abilities/api_ability.rb index 96ed9b080..13a453eb5 100644 --- a/app/abilities/api_ability.rb +++ b/app/abilities/api_ability.rb @@ -3,40 +3,50 @@ class ApiAbility include CanCan::Ability - def initialize(user) - can :show, :capability - can :index, :map - can :show, :permission - can :show, :version + def initialize(token) + can :read, [:version, :capability, :permission, :map] if Settings.status != "database_offline" - can [:index, :show, :download], Changeset - can [:index, :create, :feed, :show, :search], Note - can :index, Tracepoint - can [:index, :show], User - can [:index, :show], Node - can [:index, :show, :full, :ways_for_node], Way - can [:index, :show, :full, :relations_for_node, :relations_for_way, :relations_for_relation], Relation - can [:history, :show], [OldNode, OldWay, OldRelation] - can [:show], UserBlock + user = User.find(token.resource_owner_id) if token + + can [:read, :feed, :search], Note + can :create, Note unless token + + can [:read, :download], Changeset + can :read, Tracepoint + can :read, User + can :read, Node + can [:read, :full, :ways_for_node], Way + can [:read, :full, :relations_for_node, :relations_for_way, :relations_for_relation], Relation + can [:history, :read], [OldNode, OldWay, OldRelation] + can :read, UserBlock if user&.active? - can [:comment, :close, :reopen], Note - can [:create, :show, :update, :destroy, :data], Trace - can [:details, :gpx_files], User - can [:index, :show, :update, :update_all, :destroy], UserPreference + can [:create, :comment, :close, :reopen], Note if scope?(token, :write_notes) + can [:create, :destroy], NoteSubscription if scope?(token, :write_notes) + + can :read, Trace if scope?(token, :read_gpx) + can [:create, :update, :destroy], Trace if scope?(token, :write_gpx) + + can :details, User if scope?(token, :read_prefs) + can :read, UserPreference if scope?(token, :read_prefs) + can [:update, :update_all, :destroy], UserPreference if scope?(token, :write_prefs) + + can [:read, :update, :destroy], Message if scope?(token, :consume_messages) + can :create, Message if scope?(token, :send_messages) if user.terms_agreed? - can [:create, :update, :upload, :close, :subscribe, :unsubscribe], Changeset - can :create, ChangesetComment - can [:create, :update, :delete], [Node, Way, Relation] + can [:create, :update, :upload, :close, :subscribe, :unsubscribe], Changeset if scope?(token, :write_api) + can :create, ChangesetComment if scope?(token, :write_api) + can [:create, :update, :delete], [Node, Way, Relation] if scope?(token, :write_api) end if user.moderator? - can [:destroy, :restore], ChangesetComment - can :destroy, Note + can [:destroy, :restore], ChangesetComment if scope?(token, :write_api) - can :redact, [OldNode, OldWay, OldRelation] if user.terms_agreed? + can :destroy, Note if scope?(token, :write_notes) + + can :redact, [OldNode, OldWay, OldRelation] if user&.terms_agreed? && scope?(token, :write_redactions) end end end @@ -68,4 +78,10 @@ class ApiAbility # See the wiki for details: # https://github.com/CanCanCommunity/cancancan/wiki/Defining-Abilities end + + private + + def scope?(token, scope) + token&.includes_scope?(scope) + end end diff --git a/app/abilities/api_capability.rb b/app/abilities/api_capability.rb deleted file mode 100644 index 07345d254..000000000 --- a/app/abilities/api_capability.rb +++ /dev/null @@ -1,41 +0,0 @@ -# frozen_string_literal: true - -class ApiCapability - include CanCan::Ability - - def initialize(token) - if Settings.status != "database_offline" - user = User.find(token.resource_owner_id) - - if user&.active? - can [:create, :comment, :close, :reopen], Note if scope?(token, :write_notes) - can [:show, :data], Trace if scope?(token, :read_gpx) - can [:create, :update, :destroy], Trace if scope?(token, :write_gpx) - can [:details], User if scope?(token, :read_prefs) - can [:gpx_files], User if scope?(token, :read_gpx) - can [:index, :show], UserPreference if scope?(token, :read_prefs) - can [:update, :update_all, :destroy], UserPreference if scope?(token, :write_prefs) - can [:inbox, :outbox, :show, :update, :destroy], Message if scope?(token, :consume_messages) - can [:create], Message if scope?(token, :send_messages) - - if user.terms_agreed? - can [:create, :update, :upload, :close, :subscribe, :unsubscribe], Changeset if scope?(token, :write_api) - can :create, ChangesetComment if scope?(token, :write_api) - can [:create, :update, :delete], [Node, Way, Relation] if scope?(token, :write_api) - end - - if user.moderator? - can [:destroy, :restore], ChangesetComment if scope?(token, :write_api) - can :destroy, Note if scope?(token, :write_notes) - can :redact, [OldNode, OldWay, OldRelation] if user&.terms_agreed? && (scope?(token, :write_api) || scope?(token, :write_redactions)) - end - end - end - end - - private - - def scope?(token, scope) - token&.includes_scope?(scope) - end -end diff --git a/app/assets/config/manifest.js b/app/assets/config/manifest.js index 85b5cb860..3b1f63f8b 100644 --- a/app/assets/config/manifest.js +++ b/app/assets/config/manifest.js @@ -17,8 +17,6 @@ //= link_tree ../../../vendor/assets/leaflet .png -//= link_directory ../../../vendor/assets/polyfill .js - //= link leaflet/dist/images/marker-icon.png //= link leaflet/dist/images/marker-icon-2x.png //= link leaflet/dist/images/marker-shadow.png diff --git a/app/assets/images/browse/node.svg b/app/assets/images/browse/node.svg new file mode 100644 index 000000000..35e592763 --- /dev/null +++ b/app/assets/images/browse/node.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/app/assets/images/browse/relation.svg b/app/assets/images/browse/relation.svg new file mode 100644 index 000000000..9cb52fc67 --- /dev/null +++ b/app/assets/images/browse/relation.svg @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/app/assets/images/browse/way.svg b/app/assets/images/browse/way.svg new file mode 100644 index 000000000..98eae574f --- /dev/null +++ b/app/assets/images/browse/way.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/app/assets/images/key/opnvkarte/bus_stop13.svg b/app/assets/images/key/opnvkarte/bus_stop13.svg deleted file mode 100644 index ae4ffeeaf..000000000 --- a/app/assets/images/key/opnvkarte/bus_stop13.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/app/assets/images/key/opnvkarte/bus_stop15.svg b/app/assets/images/key/opnvkarte/bus_stop15.svg deleted file mode 100644 index a25991954..000000000 --- a/app/assets/images/key/opnvkarte/bus_stop15.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/app/assets/images/key/opnvkarte/rail17.svg b/app/assets/images/key/opnvkarte/rail17.svg deleted file mode 100644 index 8cada29c9..000000000 --- a/app/assets/images/key/opnvkarte/rail17.svg +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - - - diff --git a/app/assets/images/key/opnvkarte/stop13.svg b/app/assets/images/key/opnvkarte/stop13.svg deleted file mode 100644 index cfe65b344..000000000 --- a/app/assets/images/key/opnvkarte/stop13.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/app/assets/images/key/opnvkarte/stop15.svg b/app/assets/images/key/opnvkarte/stop15.svg deleted file mode 100644 index 3ae62672e..000000000 --- a/app/assets/images/key/opnvkarte/stop15.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/app/assets/images/social_icons/bluesky.svg b/app/assets/images/social_icons/bluesky.svg new file mode 100644 index 000000000..7e7c880d7 --- /dev/null +++ b/app/assets/images/social_icons/bluesky.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/app/assets/images/social_icons/email.svg b/app/assets/images/social_icons/email.svg new file mode 100644 index 000000000..d35d51d60 --- /dev/null +++ b/app/assets/images/social_icons/email.svg @@ -0,0 +1,4 @@ + + + + diff --git a/app/assets/images/social_icons/facebook.svg b/app/assets/images/social_icons/facebook.svg new file mode 100644 index 000000000..80241473e --- /dev/null +++ b/app/assets/images/social_icons/facebook.svg @@ -0,0 +1,4 @@ + + + + diff --git a/app/assets/images/social_icons/linkedin.svg b/app/assets/images/social_icons/linkedin.svg new file mode 100644 index 000000000..908c86751 --- /dev/null +++ b/app/assets/images/social_icons/linkedin.svg @@ -0,0 +1,4 @@ + + + + diff --git a/app/assets/images/social_icons/mastodon.svg b/app/assets/images/social_icons/mastodon.svg new file mode 100644 index 000000000..a8b4bc44e --- /dev/null +++ b/app/assets/images/social_icons/mastodon.svg @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/app/assets/images/social_icons/telegram.svg b/app/assets/images/social_icons/telegram.svg new file mode 100644 index 000000000..84c087929 --- /dev/null +++ b/app/assets/images/social_icons/telegram.svg @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + diff --git a/app/assets/images/social_icons/x.svg b/app/assets/images/social_icons/x.svg new file mode 100644 index 000000000..937dcc732 --- /dev/null +++ b/app/assets/images/social_icons/x.svg @@ -0,0 +1 @@ + diff --git a/app/assets/javascripts/application.js b/app/assets/javascripts/application.js index 054742126..29563cb4a 100644 --- a/app/assets/javascripts/application.js +++ b/app/assets/javascripts/application.js @@ -1,6 +1,5 @@ //= require jquery3 //= require jquery_ujs -//= require jquery.timers //= require jquery.throttle-debounce //= require js-cookie/dist/js.cookie //= require popper @@ -17,6 +16,29 @@ //= require richtext //= require qs/dist/qs +{ + const application_data = $("head").data(); + + I18n.default_locale = OSM.DEFAULT_LOCALE; + I18n.locale = application_data.locale; + I18n.fallbacks = true; + + OSM.preferred_editor = application_data.preferredEditor; + OSM.preferred_languages = application_data.preferredLanguages; + + if (application_data.user) { + OSM.user = application_data.user; + + if (application_data.userHome) { + OSM.home = application_data.userHome; + } + } + + if (application_data.location) { + OSM.location = application_data.location; + } +} + /* * Called as the user scrolls/zooms around to manipulate hrefs of the * view tab and various other links @@ -123,27 +145,6 @@ $(document).ready(function () { $("header").toggleClass("closed"); }); - var application_data = $("head").data(); - - I18n.default_locale = OSM.DEFAULT_LOCALE; - I18n.locale = application_data.locale; - I18n.fallbacks = true; - - OSM.preferred_editor = application_data.preferredEditor; - OSM.preferred_languages = application_data.preferredLanguages; - - if (application_data.user) { - OSM.user = application_data.user; - - if (application_data.userHome) { - OSM.home = application_data.userHome; - } - } - - if (application_data.location) { - OSM.location = application_data.location; - } - $("#edit_tab") .attr("title", I18n.t("javascripts.site.edit_disabled_tooltip")); }); diff --git a/app/assets/javascripts/diary_entry.js b/app/assets/javascripts/diary_entry.js index 625d43a52..bd5fd4dd6 100644 --- a/app/assets/javascripts/diary_entry.js +++ b/app/assets/javascripts/diary_entry.js @@ -2,8 +2,10 @@ $(document).ready(function () { var marker, map; function setLocation(e) { - $("#latitude").val(e.latlng.lat); - $("#longitude").val(e.latlng.lng); + const latlng = e.latlng.wrap(); + + $("#latitude").val(latlng.lat); + $("#longitude").val(latlng.lng); if (marker) { map.removeLayer(marker); diff --git a/app/assets/javascripts/edit/id.js.erb b/app/assets/javascripts/edit/id.js.erb index 90318788c..08e04dcf3 100644 --- a/app/assets/javascripts/edit/id.js.erb +++ b/app/assets/javascripts/edit/id.js.erb @@ -10,15 +10,15 @@ $(document).ready(function () { var params = {}; if (mapParams.object) { - params.id = mapParams.object.type + '/' + mapParams.object.id; + params.id = mapParams.object.type + "/" + mapParams.object.id; mapParams = OSM.parseHash(location.hash); if (mapParams.center) { - params.map = mapParams.zoom + '/' + mapParams.center.lat + '/' + mapParams.center.lng; + params.map = mapParams.zoom + "/" + mapParams.center.lat + "/" + mapParams.center.lng; } } else if (id.data("lat") && id.data("lon")) { params.map = "16/" + id.data("lat") + "/" + id.data("lon"); } else { - params.map = (mapParams.zoom || 17) + '/' + mapParams.lat + '/' + mapParams.lon; + params.map = (mapParams.zoom || 17) + "/" + mapParams.lat + "/" + mapParams.lon; } if (hashParams.background) params.background = hashParams.background; diff --git a/app/assets/javascripts/embed.js.erb b/app/assets/javascripts/embed.js.erb index 9a0ec07d3..34a8b68bc 100644 --- a/app/assets/javascripts/embed.js.erb +++ b/app/assets/javascripts/embed.js.erb @@ -1,5 +1,6 @@ //= depend_on settings.yml //= depend_on settings.local.yml +//= depend_on layers.yml //= require leaflet/dist/leaflet-src //= require leaflet.osm //= require i18n @@ -11,77 +12,77 @@ if (navigator.languages) { I18n.locale = navigator.language; } -window.onload = function () { - var query = (window.location.search || '?').slice(1), - args = {}; +I18n.default_locale = <%= I18n.default_locale.to_json %>; +I18n.fallbacks = true; - var pairs = query.split('&'); - for (var i = 0; i < pairs.length; i++) { - var parts = pairs[i].split('='); - args[parts[0]] = decodeURIComponent(parts[1] || ''); - } +window.onload = function () { + const args = Object.fromEntries(new URLSearchParams(window.location.search)); - var mapnikOptions = { + const tileOptions = { + mapnik: { <% if Settings.key?(:tile_cdn_url) %> - url: <%= Settings.tile_cdn_url.to_json %> + url: <%= Settings.tile_cdn_url.to_json %> <% end %> + } }; - var thunderforestOptions = { + const apiKeys = { <% if Settings.key?(:thunderforest_key) %> - apikey: <%= Settings.thunderforest_key.to_json %> + THUNDERFOREST_KEY: <%= Settings.thunderforest_key.to_json %> <% end %> }; var map = L.map("map"); - map.attributionControl.setPrefix(''); + map.attributionControl.setPrefix(""); map.removeControl(map.attributionControl); - if (!args.layer || args.layer === "mapnik" || args.layer === "osmarender" || args.layer === "mapquest") { - new L.OSM.Mapnik(mapnikOptions).addTo(map); - } else if (args.layer === "cyclosm") { - new L.OSM.CyclOSM().addTo(map); - } else if (args.layer === "cyclemap" || args.layer === "cycle map") { - new L.OSM.CycleMap(thunderforestOptions).addTo(map); - } else if (args.layer === "transportmap") { - new L.OSM.TransportMap(thunderforestOptions).addTo(map); - } else if (args.layer === "opnvkarte") { - new L.OSM.OPNVKarte().addTo(map); - } else if (args.layer === "hot") { - new L.OSM.HOT().addTo(map); - } + const isDarkTheme = args.theme === "dark" || (args.theme !== "light" && window.matchMedia("(prefers-color-scheme: dark)").matches); + const layers = <%= + YAML.load_file(Rails.root.join("config/layers.yml")) + .select { |entry| entry["canEmbed"] } + .each_with_object({}) do |entry, obj| + obj[entry["layerId"]] = { + layer: entry["leafletOsmId"], + darkLayer: entry["leafletOsmDarkId"], + apiKeyId: entry["apiKeyId"] + }.compact + end.to_json + %>; + const layerId = (args.layer || "").replaceAll(" ", ""); + const layerConfig = layers[layerId] || layers.mapnik; + const { layer, ...options } = { + layer: layerConfig.darkLayer && isDarkTheme ? layerConfig.darkLayer : layerConfig.layer, + apikey: apiKeys[layerConfig.apiKeyId], + ...tileOptions[layerId] + }; + new L.OSM[layer](options).addTo(map); if (args.marker) { - L.marker(args.marker.split(','), {icon: L.icon({ + L.marker(args.marker.split(","), { icon: L.icon({ iconUrl: <%= asset_path('leaflet/dist/images/marker-icon.png').to_json %>, iconSize: new L.Point(25, 41), iconAnchor: new L.Point(12, 41), shadowUrl: <%= asset_path('leaflet/dist/images/marker-shadow.png').to_json %>, shadowSize: new L.Point(41, 41) - })}).addTo(map); + }) }).addTo(map); } - if (args.bbox) { - var bbox = args.bbox.split(','); - map.fitBounds([L.latLng(bbox[1], bbox[0]), - L.latLng(bbox[3], bbox[2])]); - } else { - map.fitWorld(); - } + const bbox = (args.bbox || "-180,-90,180,90").split(","); + map.fitBounds([[bbox[1], bbox[0]], [bbox[3], bbox[2]]]); map.addControl(new L.Control.OSMReportAProblem()); }; L.Control.OSMReportAProblem = L.Control.Attribution.extend({ options: { - position: 'bottomright', - prefix: ''+I18n.t('javascripts.embed.report_problem')+'' + position: "bottomright", + prefix: `${I18n.t("javascripts.embed.report_problem")}` }, onAdd: function (map) { var container = L.Control.Attribution.prototype.onAdd.call(this, map); - map.on('moveend', this._update, this); + map.on("moveend", this._update, this); return container; }, @@ -91,8 +92,8 @@ L.Control.OSMReportAProblem = L.Control.Attribution.extend({ this._container.innerHTML = this._container.innerHTML - .replace('{x}', this._map.getCenter().lat) - .replace('{y}', this._map.getCenter().lng) - .replace('{z}', this._map.getZoom()); + .replace("{x}", this._map.getCenter().lat) + .replace("{y}", this._map.getCenter().lng) + .replace("{z}", this._map.getZoom()); } }); diff --git a/app/assets/javascripts/id.js b/app/assets/javascripts/id.js index 706097bba..15f77b1c1 100644 --- a/app/assets/javascripts/id.js +++ b/app/assets/javascripts/id.js @@ -28,6 +28,12 @@ document.addEventListener("DOMContentLoaded", function () { .containerNode(container) .init(); + if (parent === window) { + // iD not opened in an iframe -> skip setting of parent handlers + return; + } + + var hashChangedAutomatically = false; id.map().on("move.embed", parent.$.throttle(250, function () { if (id.inIntro()) return; var zoom = ~~id.map().zoom(), @@ -40,14 +46,12 @@ document.addEventListener("DOMContentLoaded", function () { // https://gist.github.com/jfirebaugh/5439412 var hash = parent.OSM.formatHash(llz); if (hash !== parent.location.hash) { + hashChangedAutomatically = true; parent.location.replace(parent.location.href.replace(/(#.*|$)/, hash)); } })); - parent.$("body").on("click", "a.set_position", function (e) { - e.preventDefault(); - var data = parent.$(this).data(); - + function goToLocation(data) { // 0ms timeout to avoid iframe JS context weirdness. // https://gist.github.com/jfirebaugh/5439412 setTimeout(function () { @@ -55,6 +59,22 @@ document.addEventListener("DOMContentLoaded", function () { [data.lon, data.lat], Math.max(data.zoom || 15, 13)); }, 0); + } + + parent.$("body").on("click", "a.set_position", function (e) { + e.preventDefault(); + var data = parent.$(this).data(); + goToLocation(data); + }); + + parent.addEventListener("hashchange", function (e) { + if (hashChangedAutomatically) { + hashChangedAutomatically = false; + return; + } + e.preventDefault(); + var data = parent.OSM.mapParams(); + goToLocation(data); }); } }); diff --git a/app/assets/javascripts/index.js b/app/assets/javascripts/index.js index 4f3c414f5..d61191fb4 100644 --- a/app/assets/javascripts/index.js +++ b/app/assets/javascripts/index.js @@ -1,7 +1,7 @@ //= require_self //= require leaflet.sidebar //= require leaflet.sidebar-pane -//= require leaflet.locatecontrol/src/L.Control.Locate +//= require leaflet.locatecontrol/dist/L.Control.Locate.umd //= require leaflet.locate //= require leaflet.layers //= require leaflet.key @@ -25,8 +25,6 @@ //= require qs/dist/qs $(document).ready(function () { - var loaderTimeout; - var map = new L.OSM.Map("map", { zoomControl: false, layerControl: false, @@ -39,11 +37,7 @@ $(document).ready(function () { map.setSidebarOverlaid(false); - clearTimeout(loaderTimeout); - - loaderTimeout = setTimeout(function () { - $("#sidebar_loader").show(); - }, 200); + $("#sidebar_loader").show().addClass("delayed-fade-in"); // IE<10 doesn't respect Vary: X-Requested-With header, so // prevent caching the XHR response as a full-page URL. @@ -60,9 +54,8 @@ $(document).ready(function () { url: content_path, dataType: "html", complete: function (xhr) { - clearTimeout(loaderTimeout); $("#flash").empty(); - $("#sidebar_loader").hide(); + $("#sidebar_loader").removeClass("delayed-fade-in").hide(); var content = $(xhr.responseText); @@ -180,7 +173,7 @@ $(document).ready(function () { var expiry = new Date(); expiry.setYear(expiry.getFullYear() + 10); - map.on("moveend layeradd layerremove", function () { + map.on("moveend baselayerchange overlayadd overlayremove", function () { updateLinks( map.getCenter().wrap(), map.getZoom(), @@ -212,9 +205,9 @@ $(document).ready(function () { }); if (OSM.MATOMO) { - map.on("layeradd", function (e) { + map.on("baselayerchange overlayadd", function (e) { if (e.layer.options) { - var goal = OSM.MATOMO.goals[e.layer.options.keyid]; + var goal = OSM.MATOMO.goals[e.layer.options.layerId]; if (goal) { $("body").trigger("matomogoal", goal); @@ -262,20 +255,10 @@ $(document).ready(function () { }); function sendRemoteEditCommand(url, callback) { - var iframe = $("