Merge branch 'master' into feature/add-communities-page

# Conflicts:
#	Gemfile.lock
#	package.json
#	yarn.lock
This commit is contained in:
Adam Hoyle 2022-09-10 16:10:08 +01:00
commit 7e5cbe87ed
527 changed files with 147625 additions and 149175 deletions

View file

@ -1,4 +1,4 @@
blank_issues_enabled: false
blank_issues_enabled: true
contact_links:
- name: There is an issue with the default map layer shown on the front page
url: https://github.com/gravitystorm/openstreetmap-carto

View file

@ -4,18 +4,26 @@ description: You want to request a feature, share an idea or have a question.
# labels:
# assignees:
body:
- type: textarea
id: idea
attributes:
label: Problem
description: Please describe the problem or use case that is the cause for your feature request or idea.
placeholder:
validations:
required: false
- type: textarea
id: idea
attributes:
label: Description
description: Please describe you feature request, idea or question.
description: Please describe your feature request, idea or question.
placeholder:
validations:
required: false
- type: textarea
attributes:
label: Screenshots
description: Please add screenshots if they can help us understand your request/idea/question.
description: Please add screenshots or mockups if they can help us understand your request/idea/question.
placeholder:
validations:
required: false

View file

@ -2,13 +2,16 @@ name: Docker
on:
- push
- pull_request
concurrency:
group: ${{ github.workflow }}-{{ github.head_ref || github.ref }}
cancel-in-progress: true
jobs:
test:
name: Docker
runs-on: ubuntu-20.04
steps:
- name: Checkout source
uses: actions/checkout@v2.4.0
uses: actions/checkout@v3
- name: Poke config
run: |
cp config/example.storage.yml config/storage.yml
@ -23,8 +26,8 @@ jobs:
sleep 15 # let the DB warm up a little
- name: Prepare Database
run: |
docker-compose run --rm web rake db:migrate
docker-compose run web bundle exec rake i18n:js:export
docker-compose run --rm web bundle exec rails db:migrate
docker-compose run --rm web bundle exec rails i18n:js:export
docker-compose run --rm web osmosis --rx docker/null-island.osm.xml --wd host=db database=openstreetmap user=openstreetmap password=openstreetmap validateSchemaVersion=no
- name: Test Basic Website
run: |

View file

@ -2,6 +2,9 @@ name: Lint
on:
- push
- pull_request
concurrency:
group: ${{ github.workflow }}-{{ github.head_ref || github.ref }}
cancel-in-progress: true
env:
os: ubuntu-20.04
ruby: 2.7
@ -11,23 +14,12 @@ jobs:
runs-on: ubuntu-20.04
steps:
- name: Check out code
uses: actions/checkout@v2.4.0
uses: actions/checkout@v3
- name: Setup ruby
uses: actions/setup-ruby@v1.1.3
uses: ruby/setup-ruby@v1
with:
ruby-version: ${{ env.ruby }}
- name: Cache gems
uses: actions/cache@v2.1.7
with:
path: vendor/bundle
key: bundle-${{ env.os }}-${{ env.ruby }}-${{ hashFiles('Gemfile.lock') }}
restore-keys: |
bundle-${{ env.os }}-${{ env.ruby }}-
- name: Install gems
run: |
gem install bundler
bundle config set deployment true
bundle install --jobs 4 --retry 3
bundler-cache: true
- name: Run rubocop
run: bundle exec rubocop --format fuubar
erblint:
@ -35,23 +27,12 @@ jobs:
runs-on: ubuntu-20.04
steps:
- name: Check out code
uses: actions/checkout@v2.4.0
uses: actions/checkout@v3
- name: Setup ruby
uses: actions/setup-ruby@v1.1.3
uses: ruby/setup-ruby@v1
with:
ruby-version: ${{ env.ruby }}
- name: Cache gems
uses: actions/cache@v2.1.7
with:
path: vendor/bundle
key: bundle-${{ env.os }}-${{ env.ruby }}-${{ hashFiles('Gemfile.lock') }}
restore-keys: |
bundle-${{ env.os }}-${{ env.ruby }}-
- name: Install gems
run: |
gem install bundler
bundle config set deployment true
bundle install --jobs 4 --retry 3
bundler-cache: true
- name: Run erblint
run: bundle exec erblint .
eslint:
@ -59,30 +40,19 @@ jobs:
runs-on: ubuntu-20.04
steps:
- name: Check out code
uses: actions/checkout@v2.4.0
uses: actions/checkout@v3
- name: Setup ruby
uses: actions/setup-ruby@v1.1.3
uses: ruby/setup-ruby@v1
with:
ruby-version: ${{ env.ruby }}
- name: Cache gems
uses: actions/cache@v2.1.7
with:
path: vendor/bundle
key: bundle-${{ env.os }}-${{ env.ruby }}-${{ hashFiles('Gemfile.lock') }}
restore-keys: |
bundle-${{ env.os }}-${{ env.ruby }}-
bundler-cache: true
- name: Cache node modules
uses: actions/cache@v2.1.7
uses: actions/cache@v3
with:
path: node_modules
key: yarn-${{ env.os }}-${{ hashFiles('yarn.lock') }}
restore-keys: |
yarn-${{ env.os }}-
- name: Install gems
run: |
gem install bundler
bundle config set deployment true
bundle install --jobs 4 --retry 3
- name: Install node modules
run: bundle exec rake yarn:install
- name: Create dummy database configuration
@ -94,22 +64,11 @@ jobs:
runs-on: ubuntu-20.04
steps:
- name: Check out code
uses: actions/checkout@v2.4.0
uses: actions/checkout@v3
- name: Setup ruby
uses: actions/setup-ruby@v1.1.3
uses: ruby/setup-ruby@v1
with:
ruby-version: ${{ env.ruby }}
- name: Cache gems
uses: actions/cache@v2.1.7
with:
path: vendor/bundle
key: bundle-${{ env.os }}-${{ env.ruby }}-${{ hashFiles('Gemfile.lock') }}
restore-keys: |
bundle-${{ env.os }}-${{ env.ruby }}-
- name: Install gems
run: |
gem install bundler
bundle config set deployment true
bundle install --jobs 4 --retry 3
bundler-cache: true
- name: Run brakeman
run: bundle exec brakeman -q

View file

@ -2,12 +2,15 @@ name: Tests
on:
- push
- pull_request
concurrency:
group: ${{ github.workflow }}-{{ github.head_ref || github.ref }}
cancel-in-progress: true
jobs:
test:
name: Ubuntu ${{ matrix.ubuntu }}, Ruby ${{ matrix.ruby }}
strategy:
matrix:
ubuntu: [18.04, 20.04]
ubuntu: [20.04]
ruby: [2.7, 3.0]
runs-on: ubuntu-${{ matrix.ubuntu }}
env:
@ -15,20 +18,14 @@ jobs:
OPENSTREETMAP_MEMCACHE_SERVERS: 127.0.0.1
steps:
- name: Checkout source
uses: actions/checkout@v2.4.0
uses: actions/checkout@v3
- name: Setup ruby
uses: actions/setup-ruby@v1.1.3
uses: ruby/setup-ruby@v1
with:
ruby-version: ${{ matrix.ruby }}
- name: Cache gems
uses: actions/cache@v2.1.7
with:
path: vendor/bundle
key: bundle-ubuntu-${{ matrix.ubuntu }}-ruby-${{ matrix.ruby }}-${{ hashFiles('Gemfile.lock') }}
restore-keys: |
bundle-ubuntu-${{ matrix.ubuntu }}-ruby-${{ matrix.ruby }}-
bundler-cache: true
- name: Cache node modules
uses: actions/cache@v2.1.7
uses: actions/cache@v3
with:
path: node_modules
key: yarn-ubuntu-${{ matrix.ubuntu }}-${{ hashFiles('yarn.lock') }}
@ -37,12 +34,7 @@ jobs:
- name: Install packages
run: |
sudo apt-get -yqq update
sudo apt-get -yqq install memcached
- name: Install gems
run: |
gem install bundler
bundle config set deployment true
bundle install --jobs 4 --retry 3
sudo apt-get -yqq install memcached libvips-dev
- name: Create database
run: |
sudo systemctl start postgresql

1
.gitignore vendored
View file

@ -6,7 +6,6 @@
.vagrant
app/assets/javascripts/i18n
config/environments/*.local.yml
config/piwik.yml
config/settings.local.yml
config/settings/*.local.yml
coverage

View file

@ -55,9 +55,6 @@ Rails/HasManyOrHasOneDependent:
Rails/HttpPositionalArguments:
Enabled: false
Rails/InverseOf:
Enabled: false
Rails/ReflectionClassName:
Enabled: false

View file

@ -1,6 +1,6 @@
# This configuration was generated by
# `rubocop --auto-gen-config`
# on 2021-09-14 19:29:59 UTC using RuboCop version 1.21.0.
# on 2022-02-23 19:11:04 UTC using RuboCop version 1.25.1.
# The point is for the user to remove these configuration records
# one by one as the offenses are removed from the code base.
# Note that changes in the inspected code, or installation of new
@ -14,30 +14,18 @@ require:
- rubocop-rails
- rubocop-rake
# Offense count: 524
# Offense count: 550
# Cop supports --auto-correct.
# Configuration parameters: AllowHeredoc, AllowURI, URISchemes, IgnoreCopDirectives, IgnoredPatterns.
# URISchemes: http, https
Layout/LineLength:
Max: 270
# Offense count: 62
# Cop supports --auto-correct.
Lint/AmbiguousOperatorPrecedence:
Exclude:
- 'app/controllers/geocoder_controller.rb'
- 'app/models/user.rb'
- 'lib/bounding_box.rb'
- 'lib/osm.rb'
- 'lib/rich_text.rb'
- 'lib/short_link.rb'
- 'test/controllers/api/old_nodes_controller_test.rb'
- 'test/lib/short_link_test.rb'
# Offense count: 34
# Configuration parameters: AllowSafeAssignment.
Lint/AssignmentInCondition:
Exclude:
- 'app/controllers/accounts_controller.rb'
- 'app/controllers/api/traces_controller.rb'
- 'app/controllers/api/user_preferences_controller.rb'
- 'app/controllers/application_controller.rb'
@ -53,22 +41,12 @@ Lint/AssignmentInCondition:
- 'lib/osm.rb'
- 'script/deliver-message'
# Offense count: 8
# Configuration parameters: IgnoreLiteralBranches, IgnoreConstantBranches.
Lint/DuplicateBranch:
Exclude:
- 'app/controllers/api_controller.rb'
- 'app/controllers/diary_entries_controller.rb'
- 'app/controllers/geocoder_controller.rb'
- 'app/helpers/browse_tags_helper.rb'
- 'lib/password_hash.rb'
# Offense count: 643
# Offense count: 665
# Configuration parameters: IgnoredMethods, CountRepeatedAttributes.
Metrics/AbcSize:
Max: 189
# Offense count: 69
# Offense count: 73
# Configuration parameters: CountComments, CountAsOne, ExcludedMethods, IgnoredMethods.
# IgnoredMethods: refine
Metrics/BlockLength:
@ -82,14 +60,14 @@ Metrics/BlockNesting:
# Offense count: 25
# Configuration parameters: CountComments, CountAsOne.
Metrics/ClassLength:
Max: 337
Max: 313
# Offense count: 58
# Configuration parameters: IgnoredMethods.
Metrics/CyclomaticComplexity:
Max: 25
# Offense count: 716
# Offense count: 742
# Configuration parameters: CountComments, CountAsOne, ExcludedMethods, IgnoredMethods.
Metrics/MethodLength:
Max: 179
@ -99,21 +77,19 @@ Metrics/MethodLength:
Metrics/ParameterLists:
Max: 6
# Offense count: 62
# Offense count: 59
# Configuration parameters: IgnoredMethods.
Metrics/PerceivedComplexity:
Max: 26
# Offense count: 528
# Offense count: 550
Minitest/MultipleAssertions:
Max: 54
Max: 52
# Offense count: 3
# Offense count: 1
Naming/AccessorMethodName:
Exclude:
- 'app/controllers/application_controller.rb'
- 'app/helpers/title_helper.rb'
- 'lib/osm.rb'
# Offense count: 8
# Configuration parameters: NamePrefix, ForbiddenPrefixes, AllowedMethods, MethodDefinitionMacros.
@ -123,12 +99,7 @@ Naming/AccessorMethodName:
# MethodDefinitionMacros: define_method, define_singleton_method
Naming/PredicateName:
Exclude:
- 'app/models/changeset.rb'
- 'app/models/old_node.rb'
- 'app/models/old_relation.rb'
- 'app/models/old_way.rb'
- 'app/models/user.rb'
- 'lib/classic_pagination/pagination.rb'
# Offense count: 5
# Configuration parameters: Database, Include.
@ -155,12 +126,26 @@ Rails/HelperInstanceVariable:
Exclude:
- 'app/helpers/title_helper.rb'
# Offense count: 1
# Offense count: 16
# Configuration parameters: IgnoreScopes, Include.
# Include: app/models/**/*.rb
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'
- 'app/models/user.rb'
# Offense count: 2
# Configuration parameters: Include.
# Include: app/controllers/**/*.rb
Rails/LexicallyScopedActionFilter:
Exclude:
- 'app/controllers/oauth2_applications_controller.rb'
- 'app/controllers/oauth2_authorizations_controller.rb'
# Offense count: 5
# Configuration parameters: Include.
@ -182,13 +167,6 @@ Rails/OutputSafety:
- 'lib/rich_text.rb'
- 'test/helpers/application_helper_test.rb'
# Offense count: 91
# Cop supports --auto-correct.
# Configuration parameters: EnforcedStyle.
# SupportedStyles: strict, flexible
Rails/TimeZone:
Enabled: false
# Offense count: 6
# Cop supports --auto-correct.
Rake/Desc:
@ -198,15 +176,15 @@ Rake/Desc:
- 'lib/tasks/subscribe_diary_authors.rake'
- 'lib/tasks/subscribe_old_changesets.rake'
# Offense count: 602
# Offense count: 617
# Cop supports --auto-correct.
# Configuration parameters: EnforcedStyle.
# SupportedStyles: always, always_true, never
Style/FrozenStringLiteralComment:
Enabled: false
# Offense count: 73
# Offense count: 72
# Cop supports --auto-correct.
# Configuration parameters: Strict.
# Configuration parameters: Strict, AllowedNumbers.
Style/NumericLiterals:
MinDigits: 15

View file

@ -30,9 +30,7 @@ If you create a user by signing up to your local website, you need to confirm th
$ bundle exec rails console
>> user = User.find_by(:display_name => "My New User Name")
=> #[ ... ]
>> user.status = "active"
=> "active"
>> user.save!
>> user.activate!
=> true
>> quit
```
@ -56,26 +54,26 @@ $ bundle exec rails console
## OAuth Consumer Keys
Three of the built-in applications communicate via the API, and therefore need OAuth consumer keys configured. These are:
There are two built-in applications which communicate via the API, and therefore need to be registered as OAuth 2 applications. These are:
* iD
* The website itself (for the Notes functionality)
For example, to use the iD editor you need to register it as an OAuth application.
For iD, do the following:
Do the following:
* Log into your Rails Port instance - e.g. http://localhost:3000
* Click on your user name to go to your user page
* Click on "my settings" on the user page
* Click on "oauth settings" on the My settings page
* Click on 'Register your application'.
* Unless you have set up alternatives, use Name: "Local iD" and URL: "http://localhost:3000"
* Check the 'modify the map' box.
* Everything else can be left with the default blank values.
* Click the "Register" button
* On the next page, copy the "consumer key"
* Go to "[OAuth 2 applications](http://localhost:3000/oauth2/applications)" on the My settings page.
* Click on "Register new application".
* Unless you have set up alternatives, use Name: "Local iD" and Main Application URL: "http://localhost:3000"
* Check boxes for the following Permissions
* 'Read user preferences'
* 'Modify user preferences'
* 'Modify the map'
* 'Read private GPS traces'
* 'Upload GPS traces'
* 'Modify notes'
* On the next page, copy the "Client ID"
* Edit config/settings.local.yml in your rails tree
* Add the "id_key" configuration key and the consumer key as the value
* Add the "id_application" configuration with the "Client ID" as the value
* Restart your rails server
An example excerpt from settings.local.yml:
@ -83,11 +81,32 @@ An example excerpt from settings.local.yml:
```
# Default editor
default_editor: "id"
# OAuth consumer key for iD
id_key: "8lFmZPsagHV4l3rkAHq0hWY5vV3Ctl3oEFY1aXth"
# OAuth 2 Client ID for iD
id_application: "Snv…OA0"
```
Follow the same process for registering and configuring the website/Notes (`oauth_key`), or to save time, simply reuse the same consumer key for each.
To allow [Notes](https://wiki.openstreetmap.org/wiki/Notes) and changeset discussions to work, follow a similar process, this time registering an OAuth 2 application for the web site:
* Go to "[OAuth 2 applications](http://localhost:3000/oauth2/applications)" on the My settings page.
* Click on "Register new application".
* Use Name: "OpenStreetMap Web Site" and Redirect URIs: "http://localhost:3000"
* Check boxes for the following Permissions
* 'Modify the map'
* 'Modify notes'
* On the next page, copy the "Client Secret" and "Client ID"
* Edit config/settings.local.yml in your rails tree
* Add the "oauth_application" configuration with the "Client ID" as the value
* Add the "oauth_key" configuration with the "Client Secret" as the value
* Restart your rails server
An example excerpt from settings.local.yml:
```
# OAuth 2 Client ID for the web site
oauth_application: "SGm8QJ6tmoPXEaUPIZzLUmm1iujltYZVWCp9hvGsqXg"
# OAuth 2 Client Secret for the web site
oauth_key: "eRHPm4GtEnw9ovB1Iw7EcCLGtUb66bXbAAspv3aJxlI"
```
## Troubleshooting

View file

@ -38,8 +38,8 @@ bundle exec rails test:all
You can view test coverage statistics by browsing the `coverage` directory.
The tests are automatically run on Pull Requests and other commits with the
results shown on [Travis CI](https://travis-ci.org/openstreetmap/openstreetmap-website).
The tests are automatically run on Pull Requests and other commits via github
actions. The results shown are within the PR display on github.
## Static Analysis
@ -79,14 +79,6 @@ 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.
## Code Documentation
To generate the HTML documentation of the API/rails code, run the command
```
rake doc:app
```
## Committing
When you submit patches, the project maintainer has to read them and

View file

@ -10,13 +10,12 @@ RUN apt-get update \
default-jre-headless \
file \
firefox-geckodriver \
imagemagick \
libarchive-dev \
libffi-dev \
libgd-dev \
libmagickwand-dev \
libpq-dev \
libsasl2-dev \
libvips-dev \
libxml2-dev \
libxslt1-dev \
locales \

21
Gemfile
View file

@ -1,7 +1,7 @@
source "https://rubygems.org"
# Require rails
gem "rails", "6.1.4.1"
gem "rails", "7.0.4"
# Require json for multi_json
gem "json"
@ -36,23 +36,26 @@ gem "image_optim_rails"
# Use argon2 for password hashing
gem "argon2"
# Support brotli compression for assets
gem "sprockets-exporters_pack"
# Load rails plugins
gem "actionpack-page_caching", ">= 1.2.0"
gem "activerecord-import"
gem "active_record_union"
gem "bootstrap", "~> 4.5.0"
gem "bootstrap_form", "~> 4.0"
gem "bootstrap", "~> 5.1.0"
gem "bootstrap_form", "~> 5.0"
gem "cancancan"
gem "composite_primary_keys", "~> 13.0.0", "!= 13.0.1"
gem "composite_primary_keys", "~> 14.0.0"
gem "config"
gem "delayed_job_active_record"
gem "frozen_record"
gem "http_accept_language", "~> 2.1.1"
gem "i18n-js", ">= 3.0.0"
gem "i18n-js", "~> 3.9.2"
gem "oauth-plugin", ">= 0.5.1"
gem "openstreetmap-deadlock_retry", ">= 1.3.1", :require => "deadlock_retry"
gem "rack-cors"
gem "rails-i18n", "~> 6.0.0"
gem "rails-i18n", "~> 7.0.0"
gem "rinku", ">= 2.0.6", :require => "rails_rinku"
gem "strong_migrations"
gem "validates_email_format_of", ">= 1.5.1"
@ -74,7 +77,7 @@ gem "omniauth-rails_csrf_protection", "~> 1.0"
gem "omniauth-windowslive"
# Doorkeeper for OAuth2
gem "doorkeeper"
gem "doorkeeper", "~> 5.5.4"
gem "doorkeeper-i18n"
# Markdown formatting support
@ -145,14 +148,14 @@ group :test do
gem "erb_lint", :require => false
gem "factory_bot_rails"
gem "minitest", "~> 5.1"
gem "puma", "~> 5.3"
gem "puma", "~> 5.6"
gem "rails-controller-testing"
gem "rubocop"
gem "rubocop-minitest"
gem "rubocop-performance"
gem "rubocop-rails"
gem "rubocop-rake"
gem "selenium-webdriver", "~> 3.142.7"
gem "selenium-webdriver"
gem "simplecov", :require => false
gem "simplecov-lcov", :require => false
gem "webmock"

View file

@ -1,132 +1,138 @@
GEM
remote: https://rubygems.org/
specs:
aasm (5.2.0)
aasm (5.3.0)
concurrent-ruby (~> 1.0)
actioncable (6.1.4.1)
actionpack (= 6.1.4.1)
activesupport (= 6.1.4.1)
actioncable (7.0.4)
actionpack (= 7.0.4)
activesupport (= 7.0.4)
nio4r (~> 2.0)
websocket-driver (>= 0.6.1)
actionmailbox (6.1.4.1)
actionpack (= 6.1.4.1)
activejob (= 6.1.4.1)
activerecord (= 6.1.4.1)
activestorage (= 6.1.4.1)
activesupport (= 6.1.4.1)
actionmailbox (7.0.4)
actionpack (= 7.0.4)
activejob (= 7.0.4)
activerecord (= 7.0.4)
activestorage (= 7.0.4)
activesupport (= 7.0.4)
mail (>= 2.7.1)
actionmailer (6.1.4.1)
actionpack (= 6.1.4.1)
actionview (= 6.1.4.1)
activejob (= 6.1.4.1)
activesupport (= 6.1.4.1)
net-imap
net-pop
net-smtp
actionmailer (7.0.4)
actionpack (= 7.0.4)
actionview (= 7.0.4)
activejob (= 7.0.4)
activesupport (= 7.0.4)
mail (~> 2.5, >= 2.5.4)
net-imap
net-pop
net-smtp
rails-dom-testing (~> 2.0)
actionpack (6.1.4.1)
actionview (= 6.1.4.1)
activesupport (= 6.1.4.1)
rack (~> 2.0, >= 2.0.9)
actionpack (7.0.4)
actionview (= 7.0.4)
activesupport (= 7.0.4)
rack (~> 2.0, >= 2.2.0)
rack-test (>= 0.6.3)
rails-dom-testing (~> 2.0)
rails-html-sanitizer (~> 1.0, >= 1.2.0)
actionpack-page_caching (1.2.4)
actionpack (>= 4.0.0)
actiontext (6.1.4.1)
actionpack (= 6.1.4.1)
activerecord (= 6.1.4.1)
activestorage (= 6.1.4.1)
activesupport (= 6.1.4.1)
actiontext (7.0.4)
actionpack (= 7.0.4)
activerecord (= 7.0.4)
activestorage (= 7.0.4)
activesupport (= 7.0.4)
globalid (>= 0.6.0)
nokogiri (>= 1.8.5)
actionview (6.1.4.1)
activesupport (= 6.1.4.1)
actionview (7.0.4)
activesupport (= 7.0.4)
builder (~> 3.1)
erubi (~> 1.4)
rails-dom-testing (~> 2.0)
rails-html-sanitizer (~> 1.1, >= 1.2.0)
active_record_union (1.3.0)
activerecord (>= 4.0)
activejob (6.1.4.1)
activesupport (= 6.1.4.1)
activejob (7.0.4)
activesupport (= 7.0.4)
globalid (>= 0.3.6)
activemodel (6.1.4.1)
activesupport (= 6.1.4.1)
activerecord (6.1.4.1)
activemodel (= 6.1.4.1)
activesupport (= 6.1.4.1)
activerecord-import (1.2.0)
activerecord (>= 3.2)
activestorage (6.1.4.1)
actionpack (= 6.1.4.1)
activejob (= 6.1.4.1)
activerecord (= 6.1.4.1)
activesupport (= 6.1.4.1)
marcel (~> 1.0.0)
activemodel (7.0.4)
activesupport (= 7.0.4)
activerecord (7.0.4)
activemodel (= 7.0.4)
activesupport (= 7.0.4)
activerecord-import (1.4.0)
activerecord (>= 4.2)
activestorage (7.0.4)
actionpack (= 7.0.4)
activejob (= 7.0.4)
activerecord (= 7.0.4)
activesupport (= 7.0.4)
marcel (~> 1.0)
mini_mime (>= 1.1.0)
activesupport (6.1.4.1)
activesupport (7.0.4)
concurrent-ruby (~> 1.0, >= 1.0.2)
i18n (>= 1.6, < 2)
minitest (>= 5.1)
tzinfo (~> 2.0)
zeitwerk (~> 2.3)
addressable (2.8.0)
public_suffix (>= 2.0.2, < 5.0)
annotate (3.1.1)
activerecord (>= 3.2, < 7.0)
addressable (2.8.1)
public_suffix (>= 2.0.2, < 6.0)
annotate (3.2.0)
activerecord (>= 3.2, < 8.0)
rake (>= 10.4, < 14.0)
argon2 (2.1.1)
ffi (~> 1.14)
ffi-compiler (~> 1.0)
ast (2.4.2)
autoprefixer-rails (10.3.3.0)
autoprefixer-rails (10.4.7.0)
execjs (~> 2)
aws-eventstream (1.2.0)
aws-partitions (1.539.0)
aws-sdk-core (3.124.0)
aws-partitions (1.628.0)
aws-sdk-core (3.144.0)
aws-eventstream (~> 1, >= 1.0.2)
aws-partitions (~> 1, >= 1.525.0)
aws-sigv4 (~> 1.1)
jmespath (~> 1.0)
aws-sdk-kms (1.52.0)
aws-sdk-core (~> 3, >= 3.122.0)
jmespath (~> 1, >= 1.6.1)
aws-sdk-kms (1.58.0)
aws-sdk-core (~> 3, >= 3.127.0)
aws-sigv4 (~> 1.1)
aws-sdk-s3 (1.109.0)
aws-sdk-core (~> 3, >= 3.122.0)
aws-sdk-s3 (1.114.0)
aws-sdk-core (~> 3, >= 3.127.0)
aws-sdk-kms (~> 1)
aws-sigv4 (~> 1.4)
aws-sigv4 (1.4.0)
aws-sigv4 (1.5.1)
aws-eventstream (~> 1, >= 1.0.2)
better_errors (2.9.1)
coderay (>= 1.0.0)
erubi (>= 1.0.0)
rack (>= 0.9.0)
better_html (1.0.16)
actionview (>= 4.0)
activesupport (>= 4.0)
better_html (2.0.1)
actionview (>= 6.0)
activesupport (>= 6.0)
ast (~> 2.0)
erubi (~> 1.4)
html_tokenizer (~> 0.0.6)
parser (>= 2.4)
smart_properties
binding_of_caller (1.0.0)
debug_inspector (>= 0.0.1)
bootsnap (1.9.3)
msgpack (~> 1.0)
bootstrap (4.5.3)
bootsnap (1.13.0)
msgpack (~> 1.2)
bootstrap (5.1.3)
autoprefixer-rails (>= 9.1.0)
popper_js (>= 1.14.3, < 2)
popper_js (>= 2.9.3, < 3)
sassc-rails (>= 2.0.0)
bootstrap_form (4.5.0)
bootstrap_form (5.1.0)
actionpack (>= 5.2)
activemodel (>= 5.2)
brakeman (5.1.2)
brakeman (5.3.1)
brotli (0.4.0)
browser (5.3.1)
builder (3.2.4)
bzip2-ffi (1.1.0)
ffi (~> 1.0)
cancancan (3.3.0)
canonical-rails (0.2.13)
rails (>= 4.1, <= 7.0)
capybara (3.36.0)
cancancan (3.4.0)
canonical-rails (0.2.14)
rails (>= 4.1, <= 7.1)
capybara (3.37.1)
addressable
matrix
mini_mime (>= 0.1.3)
@ -135,45 +141,45 @@ GEM
rack-test (>= 0.6.3)
regexp_parser (>= 1.5, < 3.0)
xpath (~> 3.2)
childprocess (3.0.0)
childprocess (4.1.0)
coderay (1.1.3)
composite_primary_keys (13.0.0)
activerecord (~> 6.1.0)
concurrent-ruby (1.1.9)
config (3.1.0)
composite_primary_keys (14.0.4)
activerecord (~> 7.0.2)
concurrent-ruby (1.1.10)
config (4.0.0)
deep_merge (~> 1.2, >= 1.2.1)
dry-validation (~> 1.0, >= 1.0.0)
connection_pool (2.2.5)
crack (0.4.5)
rexml
crass (1.0.6)
dalli (3.1.0)
dalli (3.2.2)
debug_inspector (1.1.0)
deep_merge (1.2.1)
delayed_job (4.1.9)
activesupport (>= 3.0, < 6.2)
delayed_job_active_record (4.1.6)
activerecord (>= 3.0, < 6.2)
deep_merge (1.2.2)
delayed_job (4.1.10)
activesupport (>= 3.0, < 8.0)
delayed_job_active_record (4.1.7)
activerecord (>= 3.0, < 8.0)
delayed_job (>= 3.0, < 5)
digest (3.1.0)
docile (1.4.0)
doorkeeper (5.5.4)
railties (>= 5)
doorkeeper-i18n (5.2.3)
doorkeeper-i18n (5.2.4)
doorkeeper (>= 5.2)
dry-configurable (0.13.0)
dry-configurable (0.15.0)
concurrent-ruby (~> 1.0)
dry-core (~> 0.6)
dry-container (0.9.0)
dry-container (0.10.1)
concurrent-ruby (~> 1.0)
dry-configurable (~> 0.13, >= 0.13.0)
dry-core (0.7.1)
dry-core (0.8.1)
concurrent-ruby (~> 1.0)
dry-inflector (0.2.1)
dry-initializer (3.0.4)
dry-inflector (0.3.0)
dry-initializer (3.1.1)
dry-logic (1.2.0)
concurrent-ruby (~> 1.0)
dry-core (~> 0.5, >= 0.5)
dry-schema (1.8.0)
dry-schema (1.10.2)
concurrent-ruby (~> 1.0)
dry-configurable (~> 0.13, >= 0.13.0)
dry-core (~> 0.5, >= 0.5)
@ -186,48 +192,32 @@ GEM
dry-core (~> 0.5, >= 0.5)
dry-inflector (~> 0.1, >= 0.1.2)
dry-logic (~> 1.0, >= 1.0.2)
dry-validation (1.7.0)
dry-validation (1.8.1)
concurrent-ruby (~> 1.0)
dry-container (~> 0.7, >= 0.7.1)
dry-core (~> 0.5, >= 0.5)
dry-initializer (~> 3.0)
dry-schema (~> 1.8, >= 1.8.0)
erb_lint (0.1.1)
erb_lint (0.2.0)
activesupport
better_html (~> 1.0.7)
html_tokenizer
better_html (>= 2.0.1)
parser (>= 2.7.1.4)
rainbow
rubocop
smart_properties
erubi (1.10.0)
erubi (1.11.0)
execjs (2.8.1)
exifr (1.3.9)
factory_bot (6.2.0)
factory_bot (6.2.1)
activesupport (>= 5.0.0)
factory_bot_rails (6.2.0)
factory_bot (~> 6.2.0)
railties (>= 5.0.0)
faraday (1.8.0)
faraday-em_http (~> 1.0)
faraday-em_synchrony (~> 1.0)
faraday-excon (~> 1.1)
faraday-httpclient (~> 1.0.1)
faraday-net_http (~> 1.0)
faraday-net_http_persistent (~> 1.1)
faraday-patron (~> 1.0)
faraday-rack (~> 1.0)
multipart-post (>= 1.2, < 3)
faraday (2.5.2)
faraday-net_http (>= 2.0, < 3.1)
ruby2_keywords (>= 0.0.4)
faraday-em_http (1.0.0)
faraday-em_synchrony (1.0.0)
faraday-excon (1.1.0)
faraday-httpclient (1.0.1)
faraday-net_http (1.0.1)
faraday-net_http_persistent (1.2.0)
faraday-patron (1.0.0)
faraday-rack (1.0.0)
ffi (1.15.4)
faraday-net_http (3.0.0)
ffi (1.15.5)
ffi-compiler (1.0.1)
ffi (>= 1.0.0)
rake
@ -242,12 +232,11 @@ GEM
activesupport (>= 5.0)
hashdiff (1.0.1)
hashie (5.0.0)
html_tokenizer (0.0.7)
htmlentities (4.3.4)
http_accept_language (2.1.1)
i18n (1.8.11)
i18n (1.12.0)
concurrent-ruby (~> 1.0)
i18n-js (3.9.0)
i18n-js (3.9.2)
i18n (>= 0.6.6)
image_optim (0.31.1)
exifr (~> 1.2, >= 1.2.2)
@ -255,35 +244,36 @@ GEM
image_size (>= 1.5, < 4)
in_threads (~> 1.3)
progress (~> 3.0, >= 3.0.1)
image_optim_rails (0.4.3)
image_optim_rails (0.5.0)
image_optim (~> 0.24)
rails
railties
sprockets
image_processing (1.12.1)
image_processing (1.12.2)
mini_magick (>= 4.9.5, < 5)
ruby-vips (>= 2.0.17, < 3)
image_size (3.0.1)
in_threads (1.5.4)
jbuilder (2.11.3)
image_size (3.0.2)
in_threads (1.6.0)
jbuilder (2.11.5)
actionview (>= 5.0.0)
activesupport (>= 5.0.0)
jmespath (1.4.0)
jquery-rails (4.4.0)
jmespath (1.6.1)
jquery-rails (4.5.0)
rails-dom-testing (>= 1, < 3)
railties (>= 4.2.0)
thor (>= 0.14, < 2.0)
json (2.6.1)
jwt (2.3.0)
json (2.6.2)
jwt (2.5.0)
kgio (2.11.4)
kramdown (2.3.1)
kramdown (2.4.0)
rexml
libxml-ruby (3.2.1)
listen (3.7.0)
libxml-ruby (3.2.3)
listen (3.7.1)
rb-fsevent (~> 0.10, >= 0.10.3)
rb-inotify (~> 0.9, >= 0.9.10)
logstasher (2.1.5)
activesupport (>= 5.2)
request_store
loofah (2.12.0)
loofah (2.18.0)
crass (~> 1.0.2)
nokogiri (>= 1.5.9)
mail (2.7.1)
@ -294,15 +284,28 @@ GEM
method_source (1.0.0)
mini_magick (4.11.0)
mini_mime (1.1.2)
mini_portile2 (2.6.1)
minitest (5.14.4)
msgpack (1.4.2)
mini_portile2 (2.8.0)
minitest (5.16.3)
msgpack (1.5.6)
multi_json (1.15.0)
multi_xml (0.6.0)
multipart-post (2.1.1)
net-imap (0.2.3)
digest
net-protocol
strscan
net-pop (0.1.1)
digest
net-protocol
timeout
net-protocol (0.1.3)
timeout
net-smtp (0.3.1)
digest
net-protocol
timeout
nio4r (2.5.8)
nokogiri (1.12.5)
mini_portile2 (~> 2.6.1)
nokogiri (1.13.8)
mini_portile2 (~> 2.8.0)
racc (~> 1.4)
oauth (0.4.7)
oauth-plugin (0.5.1)
@ -310,8 +313,8 @@ GEM
oauth (~> 0.4.4)
oauth2 (>= 0.5.0)
rack
oauth2 (1.4.7)
faraday (>= 0.8, < 2.0)
oauth2 (1.4.10)
faraday (>= 0.17.3, < 3.0)
jwt (>= 1.0, < 3.0)
multi_json (~> 1.3)
multi_xml (~> 0.5)
@ -325,7 +328,7 @@ GEM
omniauth-github (2.0.0)
omniauth (~> 2.0)
omniauth-oauth2 (~> 1.7.1)
omniauth-google-oauth2 (1.0.0)
omniauth-google-oauth2 (1.0.1)
jwt (>= 2.0)
oauth2 (~> 1.1)
omniauth (~> 2.0)
@ -336,57 +339,56 @@ GEM
omniauth-oauth (1.2.0)
oauth
omniauth (>= 1.0, < 3)
omniauth-oauth2 (1.7.2)
oauth2 (~> 1.4)
omniauth-oauth2 (1.7.3)
oauth2 (>= 1.4, < 3)
omniauth (>= 1.9, < 3)
omniauth-openid (2.0.1)
omniauth (>= 1.0, < 3.0)
rack-openid (~> 1.4.0)
omniauth-rails_csrf_protection (1.0.0)
omniauth-rails_csrf_protection (1.0.1)
actionpack (>= 4.2)
omniauth (~> 2.0)
omniauth-windowslive (0.0.12)
multi_json (~> 1.12)
omniauth-oauth2 (~> 1.4)
openstreetmap-deadlock_retry (1.3.1)
parallel (1.21.0)
parser (3.0.3.1)
parallel (1.22.1)
parser (3.1.2.1)
ast (~> 2.4.1)
pg (1.2.3)
popper_js (1.16.0)
pg (1.4.3)
popper_js (2.11.5)
progress (3.6.0)
public_suffix (4.0.6)
puma (5.5.2)
public_suffix (5.0.0)
puma (5.6.5)
nio4r (~> 2.0)
quad_tile (1.0.1)
r2 (0.2.7)
racc (1.6.0)
rack (2.2.3)
rack (2.2.4)
rack-cors (1.1.1)
rack (>= 2.0.0)
rack-openid (1.4.2)
rack (>= 1.1.0)
ruby-openid (>= 2.1.8)
rack-protection (2.1.0)
rack-protection (2.2.2)
rack
rack-test (1.1.0)
rack (>= 1.0, < 3)
rack-test (2.0.2)
rack (>= 1.3)
rack-uri_sanitizer (0.0.2)
rails (6.1.4.1)
actioncable (= 6.1.4.1)
actionmailbox (= 6.1.4.1)
actionmailer (= 6.1.4.1)
actionpack (= 6.1.4.1)
actiontext (= 6.1.4.1)
actionview (= 6.1.4.1)
activejob (= 6.1.4.1)
activemodel (= 6.1.4.1)
activerecord (= 6.1.4.1)
activestorage (= 6.1.4.1)
activesupport (= 6.1.4.1)
rails (7.0.4)
actioncable (= 7.0.4)
actionmailbox (= 7.0.4)
actionmailer (= 7.0.4)
actionpack (= 7.0.4)
actiontext (= 7.0.4)
actionview (= 7.0.4)
activejob (= 7.0.4)
activemodel (= 7.0.4)
activerecord (= 7.0.4)
activestorage (= 7.0.4)
activesupport (= 7.0.4)
bundler (>= 1.15.0)
railties (= 6.1.4.1)
sprockets-rails (>= 2.0.0)
railties (= 7.0.4)
rails-controller-testing (1.0.5)
actionpack (>= 5.0.1.rc1)
actionview (>= 5.0.1.rc1)
@ -394,48 +396,50 @@ GEM
rails-dom-testing (2.0.3)
activesupport (>= 4.2.0)
nokogiri (>= 1.6)
rails-html-sanitizer (1.4.2)
rails-html-sanitizer (1.4.3)
loofah (~> 2.3)
rails-i18n (6.0.0)
rails-i18n (7.0.5)
i18n (>= 0.7, < 2)
railties (>= 6.0.0, < 7)
railties (6.1.4.1)
actionpack (= 6.1.4.1)
activesupport (= 6.1.4.1)
railties (>= 6.0.0, < 8)
railties (7.0.4)
actionpack (= 7.0.4)
activesupport (= 7.0.4)
method_source
rake (>= 0.13)
rake (>= 12.2)
thor (~> 1.0)
rainbow (3.0.0)
zeitwerk (~> 2.5)
rainbow (3.1.1)
rake (13.0.6)
rb-fsevent (0.11.0)
rb-fsevent (0.11.2)
rb-inotify (0.10.1)
ffi (~> 1.0)
regexp_parser (2.2.0)
request_store (1.5.0)
regexp_parser (2.5.0)
request_store (1.5.1)
rack (>= 1.4)
rexml (3.2.5)
rinku (2.0.6)
rotp (6.2.0)
rubocop (1.23.0)
rubocop (1.36.0)
json (~> 2.3)
parallel (~> 1.10)
parser (>= 3.0.0.0)
parser (>= 3.1.2.1)
rainbow (>= 2.2.2, < 4.0)
regexp_parser (>= 1.8, < 3.0)
rexml
rubocop-ast (>= 1.12.0, < 2.0)
rexml (>= 3.2.5, < 4.0)
rubocop-ast (>= 1.20.1, < 2.0)
ruby-progressbar (~> 1.7)
unicode-display_width (>= 1.4.0, < 3.0)
rubocop-ast (1.14.0)
parser (>= 3.0.1.1)
rubocop-minitest (0.17.0)
rubocop-ast (1.21.0)
parser (>= 3.1.1.0)
rubocop-minitest (0.22.0)
rubocop (>= 0.90, < 2.0)
rubocop-performance (1.12.0)
rubocop-performance (1.14.3)
rubocop (>= 1.7.0, < 2.0)
rubocop-ast (>= 0.4.0)
rubocop-rails (2.12.4)
rubocop-rails (2.16.0)
activesupport (>= 4.2.0)
rack (>= 1.1)
rubocop (>= 1.7.0, < 2.0)
rubocop (>= 1.33.0, < 2.0)
rubocop-rake (0.6.0)
rubocop (~> 1.0)
ruby-openid (2.9.2)
@ -455,47 +459,55 @@ GEM
sprockets (> 3.0)
sprockets-rails
tilt
secure_headers (6.3.3)
selenium-webdriver (3.142.7)
childprocess (>= 0.5, < 4.0)
rubyzip (>= 1.2.2)
secure_headers (6.4.0)
selenium-webdriver (4.4.0)
childprocess (>= 0.5, < 5.0)
rexml (~> 3.2, >= 3.2.5)
rubyzip (>= 1.2.2, < 3.0)
websocket (~> 1.0)
simplecov (0.21.2)
docile (~> 1.1)
simplecov-html (~> 0.11)
simplecov_json_formatter (~> 0.1)
simplecov-html (0.12.3)
simplecov-lcov (0.8.0)
simplecov_json_formatter (0.1.3)
smart_properties (1.16.3)
sprockets (4.0.2)
simplecov_json_formatter (0.1.4)
smart_properties (1.17.0)
sprockets (4.1.1)
concurrent-ruby (~> 1.0)
rack (> 1, < 3)
sprockets-rails (3.4.1)
sprockets-exporters_pack (0.1.2)
brotli (>= 0.2.0)
sprockets (>= 4.0.0.beta3)
sprockets-rails (3.4.2)
actionpack (>= 5.2)
activesupport (>= 5.2)
sprockets (>= 3.0.0)
strong_migrations (0.7.8)
activerecord (>= 5)
terser (1.1.8)
strong_migrations (1.3.0)
activerecord (>= 5.2)
strscan (3.0.4)
terser (1.1.12)
execjs (>= 0.3.0, < 3)
thor (1.1.0)
tilt (2.0.10)
tzinfo (2.0.4)
thor (1.2.1)
tilt (2.0.11)
timeout (0.3.0)
tzinfo (2.0.5)
concurrent-ruby (~> 1.0)
unicode-display_width (2.1.0)
validates_email_format_of (1.6.3)
unicode-display_width (2.2.0)
validates_email_format_of (1.7.2)
i18n
vendorer (0.2.0)
webmock (3.14.0)
webmock (3.18.1)
addressable (>= 2.8.0)
crack (>= 0.3.2)
hashdiff (>= 0.4.0, < 2.0.0)
websocket (1.2.9)
websocket-driver (0.7.5)
websocket-extensions (>= 0.1.0)
websocket-extensions (0.1.5)
xpath (3.2.0)
nokogiri (~> 1.8)
zeitwerk (2.5.1)
zeitwerk (2.6.0)
PLATFORMS
ruby
@ -512,21 +524,21 @@ DEPENDENCIES
better_errors
binding_of_caller
bootsnap (>= 1.4.2)
bootstrap (~> 4.5.0)
bootstrap_form (~> 4.0)
bootstrap (~> 5.1.0)
bootstrap_form (~> 5.0)
brakeman
browser
bzip2-ffi
cancancan
canonical-rails
capybara (>= 2.15)
composite_primary_keys (~> 13.0.0, != 13.0.1)
composite_primary_keys (~> 14.0.0)
config
connection_pool
dalli
debug_inspector
delayed_job_active_record
doorkeeper
doorkeeper (~> 5.5.4)
doorkeeper-i18n
erb_lint
factory_bot_rails
@ -536,7 +548,7 @@ DEPENDENCIES
gd2-ffij (>= 0.4.0)
htmlentities
http_accept_language (~> 2.1.1)
i18n-js (>= 3.0.0)
i18n-js (~> 3.9.2)
image_optim_rails
image_processing
jbuilder (~> 2.7)
@ -561,14 +573,14 @@ DEPENDENCIES
omniauth-windowslive
openstreetmap-deadlock_retry (>= 1.3.1)
pg
puma (~> 5.3)
puma (~> 5.6)
quad_tile (~> 1.0.1)
r2 (~> 0.2.7)
rack-cors
rack-uri_sanitizer
rails (= 6.1.4.1)
rails (= 7.0.4)
rails-controller-testing
rails-i18n (~> 6.0.0)
rails-i18n (~> 7.0.0)
rinku (>= 2.0.6)
rotp
rubocop
@ -579,9 +591,10 @@ DEPENDENCIES
sanitize
sassc-rails
secure_headers
selenium-webdriver (~> 3.142.7)
selenium-webdriver
simplecov
simplecov-lcov
sprockets-exporters_pack
strong_migrations
terser
validates_email_format_of (>= 1.5.1)
@ -589,4 +602,4 @@ DEPENDENCIES
webmock
BUNDLED WITH
2.2.19
2.3.7

View file

@ -24,7 +24,6 @@ of packages required before you can get the various gems installed.
* Ruby 2.7+
* PostgreSQL 9.1+
* ImageMagick
* Bundler (see note below about [developer Ruby setup](#rbenv))
* Javascript Runtime
@ -33,10 +32,10 @@ These can be installed on Ubuntu 20.04 or later with:
```
sudo apt-get update
sudo apt-get install ruby2.7 libruby2.7 ruby2.7-dev \
libmagickwand-dev libxml2-dev libxslt1-dev nodejs \
libvips-dev libxml2-dev libxslt1-dev nodejs \
apache2 apache2-dev build-essential git-core firefox-geckodriver \
postgresql postgresql-contrib libpq-dev libsasl2-dev \
imagemagick libffi-dev libgd-dev libarchive-dev libbz2-dev yarnpkg
libffi-dev libgd-dev libarchive-dev libbz2-dev yarnpkg
sudo gem2.7 install bundler
```
@ -51,8 +50,8 @@ sudo dnf install ruby ruby-devel rubygem-rdoc rubygem-bundler rubygems \
libxml2-devel nodejs \
gcc gcc-c++ git \
postgresql postgresql-server postgresql-contrib libpq-devel \
perl-podlators ImageMagick libffi-devel gd-devel libarchive-devel \
bzip2-devel nodejs-yarn
perl-podlators libffi-devel gd-devel libarchive-devel \
bzip2-devel nodejs-yarn vips-devel
```
If you didn't already have PostgreSQL installed then create a PostgreSQL instance and start the server:
@ -90,7 +89,7 @@ Installing other dependencies:
* Install Homebrew from https://brew.sh/
* Install the latest version of Ruby: `brew install ruby`
* Install other dependencies: `brew install imagemagick libxml2 gd yarn pngcrush optipng pngquant jhead jpegoptim gifsicle svgo`
* Install other dependencies: `brew install libxml2 gd yarn pngcrush optipng pngquant jhead jpegoptim gifsicle svgo advancecomp vips`
* Install Bundler: `gem install bundler` (you might need to `sudo gem install bundler` if you get an error about permissions - or see note below about [developer Ruby setup](#rbenv))
You will need to tell `bundler` that `libxml2` is installed in a Homebrew location. If it uses the system-installed one then you will get errors installing the `libxml-ruby` gem later on<a name="macosx-bundle-config"></a>.
@ -102,8 +101,7 @@ bundle config build.libxml-ruby --with-xml2-config=/usr/local/opt/libxml2/bin/xm
If you want to run the tests, you need `geckodriver` as well:
```
brew tap homebrew/cask
brew cask install geckodriver
brew install geckodriver
```
Note that OS X does not have a /home directory by default, so if you are using the GPX functions, you will need to change the directories specified in config/application.yml.

9
SECURITY.md Normal file
View file

@ -0,0 +1,9 @@
# Security Policy
## Reporting a Vulnerability
We welcome any reports of security vulnerabilities, and we will respond to you quickly to acknowledge receipt.
To report a vulnerability please email [the maintainers using this link](mailto:tom@compton.nu;openstreetmap-website@gravitystorm.co.uk;security@openstreetmap.org). This will also notify the security team for the main deployment of this software.
Please note that we do not offer any bug bounties and we do not participate in any bug programs. If your security report is validated by us, then we are happy to credit you publicly in our issue tracker, on request.

View file

@ -4,28 +4,28 @@ folder 'vendor/assets' do
end
folder 'leaflet' do
from 'git://github.com/aratcliffe/Leaflet.contextmenu.git', :tag => 'v1.5.1' do
from 'https://github.com/aratcliffe/Leaflet.contextmenu.git', :tag => 'v1.5.1' do
file 'leaflet.contextmenu.js', 'dist/leaflet.contextmenu.js'
file 'leaflet.contextmenu.css', 'dist/leaflet.contextmenu.css'
end
from 'git://github.com/kajic/leaflet-locationfilter.git' do
from 'https://github.com/kajic/leaflet-locationfilter.git' do
file 'leaflet.locationfilter.css', 'src/locationfilter.css'
file 'leaflet.locationfilter.js', 'src/locationfilter.js'
folder 'img', 'src/img'
end
from 'git://github.com/jfirebaugh/leaflet-osm.git' do
from 'https://github.com/jfirebaugh/leaflet-osm.git' do
file 'leaflet.osm.js', 'leaflet-osm.js'
end
from 'git://github.com/jieter/Leaflet.encoded.git', :tag => '0.0.9' do
from 'https://github.com/jieter/Leaflet.encoded.git', :tag => '0.0.9' do
file 'leaflet.polyline.js', 'Polyline.encoded.js'
end
end
folder 'iD' do
from 'git://github.com/openstreetmap/iD', :branch => 'release' do
from 'https://github.com/openstreetmap/iD', :branch => 'release' do
folder 'iD/data', 'dist/data'
folder 'iD/img', 'dist/img'
folder 'iD/locales', 'dist/locales'
@ -38,7 +38,7 @@ folder 'vendor/assets' do
end
end
file 'iD.js', 'dist/iD.legacy.js'
file 'iD.js', 'dist/iD.js'
end
end

View file

@ -23,7 +23,7 @@ class Ability
can [:index, :show], Redaction
can [:new, :create, :destroy], :session
can [:index, :show, :data, :georss, :picture, :icon], Trace
can [:terms, :new, :create, :save, :show, :auth_success, :auth_failure], User
can [:terms, :new, :create, :save, :suspended, :show, :auth_success, :auth_failure], User
can [:index, :show, :blocks_on, :blocks_by], UserBlock
can [:index, :show], Node
can [:index, :show, :full, :ways_for_node], Way
@ -36,12 +36,14 @@ class Ability
if user
can :welcome, :site
can [:revoke, :authorize], :oauth
can [:show], :deletion
if Settings.status != "database_offline"
can [:index, :new, :create, :show, :edit, :update, :destroy], ClientApplication
can [:index, :new, :create, :show, :edit, :update, :destroy], :oauth2_application
can [:index, :destroy], :oauth2_authorized_application
can [:new, :show, :create, :destroy], :oauth2_authorization
can [:edit, :update, :destroy], :account
can [:show], :dashboard
can [:new, :create, :edit, :update, :comment, :subscribe, :unsubscribe], DiaryEntry
can [:make_friend, :remove_friend], Friendship

View file

@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" width="36" height="36" viewBox="0 0 36 36"><defs><style>.a{fill:#231f20;}.b{fill:#fff;}</style></defs><rect class="a" width="36" height="36"/><path class="b" d="M19.15,19.33a1.73,1.73,0,1,0,1.72-1.83,1.76,1.76,0,0,0-1.72,1.83Zm9,3.64H26V13.33h2.18Zm3.44-11.45.73-2.32L27.7,7.75,24.5,17.94a4.25,4.25,0,0,1,.25,1.39,3.77,3.77,0,0,1-3.88,3.84,3.79,3.79,0,0,1-2.43-.8l-.62,2.69L22,26l-.78,2.47,4.61,1.45,1.44-4.6,4.09.49.37-3.13a1.46,1.46,0,0,1-2.39-1.11,1.45,1.45,0,0,1,2.62-.85l1.08-9Zm-20.5,8.1h2l-1-3.36Zm9.79-4.14a3.69,3.69,0,0,1,3.57,2.32l2.22-9.7L22,7l-.41,1.8-.86-.56-5.42,8.3,1.79-5.69L12.44,9.42l-1.23,3.91h1.85L16.82,23h-2.6l-.47-1.4H10.41L9.9,23H7.32l3.21-8.14,2.28-8.65L8.14,5,3,24.46l4.35,1.15-1.26,4,4.61,1.45,1.7-5.39,1.88,1.23,3.44-5.27A4.07,4.07,0,0,1,17,19.33a3.77,3.77,0,0,1,3.87-3.85"/></svg>

Before

Width:  |  Height:  |  Size: 849 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 82 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 45 KiB

View file

@ -7,7 +7,7 @@
<g id="Layer_1">
<g id="query">
<text transform="matrix(1, 0, 0, 1, 276.165, 12.5)" id="tspan3023">
<tspan x="-3.665" y="6.012" font-family="Helvetica-Bold" font-size="12" fill="#FFFFFF">?</tspan>
<tspan x="-3.665" y="6.012" font-family="Helvetica, sans-serif" font-weight="Bold" font-size="12" fill="#FFFFFF">?</tspan>
</text>
<path d="M263,1 C263,1 272,8 272,8 C270.944,8.587 269.888,9.173 268.832,9.76 L271.863,16.375 C272.209,17.128 271.878,18.018 271.125,18.364 C270.372,18.709 269.482,18.378 269.136,17.625 L266.201,11.221 C265.134,11.814 264.067,12.407 263,13 L263,1 z" fill="#FFFFFF"/>
</g>

Before

Width:  |  Height:  |  Size: 5.4 KiB

After

Width:  |  Height:  |  Size: 5.4 KiB

Before After
Before After

View file

@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" width="36" height="36" viewBox="0 0 36 36"><defs><style>.a{fill:#32373c;}.b{fill:#fff;}</style></defs><rect class="a" width="36" height="36"/><path class="b" d="M6,18a12,12,0,0,0,6.76,10.8L7,13.12A11.89,11.89,0,0,0,6,18Zm20.09-.6a6.38,6.38,0,0,0-1-3.32,5.48,5.48,0,0,1-1.21-2.8,2.08,2.08,0,0,1,2-2.13h.16A12,12,0,0,0,9.16,9.9,12.18,12.18,0,0,0,8,11.41h.76c1.26,0,3.2-.16,3.2-.16a.49.49,0,0,1,.39.59.48.48,0,0,1-.32.37s-.65.08-1.37.12l4.37,13,2.63-7.88-1.88-5.11a7.37,7.37,0,0,1-1.25-.12.5.5,0,0,1-.3-.64.52.52,0,0,1,.38-.32s2,.16,3.15.16,3.2-.13,3.2-.13a.5.5,0,0,1,.39.59.48.48,0,0,1-.32.37s-.64.08-1.37.12L24,25.26l1.23-3.93A14,14,0,0,0,26.1,17.4Zm-7.89,1.65-3.6,10.47a12,12,0,0,0,7.37-.2l-.09-.16Zm10.34-6.81a10.32,10.32,0,0,1,.08,1.24,11.35,11.35,0,0,1-.91,4.3L24.05,28.34A12,12,0,0,0,28.55,12.24Z"/><path class="b" d="M32.49,18A14.49,14.49,0,1,1,18,3.51,14.49,14.49,0,0,1,32.49,18ZM18,4.38A13.62,13.62,0,1,0,31.62,18h0A13.62,13.62,0,0,0,18,4.38Z"/></svg>

Before

Width:  |  Height:  |  Size: 998 B

View file

@ -13,7 +13,7 @@
//= require leaflet.locationfilter
//= require i18n
//= require oauth
//= require piwik
//= require matomo
//= require richtext
//= require qs/dist/qs
//= require bs-custom-file-input
@ -59,11 +59,13 @@ window.updateLinks = function (loc, zoom, layers, object) {
var editDisabled = zoom < 13;
$("#edit_tab")
.tooltip({ placement: "bottom" })
.off("click.minzoom")
.on("click.minzoom", function () { return !editDisabled; })
.attr("data-bs-original-title", editDisabled ?
I18n.t("javascripts.site.edit_disabled_tooltip") : "")
// Disable the button group and also the buttons to avoid
// inconsistent behaviour when zooming
.toggleClass("disabled", editDisabled)
.attr("data-original-title", editDisabled ?
I18n.t("javascripts.site.edit_disabled_tooltip") : "");
.find("a")
.toggleClass("disabled", editDisabled);
};
window.maximiseMap = function () {

View file

@ -3,8 +3,9 @@
$(document).ready(function () {
var id = $("#id-embed");
if (id.data("key")) {
var hashParams = OSM.params(location.hash.substring(1));
if (id.data("configured") === true) {
var hash = location.hash.substring(1);
var hashParams = hash ? OSM.params(hash) : {};
var mapParams = OSM.mapParams();
var params = {};

View file

@ -12,7 +12,7 @@ window.onload = function () {
I18n.locale = navigator.language;
}
var query = (window.location.search || '?').substr(1),
var query = (window.location.search || '?').slice(1),
args = {};
var pairs = query.split('&');

View file

@ -7,7 +7,7 @@ document.addEventListener("DOMContentLoaded", function () {
if (typeof iD === "undefined" || !iD.utilDetect().support) {
container.innerHTML = "This editor is supported " +
"in Firefox, Chrome, Safari, Opera, Edge, and Internet Explorer 11. " +
"in Firefox, Chrome, Safari, Opera and Edge. " +
"Please upgrade your browser or use JOSM to edit the map.";
container.className = "unsupported";
} else {
@ -17,11 +17,8 @@ document.addEventListener("DOMContentLoaded", function () {
.assetMap(JSON.parse(container.dataset.assetMap))
.locale(container.dataset.locale)
.preauth({
urlroot: location.protocol + "//" + location.host,
oauth_consumer_key: container.dataset.consumerKey,
oauth_secret: container.dataset.consumerSecret,
oauth_token: container.dataset.token,
oauth_token_secret: container.dataset.tokenSecret
url: location.protocol + "//" + location.host,
access_token: container.dataset.token
})
.containerNode(container)
.init();

View file

@ -1,5 +1,6 @@
//= require_self
//= require leaflet.sidebar
//= require leaflet.sidebar-pane
//= require leaflet.locatecontrol/src/L.Control.Locate
//= require leaflet.layers
//= require leaflet.key
@ -195,7 +196,7 @@ $(document).ready(function () {
$(".welcome").addClass("visible");
}
$(".welcome .close").on("click", function () {
$(".welcome .btn-close").on("click", function () {
$(".welcome").removeClass("visible");
Cookies.set("_osm_welcome", "hide", { secure: true, expires: expiry, path: "/", samesite: "lax" });
});
@ -203,7 +204,7 @@ $(document).ready(function () {
var bannerExpiry = new Date();
bannerExpiry.setYear(bannerExpiry.getFullYear() + 1);
$("#banner .close-wrap").on("click", function (e) {
$("#banner .btn-close").on("click", function (e) {
var cookieId = e.target.id;
$("#banner").hide();
e.preventDefault();
@ -212,13 +213,13 @@ $(document).ready(function () {
}
});
if (OSM.PIWIK) {
if (OSM.MATOMO) {
map.on("layeradd", function (e) {
if (e.layer.options) {
var goal = OSM.PIWIK.goals[e.layer.options.keyid];
var goal = OSM.MATOMO.goals[e.layer.options.keyid];
if (goal) {
$("body").trigger("piwikgoal", goal);
$("body").trigger("matomogoal", goal);
}
}
});

View file

@ -1,6 +1,5 @@
OSM.initializeBrowse = function (map) {
var browseBounds;
var selectedLayer;
var dataLayer = map.dataLayer;
dataLayer.setStyle({
@ -49,20 +48,20 @@ OSM.initializeBrowse = function (map) {
function displayFeatureWarning(count, limit, add, cancel) {
$("#browse_status").html(
$("<div>")
.append(
$("<h2>")
.text(I18n.t("browse.start_rjs.load_data"))
.prepend($("<span class='icon close'></span>").click(cancel)))
.append(
$("<div>")
.append(
$("<p class='alert alert-warning clearfix'></p>")
.text(I18n.t("browse.start_rjs.feature_warning", { num_features: count, max_features: limit })))
.append(
$("<input type='submit' class='btn btn-primary'>")
.val(I18n.t("browse.start_rjs.load_data"))
.click(add))));
$("<div>").append(
$("<div class='d-flex'>").append(
$("<div class='flex-grow-1 text-break'>").append(
$("<h2>")
.text(I18n.t("browse.start_rjs.load_data"))),
$("<div>").append(
$("<button type='button' class='btn-close mt-1'>")
.click(cancel))),
$("<div>").append(
$("<p class='alert alert-warning'></p>")
.text(I18n.t("browse.start_rjs.feature_warning", { num_features: count, max_features: limit })),
$("<input type='submit' class='btn btn-primary'>")
.val(I18n.t("browse.start_rjs.load_data"))
.click(add))));
}
var dataLoader;
@ -92,7 +91,6 @@ OSM.initializeBrowse = function (map) {
url: url,
success: function (xml) {
dataLayer.clearLayers();
selectedLayer = null;
var features = dataLayer.buildFeatures(xml);
@ -112,24 +110,16 @@ OSM.initializeBrowse = function (map) {
displayFeatureWarning(features.length, maxFeatures, addFeatures, cancelAddFeatures);
}
if (map._objectLayer) {
map._objectLayer.bringToFront();
}
dataLoader = null;
}
});
}
function onSelect(layer) {
// Unselect previously selected feature
if (selectedLayer) {
selectedLayer.setStyle(selectedLayer.originalStyle);
}
// Redraw in selected style
layer.originalStyle = layer.options;
layer.setStyle({ color: "#0000ff", weight: 8 });
OSM.router.route("/" + layer.feature.type + "/" + layer.feature.id);
// Stash the currently drawn feature
selectedLayer = layer;
}
};

View file

@ -157,7 +157,7 @@ OSM.Directions = function (map) {
}));
});
$(".directions_form .close").on("click", function (e) {
$(".directions_form .btn-close").on("click", function (e) {
e.preventDefault();
var route_from = endpoints[0].value;
if (route_from) {
@ -254,20 +254,30 @@ OSM.Directions = function (map) {
map.fitBounds(polyline.getBounds().pad(0.05));
}
var html = "<h2><a class=\"geolink\" href=\"#\">" +
"<span class=\"icon close\"></span></a>" + I18n.t("javascripts.directions.directions") +
"</h2><p>" +
var distanceText = $("<p>").append(
I18n.t("javascripts.directions.distance") + ": " + formatDistance(route.distance) + ". " +
I18n.t("javascripts.directions.time") + ": " + formatTime(route.time) + ".";
I18n.t("javascripts.directions.time") + ": " + formatTime(route.time) + ".");
if (typeof route.ascend !== "undefined" && typeof route.descend !== "undefined") {
html += "<br />" +
distanceText.append(
$("<br>"),
I18n.t("javascripts.directions.ascend") + ": " + Math.round(route.ascend) + "m. " +
I18n.t("javascripts.directions.descend") + ": " + Math.round(route.descend) + "m.";
I18n.t("javascripts.directions.descend") + ": " + Math.round(route.descend) + "m.");
}
html += "</p><table id=\"turnbyturn\" class=\"mb-3\"/>";
var turnByTurnTable = $("<table class='mb-3'>");
var directionsCloseButton = $("<button type='button' class='btn-close mt-1'>");
$("#sidebar_content")
.html(html);
.empty()
.append(
$("<div class='d-flex'>").append(
$("<div class='flex-grow-1 text-break'>").append(
$("<h2>")
.text(I18n.t("javascripts.directions.directions"))),
$("<div>").append(directionsCloseButton)),
distanceText,
turnByTurnTable
);
// Add each row
route.steps.forEach(function (step) {
@ -309,15 +319,14 @@ OSM.Directions = function (map) {
map.removeLayer(highlight);
});
$("#turnbyturn").append(row);
turnByTurnTable.append(row);
});
$("#sidebar_content").append("<p class=\"text-center\">" +
I18n.t("javascripts.directions.instructions.courtesy", { link: chosenEngine.creditline }) +
"</p>");
$("#sidebar_content a.geolink").on("click", function (e) {
e.preventDefault();
directionsCloseButton.on("click", function () {
map.removeLayer(polyline);
$("#sidebar_content").html("");
map.setSidebarOverlaid(true);

View file

@ -14,7 +14,7 @@ function GraphHopperEngine(id, vehicleType) {
return {
id: id,
creditline: "<a href=\"https://www.graphhopper.com/\" target=\"_blank\">Graphhopper</a>",
creditline: "<a href=\"https://www.graphhopper.com/\" target=\"_blank\">GraphHopper</a>",
draggable: false,
getRoute: function (points, callback) {

View file

@ -10,18 +10,6 @@ OSM.History = function (map) {
})
.on("mouseout", "[data-changeset]", function () {
unHighlightChangeset($(this).data("changeset").id);
})
.on("mousedown", "[data-changeset]", function () {
var moved = false;
$(this)
.one("click", function (e) {
if (!moved && !$(e.target).is("a")) {
clickChangeset($(this).data("changeset").id, e);
}
})
.one("mousemove", function () {
moved = true;
});
});
var group = L.featureGroup()

View file

@ -85,10 +85,12 @@ OSM.NewNote = function (map) {
};
function newHalo(loc, a) {
if (a === "dragstart" && map.hasLayer(halo)) {
var hasHalo = halo && map.hasLayer(halo);
if (a === "dragstart" && hasHalo) {
map.removeLayer(halo);
} else {
if (map.hasLayer(halo)) map.removeLayer(halo);
if (hasHalo) map.removeLayer(halo);
halo = L.circleMarker(loc, {
weight: 2.5,
@ -160,8 +162,8 @@ OSM.NewNote = function (map) {
};
page.unload = function () {
noteLayer.removeLayer(newNote);
map.removeLayer(halo);
if (newNote) noteLayer.removeLayer(newNote);
if (halo) map.removeLayer(halo);
addNoteButton.removeClass("active");
};

View file

@ -73,7 +73,7 @@ OSM.Note = function (map) {
var data = $(".details").data(),
latLng = L.latLng(data.coordinates.split(","));
if (!map.hasLayer(halo)) {
if (!halo || !map.hasLayer(halo)) {
halo = L.circleMarker(latLng, {
weight: 2.5,
radius: 20,
@ -83,7 +83,8 @@ OSM.Note = function (map) {
map.addLayer(halo);
}
if (map.hasLayer(currentNote)) map.removeLayer(currentNote);
if (currentNote && map.hasLayer(currentNote)) map.removeLayer(currentNote);
currentNote = L.marker(latLng, {
icon: noteIcons[data.status],
opacity: 1,

View file

@ -103,8 +103,8 @@ OSM.Query = function (map) {
value = tags[key];
if (prefixes[key]) {
var first = value.substr(0, 1).toUpperCase(),
rest = value.substr(1).replace(/_/g, " ");
var first = value.slice(0, 1).toUpperCase(),
rest = value.slice(1).replace(/_/g, " ");
return first + rest;
}

View file

@ -1,38 +1,11 @@
L.OSM.key = function (options) {
var control = L.control(options);
control.onAdd = function (map) {
var $container = $("<div>")
.attr("class", "control-key");
var button = $("<a>")
.attr("class", "control-button")
.attr("href", "#")
.html("<span class=\"icon key\"></span>")
.on("click", toggle)
.appendTo($container);
var $ui = $("<div>")
.attr("class", "key-ui");
$("<div>")
.attr("class", "sidebar_heading")
.appendTo($ui)
.append(
$("<span>")
.text(I18n.t("javascripts.close"))
.attr("class", "icon close")
.bind("click", toggle))
.append(
$("<h4>")
.text(I18n.t("javascripts.key.title")));
var control = L.OSM.sidebarPane(options, "key", null, "javascripts.key.title");
control.onAddPane = function (map, button, $ui) {
var $section = $("<div>")
.attr("class", "section")
.appendTo($ui);
options.sidebar.addPane($ui);
$ui
.on("show", shown)
.on("hide", hidden);
@ -50,20 +23,11 @@ L.OSM.key = function (options) {
map.off("zoomend baselayerchange", update);
}
function toggle(e) {
e.stopPropagation();
e.preventDefault();
if (!button.hasClass("disabled")) {
options.sidebar.togglePane($ui, button);
}
$(".leaflet-control .control-button").tooltip("hide");
}
function updateButton() {
var disabled = ["mapnik", "cyclemap"].indexOf(map.getMapBaseLayerId()) === -1;
button
.toggleClass("disabled", disabled)
.attr("data-original-title",
.attr("data-bs-original-title",
I18n.t(disabled ?
"javascripts.key.tooltip_disabled" :
"javascripts.key.tooltip"));
@ -82,8 +46,6 @@ L.OSM.key = function (options) {
}
});
}
return $container[0];
};
return control;

View file

@ -1,35 +1,9 @@
L.OSM.layers = function (options) {
var control = L.control(options);
var control = L.OSM.sidebarPane(options, "layers", "javascripts.map.layers.title", "javascripts.map.layers.header");
control.onAdd = function (map) {
control.onAddPane = function (map, button, $ui, toggle) {
var layers = options.layers;
var $container = $("<div>")
.attr("class", "control-layers");
var button = $("<a>")
.attr("class", "control-button")
.attr("href", "#")
.attr("title", I18n.t("javascripts.map.layers.title"))
.html("<span class=\"icon layers\"></span>")
.on("click", toggle)
.appendTo($container);
var $ui = $("<div>")
.attr("class", "layers-ui");
$("<div>")
.attr("class", "sidebar_heading")
.appendTo($ui)
.append(
$("<span>")
.text(I18n.t("javascripts.close"))
.attr("class", "icon close")
.bind("click", toggle))
.append(
$("<h4>")
.text(I18n.t("javascripts.map.layers.header")));
var baseSection = $("<div>")
.attr("class", "section base-layers")
.appendTo($ui);
@ -171,7 +145,7 @@ L.OSM.layers = function (options) {
}
$(item).attr("class", disabled ? "disabled" : "");
item.attr("data-original-title", disabled ?
item.attr("data-bs-original-title", disabled ?
I18n.t("javascripts.site.map_" + name + "_zoom_in_tooltip") : "");
});
};
@ -180,17 +154,6 @@ L.OSM.layers = function (options) {
addOverlay(map.dataLayer, "data", OSM.MAX_REQUEST_AREA);
addOverlay(map.gpsLayer, "gps", Number.POSITIVE_INFINITY);
}
options.sidebar.addPane($ui);
function toggle(e) {
e.stopPropagation();
e.preventDefault();
options.sidebar.togglePane($ui, button);
$(".leaflet-control .control-button").tooltip("hide");
}
return $container[0];
};
return control;

View file

@ -17,7 +17,7 @@ L.OSM.note = function (options) {
var disabled = OSM.STATUS === "database_offline" || map.getZoom() < 12;
link
.toggleClass("disabled", disabled)
.attr("data-original-title", I18n.t(disabled ?
.attr("data-bs-original-title", I18n.t(disabled ?
"javascripts.site.createnote_disabled_tooltip" :
"javascripts.site.createnote_tooltip"));
}

View file

@ -20,7 +20,7 @@ L.OSM.query = function (options) {
isDisabled = map.getZoom() < 14;
link
.toggleClass("disabled", isDisabled)
.attr("data-original-title", I18n.t(isDisabled ?
.attr("data-bs-original-title", I18n.t(isDisabled ?
"javascripts.site.queryfeature_disabled_tooltip" :
"javascripts.site.queryfeature_tooltip"));

View file

@ -1,38 +1,12 @@
L.OSM.share = function (options) {
var control = L.control(options),
var control = L.OSM.sidebarPane(options, "share", "javascripts.share.title", "javascripts.share.title"),
marker = L.marker([0, 0], { draggable: true }),
locationFilter = new L.LocationFilter({
enableButton: false,
adjustButton: false
});
control.onAdd = function (map) {
var $container = $("<div>")
.attr("class", "control-share");
var button = $("<a>")
.attr("class", "control-button")
.attr("href", "#")
.attr("title", I18n.t("javascripts.share.title"))
.html("<span class=\"icon share\"></span>")
.on("click", toggle)
.appendTo($container);
var $ui = $("<div>")
.attr("class", "share-ui");
$("<div>")
.attr("class", "sidebar_heading")
.appendTo($ui)
.append(
$("<span>")
.text(I18n.t("javascripts.close"))
.attr("class", "icon close")
.bind("click", toggle))
.append(
$("<h4>")
.text(I18n.t("javascripts.share.title")));
control.onAddPane = function (map, button, $ui) {
// Link / Embed
var $linkSection = $("<div>")
@ -47,7 +21,7 @@ L.OSM.share = function (options) {
.appendTo($linkSection);
$("<div>")
.attr("class", "form-check form-group")
.attr("class", "form-check mb-3")
.appendTo($form)
.append(
$("<label>")
@ -159,7 +133,7 @@ L.OSM.share = function (options) {
.appendTo($imageSection);
$("<div>")
.attr("class", "form-group form-check")
.attr("class", "mb-3 form-check")
.appendTo($form)
.append(
$("<label>")
@ -246,11 +220,15 @@ L.OSM.share = function (options) {
map.on("move", movedMap);
map.on("moveend layeradd layerremove", update);
options.sidebar.addPane($ui);
$ui
.on("show", shown)
.on("hide", hidden);
function shown() {
$("#mapnik_scale").val(getScale());
update();
}
function hidden() {
map.removeLayer(marker);
map.options.scrollWheelZoom = map.options.doubleClickZoom = true;
@ -258,18 +236,6 @@ L.OSM.share = function (options) {
update();
}
function toggle(e) {
e.stopPropagation();
e.preventDefault();
$("#mapnik_scale").val(getScale());
marker.setLatLng(map.getCenter());
update();
options.sidebar.togglePane($ui, button);
$(".leaflet-control .control-button").tooltip("hide");
}
function toggleMarker() {
if ($(this).is(":checked")) {
marker.setLatLng(map.getCenter());
@ -414,8 +380,6 @@ L.OSM.share = function (options) {
var precision = 5 * Math.pow(10, Math.floor(Math.LOG10E * Math.log(scale)) - 2);
return precision * Math.ceil(scale / precision);
}
return $container[0];
};
return control;

View file

@ -0,0 +1,53 @@
L.OSM.sidebarPane = function (options, uiClass, buttonTitle, paneTitle) {
var control = L.control(options);
control.onAdd = function (map) {
var $container = $("<div>")
.attr("class", "control-" + uiClass);
var button = $("<a>")
.attr("class", "control-button")
.attr("href", "#")
.html("<span class=\"icon " + uiClass + "\"></span>")
.on("click", toggle);
if (buttonTitle) {
button.attr("title", I18n.t(buttonTitle));
}
button.appendTo($container);
var $ui = $("<div>")
.attr("class", uiClass + "-ui");
$("<div class='sidebar_heading d-flex'>")
.appendTo($ui)
.append($("<div class='flex-grow-1 text-break'>")
.append($("<h4>")
.text(I18n.t(paneTitle))))
.append($("<div>")
.append($("<button type='button' class='btn-close mt-1'>")
.attr("aria-label", I18n.t("javascripts.close"))
.bind("click", toggle)));
options.sidebar.addPane($ui);
this.onAddPane(map, button, $ui, toggle);
function toggle(e) {
e.stopPropagation();
e.preventDefault();
if (!button.hasClass("disabled")) {
options.sidebar.togglePane($ui, button);
}
$(".leaflet-control .control-button").tooltip("hide");
}
return $container[0];
};
// control.onAddPane = function (map, button, $ui, toggle) {
// }
return control;
};

View file

@ -0,0 +1,32 @@
if (OSM.MATOMO) {
$(document).ready(function () {
var base = document.location.protocol + "//" + OSM.MATOMO.location + "/";
var matomoTracker;
var matomoLoader = $.ajax({
url: base + "matomo.js",
dataType: "script",
cache: true,
success: function () {
matomoTracker = Matomo.getTracker(base + "matomo.php", OSM.MATOMO.site);
if (OSM.user) {
matomoTracker.setUserId(OSM.user.toString());
}
matomoTracker.trackPageView();
matomoTracker.enableLinkTracking();
$("meta[name=matomo-goal]").each(function () {
matomoTracker.trackGoal($(this).attr("content"));
});
}
});
$("body").on("matomogoal", function (e, goal) {
matomoLoader.done(function () {
matomoTracker.trackGoal(goal);
});
});
});
}

View file

@ -3,8 +3,8 @@
//= require qs/dist/qs
OSM = {
<% if defined?(PIWIK) %>
PIWIK: <%= PIWIK.to_json %>,
<% if defined?(Settings.matomo) %>
MATOMO: <%= Settings.matomo.to_json %>,
<% end %>
MAX_REQUEST_AREA: <%= Settings.max_request_area.to_json %>,
@ -146,7 +146,7 @@ OSM = {
return args;
}
hash = Qs.parse(hash.substr(i + 1));
hash = Qs.parse(hash.slice(i + 1));
var map = (hash.map || '').split('/'),
zoom = parseInt(map[0], 10),

View file

@ -1,32 +0,0 @@
if (OSM.PIWIK) {
$(document).ready(function () {
var base = document.location.protocol + "//" + OSM.PIWIK.location + "/";
var piwikTracker;
var piwikLoader = $.ajax({
url: base + "piwik.js",
dataType: "script",
cache: true,
success: function () {
piwikTracker = Piwik.getTracker(base + "piwik.php", OSM.PIWIK.site);
if (OSM.user) {
piwikTracker.setUserId(OSM.user.toString());
}
piwikTracker.trackPageView();
piwikTracker.enableLinkTracking();
$("meta[name=piwik-goal]").each(function () {
piwikTracker.trackGoal($(this).attr("content"));
});
}
});
$("body").on("piwikgoal", function (e, goal) {
piwikLoader.done(function () {
piwikTracker.trackGoal(goal);
});
});
});
}

View file

@ -3,15 +3,14 @@
xmlns:moz="http://www.mozilla.org/2006/browser/search/">
<ShortName>OpenStreetMap</ShortName>
<LongName>OpenStreetMap Search</LongName>
<Description>Search for a place in OpenStreetMap, the Wiki World Map</Description>
<Description>Search for a place in OpenStreetMap</Description>
<InputEncoding>UTF-8</InputEncoding>
<OutputEncoding>UTF-8</OutputEncoding>
<Image width="16" height="16">data:image/png,%89PNG%0D%0A%1A%0A%00%00%00%0DIHDR%00%00%00%10%00%00%00%10%08%06%00%00%00%1F%F3%FFa%00%00%00%19tEXtSoftware%00www.inkscape.org%9B%EE%3C%1A%00%00%01'IDATx%9C%9D%93%B1q%C30%0CE%1F%5Dd%80l%90I%C4*%A7%22%95%CF%85J%97%AE%B2%82%3DC%CE%03%A0%B3%9B%B8f%95%A3%17H%93%05%5C%A7%C8%0A%3F%05E%8A%92l%E7.%D0%E9N%02%81%07%E0Cr%FCaf%A6%FC%BC%5E%AF%DD%F4%3C%3Bt%C5%87%99)%84P%0E%DA%B6%BD%0AI%00%A1%A8%A8%1E%263S%D7u%C9'F%FE%9B%80%0C%C9%C9%22%BD'%B0%0A%AC%86%2CJ%DB%0E%22%11%8F%2F%D4%B3%22%8D%F34%CE%13u%06R%0C%40%D7u%AA%01%C5r%40%0Dq%88%C6%F9i%E8%7C%8CX%5D%A9M%95%D6%A3%A2Ti%C3Xx%CA%9C%F5mf3h%11%B6%07%B8%0APh%97%DD%1E%9E%5E%08!%D0%B6m%F1%87%108%1E%8EY5%007%03%5Cv%7B%00%3E%BF%3E%F8~x%1E%CD%B89l%00%F0I%0FWw%00%20%DB%AEJr%B6%E5%FB%09%80%C6y%CE%7D%91%1AP%B6p%2B%D9%BB%06%18V%3A%B5E%9F%AC%5B%95%AFY%3F%EE%20%A2mW%AA%93%DFN%3F%A0%E1%9B%F0u%E5%BC%BC%89%88%BC.%1F%D5'%DF%FD%C1%EE%F8%FFg%BFp%96%DF%E2%DCw%25%2B%00%00%00%00IEND%AEB%60%82</Image>
<Image width="32" height="32">data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAMAAABEpIrGAAAAHXRFWHRUaXRsZQBPcGVuU3RyZWV0TWFwIGxvZ28gMjAxMbBaqq0AAAAZdEVYdFNvZnR3YXJlAHd3dy5pbmtzY2FwZS5vcmeb7jwaAAAACXBIWXMAAAIJAAACCQGIszliAAADAFBMVEVHcEzJ66zM7KnL5Kap3Y3U7LfR7rLd9KrS8K+99bLG6J/P7a3Z8r7O7KrC6aSswcPT77PW78HH46jJ6KfO7K7X8Lqy4ZXK7KrN66vY87vN7Kuu3ZEkMTHC35/W7byswcGzz67O7azA5aHP67mt35G245na88DB5KC41cXN2KLC4KwvLzbA0M7G5b2brJPT5eKarap6iooxPT3D3K6kurlcUz4tLSPwxTSElXs3NSL14qHL06ra8sFpaVaFl4VUXF6UpqWvxLnE2pvb88LN7avE56S546Da88HM6onM6MrM7KrL6qik24m337na8r/V8LfT8LTU5PLZ7trW8bnJ56fQ77Di8uSg1oXQ7LHO6czU7bjI4dPF46LA2Oze8eDS69PP7K3n8+rH5aTJzJzC56LA56vY8bzj8+bQ6s++1era7d7G4sc4Nzq95qjI6KS24KG846fc6fTX5vLG2+jS77LR4vHU5tei2IjV6d8xMTPW2K7N6qzN5M/R5NW60ueIn4Kox4+31b/T0qs8Oz+nvb1AP0JIRUnC5MPM3qXd7eGuz7PK5cqSv4DIxZnQ7bU7PTG60aK/1+W7456915y+2aPK4qvB3aefuo1TWVCSqZus3JFbY1ZygWfQ6r1BQ0fG487N6dHK3u7X7dfK16Gf04O24Jjo7ua13LjB2tGy0rssLC4xMC/OxqCbzoI1NTWtxrzi8OXF56660Mm0yMPB3p6x3pq+1uCFmHfL577I2dV3h2vM56jAvZS/3ce92L3I4M680t3M0J7U5+zN4O/Z27Owy7DB4r4mJymQoJOtzpSjtaHS4rC30cnW5bSxxqCmv43J6LKcs4aXrIjD3cOZtoe1z5qtwrLB4bRIUklob16Qp3y2zNhvbk13hoXb88JPV12it9BXYmrG393i8OuswpqUyoCx2LWeuYe1uo+TxHyXln6vqIbP5dvQ5dyt0ZrZ8MaZw5mcsZet1q/CzbqkrIeyrK2dsbRCRTeyxtJ6iZGnk1dhbHF+dmKBkJ+ppJCUo7fdGvRRAAAASHRSTlMAi9Bd/klPCBAEJb2KHzftZ6ky+aTOt3OfPvPeCtz2/f2XZ+vqk+V7z3DzJvVs4PbJsD9I4uPTtR2HrvHA88Z91PqX+Kn06tFpmiUEAAADw0lEQVQ4y2NQdGEU4GKAAA5GN2kxJXYIh5NV0B1EC3h5eXkzabJzsPHYOOcWlIezuCryMfAKCCqn3bsnCdIlBVJxUCrNzy/tQkHBrFneMRc/Om6+NnN6QZQfD8gIEaB8R4e3f3BfQX5uwdZJl9a/3bBhQ1bFzpaPfqIgBWxe3t7J3iEJ/sEb8wvyt9ZHV9TWlpXV1tRGnzkhB3IPh7e398HkxTEJEREbgfJvOsuyshYF1dbU7G88c4IVZASTt3dITPLihJKAgKlA/Z1ZYUGL4uMzamq6Vp6xAzsioTokJqQ3yduretr6srJFQVlxcWFxcfHd+7oa5WWACtT8/IL9oqoivJO2bN9bFhcWlJ6eGJYVtsB3edfRTfYMDJJ+aWlRaVFHEmJmLZkZnZWeFfa4qLDoUEW7L1BFowMDA08aUIG//5GQpT5rW4oDiwIDA0uBeOE2oIpnK/kZGESDg4MjIoKbtrD4fJCfCJQrBKKihbF184FGABWwB/j7+0dEVE318fmk0LKt9PChQ4dLD8fGxtZl+/r6NvIzsAYAQXXT6SSfWRunfKtbeDgoqKI4MzY2E2SF714TBuYQ79DQ0DXVeSnXU/J/TswsXhSfsWBiJtCG7mPt8VmGDBzCIUkhCR3JeQk7JjX+/ZE50dPTMzLyUXG0p2dGXLq+EQMDNygg1yRVB8/N3/d/3u26bk9PsJqMRWGJiQbAcBIHRnfvbu/d/vtT9z64/7AuGqzAMz4obNkyHW2gAiFgdG9J9oqZe2D/gQcP235viwQrCFq2LH2FHjhpCefleXUkT8suiZh6t611xv2WBUAl8enLsjafUuAEqZCNyYvZkvy5ac/Nf39ab8yZc7dl04ro6J2bZ7ZcfSIEUsDtHZN0fe/iybPntf26s+vW9lsN51/VX3p+8uq5a1NA6YFdbm53dp8XSH5e852Z0642TL8y/XVDT0/PyaNaoBTF9j6yL7hkyuzWttac7/Vfm5qipk6b8KKh5+XTtZM0QDYIRqbmRk2Z3dzauq5/zpd9t/2bqvpOr5m8NhwI1EEKlFNTL0yY0dzcXNl/80pv9YHLAWerzkYtLV8SXu7DB/JlburcCTNymnMq++dc3BNzuSR7f4B/cEr5Eh8fHxZwqi8oODdjXc66Vf27nu/pPQaMuB0r/P0nAbX7+KiAFDDmvrtRua4SKH9+8m5rCaYEoIrsgJRwoLyPGEgBn7iTbeXq46u3N8hKMAN9xSvOHZodGrOURVoVlqc5rUyPm1kYw7jArKTLyMzLgATYzS1lGHABANfVkEkFerrFAAAAAElFTkSuQmCC</Image>
<Url type="text/html" method="get" template="https://www.openstreetmap.org/search">
<Param name="query" value="{searchTerms}"/>
</Url>
<Query role="example" searchTerms="Reigate" />
<Developer>Jonathan Bennett</Developer>
<AdultContent>false</AdultContent>
<Attribution>Data &amp;copy; OpenStreetMap contributors, Some Rights Reserved. CC by-sa 2.0. Geolocation provided by npemap.org.uk, geocoder.us, geocoder.ca and geonames.org.</Attribution>
<Attribution>&amp;copy; OpenStreetMap contributors. ODbL 1.0. Geocoding provided by nominatim.openstreetmap.org.</Attribution>
</OpenSearchDescription>

View file

@ -55,24 +55,12 @@ small, aside {
.icon.clipboard { /* no-r2 */ background-position: -160px 0; }
.icon.link { /* no-r2 */ background-position: -180px 0; }
.icon.close { /* no-r2 */ background-position: -200px 0; }
.close-wrap:hover .icon.close,
.icon.close:hover { /* no-r2 */ background-position: -200px -20px; }
.icon.check { /* no-r2 */ background-position: -220px 0; }
.icon.note { /* no-r2 */ background-position: -240px 0; }
.icon.note.grey { /* no-r2 */ background-position: -240px -20px; }
.icon.query { /* no-r2 */ background-position: -260px 0; }
/* Rules for links */
a {
color: #24d;
text-decoration: none;
outline: 0;
&:hover {
text-decoration: underline;
}
}
/* Utility for de-emphasizing content */
.text-muted a {
@ -157,7 +145,7 @@ header {
nav.primary {
.btn-outline-primary {
@include button-outline-variant($green, $white);
@include button-outline-variant($green, $color-hover: $white, $active-color: $white);
}
.disabled {
@ -202,27 +190,17 @@ nav.secondary {
.login-menu {
.btn-outline-secondary {
@include button-outline-variant($darkgrey);
@include button-outline-variant($darkgrey, $color-hover: $white, $active-color: $white);
}
}
.user-menu {
.btn-outline-secondary {
@include button-outline-variant($darkgrey, $darkgrey, white, $darkgrey);
@include button-outline-variant($darkgrey, $color-hover: $darkgrey, $active-background: white, $active-border: $darkgrey);
border-color: $grey;
&:hover {
border-color: $grey;
}
&:focus {
background-color: white;
box-shadow: none;
}
}
&.show .btn-outline-secondary {
background-color: white;
&:focus {
box-shadow: none;
}
}
}
@ -295,7 +273,8 @@ body.compact-nav {
margin-bottom: 10px;
outline: none;
&:hover {
&:hover,
&:focus {
background-color: black;
}
@ -369,30 +348,6 @@ body.compact-nav {
clear: both;
width: 100%;
}
h2 {
font-size: 1.5rem;
}
h3, h4 {
font-size: 1.25rem;
}
.close-wrap {
cursor: pointer;
position: absolute;
top: 0;
right: 0;
width: 60px;
height: 60px;
.icon.close {
pointer-events: none;
position: absolute;
right: 20px;
top: 20px;
}
}
}
.overlay-sidebar #sidebar {
@ -644,11 +599,6 @@ body.compact-nav {
padding: $lineheight/2 $lineheight;
// background: $offwhite;
// border-bottom: 1px solid $grey;
> .close {
float: right;
margin-top: 2px;
cursor: pointer;
}
}
#browse_status {
@ -686,6 +636,28 @@ form {
}
}
/* Stop bootstrap 5 from floating legends when they don't need to be */
legend {
float: none;
}
/* Override the text colour for primary and secondary buttons, to match our
bootstrap 4 colours. Note this has accessibility issues, which is why
bootstrap 5 calculates black as the appropriate colour, and we should
reconsider our colours at some point with that in mind. */
.btn-primary {
@include button-variant($primary, $primary, $color: $white, $hover-color: $white, $active-color: $white, $disabled-color: $white);
}
.btn-secondary {
@include button-variant($secondary, $secondary, $color: $white, $hover-color: $white, $active-color: $white, $disabled-color: $white);
}
.btn-outline-secondary {
@include button-outline-variant($secondary, $color-hover: $white, $active-color: $white);
}
/* Rules for the search and direction forms */
header .search_forms,
@ -770,10 +742,13 @@ tr.turn:hover {
#sidebar .changesets {
li {
cursor: pointer;
&.selected { background: $list-highlight; }
/* color is derived from changeset bbox fillColor in history.js */
a.stretched-link > span, a:not(.stretched-link), [title] {
position: relative;
z-index: 2; /* needs to be higher than Bootstrap's stretched link ::after z-index */
}
}
.comments {
@ -1182,8 +1157,9 @@ tr.turn:hover {
/* Rules for messages pages */
.messages {
input[type="submit"] {
button[type="submit"] {
margin: auto;
white-space: nowrap;
}
.inbox-row {
@ -1306,10 +1282,6 @@ tr.turn:hover {
vertical-align: middle;
}
}
a.reverse_directions {
cursor: pointer;
}
}
/* Rules for user images */
@ -1435,13 +1407,6 @@ dl.dl-inline {
}
}
/* Customise the background colour of striped tables */
.table-striped > tbody > tr:nth-child(2n+1) > td,
.table-striped > tbody > tr:nth-child(2n+1) > th {
background-color: $offwhite;
}
/* Rules for OpenID logo */
.openid_logo {
@ -1454,13 +1419,11 @@ dl.dl-inline {
.richtext,
.prose {
code {
font-size: 13px;
background: $lightgrey;
padding: 2px 3px;
}
pre {
font-size: 13px;
background: $lightgrey;
padding: 2px 3px;
white-space: pre-wrap;
@ -1503,19 +1466,10 @@ dl.dl-inline {
/* Rules for the "Welcome" page */
.site-welcome, .site-fixthemap {
.center {
text-align: center;
.sprite {
float: none;
margin: auto;
}
}
.sprite {
background-image: image-url("welcome-sprite.png");
background-size: 500px 250px;
display: block;
float: left;
}
.icon-list {
@ -1565,15 +1519,6 @@ dl.dl-inline {
/* no-r2 */ background-position: -350px 0;
}
.start-mapping {
margin: auto;
cursor: pointer;
border: none;
padding: 20px 40px;
font-size: 30px;
text-decoration: none;
}
.icon.note {
background-color: #333;
border-radius: 4px;

View file

@ -17,3 +17,12 @@ $sidebarWidth: 350px;
$keyline: 1px solid $lightgrey;
$list-highlight: #FFFFC0;
$border: 1px solid $grey;
$link-color: #24d;
$link-hover-color: #24d;
$link-decoration: none;
$link-hover-decoration: underline;
$table-striped-bg: $offwhite;
$enable-negative-margins: true;

View file

@ -0,0 +1,12 @@
module Account
class DeletionsController < ApplicationController
layout "site"
before_action :authorize_web
before_action :set_locale
authorize_resource :class => false
def show; end
end
end

View file

@ -0,0 +1,64 @@
class AccountsController < ApplicationController
include SessionMethods
include UserMethods
layout "site"
before_action :authorize_web
before_action :set_locale
authorize_resource :class => false
before_action :check_database_readable
before_action :check_database_writable, :only => [:update]
before_action :allow_thirdparty_images, :only => [:edit, :update]
def edit
@tokens = current_user.oauth_tokens.authorized
append_content_security_policy_directives(
:form_action => %w[accounts.google.com *.facebook.com login.live.com github.com meta.wikimedia.org]
)
if errors = session.delete(:user_errors)
errors.each do |attribute, error|
current_user.errors.add(attribute, error)
end
end
@title = t ".title"
end
def update
@tokens = current_user.oauth_tokens.authorized
append_content_security_policy_directives(
:form_action => %w[accounts.google.com *.facebook.com login.live.com github.com meta.wikimedia.org]
)
user_params = params.require(:user).permit(:display_name, :new_email, :pass_crypt, :pass_crypt_confirmation, :auth_provider)
if params[:user][:auth_provider].blank? ||
(params[:user][:auth_provider] == current_user.auth_provider &&
params[:user][:auth_uid] == current_user.auth_uid)
update_user(current_user, user_params)
if current_user.errors.count.zero?
redirect_to edit_account_path
else
render :edit
end
else
session[:new_user_settings] = user_params.to_h
redirect_to auth_url(params[:user][:auth_provider], params[:user][:auth_uid]), :status => :temporary_redirect
end
end
def destroy
current_user.soft_destroy!
session.delete(:user)
session_expires_automatically
flash[:notice] = t ".success"
redirect_to root_path
end
end

View file

@ -1,12 +1,13 @@
module Api
class ChangesetCommentsController < ApiController
before_action :check_api_writable
before_action :check_api_readable, :except => [:create]
before_action :authorize
authorize_resource
before_action :require_public_data, :only => [:create]
before_action :check_api_writable
before_action :check_api_readable, :except => [:create]
before_action :set_request_formats
around_action :api_call_handle_error
around_action :api_call_timeout
@ -23,7 +24,7 @@ module Api
# Find the changeset and check it is valid
changeset = Changeset.find(id)
raise OSM::APIChangesetNotYetClosedError, changeset if changeset.is_open?
raise OSM::APIChangesetNotYetClosedError, changeset if changeset.open?
# Add a comment to the changeset
comment = changeset.comments.create(:changeset => changeset,
@ -41,6 +42,11 @@ module Api
# Return a copy of the updated changeset
@changeset = changeset
render "api/changesets/changeset"
respond_to do |format|
format.xml
format.json
end
end
##
@ -61,6 +67,11 @@ module Api
# Return a copy of the updated changeset
@changeset = comment.changeset
render "api/changesets/changeset"
respond_to do |format|
format.xml
format.json
end
end
##
@ -81,6 +92,11 @@ module Api
# Return a copy of the updated changeset
@changeset = comment.changeset
render "api/changesets/changeset"
respond_to do |format|
format.xml
format.json
end
end
end
end

View file

@ -4,14 +4,14 @@ module Api
class ChangesetsController < ApiController
require "xml/libxml"
before_action :check_api_writable, :only => [:create, :update, :upload, :subscribe, :unsubscribe]
before_action :check_api_readable, :except => [:create, :update, :upload, :download, :query, :subscribe, :unsubscribe]
before_action :authorize, :only => [:create, :update, :upload, :close, :subscribe, :unsubscribe]
authorize_resource
before_action :require_public_data, :only => [:create, :update, :upload, :close, :subscribe, :unsubscribe]
before_action :check_api_writable, :only => [:create, :update, :upload, :subscribe, :unsubscribe]
before_action :check_api_readable, :except => [:create, :update, :upload, :download, :query, :subscribe, :unsubscribe]
before_action :set_request_formats, :only => [:download]
before_action :set_request_formats, :except => [:create, :close, :upload]
around_action :api_call_handle_error
around_action :api_call_timeout, :except => [:upload]
@ -42,6 +42,11 @@ module Api
@changeset = Changeset.find(params[:id])
@include_discussion = params[:include_discussion].presence
render "changeset"
respond_to do |format|
format.xml
format.json
end
end
##
@ -171,6 +176,11 @@ module Api
# preload users, tags and comments, and render result
@changesets = changesets.preload(:user, :changeset_tags, :comments)
render "changesets"
respond_to do |format|
format.xml
format.json
end
end
##
@ -191,6 +201,11 @@ module Api
check_changeset_consistency(@changeset, current_user)
@changeset.update_from(new_changeset, current_user)
render "changeset"
respond_to do |format|
format.xml
format.json
end
end
##
@ -212,6 +227,11 @@ module Api
# Return a copy of the updated changeset
@changeset = changeset
render "changeset"
respond_to do |format|
format.xml
format.json
end
end
##
@ -233,6 +253,11 @@ module Api
# Return a copy of the updated changeset
@changeset = changeset
render "changeset"
respond_to do |format|
format.xml
format.json
end
end
private
@ -307,11 +332,11 @@ module Api
times = time.split(",")
raise OSM::APIBadUserInput, "bad time range" if times.size != 2
from, to = times.collect { |t| Time.parse(t) }
from, to = times.collect { |t| Time.parse(t).utc }
changesets.where("closed_at >= ? and created_at <= ?", from, to)
else
# if there is no comma, assume its a lower limit on time
changesets.where("closed_at >= ?", Time.parse(time))
changesets.where("closed_at >= ?", Time.parse(time).utc)
end
# stupid Time seems to throw both of these for bad parsing, so
# we have to catch both and ensure the correct code path is taken.
@ -329,7 +354,7 @@ module Api
changesets
else
changesets.where("closed_at >= ? and num_changes <= ?",
Time.now.getutc, Changeset::MAX_ELEMENTS)
Time.now.utc, Changeset::MAX_ELEMENTS)
end
end
@ -341,7 +366,7 @@ module Api
changesets
else
changesets.where("closed_at < ? or num_changes > ?",
Time.now.getutc, Changeset::MAX_ELEMENTS)
Time.now.utc, Changeset::MAX_ELEMENTS)
end
end

View file

@ -1,8 +1,9 @@
module Api
class MapController < ApiController
before_action :check_api_readable
authorize_resource :class => false
before_action :check_api_readable
around_action :api_call_handle_error, :api_call_timeout
before_action :set_request_formats

View file

@ -4,13 +4,13 @@ module Api
class NodesController < ApiController
require "xml/libxml"
before_action :check_api_writable, :only => [:create, :update, :delete]
before_action :check_api_readable, :except => [:create, :update, :delete]
before_action :authorize, :only => [:create, :update, :delete]
authorize_resource
before_action :require_public_data, :only => [:create, :update, :delete]
before_action :check_api_writable, :only => [:create, :update, :delete]
before_action :check_api_readable, :except => [:create, :update, :delete]
around_action :api_call_handle_error, :api_call_timeout
before_action :set_request_formats, :except => [:create, :update, :delete]

View file

@ -1,12 +1,12 @@
module Api
class NotesController < ApiController
before_action :check_api_readable
before_action :setup_user_auth, :only => [:create, :comment, :show]
before_action :check_api_writable, :only => [:create, :comment, :close, :reopen, :destroy]
before_action :setup_user_auth, :only => [:create, :show]
before_action :authorize, :only => [:close, :reopen, :destroy, :comment]
authorize_resource
before_action :check_api_writable, :only => [:create, :comment, :close, :reopen, :destroy]
before_action :set_locale
around_action :api_call_handle_error, :api_call_timeout
@ -277,16 +277,16 @@ module Api
# Add any date filter
if params[:from]
begin
from = Time.parse(params[:from])
from = Time.parse(params[:from]).utc
rescue ArgumentError
raise OSM::APIBadUserInput, "Date #{params[:from]} is in a wrong format"
end
begin
to = if params[:to]
Time.parse(params[:to])
Time.parse(params[:to]).utc
else
Time.now
Time.now.utc
end
rescue ArgumentError
raise OSM::APIBadUserInput, "Date #{params[:to]} is in a wrong format"
@ -361,7 +361,7 @@ module Api
elsif closed_since.positive?
notes.where(:status => "open")
.or(notes.where(:status => "closed")
.where(notes.arel_table[:closed_at].gt(Time.now - closed_since.days)))
.where(notes.arel_table[:closed_at].gt(Time.now.utc - closed_since.days)))
else
notes.where(:status => "open")
end

View file

@ -5,13 +5,13 @@ module Api
class OldController < ApiController
require "xml/libxml"
before_action :check_api_readable
before_action :check_api_writable, :only => [:redact]
before_action :setup_user_auth, :only => [:history, :version]
before_action :authorize, :only => [:redact]
authorize_resource
before_action :check_api_readable
before_action :check_api_writable, :only => [:redact]
around_action :api_call_handle_error, :api_call_timeout
before_action :lookup_old_element, :except => [:history]
before_action :lookup_old_element_versions, :only => [:history]

View file

@ -1,9 +1,11 @@
module Api
class PermissionsController < ApiController
before_action :check_api_readable
authorize_resource :class => false
before_action :check_api_readable
before_action :setup_user_auth
before_action :set_request_formats
around_action :api_call_handle_error, :api_call_timeout
# External apps that use the api are able to query which permissions
@ -21,6 +23,11 @@ module Api
else
[]
end
respond_to do |format|
format.xml
format.json
end
end
end
end

View file

@ -2,13 +2,13 @@ module Api
class RelationsController < ApiController
require "xml/libxml"
before_action :check_api_writable, :only => [:create, :update, :delete]
before_action :check_api_readable, :except => [:create, :update, :delete]
before_action :authorize, :only => [:create, :update, :delete]
authorize_resource
before_action :require_public_data, :only => [:create, :update, :delete]
before_action :check_api_writable, :only => [:create, :update, :delete]
before_action :check_api_readable, :except => [:create, :update, :delete]
around_action :api_call_handle_error, :api_call_timeout
before_action :set_request_formats, :except => [:create, :update, :delete]

View file

@ -1,8 +1,9 @@
module Api
class TracepointsController < ApiController
before_action :check_api_readable
authorize_resource
before_action :check_api_readable
around_action :api_call_handle_error, :api_call_timeout
# Get an XML response containing a list of tracepoints that have been uploaded

View file

@ -1,13 +1,13 @@
module Api
class TracesController < ApiController
before_action :check_database_readable, :except => [:show, :data]
before_action :check_database_writable, :only => [:create, :update, :destroy]
before_action :authorize_web
before_action :set_locale
before_action :authorize
authorize_resource
before_action :check_database_readable, :except => [:show, :data]
before_action :check_database_writable, :only => [:create, :update, :destroy]
before_action :check_api_readable, :only => [:show, :data]
before_action :check_api_writable, :only => [:create, :update, :destroy]
before_action :offline_error, :only => [:create, :destroy, :data]
@ -54,6 +54,8 @@ module Api
send_data(trace.xml_file.read, :filename => "#{trace.id}.xml", :type => request.format.to_s, :disposition => "attachment")
elsif request.format == Mime[:gpx]
send_data(trace.xml_file.read, :filename => "#{trace.id}.gpx", :type => request.format.to_s, :disposition => "attachment")
elsif trace.file.attached?
redirect_to rails_blob_path(trace.file, :disposition => "attachment")
else
send_file(trace.trace_name, :filename => "#{trace.id}#{trace.extension_name}", :type => trace.mime_type, :disposition => "attachment")
end
@ -97,12 +99,6 @@ module Api
# Sanitise the user's filename
name = file.original_filename.gsub(/[^a-zA-Z0-9.]/, "_")
# Get a temporary filename...
filename = "/tmp/#{rand}"
# ...and save the uploaded file to that location
File.open(filename, "wb") { |f| f.write(file.read) }
# Create the trace object, falsely marked as already
# inserted to stop the import daemon trying to load it
trace = Trace.new(
@ -110,40 +106,14 @@ module Api
:tagstring => tags,
:description => description,
:visibility => visibility,
:inserted => true,
:inserted => false,
:user => current_user,
:timestamp => Time.now.getutc
:timestamp => Time.now.utc,
:file => file
)
if trace.valid?
Trace.transaction do
begin
# Save the trace object
trace.save!
# Rename the temporary file to the final name
FileUtils.mv(filename, trace.trace_name)
rescue StandardError
# Remove the file as we have failed to update the database
FileUtils.rm_f(filename)
# Pass the exception on
raise
end
begin
# Clear the inserted flag to make the import daemon load the trace
trace.inserted = false
trace.save!
rescue StandardError
# Remove the file as we have failed to update the database
FileUtils.rm_f(trace.trace_name)
# Pass the exception on
raise
end
end
end
# Save the trace object
trace.save!
# Finally save the user's preferred privacy level
if pref = current_user.preferences.where(:k => "gps.trace.visibility").first

View file

@ -1,12 +1,12 @@
module Api
class UsersController < ApiController
before_action :check_api_readable
before_action :disable_terms_redirect, :only => [:details]
before_action :setup_user_auth, :only => [:show, :index]
before_action :authorize, :only => [:details, :gpx_files]
authorize_resource
before_action :check_api_readable
around_action :api_call_handle_error
before_action :lookup_user_by_id, :only => [:show]

View file

@ -2,13 +2,13 @@ module Api
class WaysController < ApiController
require "xml/libxml"
before_action :check_api_writable, :only => [:create, :update, :delete]
before_action :check_api_readable, :except => [:create, :update, :delete]
before_action :authorize, :only => [:create, :update, :delete]
authorize_resource
before_action :require_public_data, :only => [:create, :update, :delete]
before_action :check_api_writable, :only => [:create, :update, :delete]
before_action :check_api_readable, :except => [:create, :update, :delete]
around_action :api_call_handle_error, :api_call_timeout
before_action :set_request_formats, :except => [:create, :update, :delete]

View file

@ -31,9 +31,8 @@ class ApiController < ApplicationController
# as XML for backwards compatibility - all other formats are discarded
# which will result in a 406 Not Acceptable response being sent
formats = mimetypes.map do |mime|
if mime.symbol == :xml then :xml
if mime.symbol == :xml || mime == "*/*" then :xml
elsif mime.symbol == :json then :json
elsif mime == "*/*" then :xml
end
end
else
@ -53,8 +52,13 @@ class ApiController < ApplicationController
# handle authenticate pass/fail
unless current_user
# no auth, the user does not exist or the password was wrong
response.headers["WWW-Authenticate"] = "Basic realm=\"#{realm}\""
render :plain => errormessage, :status => :unauthorized
if Settings.basic_auth_support
response.headers["WWW-Authenticate"] = "Basic realm=\"#{realm}\""
render :plain => errormessage, :status => :unauthorized
else
render :plain => errormessage, :status => :forbidden
end
false
end
end
@ -76,11 +80,13 @@ class ApiController < ApplicationController
report_error t("oauth.permissions.missing"), :forbidden
elsif current_user
head :forbidden
else
elsif Settings.basic_auth_support
realm = "Web Password"
errormessage = "Couldn't authenticate you"
response.headers["WWW-Authenticate"] = "Basic realm=\"#{realm}\""
render :plain => errormessage, :status => :unauthorized
else
render :plain => errormessage, :status => :forbidden
end
end
@ -95,13 +101,14 @@ class ApiController < ApplicationController
# from the authorize method, but can be called elsewhere if authorisation
# is optional.
def setup_user_auth
logger.info " setup_user_auth"
# try and setup using OAuth
if doorkeeper_token&.accessible?
self.current_user = User.find(doorkeeper_token.resource_owner_id)
elsif Authenticator.new(self, [:token]).allow?
# self.current_user setup by OAuth
else
username, passwd = get_auth_data # parse from headers
elsif Settings.basic_auth_support
username, passwd = auth_data # parse from headers
# authenticate per-scheme
self.current_user = if username.nil?
nil # no authentication provided - perhaps first connect (client should retry after 401)
@ -110,6 +117,8 @@ class ApiController < ApplicationController
else
User.authenticate(:username => username, :password => passwd) # basic auth
end
# log if we have authenticated using basic auth
logger.info "Authenticated as user #{current_user.id} using basic authentication" if current_user
end
# have we identified the user?

View file

@ -161,7 +161,7 @@ class ApplicationController < ActionController::Base
response.headers["Error"] = message
if request.headers["X-Error-Format"]&.casecmp("xml")&.zero?
result = OSM::API.new.get_xml_doc
result = OSM::API.new.xml_doc
result.root.name = "osmError"
result.root << (XML::Node.new("status") << "#{Rack::Utils.status_code(status)} #{Rack::Utils::HTTP_STATUS_CODES[status]}")
result.root << (XML::Node.new("message") << message)
@ -349,7 +349,7 @@ class ApplicationController < ActionController::Base
elsif current_user
set_locale
respond_to do |format|
format.html { redirect_to :controller => "errors", :action => "forbidden" }
format.html { redirect_to :controller => "/errors", :action => "forbidden" }
format.any { report_error t("application.permission_denied"), :forbidden }
end
elsif request.get?
@ -363,7 +363,7 @@ class ApplicationController < ActionController::Base
end
# extract authorisation credentials from headers, returns user = nil if none
def get_auth_data
def auth_data
if request.env.key? "X-HTTP_AUTHORIZATION" # where mod_rewrite might have put it
authdata = request.env["X-HTTP_AUTHORIZATION"].to_s.split
elsif request.env.key? "REDIRECT_X_HTTP_AUTHORIZATION" # mod_fcgi

View file

@ -8,7 +8,7 @@ module SessionMethods
def auth_url(provider, uid, referer = nil)
params = { :provider => provider }
params[:openid_url] = openid_expand_url(uid) if provider == "openid"
params[:openid_url] = uid if provider == "openid"
if referer.nil?
params[:origin] = request.path
@ -20,23 +20,6 @@ module SessionMethods
auth_path(params)
end
##
# special case some common OpenID providers by applying heuristics to
# try and come up with the correct URL based on what the user entered
def openid_expand_url(openid_url)
if openid_url.nil?
nil
elsif openid_url.match(%r{(.*)gmail.com(/?)$}) || openid_url.match(%r{(.*)googlemail.com(/?)$})
# Special case gmail.com as it is potentially a popular OpenID
# provider and, unlike yahoo.com, where it works automatically, Google
# have hidden their OpenID endpoint somewhere obscure this making it
# somewhat less user friendly.
"https://www.google.com/accounts/o8/id"
else
openid_url
end
end
##
# process a successful login
def successful_login(user, referer = nil)

View file

@ -0,0 +1,47 @@
module UserMethods
extend ActiveSupport::Concern
private
##
# update a user's details
def update_user(user, params)
user.display_name = params[:display_name]
user.new_email = params[:new_email]
unless params[:pass_crypt].empty? && params[:pass_crypt_confirmation].empty?
user.pass_crypt = params[:pass_crypt]
user.pass_crypt_confirmation = params[:pass_crypt_confirmation]
end
if params[:auth_provider].nil? || params[:auth_provider].blank?
user.auth_provider = nil
user.auth_uid = nil
end
if user.save
session[:fingerprint] = user.fingerprint
if user.new_email.blank? || user.new_email == user.email
flash[:notice] = t "accounts.update.success"
else
user.email = user.new_email
if user.valid?
flash[:notice] = t "accounts.update.success_confirm_needed"
begin
UserMailer.email_confirm(user, user.tokens.create).deliver_later
rescue StandardError
# Ignore errors sending email
end
else
current_user.errors.add(:new_email, current_user.errors[:email])
current_user.errors.add(:email, [])
end
user.restore_email!
end
end
end
end

View file

@ -25,7 +25,7 @@ class ConfirmationsController < ApplicationController
render_unknown_user token.user.display_name
else
user = token.user
user.status = "active"
user.activate
user.email_valid = true
flash[:notice] = gravatar_status_message(user) if gravatar_enable(user)
user.save!
@ -93,10 +93,10 @@ class ConfirmationsController < ApplicationController
current_user.tokens.delete_all
session[:user] = current_user.id
session[:fingerprint] = current_user.fingerprint
redirect_to user_account_path(current_user)
redirect_to edit_account_path
elsif token
flash[:error] = t "confirmations.confirm_email.failure"
redirect_to user_account_path(token.user)
redirect_to edit_account_path
else
flash[:error] = t "confirmations.confirm_email.unknown_token"
end

View file

@ -60,9 +60,8 @@ class DiaryEntriesController < ApplicationController
@title = t "diary_entries.edit.title"
@diary_entry = DiaryEntry.find(params[:id])
if current_user != @diary_entry.user
redirect_to diary_entry_path(@diary_entry.user, @diary_entry)
elsif params[:diary_entry] && @diary_entry.update(entry_params)
if current_user != @diary_entry.user ||
(params[:diary_entry] && @diary_entry.update(entry_params))
redirect_to diary_entry_path(@diary_entry.user, @diary_entry)
else
set_map_location

View file

@ -21,7 +21,7 @@ class ExportController < ApplicationController
format = params[:mapnik_format]
scale = params[:mapnik_scale]
redirect_to "https://render.openstreetmap.org/cgi-bin/export?bbox=#{bbox}&scale=#{scale}&format=#{format}"
redirect_to "https://render.openstreetmap.org/cgi-bin/export?bbox=#{bbox}&scale=#{scale}&format=#{format}", :allow_other_host => true
end
end

View file

@ -17,9 +17,9 @@ class FriendshipsController < ApplicationController
friendship = Friendship.new
friendship.befriender = current_user
friendship.befriendee = @new_friend
if current_user.is_friends_with?(@new_friend)
if current_user.friends_with?(@new_friend)
flash[:warning] = t "friendships.make_friend.already_a_friend", :name => @new_friend.display_name
elsif current_user.friendships.where("created_at >= ?", Time.now.getutc - 1.hour).count >= current_user.max_friends_per_hour
elsif current_user.friendships.where("created_at >= ?", Time.now.utc - 1.hour).count >= current_user.max_friends_per_hour
flash.now[:error] = t "friendships.make_friend.limit_exceeded"
elsif friendship.save
flash[:notice] = t "friendships.make_friend.success", :name => @new_friend.display_name
@ -42,7 +42,7 @@ class FriendshipsController < ApplicationController
if @friend
if request.post?
if current_user.is_friends_with?(@friend)
if current_user.friends_with?(@friend)
Friendship.where(:befriender => current_user, :befriendee => @friend).delete_all
flash[:notice] = t "friendships.remove_friend.success", :name => @friend.display_name
else

View file

@ -18,9 +18,8 @@ class GeocoderController < ApplicationController
@sources.push "geonames_reverse" if Settings.key?(:geonames_username)
elsif @params[:query]
case @params[:query]
when /^\d{5}(-\d{4})?$/
@sources.push "osm_nominatim"
when /^(GIR 0AA|[A-PR-UWYZ]([0-9]{1,2}|([A-HK-Y][0-9]|[A-HK-Y][0-9]([0-9]|[ABEHMNPRV-Y]))|[0-9][A-HJKS-UW])\s*[0-9][ABD-HJLNP-UW-Z]{2})$/i
when /^\d{5}(-\d{4})?$/,
/^(GIR 0AA|[A-PR-UWYZ]([0-9]{1,2}|([A-HK-Y][0-9]|[A-HK-Y][0-9]([0-9]|[ABEHMNPRV-Y]))|[0-9][A-HJKS-UW])\s*[0-9][ABD-HJLNP-UW-Z]{2})$/i
@sources.push "osm_nominatim"
when /^[A-Z]\d[A-Z]\s*\d[A-Z]\d$/i
@sources.push "ca_postcode"
@ -168,7 +167,8 @@ class GeocoderController < ApplicationController
render :action => "results"
rescue StandardError => e
@error = "Error contacting nominatim.openstreetmap.org: #{e}"
host = URI(Settings.nominatim_url).host
@error = "Error contacting #{host}: #{e}"
render :action => "error"
end
@ -232,7 +232,8 @@ class GeocoderController < ApplicationController
render :action => "results"
rescue StandardError => e
@error = "Error contacting nominatim.openstreetmap.org: #{e}"
host = URI(Settings.nominatim_url).host
@error = "Error contacting #{host}: #{e}"
render :action => "error"
end
@ -291,22 +292,19 @@ class GeocoderController < ApplicationController
if query = params[:query]
query.strip!
if latlon = query.match(/^([NS])\s*(\d{1,3}(\.\d*)?)\W*([EW])\s*(\d{1,3}(\.\d*)?)$/).try(:captures) # [NSEW] decimal degrees
params.merge!(nsew_to_decdeg(latlon)).delete(:query)
elsif latlon = query.match(/^(\d{1,3}(\.\d*)?)\s*([NS])\W*(\d{1,3}(\.\d*)?)\s*([EW])$/).try(:captures) # decimal degrees [NSEW]
if latlon = query.match(/^([NS])\s*(\d{1,3}(\.\d*)?)\W*([EW])\s*(\d{1,3}(\.\d*)?)$/).try(:captures) || # [NSEW] decimal degrees
query.match(/^(\d{1,3}(\.\d*)?)\s*([NS])\W*(\d{1,3}(\.\d*)?)\s*([EW])$/).try(:captures) # decimal degrees [NSEW]
params.merge!(nsew_to_decdeg(latlon)).delete(:query)
elsif latlon = query.match(/^([NS])\s*(\d{1,3})°?\s*(\d{1,3}(\.\d*)?)?[']?\W*([EW])\s*(\d{1,3})°?\s*(\d{1,3}(\.\d*)?)?[']?$/).try(:captures) # [NSEW] degrees, decimal minutes
params.merge!(ddm_to_decdeg(latlon)).delete(:query)
elsif latlon = query.match(/^(\d{1,3})°?\s*(\d{1,3}(\.\d*)?)?[']?\s*([NS])\W*(\d{1,3})°?\s*(\d{1,3}(\.\d*)?)?[']?\s*([EW])$/).try(:captures) # degrees, decimal minutes [NSEW]
elsif latlon = query.match(/^([NS])\s*(\d{1,3})°?(?:\s*(\d{1,3}(\.\d*)?)?[']?)?\W*([EW])\s*(\d{1,3})°?(?:\s*(\d{1,3}(\.\d*)?)?[']?)?$/).try(:captures) || # [NSEW] degrees, decimal minutes
query.match(/^(\d{1,3})°?(?:\s*(\d{1,3}(\.\d*)?)?[']?)?\s*([NS])\W*(\d{1,3})°?(?:\s*(\d{1,3}(\.\d*)?)?[']?)?\s*([EW])$/).try(:captures) # degrees, decimal minutes [NSEW]
params.merge!(ddm_to_decdeg(latlon)).delete(:query)
elsif latlon = query.match(/^([NS])\s*(\d{1,3})°?\s*(\d{1,2})[']?\s*(\d{1,3}(\.\d*)?)?["″]?\W*([EW])\s*(\d{1,3})°?\s*(\d{1,2})[']?\s*(\d{1,3}(\.\d*)?)?["″]?$/).try(:captures) # [NSEW] degrees, minutes, decimal seconds
params.merge!(dms_to_decdeg(latlon)).delete(:query)
elsif latlon = query.match(/^(\d{1,3})°?\s*(\d{1,2})[']?\s*(\d{1,3}(\.\d*)?)?["″]\s*([NS])\W*(\d{1,3})°?\s*(\d{1,2})[']?\s*(\d{1,3}(\.\d*)?)?["″]?\s*([EW])$/).try(:captures) # degrees, minutes, decimal seconds [NSEW]
elsif latlon = query.match(/^([NS])\s*(\d{1,3})°?\s*(\d{1,2})[']?(?:\s*(\d{1,3}(\.\d*)?)?["″]?)?\W*([EW])\s*(\d{1,3})°?\s*(\d{1,2})[']?(?:\s*(\d{1,3}(\.\d*)?)?["″]?)?$/).try(:captures) || # [NSEW] degrees, minutes, decimal seconds
query.match(/^(\d{1,3})°?\s*(\d{1,2})[']?(?:\s*(\d{1,3}(\.\d*)?)?["″]?)?\s*([NS])\W*(\d{1,3})°?\s*(\d{1,2})[']?(?:\s*(\d{1,3}(\.\d*)?)?["″]?)?\s*([EW])$/).try(:captures) # degrees, minutes, decimal seconds [NSEW]
params.merge!(dms_to_decdeg(latlon)).delete(:query)
elsif latlon = query.match(/^\s*([+-]?\d+(\.\d*)?)\s*[\s,]\s*([+-]?\d+(\.\d*)?)\s*$/)
elsif latlon = query.match(/^([+-]?\d+(\.\d*)?)(?:\s+|\s*,\s*)([+-]?\d+(\.\d*)?)$/)
params.merge!(:lat => latlon[1].to_f, :lon => latlon[3].to_f).delete(:query)
params[:latlon_digits] = true unless params[:whereami]
@ -331,11 +329,11 @@ class GeocoderController < ApplicationController
def ddm_to_decdeg(captures)
begin
Float(captures[0])
lat = captures[3].casecmp("s").zero? ? -(captures[0].to_f + captures[1].to_f / 60) : captures[0].to_f + captures[1].to_f / 60
lon = captures[7].casecmp("w").zero? ? -(captures[4].to_f + captures[5].to_f / 60) : captures[4].to_f + captures[5].to_f / 60
lat = captures[3].casecmp("s").zero? ? -(captures[0].to_f + (captures[1].to_f / 60)) : captures[0].to_f + (captures[1].to_f / 60)
lon = captures[7].casecmp("w").zero? ? -(captures[4].to_f + (captures[5].to_f / 60)) : captures[4].to_f + (captures[5].to_f / 60)
rescue StandardError
lat = captures[0].casecmp("s").zero? ? -(captures[1].to_f + captures[2].to_f / 60) : captures[1].to_f + captures[2].to_f / 60
lon = captures[4].casecmp("w").zero? ? -(captures[5].to_f + captures[6].to_f / 60) : captures[5].to_f + captures[6].to_f / 60
lat = captures[0].casecmp("s").zero? ? -(captures[1].to_f + (captures[2].to_f / 60)) : captures[1].to_f + (captures[2].to_f / 60)
lon = captures[4].casecmp("w").zero? ? -(captures[5].to_f + (captures[6].to_f / 60)) : captures[5].to_f + (captures[6].to_f / 60)
end
{ :lat => lat, :lon => lon }
end
@ -343,11 +341,11 @@ class GeocoderController < ApplicationController
def dms_to_decdeg(captures)
begin
Float(captures[0])
lat = captures[4].casecmp("s").zero? ? -(captures[0].to_f + (captures[1].to_f + captures[2].to_f / 60) / 60) : captures[0].to_f + (captures[1].to_f + captures[2].to_f / 60) / 60
lon = captures[9].casecmp("w").zero? ? -(captures[5].to_f + (captures[6].to_f + captures[7].to_f / 60) / 60) : captures[5].to_f + (captures[6].to_f + captures[7].to_f / 60) / 60
lat = captures[4].casecmp("s").zero? ? -(captures[0].to_f + ((captures[1].to_f + (captures[2].to_f / 60)) / 60)) : captures[0].to_f + ((captures[1].to_f + (captures[2].to_f / 60)) / 60)
lon = captures[9].casecmp("w").zero? ? -(captures[5].to_f + ((captures[6].to_f + (captures[7].to_f / 60)) / 60)) : captures[5].to_f + ((captures[6].to_f + (captures[7].to_f / 60)) / 60)
rescue StandardError
lat = captures[0].casecmp("s").zero? ? -(captures[1].to_f + (captures[2].to_f + captures[3].to_f / 60) / 60) : captures[1].to_f + (captures[2].to_f + captures[3].to_f / 60) / 60
lon = captures[5].casecmp("w").zero? ? -(captures[6].to_f + (captures[7].to_f + captures[8].to_f / 60) / 60) : captures[6].to_f + (captures[7].to_f + captures[8].to_f / 60) / 60
lat = captures[0].casecmp("s").zero? ? -(captures[1].to_f + ((captures[2].to_f + (captures[3].to_f / 60)) / 60)) : captures[1].to_f + ((captures[2].to_f + (captures[3].to_f / 60)) / 60)
lon = captures[5].casecmp("w").zero? ? -(captures[6].to_f + ((captures[7].to_f + (captures[8].to_f / 60)) / 60)) : captures[6].to_f + ((captures[7].to_f + (captures[8].to_f / 60)) / 60)
end
{ :lat => lat, :lon => lon }
end

View file

@ -11,9 +11,20 @@ class IssueCommentsController < ApplicationController
comment = @issue.comments.build(issue_comment_params)
comment.user = current_user
comment.save!
notice = t(".comment_created")
reassign_issue(@issue) if params[:reassign]
redirect_to @issue, :notice => notice
if params[:reassign]
reassign_issue(@issue)
flash[:notice] = t ".issue_reassigned"
if current_user.has_role? @issue.assigned_role
redirect_to @issue
else
redirect_to issues_path(:status => "open")
end
else
flash[:notice] = t(".comment_created")
redirect_to @issue
end
end
private

View file

@ -24,9 +24,9 @@ class MessagesController < ApplicationController
@message = Message.new(message_params)
@message.recipient = @user
@message.sender = current_user
@message.sent_on = Time.now.getutc
@message.sent_on = Time.now.utc
if current_user.sent_messages.where("sent_on >= ?", Time.now.getutc - 1.hour).count >= current_user.max_messages_per_hour
if current_user.sent_messages.where("sent_on >= ?", Time.now.utc - 1.hour).count >= current_user.max_messages_per_hour
flash.now[:error] = t ".limit_exceeded"
render :action => "new"
elsif @message.save
@ -121,11 +121,7 @@ class MessagesController < ApplicationController
referer = safe_referer(params[:referer]) if params[:referer]
if referer
redirect_to referer
else
redirect_to :action => :inbox
end
redirect_to referer || { :action => :inbox }
end
rescue ActiveRecord::RecordNotFound
@title = t "messages.no_such_message.title"

View file

@ -3,12 +3,13 @@ class Oauth2AuthorizationsController < Doorkeeper::AuthorizationsController
prepend_before_action :authorize_web
before_action :set_locale
before_action :allow_all_form_action, :only => [:new]
authorize_resource :class => false
def new
override_content_security_policy_directives(:form_action => []) if Settings.csp_enforce || Settings.key?(:csp_report_url)
private
super
def allow_all_form_action
override_content_security_policy_directives(:form_action => []) if Settings.csp_enforce || Settings.key?(:csp_report_url)
end
end

View file

@ -66,7 +66,7 @@ class OauthController < ApplicationController
@redirect_url.query += "&oauth_verifier=#{@token.verifier}" unless @token.oauth10?
redirect_to @redirect_url.to_s
redirect_to @redirect_url.to_s, :allow_other_host => true
end
else
@token.invalidate!

View file

@ -46,7 +46,7 @@ class PasswordsController < ApplicationController
if params[:user]
current_user.pass_crypt = params[:user][:pass_crypt]
current_user.pass_crypt_confirmation = params[:user][:pass_crypt_confirmation]
current_user.status = "active" if current_user.status == "pending"
current_user.activate if current_user.may_activate?
current_user.email_valid = true
if current_user.save

View file

@ -26,7 +26,7 @@ class PreferencesController < ApplicationController
flash[:notice] = { :partial => "preferences/update_success_flash" }
redirect_to preferences_path
else
flash[:error] = t ".failure"
flash.now[:error] = t ".failure"
render :edit
end
end

View file

@ -36,7 +36,7 @@ class ProfilesController < ApplicationController
flash[:notice] = t ".success"
redirect_to user_path(current_user)
else
flash[:error] = t ".failure"
flash.now[:error] = t ".failure"
render :edit
end
end

View file

@ -28,7 +28,8 @@ class ReportsController < ApplicationController
redirect_to helpers.reportable_url(@report.issue.reportable), :notice => t(".successful_report")
else
redirect_to new_report_path(:reportable_type => @report.issue.reportable_type, :reportable_id => @report.issue.reportable_id), :notice => t(".provide_details")
flash.now[:notice] = t(".provide_details")
render :action => "new"
end
end

View file

@ -12,9 +12,7 @@ class SessionsController < ApplicationController
authorize_resource :class => false
def new
append_content_security_policy_directives(
:form_action => %w[*]
)
override_content_security_policy_directives(:form_action => []) if Settings.csp_enforce || Settings.key?(:csp_report_url)
session[:referer] = safe_referer(params[:referer]) if params[:referer]
end
@ -40,11 +38,7 @@ class SessionsController < ApplicationController
referer = safe_referer(params[:referer]) if params[:referer]
if referer
redirect_to referer
else
redirect_to :controller => "site", :action => "index"
end
redirect_to referer || { :controller => "site", :action => "index" }
end
end

View file

@ -7,6 +7,7 @@ class SiteController < ApplicationController
before_action :redirect_browse_params, :only => :index
before_action :redirect_map_params, :only => [:index, :edit, :export]
before_action :require_oauth, :only => [:index]
before_action :require_user, :only => [:id]
before_action :update_totp, :only => [:index]
authorize_resource :class => false

View file

@ -28,7 +28,7 @@ class TracesController < ApplicationController
@title = if target_user.nil?
t ".public_traces"
elsif current_user && current_user == target_user
t ".my_traces"
t ".my_gps_traces"
else
t ".public_traces_from", :user => target_user.display_name
end
@ -99,12 +99,8 @@ class TracesController < ApplicationController
logger.info(params[:trace][:gpx_file].class.name)
if params[:trace][:gpx_file].respond_to?(:read)
begin
@trace = do_create(params[:trace][:gpx_file], params[:trace][:tagstring],
params[:trace][:description], params[:trace][:visibility])
rescue StandardError => e
logger.debug e
end
@trace = do_create(params[:trace][:gpx_file], params[:trace][:tagstring],
params[:trace][:description], params[:trace][:visibility])
if @trace.id
flash[:notice] = t ".trace_uploaded"
@ -123,7 +119,7 @@ class TracesController < ApplicationController
:description => params[:trace][:description],
:visibility => params[:trace][:visibility],
:inserted => false, :user => current_user,
:timestamp => Time.now.getutc)
:timestamp => Time.now.utc)
@trace.valid?
@trace.errors.add(:gpx_file, "can't be blank")
@ -141,6 +137,8 @@ class TracesController < ApplicationController
send_data(trace.xml_file.read, :filename => "#{trace.id}.xml", :type => request.format.to_s, :disposition => "attachment")
elsif request.format == Mime[:gpx]
send_data(trace.xml_file.read, :filename => "#{trace.id}.gpx", :type => request.format.to_s, :disposition => "attachment")
elsif trace.file.attached?
redirect_to rails_blob_path(trace.file, :disposition => "attachment")
else
send_file(trace.trace_name, :filename => "#{trace.id}#{trace.extension_name}", :type => trace.mime_type, :disposition => "attachment")
end
@ -217,8 +215,12 @@ class TracesController < ApplicationController
if trace.visible? && trace.inserted?
if trace.public? || (current_user && current_user == trace.user)
expires_in 7.days, :private => !trace.public?, :public => trace.public?
send_file(trace.large_picture_name, :filename => "#{trace.id}.gif", :type => "image/gif", :disposition => "inline")
if trace.icon.attached?
redirect_to rails_blob_path(trace.image, :disposition => "inline")
else
expires_in 7.days, :private => !trace.public?, :public => trace.public?
send_file(trace.large_picture_name, :filename => "#{trace.id}.gif", :type => "image/gif", :disposition => "inline")
end
else
head :forbidden
end
@ -234,8 +236,12 @@ class TracesController < ApplicationController
if trace.visible? && trace.inserted?
if trace.public? || (current_user && current_user == trace.user)
expires_in 7.days, :private => !trace.public?, :public => trace.public?
send_file(trace.icon_picture_name, :filename => "#{trace.id}_icon.gif", :type => "image/gif", :disposition => "inline")
if trace.icon.attached?
redirect_to rails_blob_path(trace.icon, :disposition => "inline")
else
expires_in 7.days, :private => !trace.public?, :public => trace.public?
send_file(trace.icon_picture_name, :filename => "#{trace.id}_icon.gif", :type => "image/gif", :disposition => "inline")
end
else
head :forbidden
end
@ -252,62 +258,29 @@ class TracesController < ApplicationController
# Sanitise the user's filename
name = file.original_filename.gsub(/[^a-zA-Z0-9.]/, "_")
# Get a temporary filename...
filename = "/tmp/#{rand}"
# ...and save the uploaded file to that location
File.open(filename, "wb") { |f| f.write(file.read) }
# Create the trace object, falsely marked as already
# inserted to stop the import daemon trying to load it
# Create the trace object
trace = Trace.new(
:name => name,
:tagstring => tags,
:description => description,
:visibility => visibility,
:inserted => true,
:inserted => false,
:user => current_user,
:timestamp => Time.now.getutc
:timestamp => Time.now.utc,
:file => file
)
if trace.valid?
Trace.transaction do
begin
# Save the trace object
trace.save!
# Rename the temporary file to the final name
FileUtils.mv(filename, trace.trace_name)
rescue StandardError
# Remove the file as we have failed to update the database
FileUtils.rm_f(filename)
# Pass the exception on
raise
end
begin
# Clear the inserted flag to make the import daemon load the trace
trace.inserted = false
trace.save!
rescue StandardError
# Remove the file as we have failed to update the database
FileUtils.rm_f(trace.trace_name)
# Pass the exception on
raise
end
# Save the trace object
if trace.save
# Finally save the user's preferred privacy level
if pref = current_user.preferences.where(:k => "gps.trace.visibility").first
pref.v = visibility
pref.save
else
current_user.preferences.create(:k => "gps.trace.visibility", :v => visibility)
end
end
# Finally save the user's preferred privacy level
if pref = current_user.preferences.where(:k => "gps.trace.visibility").first
pref.v = visibility
pref.save
else
current_user.preferences.create(:k => "gps.trace.visibility", :v => visibility)
end
trace
end

View file

@ -32,7 +32,7 @@ class UserBlocksController < ApplicationController
end
def edit
params[:user_block_period] = ((@user_block.ends_at - Time.now.getutc) / 1.hour).ceil.to_s
params[:user_block_period] = ((@user_block.ends_at - Time.now.utc) / 1.hour).ceil.to_s
end
def create
@ -41,7 +41,7 @@ class UserBlocksController < ApplicationController
:user => @user,
:creator => current_user,
:reason => params[:user_block][:reason],
:ends_at => Time.now.getutc + @block_period.hours,
:ends_at => Time.now.utc + @block_period.hours,
:needs_view => params[:user_block][:needs_view]
)
@ -62,7 +62,7 @@ class UserBlocksController < ApplicationController
flash[:error] = t(".only_creator_can_edit")
redirect_to :action => "edit"
elsif @user_block.update(
:ends_at => Time.now.getutc + @block_period.hours,
:ends_at => Time.now.utc + @block_period.hours,
:reason => params[:user_block][:reason],
:needs_view => params[:user_block][:needs_view]
)

View file

@ -1,5 +1,6 @@
class UsersController < ApplicationController
include SessionMethods
include UserMethods
layout "site"
@ -11,11 +12,10 @@ class UsersController < ApplicationController
authorize_resource
before_action :require_self, :only => [:account]
before_action :check_database_writable, :only => [:new, :account, :go_public]
before_action :check_database_writable, :only => [:new, :go_public]
before_action :require_cookies, :only => [:new]
before_action :lookup_user_by_name, :only => [:set_status, :destroy]
before_action :allow_thirdparty_images, :only => [:show, :account]
before_action :allow_thirdparty_images, :only => [:show]
def terms
@legale = params[:legale] || OSM.ip_to_country(request.remote_ip) || Settings.default_legale
@ -28,7 +28,7 @@ class UsersController < ApplicationController
if current_user&.terms_agreed?
# Already agreed to terms, so just show settings
redirect_to user_account_path(current_user)
redirect_to edit_account_path
elsif current_user.nil? && session[:new_user].nil?
redirect_to login_path(:referer => request.fullpath)
end
@ -46,7 +46,7 @@ class UsersController < ApplicationController
referer = safe_referer(params[:referer]) if params[:referer]
redirect_to referer || user_account_path(current_user)
redirect_to referer || edit_account_path
elsif params[:decline]
redirect_to t("users.terms.declined")
else
@ -55,8 +55,8 @@ class UsersController < ApplicationController
elsif current_user
unless current_user.terms_agreed?
current_user.consider_pd = params[:user][:consider_pd]
current_user.tou_agreed = Time.now.getutc
current_user.terms_agreed = Time.now.getutc
current_user.tou_agreed = Time.now.utc
current_user.terms_agreed = Time.now.utc
current_user.terms_seen = true
flash[:notice] = t "users.new.terms accepted" if current_user.save
@ -64,7 +64,7 @@ class UsersController < ApplicationController
referer = safe_referer(params[:referer]) if params[:referer]
redirect_to referer || user_account_path(current_user)
redirect_to referer || edit_account_path
else
self.current_user = session.delete(:new_user)
@ -73,8 +73,8 @@ class UsersController < ApplicationController
current_user.description = "" if current_user.description.nil?
current_user.creation_ip = request.remote_ip
current_user.languages = http_accept_language.user_preferred_languages
current_user.terms_agreed = Time.now.getutc
current_user.tou_agreed = Time.now.getutc
current_user.terms_agreed = Time.now.utc
current_user.tou_agreed = Time.now.utc
current_user.terms_seen = true
if current_user.auth_uid.blank?
@ -83,7 +83,7 @@ class UsersController < ApplicationController
end
if current_user.save
flash[:piwik_goal] = PIWIK["goals"]["signup"] if defined?(PIWIK)
flash[:matomo_goal] = Settings.matomo["goals"]["signup"] if defined?(Settings.matomo)
referer = welcome_path
@ -114,36 +114,11 @@ class UsersController < ApplicationController
end
end
def account
@tokens = current_user.oauth_tokens.authorized
append_content_security_policy_directives(
:form_action => %w[accounts.google.com *.facebook.com login.live.com github.com meta.wikimedia.org]
)
if request.post?
if params[:user][:auth_provider].blank? ||
(params[:user][:auth_provider] == current_user.auth_provider &&
params[:user][:auth_uid] == current_user.auth_uid)
update_user(current_user, params)
redirect_to user_account_url(current_user) if current_user.errors.count.zero?
else
session[:new_user_settings] = params
redirect_to auth_url(params[:user][:auth_provider], params[:user][:auth_uid]), :status => :temporary_redirect
end
elsif errors = session.delete(:user_errors)
errors.each do |attribute, error|
current_user.errors.add(attribute, error)
end
end
@title = t "users.account.title"
end
def go_public
current_user.data_public = true
current_user.save
flash[:notice] = t "users.go_public.flash success"
redirect_to user_account_path(current_user)
redirect_to edit_account_path
end
def new
@ -161,11 +136,7 @@ class UsersController < ApplicationController
if current_user
# The user is logged in already, so don't show them the signup
# page, instead send them to the home page
if @referer
redirect_to @referer
else
redirect_to :controller => "site", :action => "index"
end
redirect_to @referer || { :controller => "site", :action => "index" }
elsif params.key?(:auth_provider) && params.key?(:auth_uid)
self.current_user = User.new(:email => params[:email],
:email_confirmation => params[:email],
@ -189,8 +160,6 @@ class UsersController < ApplicationController
Rails.logger.info "create: #{session[:referer]}"
current_user.status = "pending"
if current_user.auth_provider.present? && current_user.pass_crypt.empty?
# We are creating an account with external authentication and
# no password was specified so create a random one
@ -227,15 +196,19 @@ class UsersController < ApplicationController
##
# sets a user's status
def set_status
@user.status = params[:status]
@user.save
@user.activate! if params[:event] == "activate"
@user.confirm! if params[:event] == "confirm"
@user.unconfirm! if params[:event] == "unconfirm"
@user.hide! if params[:event] == "hide"
@user.unhide! if params[:event] == "unhide"
@user.unsuspend! if params[:event] == "unsuspend"
redirect_to user_path(:display_name => params[:display_name])
end
##
# delete a user, marking them as deleted and removing personal data
# destroy a user, marking them as deleted and removing personal data
def destroy
@user.delete
@user.soft_destroy!
redirect_to user_path(:display_name => params[:display_name])
end
@ -293,12 +266,12 @@ class UsersController < ApplicationController
session[:user_errors] = current_user.errors.as_json
redirect_to user_account_path(current_user)
redirect_to edit_account_path
elsif session[:new_user]
session[:new_user].auth_provider = provider
session[:new_user].auth_uid = uid
session[:new_user].status = "active" if email_verified && email == session[:new_user].email
session[:new_user].activate if email_verified && email == session[:new_user].email
redirect_to :action => "terms"
else
@ -340,54 +313,6 @@ class UsersController < ApplicationController
private
##
# update a user's details
def update_user(user, params)
user.display_name = params[:user][:display_name]
user.new_email = params[:user][:new_email]
unless params[:user][:pass_crypt].empty? && params[:user][:pass_crypt_confirmation].empty?
user.pass_crypt = params[:user][:pass_crypt]
user.pass_crypt_confirmation = params[:user][:pass_crypt_confirmation]
end
if params[:user][:auth_provider].nil? || params[:user][:auth_provider].blank?
user.auth_provider = nil
user.auth_uid = nil
end
if user.save
session[:fingerprint] = user.fingerprint
if user.new_email.blank? || user.new_email == user.email
flash[:notice] = t "users.account.flash update success"
else
user.email = user.new_email
if user.valid?
flash[:notice] = t "users.account.flash update success confirm needed"
begin
UserMailer.email_confirm(user, user.tokens.create).deliver_later
rescue StandardError
# Ignore errors sending email
end
else
current_user.errors.add(:new_email, current_user.errors[:email])
current_user.errors.add(:email, [])
end
user.restore_email!
end
end
end
##
# require that the user in the URL is the logged in user
def require_self
head :forbidden if params[:display_name] != current_user.display_name
end
##
# ensure that there is a "user" instance variable
def lookup_user_by_name

View file

@ -47,11 +47,10 @@ module BrowseTagsHelper
# the correct page.
lookup_us = lookup.tr(" ", "_")
if page = WIKI_PAGES.dig(locale, type, lookup_us)
url = "https://wiki.openstreetmap.org/wiki/#{page}?uselang=#{locale}"
elsif page = WIKI_PAGES.dig("en", type, lookup_us)
url = "https://wiki.openstreetmap.org/wiki/#{page}?uselang=#{locale}"
end
page = WIKI_PAGES.dig(locale, type, lookup_us) ||
WIKI_PAGES.dig("en", type, lookup_us)
url = "https://wiki.openstreetmap.org/wiki/#{page}?uselang=#{locale}" if page
url
end

View file

@ -10,7 +10,7 @@ module ChangesetsHelper
end
def changeset_details(changeset)
if changeset.closed_at > Time.now
if changeset.closed_at > Time.now.utc
action = :created
time = time_ago_in_words(changeset.created_at, :scope => :"datetime.distance_in_words_ago")
title = l(changeset.created_at)

View file

@ -8,7 +8,7 @@ module UserBlocksHelper
if block.active?
# if the block hasn't expired yet show the date, if the user just needs to login show that
if block.needs_view?
if block.ends_at > Time.now.getutc
if block.ends_at > Time.now.utc
t("user_blocks.helper.time_future_and_until_login_html", :time => friendly_date(block.ends_at))
else
t("user_blocks.helper.until_login")

View file

@ -58,7 +58,7 @@ module UserHelper
def auth_button(name, provider, options = {})
link_to(
image_tag("#{name}.svg", :alt => t("sessions.new.auth_providers.#{name}.alt"), :class => "rounded-lg"),
image_tag("#{name}.svg", :alt => t("sessions.new.auth_providers.#{name}.alt"), :class => "rounded-3"),
auth_path(options.merge(:provider => provider)),
:method => :post,
:class => "auth_button",

View file

@ -183,7 +183,7 @@ class UserMailer < ApplicationMailer
end
def attach_project_logo
attachments.inline["logo.png"] = File.read(Rails.root.join("app/assets/images/osm_logo_30.png"))
attachments.inline["logo.png"] = Rails.root.join("app/assets/images/osm_logo_30.png").read
end
def attach_user_avatar(user)
@ -199,7 +199,7 @@ class UserMailer < ApplicationMailer
avatar.blob.download
end
else
File.read(Rails.root.join("app/assets/images/avatar_small.png"))
Rails.root.join("app/assets/images/avatar_small.png").read
end
end

View file

@ -36,8 +36,8 @@
#
class AccessToken < OauthToken
belongs_to :user
belongs_to :client_application
belongs_to :user, :optional => true
belongs_to :client_application, :optional => true
scope :valid, -> { where(:invalidated_at => nil) }
@ -52,6 +52,6 @@ class AccessToken < OauthToken
protected
def set_authorized_at
self.authorized_at = Time.now
self.authorized_at = Time.now.utc
end
end

View file

@ -44,8 +44,6 @@ class Changeset < ApplicationRecord
validates :id, :uniqueness => true, :presence => { :on => :update },
:numericality => { :on => :update, :only_integer => true }
validates :user_id, :presence => true,
:numericality => { :only_integer => true }
validates :num_changes, :presence => true,
:numericality => { :only_integer => true,
:greater_than_or_equal_to => 0 }
@ -67,17 +65,17 @@ class Changeset < ApplicationRecord
# Use a method like this, so that we can easily change how we
# determine whether a changeset is open, without breaking code in at
# least 6 controllers
def is_open?
def open?
# a changeset is open (that is, it will accept further changes) when
# it has not yet run out of time and its capacity is small enough.
# note that this may not be a hard limit - due to timing changes and
# concurrency it is possible that some changesets may be slightly
# longer than strictly allowed or have slightly more changes in them.
((closed_at > Time.now.getutc) && (num_changes <= MAX_ELEMENTS))
((closed_at > Time.now.utc) && (num_changes <= MAX_ELEMENTS))
end
def set_closed_time_now
self.closed_at = Time.now.getutc if is_open?
self.closed_at = Time.now.utc if open?
end
def self.from_xml(xml, create: false)
@ -97,7 +95,7 @@ class Changeset < ApplicationRecord
def self.from_xml_node(pt, create: false)
cs = Changeset.new
if create
cs.created_at = Time.now.getutc
cs.created_at = Time.now.utc
# initial close time is 1h ahead, but will be increased on each
# modification.
cs.closed_at = cs.created_at + IDLE_TIMEOUT
@ -122,7 +120,7 @@ class Changeset < ApplicationRecord
@bbox ||= BoundingBox.new(min_lon, min_lat, max_lon, max_lat)
end
def has_valid_bbox?
def bbox_valid?
bbox.complete?
end
@ -189,11 +187,11 @@ class Changeset < ApplicationRecord
# that would make it more than 24h long, in which case clip to
# 24h, as this has been decided is a reasonable time limit.
def update_closed_at
if is_open?
if open?
self.closed_at = if (closed_at - created_at) > (MAX_TIME_OPEN - IDLE_TIMEOUT)
created_at + MAX_TIME_OPEN
else
Time.now.getutc + IDLE_TIMEOUT
Time.now.utc + IDLE_TIMEOUT
end
end
end
@ -207,7 +205,7 @@ class Changeset < ApplicationRecord
raise OSM::APIUserChangesetMismatchError unless user.id == user_id
# can't change a closed changeset
raise OSM::APIChangesetAlreadyClosedError, self unless is_open?
raise OSM::APIChangesetAlreadyClosedError, self unless open?
# copy the other's tags
self.tags = other.tags

View file

@ -25,8 +25,8 @@ class ChangesetComment < ApplicationRecord
validates :id, :uniqueness => true, :presence => { :on => :update },
:numericality => { :on => :update, :only_integer => true }
validates :changeset, :presence => true, :associated => true
validates :author, :presence => true, :associated => true
validates :changeset, :associated => true
validates :author, :associated => true
validates :visible, :inclusion => [true, false]
validates :body, :characters => true

View file

@ -20,7 +20,7 @@ class ChangesetTag < ApplicationRecord
belongs_to :changeset
validates :changeset, :presence => true, :associated => true
validates :changeset, :associated => true
validates :k, :v, :allow_blank => true, :length => { :maximum => 255 }, :characters => true
validates :k, :uniqueness => { :scope => :changeset_id }
end

View file

@ -31,7 +31,7 @@
#
class ClientApplication < ApplicationRecord
belongs_to :user
belongs_to :user, :optional => true
has_many :tokens, :class_name => "OauthToken", :dependent => :delete_all
has_many :access_tokens
has_many :oauth2_verifiers

View file

@ -16,7 +16,7 @@ module ConsistencyValidations
raise OSM::APIChangesetMissingError
elsif new.changeset.user_id != user.id
raise OSM::APIUserChangesetMismatchError
elsif !new.changeset.is_open?
elsif !new.changeset.open?
raise OSM::APIChangesetAlreadyClosedError, new.changeset
end
end
@ -27,7 +27,7 @@ module ConsistencyValidations
raise OSM::APIChangesetMissingError
elsif new.changeset.user_id != user.id
raise OSM::APIUserChangesetMismatchError
elsif !new.changeset.is_open?
elsif !new.changeset.open?
raise OSM::APIChangesetAlreadyClosedError, new.changeset
end
end
@ -42,7 +42,7 @@ module ConsistencyValidations
raise OSM::APIChangesetMissingError
elsif user.id != changeset.user_id
raise OSM::APIUserChangesetMismatchError
elsif !changeset.is_open?
elsif !changeset.open?
raise OSM::APIChangesetAlreadyClosedError, changeset
end
end

View file

@ -11,7 +11,7 @@ module Redactable
def redact!(redaction)
# check that this version isn't the current version
raise OSM::APICannotRedactError if is_latest_version?
raise OSM::APICannotRedactError if latest_version?
# make the change
self.redaction = redaction

View file

@ -28,9 +28,9 @@
class DiaryEntry < ApplicationRecord
belongs_to :user, :counter_cache => true
belongs_to :language, :foreign_key => "language_code"
belongs_to :language, :foreign_key => "language_code", :inverse_of => :diary_entries
has_many :comments, -> { order(:id).preload(:user) }, :class_name => "DiaryComment"
has_many :comments, -> { order(:id).preload(:user) }, :class_name => "DiaryComment", :inverse_of => :diary_entry
has_many :visible_comments, -> { joins(:user).where(:visible => true, :users => { :status => %w[active confirmed] }).order(:id) }, :class_name => "DiaryComment"
has_many :subscriptions, :class_name => "DiaryEntrySubscription"
has_many :subscribers, :through => :subscriptions, :source => :user

View file

@ -32,9 +32,9 @@
class Issue < ApplicationRecord
belongs_to :reportable, :polymorphic => true
belongs_to :reported_user, :class_name => "User"
belongs_to :user_resolved, :class_name => "User", :foreign_key => :resolved_by
belongs_to :user_updated, :class_name => "User", :foreign_key => :updated_by
belongs_to :reported_user, :class_name => "User", :optional => true
belongs_to :user_resolved, :class_name => "User", :foreign_key => :resolved_by, :optional => true
belongs_to :user_updated, :class_name => "User", :foreign_key => :updated_by, :optional => true
has_many :reports, :dependent => :destroy
has_many :comments, :class_name => "IssueComment", :dependent => :destroy
@ -70,7 +70,7 @@ class Issue < ApplicationRecord
event :resolve do
transitions :from => :open, :to => :resolved
after do
self.resolved_at = Time.now.getutc
self.resolved_at = Time.now.utc
end
end

View file

@ -25,6 +25,8 @@ class IssueComment < ApplicationRecord
belongs_to :user
validates :body, :presence => true, :characters => true
validates :user, :presence => true
validates :issue, :presence => true
def body
RichText.new("markdown", self[:body])
end
end

View file

@ -10,7 +10,7 @@
class Language < ApplicationRecord
self.primary_key = "code"
has_many :diary_entries, :foreign_key => "language"
has_many :diary_entries, :foreign_key => "language", :inverse_of => :language
def self.load(file)
Language.transaction do

View file

@ -29,7 +29,7 @@ class Message < ApplicationRecord
belongs_to :recipient, :class_name => "User", :foreign_key => :to_user_id
validates :title, :presence => true, :utf8 => true, :length => 1..255
validates :body, :sent_on, :sender, :recipient, :presence => true
validates :body, :sent_on, :presence => true
validates :title, :body, :characters => true
def self.from_mail(mail, from, to)

View file

@ -32,7 +32,7 @@ class Node < ApplicationRecord
belongs_to :changeset
has_many :old_nodes, -> { order(:version) }
has_many :old_nodes, -> { order(:version) }, :inverse_of => :current_node
has_many :way_nodes
has_many :ways, :through => :way_nodes
@ -49,8 +49,6 @@ class Node < ApplicationRecord
:numericality => { :on => :update, :only_integer => true }
validates :version, :presence => true,
:numericality => { :only_integer => true }
validates :changeset_id, :presence => true,
:numericality => { :only_integer => true }
validates :latitude, :presence => true,
:numericality => { :only_integer => true }
validates :longitude, :presence => true,
@ -206,7 +204,7 @@ class Node < ApplicationRecord
end
def tags
@tags ||= node_tags.collect { |t| [t.k, t.v] }.to_h
@tags ||= node_tags.to_h { |t| [t.k, t.v] }
end
attr_writer :tags
@ -238,7 +236,7 @@ class Node < ApplicationRecord
private
def save_with_history!
t = Time.now.getutc
t = Time.now.utc
self.version += 1
self.timestamp = t

Some files were not shown because too many files have changed in this diff Show more