From: Anton Khorev Date: Mon, 18 Nov 2024 15:15:25 +0000 (+0300) Subject: Merge branch 'pull/5297' X-Git-Tag: live~757 X-Git-Url: https://git.openstreetmap.org/rails.git/commitdiff_plain/a25b7953b3889ce6847a13c579fa9fd32219b5e3?hp=738e66afa6231390f52679fe8b343478643f5684 Merge branch 'pull/5297' --- diff --git a/.github/workflows/danger.yml b/.github/workflows/danger.yml index 8999fca5b..67a676d87 100644 --- a/.github/workflows/danger.yml +++ b/.github/workflows/danger.yml @@ -22,6 +22,12 @@ jobs: 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.ref }}:danger_base + - name: Create head branch + run: | + git fetch ${{ github.event.pull_request.head.repo.clone_url }} ${{ github.event.pull_request.head.ref }}:danger_head - name: Danger env: DANGER_GITHUB_BEARER_TOKEN: ${{ secrets.GITHUB_TOKEN }} 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 2571b9345..e298c944f 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) diff --git a/DOCKER.md b/DOCKER.md index 4804d09b3..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,21 +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 +``` +docker compose run --rm web bundle exec rake assets:precompile +``` ### Loading an OSM extract @@ -91,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. @@ -127,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 index 3148bafb6..6e2aeced8 100644 --- a/Dangerfile +++ b/Dangerfile @@ -30,3 +30,13 @@ if git.commits.any? { |c| c.parents.count > 1 } 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/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 b83011542..277346b83 100644 --- a/Gemfile +++ b/Gemfile @@ -148,7 +148,7 @@ gem "zeitwerk", "< 2.7" group :development do gem "better_errors" gem "binding_of_caller" - gem "danger", :github => "tomhughes/danger", :ref => "pull-request-target" + gem "danger" gem "danger-auto_label" gem "debug_inspector" gem "i18n-tasks" diff --git a/Gemfile.lock b/Gemfile.lock index 5a18b24a6..5d15e550c 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,51 +1,31 @@ -GIT - remote: https://github.com/tomhughes/danger.git - revision: a265cf74d2f464a25796b48d95697f5eed553454 - ref: pull-request-target - specs: - 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) - GEM remote: https://rubygems.org/ specs: aasm (5.5.0) concurrent-ruby (~> 1.0) - actioncable (7.2.1.2) - actionpack (= 7.2.1.2) - activesupport (= 7.2.1.2) + actioncable (7.2.2) + actionpack (= 7.2.2) + activesupport (= 7.2.2) nio4r (~> 2.0) websocket-driver (>= 0.6.1) zeitwerk (~> 2.6) - actionmailbox (7.2.1.2) - actionpack (= 7.2.1.2) - activejob (= 7.2.1.2) - activerecord (= 7.2.1.2) - activestorage (= 7.2.1.2) - activesupport (= 7.2.1.2) + actionmailbox (7.2.2) + actionpack (= 7.2.2) + activejob (= 7.2.2) + activerecord (= 7.2.2) + activestorage (= 7.2.2) + activesupport (= 7.2.2) mail (>= 2.8.0) - actionmailer (7.2.1.2) - actionpack (= 7.2.1.2) - actionview (= 7.2.1.2) - activejob (= 7.2.1.2) - activesupport (= 7.2.1.2) + actionmailer (7.2.2) + actionpack (= 7.2.2) + actionview (= 7.2.2) + activejob (= 7.2.2) + activesupport (= 7.2.2) mail (>= 2.8.0) rails-dom-testing (~> 2.2) - actionpack (7.2.1.2) - actionview (= 7.2.1.2) - activesupport (= 7.2.1.2) + actionpack (7.2.2) + actionview (= 7.2.2) + activesupport (= 7.2.2) nokogiri (>= 1.8.5) racc rack (>= 2.2.4, < 3.2) @@ -56,40 +36,41 @@ GEM useragent (~> 0.16) actionpack-page_caching (1.2.4) actionpack (>= 4.0.0) - actiontext (7.2.1.2) - actionpack (= 7.2.1.2) - activerecord (= 7.2.1.2) - activestorage (= 7.2.1.2) - activesupport (= 7.2.1.2) + actiontext (7.2.2) + actionpack (= 7.2.2) + activerecord (= 7.2.2) + activestorage (= 7.2.2) + activesupport (= 7.2.2) globalid (>= 0.6.0) nokogiri (>= 1.8.5) - actionview (7.2.1.2) - activesupport (= 7.2.1.2) + actionview (7.2.2) + activesupport (= 7.2.2) 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.2.1.2) - activesupport (= 7.2.1.2) + activejob (7.2.2) + activesupport (= 7.2.2) globalid (>= 0.3.6) - activemodel (7.2.1.2) - activesupport (= 7.2.1.2) - activerecord (7.2.1.2) - activemodel (= 7.2.1.2) - activesupport (= 7.2.1.2) + activemodel (7.2.2) + activesupport (= 7.2.2) + activerecord (7.2.2) + activemodel (= 7.2.2) + activesupport (= 7.2.2) timeout (>= 0.4.0) activerecord-import (1.8.1) activerecord (>= 4.2) - activestorage (7.2.1.2) - actionpack (= 7.2.1.2) - activejob (= 7.2.1.2) - activerecord (= 7.2.1.2) - activesupport (= 7.2.1.2) + activestorage (7.2.2) + actionpack (= 7.2.2) + activejob (= 7.2.2) + activerecord (= 7.2.2) + activesupport (= 7.2.2) marcel (~> 1.0) - activesupport (7.2.1.2) + activesupport (7.2.2) base64 + benchmark (>= 0.3) bigdecimal concurrent-ruby (~> 1.0, >= 1.3.1) connection_pool (>= 2.2.5) @@ -111,8 +92,8 @@ GEM autoprefixer-rails (10.4.19.0) execjs (~> 2) aws-eventstream (1.3.0) - aws-partitions (1.997.0) - aws-sdk-core (3.211.0) + aws-partitions (1.1004.0) + aws-sdk-core (3.212.0) aws-eventstream (~> 1, >= 1.3.0) aws-partitions (~> 1, >= 1.992.0) aws-sigv4 (~> 1.9) @@ -120,13 +101,14 @@ GEM aws-sdk-kms (1.95.0) aws-sdk-core (~> 3, >= 3.210.0) aws-sigv4 (~> 1.5) - aws-sdk-s3 (1.169.0) + aws-sdk-s3 (1.170.1) aws-sdk-core (~> 3, >= 3.210.0) aws-sdk-kms (~> 1) aws-sigv4 (~> 1.5) aws-sigv4 (1.10.1) 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) @@ -188,6 +170,20 @@ GEM 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) @@ -200,16 +196,16 @@ GEM sprockets (> 3.0) sprockets-rails tilt - date (3.3.4) + date (3.4.0) debug (1.9.2) 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) @@ -295,7 +291,7 @@ GEM globalid (1.2.1) activesupport (>= 6.1) google-protobuf (3.25.5) - hashdiff (1.1.1) + hashdiff (1.1.2) hashie (5.0.0) highline (3.1.1) reline @@ -343,7 +339,7 @@ GEM rails-dom-testing (>= 1, < 3) railties (>= 4.2.0) thor (>= 0.14, < 2.0) - json (2.7.4) + json (2.8.1) jwt (2.9.3) base64 kgio (2.11.4) @@ -380,14 +376,14 @@ GEM minitest (5.25.1) minitest-focus (1.4.0) minitest (>= 4, < 6) - msgpack (1.7.3) + msgpack (1.7.5) multi_json (1.15.0) multi_xml (0.7.1) bigdecimal (~> 3.1) nap (1.1.0) - net-http (0.4.1) + net-http (0.5.0) uri - net-imap (0.5.0) + net-imap (0.5.1) date net-protocol net-pop (0.1.2) @@ -453,20 +449,20 @@ GEM omniauth (~> 2.0) open4 (1.3.4) openstreetmap-deadlock_retry (1.3.1) - ostruct (0.6.0) - overcommit (0.64.0) + ostruct (0.6.1) + overcommit (0.64.1) childprocess (>= 0.6.3, < 6) iniparse (~> 1.4) - rexml (~> 3.2) + rexml (>= 3.3.9) parallel (1.26.3) - parser (3.3.5.0) + parser (3.3.6.0) ast (~> 2.4.1) racc pg (1.5.9) popper_js (2.11.8) progress (3.6.0) pstore (0.1.3) - psych (5.1.2) + psych (5.2.0) stringio public_suffix (6.0.1) puma (5.6.9) @@ -490,20 +486,20 @@ GEM rackup (1.0.1) rack (< 3) webrick - rails (7.2.1.2) - actioncable (= 7.2.1.2) - actionmailbox (= 7.2.1.2) - actionmailer (= 7.2.1.2) - actionpack (= 7.2.1.2) - actiontext (= 7.2.1.2) - actionview (= 7.2.1.2) - activejob (= 7.2.1.2) - activemodel (= 7.2.1.2) - activerecord (= 7.2.1.2) - activestorage (= 7.2.1.2) - activesupport (= 7.2.1.2) + rails (7.2.2) + actioncable (= 7.2.2) + actionmailbox (= 7.2.2) + actionmailer (= 7.2.2) + actionpack (= 7.2.2) + actiontext (= 7.2.2) + actionview (= 7.2.2) + activejob (= 7.2.2) + activemodel (= 7.2.2) + activerecord (= 7.2.2) + activestorage (= 7.2.2) + activesupport (= 7.2.2) bundler (>= 1.15.0) - railties (= 7.2.1.2) + railties (= 7.2.2) rails-controller-testing (1.0.5) actionpack (>= 5.0.1.rc1) actionview (>= 5.0.1.rc1) @@ -521,9 +517,9 @@ GEM rails_param (1.3.1) actionpack (>= 3.2.0) activesupport (>= 3.2.0) - railties (7.2.1.2) - actionpack (= 7.2.1.2) - activesupport (= 7.2.1.2) + railties (7.2.2) + actionpack (= 7.2.2) + activesupport (= 7.2.2) irb (~> 1.13) rackup (>= 1.0.0) rake (>= 12.2) @@ -538,17 +534,17 @@ GEM rdoc (6.7.0) psych (>= 4.0.0) regexp_parser (2.9.2) - reline (0.5.10) + reline (0.5.11) io-console (~> 0.5) request_store (1.7.0) rack (>= 1.4) rexml (3.3.9) rinku (2.0.6) rotp (6.3.0) - rouge (4.4.0) + rouge (4.5.1) rtlcss (0.2.1) mini_racer (>= 0.6.3) - rubocop (1.67.0) + rubocop (1.68.0) json (~> 2.3) language_server-protocol (>= 3.17.0) parallel (~> 1.10) @@ -558,7 +554,7 @@ GEM rubocop-ast (>= 1.32.2, < 2.0) ruby-progressbar (~> 1.7) unicode-display_width (>= 2.4.0, < 3.0) - rubocop-ast (1.33.0) + rubocop-ast (1.35.0) parser (>= 3.3.1.0) rubocop-capybara (2.21.0) rubocop (~> 1.41) @@ -592,7 +588,7 @@ GEM sawyer (0.9.2) addressable (>= 2.3.5) faraday (>= 0.17.3, < 3) - securerandom (0.3.1) + securerandom (0.3.2) selenium-webdriver (4.23.0) base64 (~> 0.2) logger (~> 1.4) @@ -634,7 +630,7 @@ GEM execjs (>= 0.3.0, < 3) thor (1.3.2) tilt (2.4.0) - timeout (0.4.1) + timeout (0.4.2) turbo-rails (2.0.11) actionpack (>= 6.0.0) railties (>= 6.0.0) @@ -652,7 +648,7 @@ GEM addressable (>= 2.8.0) crack (>= 0.3.2) hashdiff (>= 0.4.0, < 2.0.0) - webrick (1.8.2) + webrick (1.9.0) websocket (1.2.11) websocket-driver (0.7.6) websocket-extensions (>= 0.1.0) @@ -688,7 +684,7 @@ DEPENDENCIES config connection_pool dalli - danger! + danger danger-auto_label dartsass-sprockets debug diff --git a/Vagrantfile b/Vagrantfile index c2869cd5f..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/noble64" 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/ubuntu2404" 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/ubuntu2404" + # 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 9a8f193a1..725ceccb2 100644 --- a/app/abilities/ability.rb +++ b/app/abilities/ability.rb @@ -68,7 +68,7 @@ class Ability can [:index, :show, :resolve, :ignore, :reopen], Issue can :create, IssueComment can [:set_status, :destroy, :index], User - can [:grant, :revoke], UserRole + can [:create, :destroy], UserRole end end end diff --git a/app/assets/javascripts/application.js b/app/assets/javascripts/application.js index 054742126..ead01a8ff 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 diff --git a/app/assets/javascripts/index.js b/app/assets/javascripts/index.js index 2b29992e9..c419f9321 100644 --- a/app/assets/javascripts/index.js +++ b/app/assets/javascripts/index.js @@ -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); @@ -412,6 +405,9 @@ $(document).ready(function () { if (OSM.router.route(this.pathname + this.search + this.hash)) { e.preventDefault(); + if (this.pathname !== "/directions") { + $("header").addClass("closed"); + } } }); diff --git a/app/assets/javascripts/index/new_note.js b/app/assets/javascripts/index/new_note.js index 59fbeeb1d..887ba043b 100644 --- a/app/assets/javascripts/index/new_note.js +++ b/app/assets/javascripts/index/new_note.js @@ -139,8 +139,6 @@ OSM.NewNote = function (map) { newNote.on("remove", function () { addNoteButton.removeClass("active"); - }).on("dragstart", function () { - $(newNote).stopTime("removenote"); }).on("dragend", function () { content.find("textarea").focus(); }); diff --git a/app/assets/javascripts/index/query.js b/app/assets/javascripts/index/query.js index 7d63280ee..09e4de31e 100644 --- a/app/assets/javascripts/index/query.js +++ b/app/assets/javascripts/index/query.js @@ -289,18 +289,10 @@ OSM.Query = function (map) { .hide(); if (marker) map.removeLayer(marker); - marker = L.circle(latlng, radius, featureStyle).addTo(map); - - $(document).everyTime(75, "fadeQueryMarker", function (i) { - if (i === 10) { - map.removeLayer(marker); - } else { - marker.setStyle({ - opacity: 1 - (i * 0.1), - fillOpacity: 0.5 - (i * 0.05) - }); - } - }, 10); + marker = L.circle(latlng, Object.assign({ + radius: radius, + className: "query-marker" + }, featureStyle)).addTo(map); runQuery(latlng, radius, nearby, $("#query-nearby"), false); runQuery(latlng, radius, isin, $("#query-isin"), true, compareSize); diff --git a/app/assets/javascripts/index/search.js b/app/assets/javascripts/index/search.js index 476ad30a1..2bfbb2e1c 100644 --- a/app/assets/javascripts/index/search.js +++ b/app/assets/javascripts/index/search.js @@ -32,6 +32,7 @@ OSM.Search = function (map) { $(".describe_location").on("click", function (e) { e.preventDefault(); + $("header").addClass("closed"); var center = map.getCenter().wrap(), precision = OSM.zoomPrecision(map.getZoom()), lat = center.lat.toFixed(precision), diff --git a/app/assets/javascripts/leaflet.layers.js b/app/assets/javascripts/leaflet.layers.js index 82efab506..dc692a4a5 100644 --- a/app/assets/javascripts/leaflet.layers.js +++ b/app/assets/javascripts/leaflet.layers.js @@ -14,7 +14,7 @@ L.OSM.layers = function (options) { var buttonContainer = $("
") .appendTo(baseSection); - var mapContainer = $("
") + var mapContainer = $("
") .appendTo(buttonContainer); var input = $("") diff --git a/app/assets/javascripts/richtext.js b/app/assets/javascripts/richtext.js index 56aad8c73..bd00d937e 100644 --- a/app/assets/javascripts/richtext.js +++ b/app/assets/javascripts/richtext.js @@ -6,8 +6,10 @@ */ $(document).on("change", ".richtext_container textarea", function () { var container = $(this).closest(".richtext_container"); + var preview = container.find(".tab-pane[id$='_preview']"); - container.find(".tab-pane[id$='_preview']").empty(); + preview.children(".richtext_placeholder").attr("hidden", true).removeClass("delayed-fade-in"); + preview.children(".richtext").empty(); }); /* @@ -31,14 +33,11 @@ var editor = container.find("textarea"); var preview = container.find(".tab-pane[id$='_preview']"); - if (preview.contents().length === 0) { - preview.oneTime(500, "loading", function () { - preview.addClass("loading"); - }); + if (preview.children(".richtext").contents().length === 0) { + preview.children(".richtext_placeholder").removeAttr("hidden").addClass("delayed-fade-in"); - preview.load(editor.data("previewUrl"), { text: editor.val() }, function () { - preview.stopTime("loading"); - preview.removeClass("loading"); + preview.children(".richtext").load(editor.data("previewUrl"), { text: editor.val() }, function () { + preview.children(".richtext_placeholder").attr("hidden", true).removeClass("delayed-fade-in"); }); } }); diff --git a/app/assets/javascripts/router.js b/app/assets/javascripts/router.js index d890f38a4..c4e524170 100644 --- a/app/assets/javascripts/router.js +++ b/app/assets/javascripts/router.js @@ -101,6 +101,16 @@ OSM.Router = function (map, rts) { var router = {}; + function updateSecondaryNav() { + $("header nav.secondary > ul > li > a").each(function () { + var active = $(this).attr("href") === window.location.pathname; + + $(this) + .toggleClass("text-secondary", !active) + .toggleClass("text-secondary-emphasis", active); + }); + } + $(window).on("popstate", function (e) { if (!e.originalEvent.state) return; // Is it a real popstate event or just a hash change? var path = window.location.pathname + window.location.search, @@ -110,6 +120,7 @@ OSM.Router = function (map, rts) { currentPath = path; currentRoute = route; currentRoute.run("popstate", currentPath); + updateSecondaryNav(); map.setState(e.originalEvent.state, { animate: false }); }); @@ -124,6 +135,7 @@ OSM.Router = function (map, rts) { currentPath = path; currentRoute = route; currentRoute.run("pushstate", currentPath); + updateSecondaryNav(); return true; }; diff --git a/app/assets/stylesheets/common.scss b/app/assets/stylesheets/common.scss index d551462b2..027e6e6a3 100644 --- a/app/assets/stylesheets/common.scss +++ b/app/assets/stylesheets/common.scss @@ -70,6 +70,28 @@ time[title] { } } +/* Utility for delayed loading spinner */ + +.delayed-fade-in { + animation: 300ms linear forwards delayed-fade-in; +} + +@keyframes delayed-fade-in { + 0% { opacity: 0 } + 66% { opacity: 0 } + 100% { opacity: 1 } +} + +/* Bootstrap close button overrides for nested light/dark themes */ + +[data-bs-theme="dark"] .btn-close { + filter: var(--bs-btn-close-white-filter); +} + +[data-bs-theme="light"] .btn-close { + filter: none; +} + /* Rules for the header */ #menu-icon { @@ -119,7 +141,7 @@ header { } nav.primary { - & > .btn-group .btn-outline-primary { + #edit_tab .btn-outline-primary { @include button-outline-variant($green, $color-hover: $white, $active-color: $white); } @@ -197,9 +219,7 @@ body.small-nav { } } - #sidebar .search_forms, - #edit_tab, - #export_tab { + #sidebar .search_forms { display: none; } @@ -207,7 +227,7 @@ body.small-nav { margin-right: 0; padding: 0; - .btn-group { + #edit_tab { width: 100%; padding: 10px; } @@ -368,6 +388,14 @@ body.small-nav { .leaflet-marker-draggable { cursor: move; } + + .query-marker { + animation: 1500ms forwards query-marker-fade; + + @keyframes query-marker-fade { + to { opacity: 0 } + } + } } #map-ui { @@ -474,17 +502,17 @@ body.small-nav { } @include color-mode(dark) { - .leaflet-tile-container, + .leaflet-tile-container .leaflet-tile, .mapkey-table-entry td:first-child > * { filter: brightness(.8); } - .leaflet-control-attribution a { + .leaflet-container .leaflet-control-attribution a { color: var(--bs-link-color); } .leaflet-control-scale-line { - @extend .border-light, .border-opacity-75; + border-color: rgba(var(--bs-light-rgb), .75) !important; } } diff --git a/app/assets/stylesheets/parameters.scss b/app/assets/stylesheets/parameters.scss index 07549d69b..28bf56901 100644 --- a/app/assets/stylesheets/parameters.scss +++ b/app/assets/stylesheets/parameters.scss @@ -20,3 +20,4 @@ $table-border-factor: .1; $list-group-hover-bg: rgba(var(--bs-emphasis-color-rgb), .075); $enable-negative-margins: true; +$color-mode-type: media-query; diff --git a/app/controllers/api/notes_controller.rb b/app/controllers/api/notes_controller.rb index 9a00814f5..7e2e7fb79 100644 --- a/app/controllers/api/notes_controller.rb +++ b/app/controllers/api/notes_controller.rb @@ -398,9 +398,13 @@ module Api comment = note.comments.create!(attributes) - note.comments.map(&:author).uniq.each do |user| - UserMailer.note_comment_notification(comment, user).deliver_later if notify && user && user != current_user && user.visible? + if notify + note.subscribers.visible.each do |user| + UserMailer.note_comment_notification(comment, user).deliver_later if current_user != user + end end + + NoteSubscription.find_or_create_by(:note => note, :user => current_user) if current_user end end end diff --git a/app/controllers/concerns/user_methods.rb b/app/controllers/concerns/user_methods.rb index 28305b5c3..d79ed48d2 100644 --- a/app/controllers/concerns/user_methods.rb +++ b/app/controllers/concerns/user_methods.rb @@ -6,9 +6,10 @@ module UserMethods ## # ensure that there is a "user" instance variable def lookup_user - @user = User.active.find_by!(:display_name => params[:display_name]) + display_name = params[:display_name] || params[:user_display_name] + @user = User.active.find_by!(:display_name => display_name) rescue ActiveRecord::RecordNotFound - render_unknown_user params[:display_name] + render_unknown_user display_name end ## diff --git a/app/controllers/sessions_controller.rb b/app/controllers/sessions_controller.rb index a3e6f42f0..abbaf5e92 100644 --- a/app/controllers/sessions_controller.rb +++ b/app/controllers/sessions_controller.rb @@ -20,7 +20,7 @@ class SessionsController < ApplicationController end def create - session[:remember_me] ||= params[:remember_me] + session[:remember_me] = params[:remember_me] == "yes" referer = safe_referer(params[:referer]) if params[:referer] diff --git a/app/controllers/user_blocks_controller.rb b/app/controllers/user_blocks_controller.rb index c42c2659d..d427e5fa5 100644 --- a/app/controllers/user_blocks_controller.rb +++ b/app/controllers/user_blocks_controller.rb @@ -29,7 +29,7 @@ class UserBlocksController < ApplicationController end def show - if current_user && current_user == @user_block.user + if current_user && current_user == @user_block.user && !@user_block.deactivates_at @user_block.needs_view = false @user_block.deactivates_at = [@user_block.ends_at, Time.now.utc].max @user_block.save! diff --git a/app/controllers/user_roles_controller.rb b/app/controllers/user_roles_controller.rb index 469b2c40b..912453be8 100644 --- a/app/controllers/user_roles_controller.rb +++ b/app/controllers/user_roles_controller.rb @@ -9,15 +9,15 @@ class UserRolesController < ApplicationController before_action :lookup_user before_action :require_valid_role - before_action :not_in_role, :only => [:grant] - before_action :in_role, :only => [:revoke] + before_action :not_in_role, :only => :create + before_action :in_role, :only => :destroy - def grant + def create @user.roles.create(:role => @role, :granter => current_user) redirect_to user_path(@user) end - def revoke + def destroy # checks that administrator role is not revoked from current user if current_user == @user && @role == "administrator" flash[:error] = t("user_role.filter.not_revoke_admin_current_user") diff --git a/app/helpers/user_helper.rb b/app/helpers/user_helper.rb index 0831cde6e..e00a2253f 100644 --- a/app/helpers/user_helper.rb +++ b/app/helpers/user_helper.rb @@ -60,7 +60,7 @@ module UserHelper :size => "36"), auth_path(options.merge(:provider => provider)), :method => :post, - :class => "auth_button btn btn-light p-2", + :class => "auth_button btn btn-outline-secondary border p-2", :title => t("application.auth_providers.#{provider}.title") ) end diff --git a/app/helpers/user_roles_helper.rb b/app/helpers/user_roles_helper.rb index e839c0ae6..02017bdb9 100644 --- a/app/helpers/user_roles_helper.rb +++ b/app/helpers/user_roles_helper.rb @@ -7,12 +7,12 @@ module UserRolesHelper if current_user&.administrator? if user.role?(role) link_to role_icon_svg_tag(role, false, t("users.show.role.revoke.#{role}")), - revoke_role_path(user, role), - :method => :post, + user_role_path(user, role), + :method => :delete, :data => { :confirm => t("user_role.revoke.are_you_sure", :name => user.display_name, :role => role) } else link_to role_icon_svg_tag(role, true, t("users.show.role.grant.#{role}")), - grant_role_path(user, role), + user_role_path(user, role), :method => :post, :data => { :confirm => t("user_role.grant.are_you_sure", :name => user.display_name, :role => role) } end diff --git a/app/models/note.rb b/app/models/note.rb index 0b0597434..6d8ca078f 100644 --- a/app/models/note.rb +++ b/app/models/note.rb @@ -23,6 +23,8 @@ class Note < ApplicationRecord has_many :comments, -> { left_joins(:author).where(:visible => true, :users => { :status => [nil, "active", "confirmed"] }).order(:created_at) }, :class_name => "NoteComment", :foreign_key => :note_id has_many :all_comments, -> { left_joins(:author).order(:created_at) }, :class_name => "NoteComment", :foreign_key => :note_id, :inverse_of => :note + has_many :subscriptions, :class_name => "NoteSubscription" + has_many :subscribers, :through => :subscriptions, :source => :user validates :id, :uniqueness => true, :presence => { :on => :update }, :numericality => { :on => :update, :only_integer => true } diff --git a/app/models/note_subscription.rb b/app/models/note_subscription.rb new file mode 100644 index 000000000..76e8a226c --- /dev/null +++ b/app/models/note_subscription.rb @@ -0,0 +1,20 @@ +# == Schema Information +# +# Table name: note_subscriptions +# +# user_id :bigint(8) not null, primary key +# note_id :bigint(8) not null, primary key +# +# Indexes +# +# index_note_subscriptions_on_note_id (note_id) +# +# Foreign Keys +# +# fk_rails_... (note_id => notes.id) +# fk_rails_... (user_id => users.id) +# +class NoteSubscription < ApplicationRecord + belongs_to :user + belongs_to :note +end diff --git a/app/models/user.rb b/app/models/user.rb index dd1c08d98..917faca21 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -66,6 +66,8 @@ class User < ApplicationRecord has_and_belongs_to_many :changeset_subscriptions, :class_name => "Changeset", :join_table => "changesets_subscribers", :foreign_key => "subscriber_id" has_many :note_comments, :foreign_key => :author_id, :inverse_of => :author has_many :notes, :through => :note_comments + has_many :note_subscriptions, :class_name => "NoteSubscription" + has_many :subscribed_notes, :through => :note_subscriptions, :source => :note has_many :oauth2_applications, :class_name => Doorkeeper.config.application_model.name, :as => :owner has_many :access_grants, :class_name => Doorkeeper.config.access_grant_model.name, :foreign_key => :resource_owner_id @@ -421,8 +423,8 @@ class User < ApplicationRecord if moderator? Settings.moderator_changeset_comments_per_hour else - previous_comments = changeset_comments.limit(200).count - max_comments = previous_comments / 200.0 * Settings.max_changeset_comments_per_hour + previous_comments = changeset_comments.limit(Settings.comments_to_max_changeset_comments).count + max_comments = previous_comments / Settings.comments_to_max_changeset_comments.to_f * Settings.max_changeset_comments_per_hour max_comments = max_comments.floor.clamp(Settings.initial_changeset_comments_per_hour, Settings.max_changeset_comments_per_hour) max_comments /= 2**active_reports max_comments.floor.clamp(Settings.min_changeset_comments_per_hour, Settings.max_changeset_comments_per_hour) diff --git a/app/views/application/_auth_providers.html.erb b/app/views/application/_auth_providers.html.erb index f6665cefe..3feda6139 100644 --- a/app/views/application/_auth_providers.html.erb +++ b/app/views/application/_auth_providers.html.erb @@ -20,7 +20,7 @@ :data => { "bs-toggle" => "collapse", "bs-target" => "#login_auth_buttons, #openid_login_form" }, :title => t(".openid.title"), - :class => "btn btn-light p-2" %> + :class => "btn btn-outline-secondary border p-2" %> <% elsif provider != @preferred_auth_provider %> <%= auth_button provider %> <% end -%> diff --git a/app/views/layouts/_banner.html.erb b/app/views/layouts/_banner.html.erb index 344c5ed71..3d2c7f774 100644 --- a/app/views/layouts/_banner.html.erb +++ b/app/views/layouts/_banner.html.erb @@ -1,5 +1,5 @@ <% unless (banner = next_banner()).nil? %> - <%= tag.div :id => "banner", :class => "position-relative", :data => { :bs_theme => token_list(:dark => banner[:dark]) } do %> + <%= tag.div :id => "banner", :class => "position-relative", :data => { :bs_theme => (banner[:dark] ? "dark" : "light") } do %> <%= link_to (image_tag banner[:img], :srcset => banner[:srcset], :alt => banner[:alt], :title => banner[:alt]), banner[:link] %> - -
- <%= link_to t("layouts.history"), history_path, :class => "btn btn-outline-primary geolink flex-grow-1", :id => "history_tab" %> - <%= link_to t("layouts.export"), export_path, :class => "btn btn-outline-primary geolink", :id => "export_tab" %> +
+ <%= link_to t("layouts.edit"), + edit_path, + :class => "btn btn-outline-primary geolink editlink", + :id => "editanchor", + :data => { :editor => preferred_editor } %> + +