Merge remote-tracking branch 'upstream/pull/2431'
authorTom Hughes <tom@compton.nu>
Wed, 15 Apr 2020 18:18:10 +0000 (19:18 +0100)
committerTom Hughes <tom@compton.nu>
Wed, 15 Apr 2020 18:18:10 +0000 (19:18 +0100)
530 files changed:
.erb-lint.yml
.rubocop.yml
.rubocop_todo.yml
.travis.yml
Gemfile
Gemfile.lock
INSTALL.md
Vendorfile
app/abilities/ability.rb
app/abilities/api_ability.rb
app/abilities/api_capability.rb
app/assets/images/banners/sotm_africa_2019.png [new file with mode: 0644]
app/assets/images/banners/sotm_asia_2019.png [deleted file]
app/assets/images/browse/alpinehut.p.16.png [deleted file]
app/assets/images/browse/brownfield.png
app/assets/images/browse/building.png
app/assets/images/browse/cemetery.png
app/assets/images/browse/commercial.png
app/assets/images/browse/farmland.png
app/assets/images/browse/farmyard.png
app/assets/images/browse/forest.png
app/assets/images/browse/golf.png
app/assets/images/browse/grass.png
app/assets/images/browse/grassland.png
app/assets/images/browse/heathland.png
app/assets/images/browse/industrial.png
app/assets/images/browse/lake.png
app/assets/images/browse/meadow.png
app/assets/images/browse/park.png
app/assets/images/browse/pitch.png
app/assets/images/browse/residential.png
app/assets/images/browse/retail.png
app/assets/images/browse/school.png [deleted file]
app/assets/images/browse/scrub.png [new file with mode: 0644]
app/assets/images/browse/tourism_alpine_hut.16.png [new file with mode: 0644]
app/assets/images/browse/tourism_wilderness_hut.16.png [new file with mode: 0644]
app/assets/images/browse/wood.png
app/assets/javascripts/application.js
app/assets/javascripts/index.js
app/assets/javascripts/index/directions.js
app/assets/javascripts/index/history.js
app/assets/javascripts/index/query.js
app/assets/javascripts/index/search.js
app/assets/javascripts/leaflet.layers.js
app/assets/javascripts/leaflet.map.js
app/assets/javascripts/leaflet.share.js
app/assets/javascripts/user.js
app/assets/stylesheets/bootstrap.css [deleted file]
app/assets/stylesheets/browse.scss
app/assets/stylesheets/common.scss
app/assets/stylesheets/parameters.scss
app/assets/stylesheets/screen-ltr.css
app/assets/stylesheets/screen-rtl.css
app/assets/stylesheets/small.scss
app/controllers/api/amf_controller.rb
app/controllers/api/changesets_controller.rb
app/controllers/api/map_controller.rb
app/controllers/api/nodes_controller.rb
app/controllers/api/notes_controller.rb
app/controllers/api/old_controller.rb
app/controllers/api/relations_controller.rb
app/controllers/api/user_preferences_controller.rb
app/controllers/api/ways_controller.rb
app/controllers/api_controller.rb
app/controllers/application_controller.rb
app/controllers/export_controller.rb
app/controllers/issues_controller.rb
app/controllers/site_controller.rb
app/controllers/traces_controller.rb
app/controllers/users_controller.rb
app/helpers/application_helper.rb
app/helpers/browse_tags_helper.rb
app/mailers/application_mailer.rb [new file with mode: 0644]
app/mailers/notifier.rb
app/models/acl.rb
app/models/application_record.rb [new file with mode: 0644]
app/models/changeset.rb
app/models/changeset_comment.rb
app/models/changeset_tag.rb
app/models/client_application.rb
app/models/concerns/geo_record.rb
app/models/diary_comment.rb
app/models/diary_entry.rb
app/models/diary_entry_subscription.rb
app/models/friendship.rb
app/models/issue.rb
app/models/issue_comment.rb
app/models/language.rb
app/models/message.rb
app/models/node.rb
app/models/node_tag.rb
app/models/note.rb
app/models/note_comment.rb
app/models/oauth_nonce.rb
app/models/oauth_token.rb
app/models/old_node.rb
app/models/old_node_tag.rb
app/models/old_relation.rb
app/models/old_relation_member.rb
app/models/old_relation_tag.rb
app/models/old_way.rb
app/models/old_way_node.rb
app/models/old_way_tag.rb
app/models/redaction.rb
app/models/relation.rb
app/models/relation_member.rb
app/models/relation_tag.rb
app/models/report.rb
app/models/trace.rb
app/models/tracepoint.rb
app/models/tracetag.rb
app/models/user.rb
app/models/user_block.rb
app/models/user_preference.rb
app/models/user_role.rb
app/models/user_token.rb
app/models/way.rb
app/models/way_node.rb
app/models/way_tag.rb
app/views/api/_root_attributes.json.jbuilder [new file with mode: 0644]
app/views/api/map/_bounds.json.jbuilder [new file with mode: 0644]
app/views/api/map/index.json.jbuilder [new file with mode: 0644]
app/views/api/nodes/_node.json.jbuilder [new file with mode: 0644]
app/views/api/nodes/index.json.jbuilder [new file with mode: 0644]
app/views/api/nodes/show.json.jbuilder [new file with mode: 0644]
app/views/api/notes/_note.json.jbuilder [moved from app/views/api/notes/_note.json.jsonify with 81% similarity]
app/views/api/notes/index.json.jbuilder [moved from app/views/api/notes/index.json.jsonify with 70% similarity]
app/views/api/notes/show.json.jbuilder [new file with mode: 0644]
app/views/api/notes/show.json.jsonify [deleted file]
app/views/api/old_nodes/_old_node.json.jbuilder [new file with mode: 0644]
app/views/api/old_nodes/history.json.jbuilder [new file with mode: 0644]
app/views/api/old_nodes/version.json.jbuilder [new file with mode: 0644]
app/views/api/old_relations/_old_relation.json.jbuilder [new file with mode: 0644]
app/views/api/old_relations/history.json.jbuilder [new file with mode: 0644]
app/views/api/old_relations/version.json.jbuilder [new file with mode: 0644]
app/views/api/old_ways/_old_way.json.jbuilder [new file with mode: 0644]
app/views/api/old_ways/history.json.jbuilder [new file with mode: 0644]
app/views/api/old_ways/version.json.jbuilder [new file with mode: 0644]
app/views/api/relations/_relation.json.jbuilder [new file with mode: 0644]
app/views/api/relations/full.json.jbuilder [new file with mode: 0644]
app/views/api/relations/index.json.jbuilder [new file with mode: 0644]
app/views/api/relations/relations_for_node.json.jbuilder [new file with mode: 0644]
app/views/api/relations/relations_for_relation.json.jbuilder [new file with mode: 0644]
app/views/api/relations/relations_for_way.json.jbuilder [new file with mode: 0644]
app/views/api/relations/show.json.jbuilder [new file with mode: 0644]
app/views/api/user_preferences/_user_preference.xml.builder [new file with mode: 0644]
app/views/api/user_preferences/index.xml.builder [new file with mode: 0644]
app/views/api/ways/_way.json.jbuilder [new file with mode: 0644]
app/views/api/ways/full.json.jbuilder [new file with mode: 0644]
app/views/api/ways/index.json.jbuilder [new file with mode: 0644]
app/views/api/ways/show.json.jbuilder [new file with mode: 0644]
app/views/api/ways/ways_for_node.json.jbuilder [new file with mode: 0644]
app/views/browse/_common_details.html.erb
app/views/browse/_containing_relation.html.erb
app/views/browse/_node.html.erb
app/views/browse/_relation.html.erb
app/views/browse/_relation_member.html.erb
app/views/browse/_way.html.erb
app/views/browse/changeset.html.erb
app/views/browse/feature.html.erb
app/views/browse/history.html.erb
app/views/browse/note.html.erb
app/views/browse/query.html.erb
app/views/changesets/_changeset.html.erb
app/views/changesets/index.atom.builder
app/views/changesets/index.html.erb
app/views/diary_entries/_diary_comment.html.erb
app/views/diary_entries/_diary_entry.html.erb
app/views/diary_entries/_form.html.erb
app/views/diary_entries/comments.html.erb
app/views/diary_entries/index.html.erb
app/views/diary_entries/show.html.erb
app/views/geocoder/results.html.erb
app/views/geocoder/search.html.erb
app/views/issues/_comments.html.erb
app/views/issues/_reports.html.erb
app/views/issues/index.html.erb
app/views/issues/show.html.erb
app/views/layouts/_flash.html.erb
app/views/layouts/_header.html.erb
app/views/layouts/error.html.erb
app/views/layouts/map.html.erb
app/views/messages/_message_summary.html.erb
app/views/messages/_sent_message_summary.html.erb
app/views/messages/destroy.json.jbuilder [moved from app/views/messages/destroy.json.jsonify with 100% similarity]
app/views/messages/inbox.html.erb
app/views/messages/mark.json.jbuilder [moved from app/views/messages/mark.json.jsonify with 100% similarity]
app/views/messages/new.html.erb
app/views/messages/outbox.html.erb
app/views/messages/show.html.erb
app/views/notes/mine.html.erb
app/views/oauth/authorize.html.erb
app/views/oauth/authorize_success.html.erb
app/views/oauth_clients/_form.html.erb
app/views/oauth_clients/index.html.erb
app/views/reports/new.html.erb
app/views/site/_potlatch.html.erb
app/views/site/_potlatch2.html.erb
app/views/site/about.html.erb
app/views/site/copyright.html.erb
app/views/site/edit.html.erb
app/views/site/export.html.erb
app/views/site/fixthemap.html.erb
app/views/site/help.html.erb
app/views/site/key.html.erb
app/views/site/welcome.html.erb
app/views/traces/_trace.html.erb
app/views/traces/edit.html.erb
app/views/traces/index.html.erb
app/views/traces/new.html.erb
app/views/traces/show.html.erb
app/views/user_blocks/_block.html.erb
app/views/user_blocks/_blocks.html.erb
app/views/user_blocks/blocks_by.html.erb
app/views/user_blocks/blocks_on.html.erb
app/views/user_blocks/edit.html.erb
app/views/user_blocks/new.html.erb
app/views/user_blocks/revoke.html.erb
app/views/user_blocks/show.html.erb
app/views/users/_contact.html.erb
app/views/users/_user.html.erb
app/views/users/account.html.erb
app/views/users/blocked.html.erb
app/views/users/index.html.erb
app/views/users/login.html.erb
app/views/users/logout.html.erb
app/views/users/new.html.erb
app/views/users/no_such_user.html.erb
app/views/users/show.html.erb
app/views/users/suspended.html.erb
app/views/users/terms.html.erb
bin/setup
config/banners.yml
config/environments/development.rb
config/environments/production.rb
config/environments/test.rb
config/eslint.json
config/initializers/active_storage.rb
config/initializers/assets.rb
config/initializers/banners.rb
config/initializers/config.rb
config/initializers/content_security_policy.rb
config/initializers/cors.rb
config/initializers/field_error.rb
config/initializers/i18n.rb
config/initializers/new_framework_defaults_5_2.rb [new file with mode: 0644]
config/initializers/new_framework_defaults_6_0.rb [new file with mode: 0644]
config/initializers/omniauth.rb
config/initializers/sanitize.rb
config/initializers/secure_headers.rb
config/initializers/wiki_pages.rb
config/initializers/wrap_parameters.rb
config/locales/af.yml
config/locales/aln.yml
config/locales/ar.yml
config/locales/arz.yml
config/locales/ast.yml
config/locales/az.yml
config/locales/be-Tarask.yml
config/locales/be.yml
config/locales/bg.yml
config/locales/bn.yml
config/locales/br.yml
config/locales/bs.yml
config/locales/ca.yml
config/locales/ce.yml
config/locales/cs.yml
config/locales/cy.yml
config/locales/da.yml
config/locales/de.yml
config/locales/diq.yml
config/locales/dsb.yml
config/locales/el.yml
config/locales/en-GB.yml
config/locales/en.yml
config/locales/eo.yml
config/locales/es.yml
config/locales/et.yml
config/locales/eu.yml
config/locales/fa.yml
config/locales/fi.yml
config/locales/fit.yml
config/locales/fr.yml
config/locales/fur.yml
config/locales/ga.yml
config/locales/gd.yml
config/locales/gl.yml
config/locales/he.yml
config/locales/hi.yml
config/locales/hr.yml
config/locales/hsb.yml
config/locales/hu.yml
config/locales/ia.yml
config/locales/id.yml
config/locales/is.yml
config/locales/it.yml
config/locales/ja.yml
config/locales/ka.yml
config/locales/kab.yml
config/locales/km.yml
config/locales/ko.yml
config/locales/ku-Latn.yml
config/locales/lb.yml
config/locales/lt.yml
config/locales/lv.yml
config/locales/mk.yml
config/locales/mo.yml
config/locales/mr.yml
config/locales/ms.yml
config/locales/my.yml
config/locales/nb.yml
config/locales/ne.yml
config/locales/nl.yml
config/locales/nn.yml
config/locales/oc.yml
config/locales/pa.yml
config/locales/pl.yml
config/locales/pt-BR.yml
config/locales/pt-PT.yml
config/locales/ro.yml
config/locales/ru.yml
config/locales/scn.yml
config/locales/sco.yml
config/locales/sk.yml
config/locales/sl.yml
config/locales/sq.yml
config/locales/sr-Latn.yml
config/locales/sr.yml
config/locales/sv.yml
config/locales/ta.yml
config/locales/te.yml
config/locales/th.yml
config/locales/tl.yml
config/locales/tr.yml
config/locales/tt.yml
config/locales/uk.yml
config/locales/vi.yml
config/locales/zh-CN.yml
config/locales/zh-TW.yml
config/puma.rb
config/routes.rb
config/settings.yml
db/functions/Makefile
db/migrate/005_tile_tracepoints.rb
db/migrate/006_tile_nodes.rb
db/migrate/008_remove_segments.rb
db/migrate/013_add_email_valid.rb
db/migrate/020_populate_node_tags_and_remove.rb
db/migrate/034_create_languages.rb
db/migrate/039_add_more_controls_to_gpx_files.rb
db/migrate/044_create_user_roles.rb
db/migrate/046_alter_user_roles_and_blocks.rb
db/migrate/051_add_status_to_user.rb
db/migrate/20110322001319_add_terms_seen_to_user.rb
db/migrate/20120208122334_merge_acl_address_and_mask.rb
db/migrate/20120808231205_add_counter_caches.rb
db/migrate/20121005195010_add_diary_entry_counter_caches.rb
db/migrate/20121012044047_add_image_use_gravatar_to_users.rb
db/migrate/20150222101847_rename_openid_url.rb
db/migrate/20180204153242_tile_users.rb
db/migrate/20191120140058_remove_nearby_from_users.rb [new file with mode: 0644]
db/structure.sql
lib/auth.rb
lib/country.rb
lib/id.rb
lib/osm.rb
lib/potlatch.rb
lib/redactable.rb
lib/rich_text.rb
lib/tasks/add_version_to_nodes.rake
lib/tasks/auto_annotate_models.rake
lib/tasks/eslint.rake
lib/tasks/testing.rake
package.json
test/abilities/api_capability_test.rb
test/application_system_test_case.rb
test/controllers/api/amf_controller_test.rb
test/controllers/api/changeset_comments_controller_test.rb
test/controllers/api/changesets_controller_test.rb
test/controllers/api/map_controller_test.rb
test/controllers/api/nodes_controller_test.rb
test/controllers/api/notes_controller_test.rb
test/controllers/api/old_nodes_controller_test.rb
test/controllers/api/old_relations_controller_test.rb
test/controllers/api/old_ways_controller_test.rb
test/controllers/api/relations_controller_test.rb
test/controllers/api/traces_controller_test.rb
test/controllers/api/user_preferences_controller_test.rb
test/controllers/api/users_controller_test.rb
test/controllers/api/ways_controller_test.rb
test/controllers/changeset_comments_controller_test.rb
test/controllers/changesets_controller_test.rb
test/controllers/diary_entries_controller_test.rb
test/controllers/geocoder_controller_test.rb
test/controllers/issues_controller_test.rb
test/controllers/messages_controller_test.rb
test/controllers/oauth_clients_controller_test.rb
test/controllers/site_controller_test.rb
test/controllers/traces_controller_test.rb
test/controllers/user_blocks_controller_test.rb
test/controllers/users_controller_test.rb
test/factories/traces.rb
test/gpx/images/.gitkeep [deleted file]
test/gpx/traces/.gitkeep [deleted file]
test/helpers/application_helper_test.rb
test/helpers/browse_helper_test.rb
test/helpers/browse_tags_helper_test.rb
test/helpers/note_helper_test.rb
test/integration/compressed_requests_test.rb
test/integration/cors_test.rb
test/integration/oauth_test.rb
test/integration/page_locale_test.rb
test/integration/user_creation_test.rb
test/integration/user_terms_seen_test.rb
test/lib/i18n_test.rb
test/lib/password_hash_test.rb
test/lib/rich_text_test.rb
test/lib/utf8_test.rb
test/models/client_application_test.rb
test/models/node_test.rb
test/models/note_test.rb
test/models/oauth_nonce_test.rb
test/models/oauth_token_test.rb
test/models/old_node_test.rb
test/models/redaction_test.rb
test/models/request_token_test.rb
test/models/trace_test.rb
test/models/user_preference_test.rb
test/models/user_test.rb
test/system/issues_test.rb
test/system/user_logout_test.rb [new file with mode: 0644]
test/test_helper.rb
vendor/assets/bootstrap/bootstrap.dropdown.js [deleted file]
vendor/assets/bootstrap/bootstrap.tooltip.js [deleted file]
vendor/assets/iD/iD.css.erb
vendor/assets/iD/iD.js
vendor/assets/iD/iD/img/community-sprite.svg
vendor/assets/iD/iD/img/fa-sprite.svg
vendor/assets/iD/iD/img/iD-sprite.svg
vendor/assets/iD/iD/img/maki-sprite.svg
vendor/assets/iD/iD/img/mapillary-object-sprite.svg
vendor/assets/iD/iD/img/mapillary-sprite.svg
vendor/assets/iD/iD/img/temaki-sprite.svg
vendor/assets/iD/iD/img/tnp-sprite.svg
vendor/assets/iD/iD/locales/af.json
vendor/assets/iD/iD/locales/ar-AA.json
vendor/assets/iD/iD/locales/ar.json
vendor/assets/iD/iD/locales/ast.json
vendor/assets/iD/iD/locales/be.json
vendor/assets/iD/iD/locales/bg.json
vendor/assets/iD/iD/locales/bn.json
vendor/assets/iD/iD/locales/bs.json
vendor/assets/iD/iD/locales/ca.json
vendor/assets/iD/iD/locales/ckb.json
vendor/assets/iD/iD/locales/cs.json
vendor/assets/iD/iD/locales/cy.json
vendor/assets/iD/iD/locales/da.json
vendor/assets/iD/iD/locales/de.json
vendor/assets/iD/iD/locales/dv.json
vendor/assets/iD/iD/locales/el.json
vendor/assets/iD/iD/locales/en-AU.json
vendor/assets/iD/iD/locales/en-GB.json
vendor/assets/iD/iD/locales/en.json
vendor/assets/iD/iD/locales/eo.json
vendor/assets/iD/iD/locales/es.json
vendor/assets/iD/iD/locales/et.json
vendor/assets/iD/iD/locales/eu.json
vendor/assets/iD/iD/locales/fa.json
vendor/assets/iD/iD/locales/fi.json
vendor/assets/iD/iD/locales/fr.json
vendor/assets/iD/iD/locales/gl.json
vendor/assets/iD/iD/locales/gu.json
vendor/assets/iD/iD/locales/he.json
vendor/assets/iD/iD/locales/hi.json
vendor/assets/iD/iD/locales/hr.json
vendor/assets/iD/iD/locales/hu.json
vendor/assets/iD/iD/locales/hy.json
vendor/assets/iD/iD/locales/id.json
vendor/assets/iD/iD/locales/is.json
vendor/assets/iD/iD/locales/it.json
vendor/assets/iD/iD/locales/ja.json
vendor/assets/iD/iD/locales/kn.json
vendor/assets/iD/iD/locales/ko.json
vendor/assets/iD/iD/locales/lt.json
vendor/assets/iD/iD/locales/lv.json
vendor/assets/iD/iD/locales/mg.json
vendor/assets/iD/iD/locales/mk.json
vendor/assets/iD/iD/locales/ml.json
vendor/assets/iD/iD/locales/ms.json
vendor/assets/iD/iD/locales/my.json [new file with mode: 0644]
vendor/assets/iD/iD/locales/ne.json
vendor/assets/iD/iD/locales/nl.json
vendor/assets/iD/iD/locales/nn.json
vendor/assets/iD/iD/locales/no.json
vendor/assets/iD/iD/locales/pap.json
vendor/assets/iD/iD/locales/pl.json
vendor/assets/iD/iD/locales/pt-BR.json
vendor/assets/iD/iD/locales/pt.json
vendor/assets/iD/iD/locales/ro.json
vendor/assets/iD/iD/locales/ru.json
vendor/assets/iD/iD/locales/si.json
vendor/assets/iD/iD/locales/sk.json
vendor/assets/iD/iD/locales/sl.json
vendor/assets/iD/iD/locales/so.json
vendor/assets/iD/iD/locales/sq.json
vendor/assets/iD/iD/locales/sr.json
vendor/assets/iD/iD/locales/sv.json
vendor/assets/iD/iD/locales/ta.json
vendor/assets/iD/iD/locales/te.json
vendor/assets/iD/iD/locales/th.json
vendor/assets/iD/iD/locales/tl.json
vendor/assets/iD/iD/locales/tr.json
vendor/assets/iD/iD/locales/uk.json
vendor/assets/iD/iD/locales/vi.json
vendor/assets/iD/iD/locales/yue.json
vendor/assets/iD/iD/locales/zh-CN.json
vendor/assets/iD/iD/locales/zh-HK.json
vendor/assets/iD/iD/locales/zh-TW.json
vendor/assets/iD/iD/locales/zh.json
vendor/assets/iD/iD/mapillary-js/mapillary.js
vendor/assets/iD/iD/mapillary-js/mapillary.js.map
vendor/assets/iD/iD/mapillary-js/mapillary.min.css
vendor/assets/iD/iD/mapillary-js/mapillary.min.js
vendor/assets/javascripts/bowser.js [deleted file]
vendor/assets/leaflet/leaflet.contextmenu.js
vendor/assets/leaflet/leaflet.js
vendor/assets/leaflet/leaflet.locate.js
vendor/assets/leaflet/leaflet.osm.js
vendor/assets/leaflet/leaflet.polyline.js
yarn.lock

index 812a6f5382dc71b6a949aa1e7e45d87851674544..0c729d38f8426e5f564d2bb311ffb2f230b42b8f 100644 (file)
@@ -10,20 +10,20 @@ linters:
         - .rubocop.yml
       Layout/InitialIndentation:
         Enabled: false
-      Layout/TrailingBlankLines:
+      Layout/LineLength:
         Enabled: false
-      Layout/TrailingWhitespace:
-        Enabled: false
-      Naming/FileName:
-        Enabled: false
-      Style/FrozenStringLiteralComment:
+      Layout/TrailingEmptyLines:
         Enabled: false
-      Metrics/LineLength:
+      Layout/TrailingWhitespace:
         Enabled: false
       Lint/UselessAssignment:
         Enabled: false
+      Naming/FileName:
+        Enabled: false
       Rails/OutputSafety:
         Enabled: false
+      Style/FrozenStringLiteralComment:
+        Enabled: false
   SelfClosingTag:
     enabled: false
   SpaceInHtmlTag:
index cbd62a052512bc82c9b6858a965391f206fb5327..34605df1d8b821fe083d88b8caa07a3ea222562b 100644 (file)
@@ -1,6 +1,7 @@
 inherit_from: .rubocop_todo.yml
 
 require:
+  - rubocop-minitest
   - rubocop-performance
   - rubocop-rails
 
@@ -29,16 +30,16 @@ Metrics/ClassLength:
   Exclude:
     - 'test/**/*'
 
+Metrics/ModuleLength:
+  Max: 150
+
 Naming/FileName:
   Exclude:
     - 'script/deliver-message'
     - 'script/locale/reload-languages'
     - 'script/update-spam-blocks'
 
-Naming/UncommunicativeMethodParamName:
-  Enabled: false
-
-Rails/ApplicationRecord:
+Naming/MethodParameterName:
   Enabled: false
 
 Rails/CreateTableWithTimestamps:
@@ -61,8 +62,8 @@ Rails/SkipsModelValidations:
     - 'db/migrate/*.rb'
     - 'app/controllers/users_controller.rb'
 
-Style/BracesAroundHashParameters:
-  EnforcedStyle: context_dependent
+Style/Documentation:
+  Enabled: false
 
 Style/FormatStringToken:
   EnforcedStyle: template
@@ -73,12 +74,21 @@ Style/IfInsideElse:
 Style/GuardClause:
   Enabled: false
 
+Style/HashEachMethods:
+  Enabled: true
+
 Style/HashSyntax:
   EnforcedStyle: hash_rockets
   Exclude:
     - 'lib/tasks/testing.rake'
     - 'config/initializers/wrap_parameters.rb'
 
+Style/HashTransformKeys:
+  Enabled: true
+
+Style/HashTransformValues:
+  Enabled: true
+
 Style/MixinUsage:
   Exclude:
     - 'bin/setup'
index 518e1eda47fde8461c99941b0e88ab12c2aace70..307a23f4b035c56539cc9d7dd2d7c841252758f0 100644 (file)
@@ -6,6 +6,20 @@
 # Note that changes in the inspected code, or installation of new
 # versions of RuboCop, may require this file to be generated again.
 
+# Work around erblint issues.
+# https://github.com/openstreetmap/openstreetmap-website/issues/2472
+require:
+  - rubocop-minitest
+  - rubocop-performance
+  - rubocop-rails
+
+# Offense count: 3338
+# Cop supports --auto-correct.
+# Configuration parameters: AutoCorrect, AllowHeredoc, AllowURI, URISchemes, IgnoreCopDirectives, IgnoredPatterns.
+# URISchemes: http, https
+Layout/LineLength:
+  Max: 260
+
 # Offense count: 35
 # Configuration parameters: AllowSafeAssignment.
 Lint/AssignmentInCondition:
@@ -27,10 +41,11 @@ Lint/AssignmentInCondition:
 
 # Offense count: 4
 # Configuration parameters: AllowComments.
-Lint/HandleExceptions:
+Lint/SuppressedException:
   Exclude:
     - 'app/controllers/api/amf_controller.rb'
     - 'app/controllers/users_controller.rb'
+    - 'app/controllers/site_controller.rb'
 
 # Offense count: 701
 Metrics/AbcSize:
@@ -173,10 +188,6 @@ Style/AsciiComments:
   Exclude:
     - 'test/models/message_test.rb'
 
-# Offense count: 263
-Style/Documentation:
-  Enabled: false
-
 # Offense count: 27
 # Configuration parameters: EnforcedStyle.
 # SupportedStyles: annotated, template, unannotated
@@ -208,10 +219,3 @@ Style/IfUnlessModifier:
 # Configuration parameters: Strict.
 Style/NumericLiterals:
   MinDigits: 11
-
-# Offense count: 3338
-# Cop supports --auto-correct.
-# Configuration parameters: AutoCorrect, AllowHeredoc, AllowURI, URISchemes, IgnoreCopDirectives, IgnoredPatterns.
-# URISchemes: http, https
-Metrics/LineLength:
-  Max: 307
index 996495c0dde16fe946a40b20f0d041a40aa8584e..31f4aec537856ac9ed3e2a8bc0de3903f792a79b 100644 (file)
@@ -1,4 +1,4 @@
-dist: xenial
+dist: bionic
 language: ruby
 rvm:
   - 2.5.3
@@ -9,11 +9,11 @@ addons:
   apt:
     packages:
       - libarchive-dev
+      - libgd-dev
+      - libffi-dev
+      - libbz2-dev
 services:
   - memcached
-env:
-  global:
-    - OSM_MEMCACHE_SERVERS="127.0.0.1"
 before_script:
   - sed -e 's/ IMMUTABLE / /' -e "/^--/d" db/structure.sql > db/structure.expected
   - psql -U postgres -c "CREATE DATABASE openstreetmap"
@@ -22,6 +22,7 @@ before_script:
   - cp config/travis.database.yml config/database.yml
   - cp config/example.storage.yml config/storage.yml
   - touch config/settings.local.yml
+  - echo -e "---\nmemcache_servers:\n  - 127.0.0.1" > config/settings/test.local.yml
   - bundle exec rake db:migrate
   - bundle exec rake i18n:js:export
   - bundle exec rake yarn:install
diff --git a/Gemfile b/Gemfile
index c2bb066b7c9c291bd8854f27dc73844a5cd9cf4a..d04996b933c6a8a843bd6f7bbed7c1e1723ac267 100644 (file)
--- a/Gemfile
+++ b/Gemfile
@@ -1,7 +1,7 @@
 source "https://rubygems.org"
 
 # Require rails
-gem "rails", "5.2.3"
+gem "rails", "6.0.2.2"
 
 # Require things which have moved to gems in ruby 1.9
 gem "bigdecimal", "~> 1.1.0", :platforms => :ruby_19
@@ -21,34 +21,31 @@ gem "sassc-rails"
 # Use Uglifier as compressor for JavaScript assets
 gem "uglifier", ">= 1.3.0"
 
-# Use CoffeeScript for .js.coffee assets and views
-gem "coffee-rails", "~> 4.2"
-
 # Use jquery as the JavaScript library
 gem "jquery-rails"
 
 # Build JSON APIs with ease. Read more: https://github.com/rails/jbuilder
-# gem 'jbuilder', '~> 2.0'
-gem "jsonify-rails"
+gem "jbuilder", "~> 2.7"
 
 # Reduces boot times through caching; required in config/boot.rb
-gem "bootsnap", ">= 1.1.0", :require => false
+gem "bootsnap", ">= 1.4.2", :require => false
 
 # Use R2 for RTL conversion
 gem "r2", "~> 0.2.7"
 
 # Use autoprefixer to generate CSS prefixes
-gem "autoprefixer-rails", "~> 8.6.3"
+gem "autoprefixer-rails"
 
 # Use image_optim to optimise images
 gem "image_optim_rails"
 
 # Load rails plugins
-gem "actionpack-page_caching"
+gem "actionpack-page_caching", ">= 1.2.0"
 gem "active_record_union"
 gem "activerecord-import"
+gem "bootstrap", "~> 4.3.1"
 gem "cancancan"
-gem "composite_primary_keys", "~> 11.1.0"
+gem "composite_primary_keys", "~> 12.0.0"
 gem "config"
 gem "delayed_job_active_record"
 gem "dynamic_form"
@@ -58,7 +55,6 @@ gem "oauth-plugin", ">= 0.5.1"
 gem "openstreetmap-deadlock_retry", ">= 1.3.0", :require => "deadlock_retry"
 gem "rack-cors"
 gem "rails-i18n", "~> 4.0.0"
-gem "record_tag_helper"
 gem "rinku", ">= 2.0.6", :require => "rails_rinku"
 gem "strong_migrations"
 gem "validates_email_format_of", ">= 1.5.1"
@@ -97,8 +93,8 @@ gem "SystemTimer", ">= 1.1.3", :require => "system_timer", :platforms => :ruby_1
 # Load faraday for mockable HTTP client
 gem "faraday"
 
-# Load geoip for querying Maxmind GeoIP database
-gem "geoip"
+# Load maxminddb for querying Maxmind GeoIP database
+gem "maxminddb"
 
 # Load rotp to generate TOTP tokens
 gem "rotp"
@@ -142,10 +138,10 @@ end
 
 # Gems needed for running tests
 group :test do
-  gem "fakefs", :require => "fakefs/safe"
   gem "minitest", "~> 5.1", :platforms => [:ruby_19, :ruby_20]
   gem "rails-controller-testing"
   gem "rubocop"
+  gem "rubocop-minitest"
   gem "rubocop-performance"
   gem "rubocop-rails"
   gem "webmock"
@@ -153,10 +149,11 @@ end
 
 # Needed in development as well so rake can see konacha tasks
 group :development, :test do
-  gem "capybara", "~> 2.13"
+  gem "capybara", ">= 2.15"
   gem "coveralls", :require => false
   gem "erb_lint", :require => false
   gem "factory_bot_rails"
   gem "poltergeist"
-  gem "puma", "~> 3.7"
+  gem "puma", "~> 3.11"
+  gem "selenium-webdriver"
 end
index 7fe5ac89fff8c18cd19bac62e87969917eb19dc8..49a843a035a10024125e9777514baedf142f17ae 100644 (file)
@@ -2,81 +2,94 @@ GEM
   remote: https://rubygems.org/
   specs:
     SystemTimer (1.2.3)
-    aasm (5.0.6)
+    aasm (5.0.8)
       concurrent-ruby (~> 1.0)
-    actioncable (5.2.3)
-      actionpack (= 5.2.3)
+    actioncable (6.0.2.2)
+      actionpack (= 6.0.2.2)
       nio4r (~> 2.0)
       websocket-driver (>= 0.6.1)
-    actionmailer (5.2.3)
-      actionpack (= 5.2.3)
-      actionview (= 5.2.3)
-      activejob (= 5.2.3)
+    actionmailbox (6.0.2.2)
+      actionpack (= 6.0.2.2)
+      activejob (= 6.0.2.2)
+      activerecord (= 6.0.2.2)
+      activestorage (= 6.0.2.2)
+      activesupport (= 6.0.2.2)
+      mail (>= 2.7.1)
+    actionmailer (6.0.2.2)
+      actionpack (= 6.0.2.2)
+      actionview (= 6.0.2.2)
+      activejob (= 6.0.2.2)
       mail (~> 2.5, >= 2.5.4)
       rails-dom-testing (~> 2.0)
-    actionpack (5.2.3)
-      actionview (= 5.2.3)
-      activesupport (= 5.2.3)
-      rack (~> 2.0)
+    actionpack (6.0.2.2)
+      actionview (= 6.0.2.2)
+      activesupport (= 6.0.2.2)
+      rack (~> 2.0, >= 2.0.8)
       rack-test (>= 0.6.3)
       rails-dom-testing (~> 2.0)
-      rails-html-sanitizer (~> 1.0, >= 1.0.2)
-    actionpack-page_caching (1.1.1)
-      actionpack (>= 4.0.0, < 6)
-    actionview (5.2.3)
-      activesupport (= 5.2.3)
+      rails-html-sanitizer (~> 1.0, >= 1.2.0)
+    actionpack-page_caching (1.2.0)
+      actionpack (>= 5.0.0)
+    actiontext (6.0.2.2)
+      actionpack (= 6.0.2.2)
+      activerecord (= 6.0.2.2)
+      activestorage (= 6.0.2.2)
+      activesupport (= 6.0.2.2)
+      nokogiri (>= 1.8.5)
+    actionview (6.0.2.2)
+      activesupport (= 6.0.2.2)
       builder (~> 3.1)
       erubi (~> 1.4)
       rails-dom-testing (~> 2.0)
-      rails-html-sanitizer (~> 1.0, >= 1.0.3)
+      rails-html-sanitizer (~> 1.1, >= 1.2.0)
     active_record_union (1.3.0)
       activerecord (>= 4.0)
-    activejob (5.2.3)
-      activesupport (= 5.2.3)
+    activejob (6.0.2.2)
+      activesupport (= 6.0.2.2)
       globalid (>= 0.3.6)
-    activemodel (5.2.3)
-      activesupport (= 5.2.3)
-    activerecord (5.2.3)
-      activemodel (= 5.2.3)
-      activesupport (= 5.2.3)
-      arel (>= 9.0)
-    activerecord-import (1.0.3)
+    activemodel (6.0.2.2)
+      activesupport (= 6.0.2.2)
+    activerecord (6.0.2.2)
+      activemodel (= 6.0.2.2)
+      activesupport (= 6.0.2.2)
+    activerecord-import (1.0.4)
       activerecord (>= 3.2)
-    activestorage (5.2.3)
-      actionpack (= 5.2.3)
-      activerecord (= 5.2.3)
+    activestorage (6.0.2.2)
+      actionpack (= 6.0.2.2)
+      activejob (= 6.0.2.2)
+      activerecord (= 6.0.2.2)
       marcel (~> 0.3.1)
-    activesupport (5.2.3)
+    activesupport (6.0.2.2)
       concurrent-ruby (~> 1.0, >= 1.0.2)
       i18n (>= 0.7, < 2)
       minitest (~> 5.1)
       tzinfo (~> 1.1)
+      zeitwerk (~> 2.2)
     addressable (2.7.0)
       public_suffix (>= 2.0.2, < 5.0)
-    annotate (3.0.2)
+    annotate (3.1.1)
       activerecord (>= 3.2, < 7.0)
-      rake (>= 10.4, < 13.0)
-    arel (9.0.0)
+      rake (>= 10.4, < 14.0)
     ast (2.4.0)
-    autoprefixer-rails (8.6.5)
+    autoprefixer-rails (9.7.6)
       execjs
     aws-eventstream (1.0.3)
-    aws-partitions (1.232.0)
-    aws-sdk-core (3.73.0)
+    aws-partitions (1.295.0)
+    aws-sdk-core (3.93.0)
       aws-eventstream (~> 1.0, >= 1.0.2)
-      aws-partitions (~> 1, >= 1.228.0)
+      aws-partitions (~> 1, >= 1.239.0)
       aws-sigv4 (~> 1.1)
       jmespath (~> 1.0)
-    aws-sdk-kms (1.25.0)
+    aws-sdk-kms (1.30.0)
       aws-sdk-core (~> 3, >= 3.71.0)
       aws-sigv4 (~> 1.1)
-    aws-sdk-s3 (1.53.0)
-      aws-sdk-core (~> 3, >= 3.71.0)
+    aws-sdk-s3 (1.61.2)
+      aws-sdk-core (~> 3, >= 3.83.0)
       aws-sdk-kms (~> 1)
       aws-sigv4 (~> 1.1)
-    aws-sigv4 (1.1.0)
+    aws-sigv4 (1.1.1)
       aws-eventstream (~> 1.0, >= 1.0.2)
-    better_errors (2.5.1)
+    better_errors (2.6.0)
       coderay (>= 1.0.0)
       erubi (>= 1.0.0)
       rack (>= 0.9.0)
@@ -91,38 +104,36 @@ GEM
     bigdecimal (1.1.0)
     binding_of_caller (0.8.0)
       debug_inspector (>= 0.0.1)
-    bootsnap (1.4.5)
+    bootsnap (1.4.6)
       msgpack (~> 1.0)
-    browser (2.6.1)
-    builder (3.2.3)
+    bootstrap (4.3.1)
+      autoprefixer-rails (>= 9.1.0)
+      popper_js (>= 1.14.3, < 2)
+      sassc-rails (>= 2.0.0)
+    browser (4.0.0)
+    builder (3.2.4)
     bzip2-ffi (1.0.0)
       ffi (~> 1.0)
-    cancancan (3.0.1)
-    canonical-rails (0.2.6)
+    cancancan (3.1.0)
+    canonical-rails (0.2.8)
       rails (>= 4.1, < 6.1)
-    capybara (2.18.0)
+    capybara (3.32.1)
       addressable
       mini_mime (>= 0.1.3)
-      nokogiri (>= 1.3.3)
-      rack (>= 1.0.0)
-      rack-test (>= 0.5.4)
-      xpath (>= 2.0, < 4.0)
+      nokogiri (~> 1.8)
+      rack (>= 1.6.0)
+      rack-test (>= 0.6.3)
+      regexp_parser (~> 1.5)
+      xpath (~> 3.2)
+    childprocess (3.0.0)
     cliver (0.3.2)
     coderay (1.1.2)
-    coffee-rails (4.2.2)
-      coffee-script (>= 2.2.0)
-      railties (>= 4.0.0)
-    coffee-script (2.4.1)
-      coffee-script-source
-      execjs
-    coffee-script-source (1.12.2)
-    composite_primary_keys (11.1.0)
-      activerecord (~> 5.2.1)
-    concurrent-ruby (1.1.5)
-    config (2.0.0)
-      activesupport (>= 4.2)
+    composite_primary_keys (12.0.1)
+      activerecord (~> 6.0.0)
+    concurrent-ruby (1.1.6)
+    config (2.2.1)
       deep_merge (~> 1.2, >= 1.2.1)
-      dry-schema (~> 1.0)
+      dry-validation (~> 1.0, >= 1.0.0)
     coveralls (0.8.23)
       json (>= 1.8, < 3)
       simplecov (~> 0.16.1)
@@ -131,7 +142,7 @@ GEM
       tins (~> 1.6)
     crack (0.4.3)
       safe_yaml (~> 1.0.0)
-    crass (1.0.5)
+    crass (1.0.6)
     dalli (2.7.10)
     debug_inspector (0.0.3)
     deep_merge (1.2.1)
@@ -141,72 +152,78 @@ GEM
       activerecord (>= 3.0, < 6.1)
       delayed_job (>= 3.0, < 5)
     docile (1.3.2)
-    dry-configurable (0.8.3)
+    dry-configurable (0.11.5)
       concurrent-ruby (~> 1.0)
       dry-core (~> 0.4, >= 0.4.7)
+      dry-equalizer (~> 0.2)
     dry-container (0.7.2)
       concurrent-ruby (~> 1.0)
       dry-configurable (~> 0.1, >= 0.1.3)
     dry-core (0.4.9)
       concurrent-ruby (~> 1.0)
-    dry-equalizer (0.2.2)
+    dry-equalizer (0.3.0)
     dry-inflector (0.2.0)
-    dry-initializer (3.0.1)
-    dry-logic (1.0.3)
+    dry-initializer (3.0.3)
+    dry-logic (1.0.6)
       concurrent-ruby (~> 1.0)
       dry-core (~> 0.2)
       dry-equalizer (~> 0.2)
-    dry-schema (1.4.1)
+    dry-schema (1.5.0)
       concurrent-ruby (~> 1.0)
       dry-configurable (~> 0.8, >= 0.8.3)
       dry-core (~> 0.4)
       dry-equalizer (~> 0.2)
       dry-initializer (~> 3.0)
       dry-logic (~> 1.0)
-      dry-types (~> 1.2)
-    dry-types (1.2.0)
+      dry-types (~> 1.4)
+    dry-types (1.4.0)
       concurrent-ruby (~> 1.0)
       dry-container (~> 0.3)
       dry-core (~> 0.4, >= 0.4.4)
-      dry-equalizer (~> 0.2, >= 0.2.2)
+      dry-equalizer (~> 0.3)
       dry-inflector (~> 0.1, >= 0.1.2)
       dry-logic (~> 1.0, >= 1.0.2)
+    dry-validation (1.5.0)
+      concurrent-ruby (~> 1.0)
+      dry-container (~> 0.7, >= 0.7.1)
+      dry-core (~> 0.4)
+      dry-equalizer (~> 0.2)
+      dry-initializer (~> 3.0)
+      dry-schema (~> 1.5)
     dynamic_form (1.1.4)
-    erb_lint (0.0.30)
+    erb_lint (0.0.32)
       activesupport
       better_html (~> 1.0.7)
       html_tokenizer
       rainbow
-      rubocop (~> 0.51)
+      rubocop (~> 0.79)
       smart_properties
     erubi (1.9.0)
     execjs (2.7.0)
     exifr (1.3.6)
-    factory_bot (5.1.1)
+    factory_bot (5.1.2)
       activesupport (>= 4.2.0)
     factory_bot_rails (5.1.1)
       factory_bot (~> 5.1.0)
       railties (>= 4.2.0)
-    fakefs (0.20.1)
-    faraday (0.17.0)
+    faraday (1.0.1)
       multipart-post (>= 1.2, < 3)
-    ffi (1.11.1)
-    ffi-libarchive (0.4.10)
+    ffi (1.12.2)
+    ffi-libarchive (1.0.0)
       ffi (~> 1.0)
     fspath (3.1.2)
     gd2-ffij (0.4.0)
       ffi (>= 1.0.0)
-    geoip (1.6.4)
     globalid (0.4.2)
       activesupport (>= 4.2.0)
-    hashdiff (1.0.0)
-    hashie (3.6.0)
+    hashdiff (1.0.1)
+    hashie (4.1.0)
     html_tokenizer (0.0.7)
     htmlentities (4.3.4)
     http_accept_language (2.0.5)
     i18n (0.9.5)
       concurrent-ruby (~> 1.0)
-    i18n-js (3.4.1)
+    i18n-js (3.6.0)
       i18n (>= 0.6.6)
     image_optim (0.26.5)
       exifr (~> 1.2, >= 1.2.2)
@@ -219,24 +236,21 @@ GEM
       rails
       sprockets
     image_size (2.0.2)
-    in_threads (1.5.3)
+    in_threads (1.5.4)
     jaro_winkler (1.5.4)
+    jbuilder (2.10.0)
+      activesupport (>= 5.0.0)
     jmespath (1.4.0)
     jquery-rails (4.3.5)
       rails-dom-testing (>= 1, < 3)
       railties (>= 4.2.0)
       thor (>= 0.14, < 2.0)
-    json (2.2.0)
-    jsonify (0.3.1)
-      multi_json (~> 1.0)
-    jsonify-rails (0.3.2)
-      actionpack
-      jsonify (< 0.4.0)
+    json (2.3.0)
     jwt (2.2.1)
-    kgio (2.11.2)
+    kgio (2.11.3)
     kramdown (2.1.0)
     libxml-ruby (3.1.0)
-    listen (3.2.0)
+    listen (3.2.1)
       rb-fsevent (~> 0.10, >= 0.10.3)
       rb-inotify (~> 0.9, >= 0.9.10)
     logstash-event (1.2.02)
@@ -244,27 +258,28 @@ GEM
       activesupport (>= 4.0)
       logstash-event (~> 1.2.0)
       request_store
-    loofah (2.3.1)
+    loofah (2.5.0)
       crass (~> 1.0.2)
       nokogiri (>= 1.5.9)
     mail (2.7.1)
       mini_mime (>= 0.1.1)
     marcel (0.3.3)
       mimemagic (~> 0.3.2)
-    method_source (0.9.2)
-    mimemagic (0.3.3)
-    mini_magick (4.9.5)
+    maxminddb (0.1.22)
+    method_source (1.0.0)
+    mimemagic (0.3.4)
+    mini_magick (4.10.1)
     mini_mime (1.0.2)
     mini_portile2 (2.4.0)
-    minitest (5.13.0)
-    msgpack (1.3.1)
+    minitest (5.14.0)
+    msgpack (1.3.3)
     multi_json (1.14.1)
     multi_xml (0.6.0)
     multipart-post (2.1.1)
     nio4r (2.5.2)
-    nokogiri (1.10.5)
+    nokogiri (1.10.9)
       mini_portile2 (~> 2.4.0)
-    nokogumbo (2.0.1)
+    nokogumbo (2.0.2)
       nokogiri (~> 1.8, >= 1.8.4)
     oauth (0.4.7)
     oauth-plugin (0.5.1)
@@ -272,18 +287,18 @@ GEM
       oauth (~> 0.4.4)
       oauth2 (>= 0.5.0)
       rack
-    oauth2 (1.4.2)
+    oauth2 (1.4.4)
       faraday (>= 0.8, < 2.0)
       jwt (>= 1.0, < 3.0)
       multi_json (~> 1.3)
       multi_xml (~> 0.5)
       rack (>= 1.2, < 3)
-    omniauth (1.9.0)
-      hashie (>= 3.4.6, < 3.7.0)
+    omniauth (1.9.1)
+      hashie (>= 3.4.6)
       rack (>= 1.6.2, < 3)
-    omniauth-facebook (5.0.0)
+    omniauth-facebook (6.0.0)
       omniauth-oauth2 (~> 1.2)
-    omniauth-github (1.3.0)
+    omniauth-github (1.4.0)
       omniauth (~> 1.5)
       omniauth-oauth2 (>= 1.4.0, < 2.0)
     omniauth-google-oauth2 (0.8.0)
@@ -306,40 +321,44 @@ GEM
       multi_json (~> 1.12)
       omniauth-oauth2 (~> 1.4)
     openstreetmap-deadlock_retry (1.3.0)
-    parallel (1.18.0)
-    parser (2.6.5.0)
+    parallel (1.19.1)
+    parser (2.7.1.0)
       ast (~> 2.4.0)
-    pg (1.1.4)
+    pg (1.2.3)
     poltergeist (1.18.1)
       capybara (>= 2.1, < 4)
       cliver (~> 0.3.1)
       websocket-driver (>= 0.2.0)
+    popper_js (1.16.0)
     progress (3.5.2)
     psych (3.1.0)
-    public_suffix (4.0.1)
-    puma (3.12.1)
+    public_suffix (4.0.4)
+    puma (3.12.4)
     quad_tile (1.0.1)
     r2 (0.2.7)
-    rack (2.0.7)
-    rack-cors (1.0.3)
+    rack (2.2.2)
+    rack-cors (1.1.1)
+      rack (>= 2.0.0)
     rack-openid (1.3.1)
       rack (>= 1.1.0)
       ruby-openid (>= 2.1.8)
     rack-test (1.1.0)
       rack (>= 1.0, < 3)
     rack-uri_sanitizer (0.0.2)
-    rails (5.2.3)
-      actioncable (= 5.2.3)
-      actionmailer (= 5.2.3)
-      actionpack (= 5.2.3)
-      actionview (= 5.2.3)
-      activejob (= 5.2.3)
-      activemodel (= 5.2.3)
-      activerecord (= 5.2.3)
-      activestorage (= 5.2.3)
-      activesupport (= 5.2.3)
+    rails (6.0.2.2)
+      actioncable (= 6.0.2.2)
+      actionmailbox (= 6.0.2.2)
+      actionmailer (= 6.0.2.2)
+      actionpack (= 6.0.2.2)
+      actiontext (= 6.0.2.2)
+      actionview (= 6.0.2.2)
+      activejob (= 6.0.2.2)
+      activemodel (= 6.0.2.2)
+      activerecord (= 6.0.2.2)
+      activestorage (= 6.0.2.2)
+      activesupport (= 6.0.2.2)
       bundler (>= 1.3.0)
-      railties (= 5.2.3)
+      railties (= 6.0.2.2)
       sprockets-rails (>= 2.0.0)
     rails-controller-testing (1.0.4)
       actionpack (>= 5.0.1.x)
@@ -353,38 +372,43 @@ GEM
     rails-i18n (4.0.2)
       i18n (~> 0.6)
       rails (>= 4.0)
-    railties (5.2.3)
-      actionpack (= 5.2.3)
-      activesupport (= 5.2.3)
+    railties (6.0.2.2)
+      actionpack (= 6.0.2.2)
+      activesupport (= 6.0.2.2)
       method_source
       rake (>= 0.8.7)
-      thor (>= 0.19.0, < 2.0)
+      thor (>= 0.20.3, < 2.0)
     rainbow (3.0.0)
-    rake (12.3.3)
+    rake (13.0.1)
     rb-fsevent (0.10.3)
-    rb-inotify (0.10.0)
+    rb-inotify (0.10.1)
       ffi (~> 1.0)
-    record_tag_helper (1.0.0)
-      actionview (~> 5.x)
-    request_store (1.4.1)
+    regexp_parser (1.7.0)
+    request_store (1.5.0)
       rack (>= 1.4)
+    rexml (3.2.4)
     rinku (2.0.6)
     rotp (5.1.0)
       addressable (~> 2.5)
-    rubocop (0.76.0)
+    rubocop (0.81.0)
       jaro_winkler (~> 1.5.1)
       parallel (~> 1.10)
-      parser (>= 2.6)
+      parser (>= 2.7.0.1)
       rainbow (>= 2.2.2, < 4.0)
+      rexml
       ruby-progressbar (~> 1.7)
-      unicode-display_width (>= 1.4.0, < 1.7)
-    rubocop-performance (1.5.0)
+      unicode-display_width (>= 1.4.0, < 2.0)
+    rubocop-minitest (0.9.0)
+      rubocop (>= 0.74)
+    rubocop-performance (1.5.2)
       rubocop (>= 0.71.0)
-    rubocop-rails (2.3.2)
+    rubocop-rails (2.5.1)
+      activesupport
       rack (>= 1.1)
       rubocop (>= 0.72.0)
     ruby-openid (2.9.2)
     ruby-progressbar (1.10.1)
+    rubyzip (2.3.0)
     safe_yaml (1.0.5)
     sanitize (5.1.0)
       crass (~> 1.0.2)
@@ -398,7 +422,10 @@ GEM
       sprockets (> 3.0)
       sprockets-rails
       tilt
-    secure_headers (6.1.1)
+    secure_headers (6.3.0)
+    selenium-webdriver (3.142.7)
+      childprocess (>= 0.5, < 4.0)
+      rubyzip (>= 1.2.2)
     simplecov (0.16.1)
       docile (~> 1.1)
       json (>= 1.8, < 3)
@@ -412,23 +439,25 @@ GEM
       actionpack (>= 4.0)
       activesupport (>= 4.0)
       sprockets (>= 3.0.0)
-    strong_migrations (0.4.2)
+    strong_migrations (0.6.3)
       activerecord (>= 5)
+    sync (0.5.0)
     term-ansicolor (1.7.1)
       tins (~> 1.0)
-    thor (0.20.3)
+    thor (1.0.1)
     thread_safe (0.3.6)
     tilt (2.0.10)
-    tins (1.22.0)
-    tzinfo (1.2.5)
+    tins (1.24.1)
+      sync
+    tzinfo (1.2.7)
       thread_safe (~> 0.1)
     uglifier (4.2.0)
       execjs (>= 0.3.0, < 3)
-    unicode-display_width (1.6.0)
+    unicode-display_width (1.7.0)
     validates_email_format_of (1.6.3)
       i18n
     vendorer (0.2.0)
-    webmock (3.7.6)
+    webmock (3.8.3)
       addressable (>= 2.3.6)
       crack (>= 0.3.2)
       hashdiff (>= 0.4.0, < 2.0.0)
@@ -437,6 +466,7 @@ GEM
     websocket-extensions (0.1.4)
     xpath (3.2.0)
       nokogiri (~> 1.8)
+    zeitwerk (2.3.0)
 
 PLATFORMS
   ruby
@@ -444,23 +474,23 @@ PLATFORMS
 DEPENDENCIES
   SystemTimer (>= 1.1.3)
   aasm
-  actionpack-page_caching
+  actionpack-page_caching (>= 1.2.0)
   active_record_union
   activerecord-import
   annotate
-  autoprefixer-rails (~> 8.6.3)
+  autoprefixer-rails
   aws-sdk-s3
   better_errors
   bigdecimal (~> 1.1.0)
   binding_of_caller
-  bootsnap (>= 1.1.0)
+  bootsnap (>= 1.4.2)
+  bootstrap (~> 4.3.1)
   browser
   bzip2-ffi
   cancancan
   canonical-rails
-  capybara (~> 2.13)
-  coffee-rails (~> 4.2)
-  composite_primary_keys (~> 11.1.0)
+  capybara (>= 2.15)
+  composite_primary_keys (~> 12.0.0)
   config
   coveralls
   dalli
@@ -468,23 +498,22 @@ DEPENDENCIES
   dynamic_form
   erb_lint
   factory_bot_rails
-  fakefs
   faraday
   ffi-libarchive
   gd2-ffij (>= 0.4.0)
-  geoip
   htmlentities
   http_accept_language (~> 2.0.0)
   i18n-js (>= 3.0.0)
   image_optim_rails
+  jbuilder (~> 2.7)
   jquery-rails
   json
-  jsonify-rails
   kgio
   kramdown
   libxml-ruby (>= 2.0.5)
   listen
   logstasher
+  maxminddb
   mimemagic
   mini_magick
   minitest (~> 5.1)
@@ -500,23 +529,24 @@ DEPENDENCIES
   pg
   poltergeist
   psych
-  puma (~> 3.7)
+  puma (~> 3.11)
   quad_tile (~> 1.0.1)
   r2 (~> 0.2.7)
   rack-cors
   rack-uri_sanitizer
-  rails (= 5.2.3)
+  rails (= 6.0.2.2)
   rails-controller-testing
   rails-i18n (~> 4.0.0)
-  record_tag_helper
   rinku (>= 2.0.6)
   rotp
   rubocop
+  rubocop-minitest
   rubocop-performance
   rubocop-rails
   sanitize
   sassc-rails
   secure_headers
+  selenium-webdriver
   strong_migrations
   uglifier (>= 1.3.0)
   validates_email_format_of (>= 1.5.1)
index e414a12e425520dfc1d3e312aa83bc65a62ee78d..3dca16677e84d80eea3fe80a91b99fe77ead73d6 100644 (file)
@@ -86,17 +86,7 @@ Installing other dependencies:
 
 * Install Homebrew from https://brew.sh/
 * Install the latest version of Ruby: `brew install ruby`
-* Install ImageMagick: `brew install imagemagick`
-* Install libxml2: `brew install libxml2`
-* Install libgd: `brew install gd`
-* Install Yarn: `brew install yarn`
-* Install pngcrush: `brew install pngcrush`
-* Install optipng: `brew install optipng`
-* Install pngquant: `brew install pngquant`
-* Install jhead: `brew install jhead`
-* Install jpegoptim: `brew install jpegoptim`
-* Install gifsicle: `brew install gifsicle`
-* Install svgo: `brew install svgo`
+* Install other dependencies: `brew install imagemagick libxml2 gd yarn pngcrush optipng pngquant jhead jpegoptim gifsicle svgo`
 * 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>.
@@ -311,4 +301,4 @@ Where `$VERSION` is the version you installed. Then install bundler:
 gem install bundler
 ```
 
-You should now be able to proceed with the rest of the installation. If you're on MacOSX, make sure you set up the [config override for the libxml2 location])(#macosx-bundle-config) _after_ installing bundler.
+You should now be able to proceed with the rest of the installation. If you're on MacOSX, make sure you set up the [config override for the libxml2 location](#macosx-bundle-config) _after_ installing bundler.
index ad59f91ebea6aef14b73f8295c84aeeb14993c05..5deed27a6eb2e7d22bafac96819bc3f8b2a1053f 100644 (file)
@@ -5,22 +5,17 @@ folder 'vendor/assets' do
     file 'jquery.simulate.js', 'https://raw.githubusercontent.com/jquery/jquery-simulate/1.0.0/jquery.simulate.js'
   end
 
-  folder 'bootstrap' do
-    file 'bootstrap.tooltip.js', 'https://raw.githubusercontent.com/twbs/bootstrap/v3.3.2/js/tooltip.js'
-    file 'bootstrap.dropdown.js', 'https://raw.githubusercontent.com/twbs/bootstrap/v3.3.2/js/dropdown.js'
-  end
-
   folder 'leaflet' do
-    file 'leaflet.js', 'https://unpkg.com/leaflet@1.5.1/dist/leaflet-src.js'
-    file 'leaflet.css', 'https://unpkg.com/leaflet@1.5.1/dist/leaflet.css'
+    file 'leaflet.js', 'https://unpkg.com/leaflet@1.6.0/dist/leaflet-src.js'
+    file 'leaflet.css', 'https://unpkg.com/leaflet@1.6.0/dist/leaflet.css'
 
     [ 'layers.png', 'layers-2x.png',
       'marker-icon.png', 'marker-icon-2x.png',
       'marker-shadow.png' ].each do |image|
-      file "images/#{image}", "https://unpkg.com/leaflet@1.5.1/dist/images/#{image}"
+      file "images/#{image}", "https://unpkg.com/leaflet@1.6.0/dist/images/#{image}"
     end
 
-    from 'git://github.com/aratcliffe/Leaflet.contextmenu.git', :tag => 'v1.5.0' do
+    from 'git://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
@@ -31,7 +26,7 @@ folder 'vendor/assets' do
       folder 'img', 'src/img'
     end
 
-    from 'git://github.com/domoritz/leaflet-locatecontrol.git', :tag => 'v0.66.0' do
+    from 'git://github.com/domoritz/leaflet-locatecontrol.git', :tag => 'v0.71.0' do
       file 'leaflet.locate.js', 'src/L.Control.Locate.js'
     end
 
@@ -39,7 +34,7 @@ folder 'vendor/assets' do
       file 'leaflet.osm.js', 'leaflet-osm.js'
     end
 
-    from 'git://github.com/jieter/Leaflet.encoded.git', :tag => '0.0.8' do
+    from 'git://github.com/jieter/Leaflet.encoded.git', :tag => '0.0.9' do
       file 'leaflet.polyline.js', 'Polyline.encoded.js'
     end
   end
@@ -74,7 +69,6 @@ folder 'vendor/assets' do
 
   folder 'javascripts' do
     file 'html5shiv.js', 'https://raw.githubusercontent.com/aFarkas/html5shiv/master/src/html5shiv.js'
-    file 'bowser.js', 'https://github.com/lancedikson/bowser/releases/download/1.9.4/bowser.js'
   end
 
   folder 'swfobject' do
index c34f357a97a41b2febfbdb7fa1b0a505ded8e3aa..f0cebb380a46ba2ee2572fc54884e0eb8bd93353 100644 (file)
@@ -40,7 +40,7 @@ class Ability
         can [:new, :create, :reply, :show, :inbox, :outbox, :mark, :destroy], Message
         can [:close, :reopen], Note
         can [:new, :create], Report
-        can [:mine, :new, :create, :edit, :update, :delete], Trace
+        can [:mine, :new, :create, :edit, :update, :destroy], Trace
         can [:account, :go_public, :make_friend, :remove_friend], User
 
         if user.moderator?
index 6d62ece5172f0f5ad9eaccb63dc2fc8c798bcd4a..62cd2b17ebbd41ad9408ebcfc5730134991b3506 100644 (file)
@@ -34,10 +34,10 @@ class ApiAbility
         can [:new, :create], Report
         can [:create, :show, :update, :destroy, :data], Trace
         can [:details, :gpx_files], User
-        can [:read, :read_one, :update, :update_one, :delete_one], UserPreference
+        can [:index, :show, :update, :update_all, :destroy], UserPreference
 
         if user.terms_agreed?
-          can [:create, :update, :upload, :close, :subscribe, :unsubscribe, :expand_bbox], Changeset
+          can [:create, :update, :upload, :close, :subscribe, :unsubscribe], Changeset
           can :create, ChangesetComment
           can [:create, :update, :delete], Node
           can [:create, :update, :delete], Way
index 7d8a13364c8fec33c091ebd32516a297b51c6050..beb4d39bf3fa62ba20641899a89220cb91594b71 100644 (file)
@@ -10,11 +10,11 @@ class ApiCapability
       can [:create, :update, :destroy], Trace if capability?(token, :allow_write_gpx)
       can [:details], User if capability?(token, :allow_read_prefs)
       can [:gpx_files], User if capability?(token, :allow_read_gpx)
-      can [:read, :read_one], UserPreference if capability?(token, :allow_read_prefs)
-      can [:update, :update_one, :delete_one], UserPreference if capability?(token, :allow_write_prefs)
+      can [:index, :show], UserPreference if capability?(token, :allow_read_prefs)
+      can [:update, :update_all, :destroy], UserPreference if capability?(token, :allow_write_prefs)
 
       if token&.user&.terms_agreed?
-        can [:create, :update, :upload, :close, :subscribe, :unsubscribe, :expand_bbox], Changeset if capability?(token, :allow_write_api)
+        can [:create, :update, :upload, :close, :subscribe, :unsubscribe], Changeset if capability?(token, :allow_write_api)
         can :create, ChangesetComment if capability?(token, :allow_write_api)
         can [:create, :update, :delete], Node if capability?(token, :allow_write_api)
         can [:create, :update, :delete], Way if capability?(token, :allow_write_api)
diff --git a/app/assets/images/banners/sotm_africa_2019.png b/app/assets/images/banners/sotm_africa_2019.png
new file mode 100644 (file)
index 0000000..aefd488
Binary files /dev/null and b/app/assets/images/banners/sotm_africa_2019.png differ
diff --git a/app/assets/images/banners/sotm_asia_2019.png b/app/assets/images/banners/sotm_asia_2019.png
deleted file mode 100644 (file)
index ef2f1c2..0000000
Binary files a/app/assets/images/banners/sotm_asia_2019.png and /dev/null differ
diff --git a/app/assets/images/browse/alpinehut.p.16.png b/app/assets/images/browse/alpinehut.p.16.png
deleted file mode 100644 (file)
index 0499c58..0000000
Binary files a/app/assets/images/browse/alpinehut.p.16.png and /dev/null differ
index 1b1603399f7d8a3c2622d98f92623ce841ba7a89..c245020ceb572cdf5f9736d108e93fea392a0fde 100644 (file)
Binary files a/app/assets/images/browse/brownfield.png and b/app/assets/images/browse/brownfield.png differ
index fd37114c97bf20ffd593aaaf28a6ef4acb1183a7..e83961d81a5b88a0dc3db7e013f517f58f3d1b16 100644 (file)
Binary files a/app/assets/images/browse/building.png and b/app/assets/images/browse/building.png differ
index f89cf18e2beca208c76352bea43f0107e2f321bf..53ae8536f878176805631b994cb7d96452ad042f 100644 (file)
Binary files a/app/assets/images/browse/cemetery.png and b/app/assets/images/browse/cemetery.png differ
index 3008f1809a613f8b184c02a69c63ec6bd48ced86..a18dc4114613d6cfdeda79b3c7bea93aad5e1bbd 100644 (file)
Binary files a/app/assets/images/browse/commercial.png and b/app/assets/images/browse/commercial.png differ
index c894c845537fe8dc6534120ba87cd4f2de6177b5..bfd959150c17baf9cae8f51e78387f74f04c1e2c 100644 (file)
Binary files a/app/assets/images/browse/farmland.png and b/app/assets/images/browse/farmland.png differ
index ca41410a8c22ba26abdd2ed9878352925de074ec..6be69f2f05becc41e18d6fcda6b9706be90862ef 100644 (file)
Binary files a/app/assets/images/browse/farmyard.png and b/app/assets/images/browse/farmyard.png differ
index da748f81d25d6a3ec54f7f01f703faa29e95545a..6bd4403fa2d38ae5cfe886ef6ee2987582ed5a76 100644 (file)
Binary files a/app/assets/images/browse/forest.png and b/app/assets/images/browse/forest.png differ
index 4f7afc4fa627be76753e70beaa8fb572d847fa77..a485580d5baa63023da64f360bfb3b982282a6b5 100644 (file)
Binary files a/app/assets/images/browse/golf.png and b/app/assets/images/browse/golf.png differ
index 79baac3ee54f2972add6fcf62661abe80cf6421b..c909bfa8791f18e60727f14236a50ac9efedaca8 100644 (file)
Binary files a/app/assets/images/browse/grass.png and b/app/assets/images/browse/grass.png differ
index 79baac3ee54f2972add6fcf62661abe80cf6421b..c909bfa8791f18e60727f14236a50ac9efedaca8 100644 (file)
Binary files a/app/assets/images/browse/grassland.png and b/app/assets/images/browse/grassland.png differ
index cb2fc225df09ffbe98cc7e0a97f7099717e4bf5c..c35d96fee8bd6f08f7433450823ad7bded7dccf0 100644 (file)
Binary files a/app/assets/images/browse/heathland.png and b/app/assets/images/browse/heathland.png differ
index 4ad4e51934e53cdc4b76232f34cc1f5abe6a11ed..3b3be37f9e8591f06a40065db79d7ebc2ed0edaa 100644 (file)
Binary files a/app/assets/images/browse/industrial.png and b/app/assets/images/browse/industrial.png differ
index c838303f6e3c7cccd3290d118883b9084ee52d0e..2fee498ed7cb6cb3717117a1686d0e8812bf5c2d 100644 (file)
Binary files a/app/assets/images/browse/lake.png and b/app/assets/images/browse/lake.png differ
index 79baac3ee54f2972add6fcf62661abe80cf6421b..c909bfa8791f18e60727f14236a50ac9efedaca8 100644 (file)
Binary files a/app/assets/images/browse/meadow.png and b/app/assets/images/browse/meadow.png differ
index a60755dd068dae9d0c0aec4c5c7ff887adc69e54..0922b56f990d49d81f86e51eb21afe6fb3c2d397 100644 (file)
Binary files a/app/assets/images/browse/park.png and b/app/assets/images/browse/park.png differ
index a49af3010c6c247c37ce4ffea26e1c05b4b61aa3..7d18def41ae91e0b67007b23152744b4319ca052 100644 (file)
Binary files a/app/assets/images/browse/pitch.png and b/app/assets/images/browse/pitch.png differ
index e3b850e465fbf8fe881704fecb5cac1cc6ab57f9..429fcd0d14983190e9d57c4186824985eed814fc 100644 (file)
Binary files a/app/assets/images/browse/residential.png and b/app/assets/images/browse/residential.png differ
index 23b097ba4e86c5dcca02b81bc353ffd8921a42f9..62309746fb64041d7f169aea872232ba3d7d86a1 100644 (file)
Binary files a/app/assets/images/browse/retail.png and b/app/assets/images/browse/retail.png differ
diff --git a/app/assets/images/browse/school.png b/app/assets/images/browse/school.png
deleted file mode 100644 (file)
index 9361041..0000000
Binary files a/app/assets/images/browse/school.png and /dev/null differ
diff --git a/app/assets/images/browse/scrub.png b/app/assets/images/browse/scrub.png
new file mode 100644 (file)
index 0000000..ca85ab8
Binary files /dev/null and b/app/assets/images/browse/scrub.png differ
diff --git a/app/assets/images/browse/tourism_alpine_hut.16.png b/app/assets/images/browse/tourism_alpine_hut.16.png
new file mode 100644 (file)
index 0000000..adeb457
Binary files /dev/null and b/app/assets/images/browse/tourism_alpine_hut.16.png differ
diff --git a/app/assets/images/browse/tourism_wilderness_hut.16.png b/app/assets/images/browse/tourism_wilderness_hut.16.png
new file mode 100644 (file)
index 0000000..33ccbfb
Binary files /dev/null and b/app/assets/images/browse/tourism_wilderness_hut.16.png differ
index 329bfa483b1d90e4c1e30a01fc6df1fb6b3bb0d7..6bd4403fa2d38ae5cfe886ef6ee2987582ed5a76 100644 (file)
Binary files a/app/assets/images/browse/wood.png and b/app/assets/images/browse/wood.png differ
index fbc76e4a8db29f442dccfeafd0904d51437f096e..e455f297be32868f6a5ae73921b4f416c5c5f3ef 100644 (file)
@@ -3,8 +3,8 @@
 //= require jquery.timers
 //= require jquery.cookie
 //= require jquery.throttle-debounce
-//= require bootstrap.tooltip
-//= require bootstrap.dropdown
+//= require popper
+//= require bootstrap-sprockets
 //= require osm
 //= require leaflet
 //= require leaflet.osm
@@ -18,7 +18,7 @@
 //= require querystring
 
 /*
- * Called as the user scrolls/zooms around to maniplate hrefs of the
+ * Called as the user scrolls/zooms around to manipulate hrefs of the
  * view tab and various other links
  */
 window.updateLinks = function (loc, zoom, layers, object) {
index 039cc0ef66b5ccb4c6da47ec98ae0c21dcd0e1d8..6584864d544a4e05c92cb2f73bed1dd6a9b1f4d6 100644 (file)
@@ -20,7 +20,6 @@
 //= require index/changeset
 //= require index/query
 //= require router
-//= require bowser
 //= require querystring
 
 $(document).ready(function () {
@@ -110,6 +109,8 @@ $(document).ready(function () {
     iconLoading: "icon geolocate",
     strings: {
       title: I18n.t("javascripts.map.locate.title"),
+      metersUnit: I18n.t("javascripts.map.locate.metersUnit"),
+      feetUnit: I18n.t("javascripts.map.locate.feetUnit"),
       popup: I18n.t("javascripts.map.locate.popup")
     }
   }).addTo(map);
@@ -255,12 +256,7 @@ $(document).ready(function () {
           bottom: bbox.getSouth() - 0.0001
         };
 
-    if (location.protocol === "http" ||
-        bowser.check({ chrome: "53", firefox: "55" })) {
-      url = "http://127.0.0.1:8111/load_and_zoom?";
-    } else {
-      url = "https://127.0.0.1:8112/load_and_zoom?";
-    }
+    url = "http://127.0.0.1:8111/load_and_zoom?";
 
     if (object) query.select = object.type + object.id;
 
index 4c39d6781e1673d61d3e7c28aa186bb509acd464..5f9027ec6b9bc85edd0f5c20739da3be2e09fa7c 100644 (file)
@@ -141,13 +141,21 @@ OSM.Directions = function (map) {
   }
 
   $(".directions_form .reverse_directions").on("click", function () {
-    var from = endpoints[0].latlng,
-        to = endpoints[1].latlng;
+    var coordFrom = endpoints[0].latlng,
+        coordTo = endpoints[1].latlng,
+        routeFrom = "",
+        routeTo = "";
+    if (coordFrom) {
+      routeFrom = coordFrom.lat + "," + coordFrom.lng;
+    }
+    if (coordTo) {
+      routeTo = coordTo.lat + "," + coordTo.lng;
+    }
 
     OSM.router.route("/directions?" + querystring.stringify({
       from: $("#route_to").val(),
       to: $("#route_from").val(),
-      route: to.lat + "," + to.lng + ";" + from.lat + "," + from.lng
+      route: routeTo + ";" + routeFrom
     }));
   });
 
index 80fb68444f6469ee4e41e03d2f09654a337dfe12..ddb454c13155e770bc4110e8b028d3a054fe13dd 100644 (file)
@@ -40,12 +40,14 @@ OSM.History = function (map) {
   };
 
   function highlightChangeset(id) {
-    group.getLayer(id).setStyle({ fillOpacity: 0.3, color: "#FF6600", weight: 3 });
+    var layer = group.getLayer(id);
+    if (layer) layer.setStyle({ fillOpacity: 0.3, color: "#FF6600", weight: 3 });
     $("#changeset_" + id).addClass("selected");
   }
 
   function unHighlightChangeset(id) {
-    group.getLayer(id).setStyle({ fillOpacity: 0, color: "#FF9500", weight: 2 });
+    var layer = group.getLayer(id);
+    if (layer) layer.setStyle({ fillOpacity: 0, color: "#FF9500", weight: 2 });
     $("#changeset_" + id).removeClass("selected");
   }
 
index 5c6e5817080efe09680a7dccb2564106efe28713..7d02760ced95afb9a0ed1793cb2a3c74b0df505f 100644 (file)
@@ -214,28 +214,28 @@ OSM.Query = function (map) {
 
           if (interestingFeature(element)) {
             var $li = $("<li>")
-              .addClass("query-result")
+              .addClass("query-result list-group-item")
               .data("geometry", featureGeometry(element))
-              .appendTo($ul);
-            var $p = $("<p>")
               .text(featurePrefix(element) + " ")
-              .appendTo($li);
+              .appendTo($ul);
 
             $("<a>")
               .attr("href", "/" + element.type + "/" + element.id)
               .text(featureName(element))
-              .appendTo($p);
+              .appendTo($li);
           }
         }
 
         if (results.remark) {
           $("<li>")
+            .addClass("query-result list-group-item")
             .text(I18n.t("javascripts.query.error", { server: url, error: results.remark }))
             .appendTo($ul);
         }
 
         if ($ul.find("li").length === 0) {
           $("<li>")
+            .addClass("query-result list-group-item")
             .text(I18n.t("javascripts.query.nothing_found"))
             .appendTo($ul);
         }
@@ -244,6 +244,7 @@ OSM.Query = function (map) {
         $section.find(".loader").stopTime("loading").hide();
 
         $("<li>")
+          .addClass("query-result list-group-item")
           .text(I18n.t("javascripts.query." + status, { server: url, error: error }))
           .appendTo($ul);
       }
index ba66f478255d4e9d65e6c16066743b38da28ae97..9ed243466d2a9402053e24a1247f8a5053085d85 100644 (file)
@@ -45,9 +45,9 @@ OSM.Search = function (map) {
   $("#sidebar_content")
     .on("click", ".search_more a", clickSearchMore)
     .on("click", ".search_results_entry a.set_position", clickSearchResult)
-    .on("mouseover", "p.search_results_entry:has(a.set_position)", showSearchResult)
-    .on("mouseout", "p.search_results_entry:has(a.set_position)", hideSearchResult)
-    .on("mousedown", "p.search_results_entry:has(a.set_position)", function () {
+    .on("mouseover", "li.search_results_entry:has(a.set_position)", showSearchResult)
+    .on("mouseout", "li.search_results_entry:has(a.set_position)", hideSearchResult)
+    .on("mousedown", "li.search_results_entry:has(a.set_position)", function () {
       var moved = false;
       $(this).one("click", function (e) {
         if (!moved && !$(e.target).is("a")) {
index 241880a20ebe874f302dfc0c4249bddf251df52c..87ff8737048a6319c91839416e41f38f23bff4da 100644 (file)
@@ -34,7 +34,7 @@ L.OSM.layers = function (options) {
       .attr("class", "section base-layers")
       .appendTo($ui);
 
-    var baseLayers = $("<ul>")
+    var baseLayers = $("<ul class='list-unstyled'>")
       .appendTo(baseSection);
 
     layers.forEach(function (layer) {
@@ -119,7 +119,7 @@ L.OSM.layers = function (options) {
         .attr("class", "deemphasize")
         .appendTo(overlaySection);
 
-      var overlays = $("<ul>")
+      var overlays = $("<ul class='list-unstyled'>")
         .appendTo(overlaySection);
 
       var addOverlay = function (layer, name, maxArea) {
index 797a57e20db99e35c6f46e2f5b67739646dec0f2..a139b6dcd7080ab40045611dc9ab0ea8779f411c 100644 (file)
@@ -18,6 +18,8 @@ L.OSM.Map = L.Map.extend({
     var copyright = I18n.t("javascripts.map.copyright", { copyright_url: "/copyright" });
     var donate = I18n.t("javascripts.map.donate_link_text", { donate_url: "https://donate.openstreetmap.org" });
     var terms = I18n.t("javascripts.map.terms", { terms_url: "https://wiki.osmfoundation.org/wiki/Terms_of_Use" });
+    var thunderforest = I18n.t("javascripts.map.thunderforest", { thunderforest_url: "https://www.thunderforest.com/" });
+    var hotosm = I18n.t("javascripts.map.hotosm", { hotosm_url: "https://www.hotosm.org/", osmfrance_url: "https://openstreetmap.fr/" });
 
     this.baseLayers = [];
 
@@ -30,7 +32,7 @@ L.OSM.Map = L.Map.extend({
 
     if (OSM.THUNDERFOREST_KEY) {
       this.baseLayers.push(new L.OSM.CycleMap({
-        attribution: copyright + ". Tiles courtesy of <a href='https://www.thunderforest.com/' target='_blank'>Andy Allan</a>. " + terms,
+        attribution: copyright + ". " + thunderforest + ". " + terms,
         apikey: OSM.THUNDERFOREST_KEY,
         code: "C",
         keyid: "cyclemap",
@@ -38,7 +40,7 @@ L.OSM.Map = L.Map.extend({
       }));
 
       this.baseLayers.push(new L.OSM.TransportMap({
-        attribution: copyright + ". Tiles courtesy of <a href='https://www.thunderforest.com/' target='_blank'>Andy Allan</a>. " + terms,
+        attribution: copyright + ". " + thunderforest + ". " + terms,
         apikey: OSM.THUNDERFOREST_KEY,
         code: "T",
         keyid: "transportmap",
@@ -47,7 +49,7 @@ L.OSM.Map = L.Map.extend({
     }
 
     this.baseLayers.push(new L.OSM.HOT({
-      attribution: copyright + ". Tiles style by <a href='https://www.hotosm.org/' target='_blank'>Humanitarian OpenStreetMap Team</a> hosted by <a href='https://openstreetmap.fr/' target='_blank'>OpenStreetMap France</a>. " + terms,
+      attribution: copyright + ". " + hotosm + ". " + terms,
       code: "H",
       keyid: "hot",
       name: I18n.t("javascripts.map.base.hot")
index eedfe3ab34abfab47f41ee4619fdf0ee6065337a..8b1446ff8d4a849d3d2f889bd3b4d137cebebaea 100644 (file)
@@ -48,7 +48,7 @@ L.OSM.share = function (options) {
       .appendTo($linkSection);
 
     $("<div>")
-      .attr("class", "form-row")
+      .attr("class", "standard-form-row")
       .appendTo($form)
       .append(
         $("<label>")
@@ -91,7 +91,7 @@ L.OSM.share = function (options) {
       });
 
     $("<div>")
-      .attr("class", "form-row share-tab")
+      .attr("class", "standard-form-row share-tab")
       .css("display", "block")
       .appendTo($form)
       .append($("<input>")
@@ -100,7 +100,7 @@ L.OSM.share = function (options) {
         .on("click", select));
 
     $("<div>")
-      .attr("class", "form-row share-tab")
+      .attr("class", "standard-form-row share-tab")
       .appendTo($form)
       .append($("<input>")
         .attr("id", "short_input")
@@ -108,7 +108,7 @@ L.OSM.share = function (options) {
         .on("click", select));
 
     $("<div>")
-      .attr("class", "form-row share-tab")
+      .attr("class", "standard-form-row share-tab")
       .appendTo($form)
       .append(
         $("<textarea>")
@@ -159,7 +159,7 @@ L.OSM.share = function (options) {
       .appendTo($imageSection);
 
     $("<div>")
-      .attr("class", "form-row")
+      .attr("class", "standard-form-row")
       .appendTo($form)
       .append(
         $("<label>")
@@ -172,7 +172,7 @@ L.OSM.share = function (options) {
           .append(I18n.t("javascripts.share.custom_dimensions")));
 
     $("<div>")
-      .attr("class", "form-row")
+      .attr("class", "standard-form-row")
       .appendTo($form)
       .append(
         $("<label>")
@@ -187,7 +187,7 @@ L.OSM.share = function (options) {
         .append($("<option>").val("pdf").text("PDF")));
 
     $("<div>")
-      .attr("class", "form-row")
+      .attr("class", "standard-form-row")
       .appendTo($form)
       .append($("<label>")
         .attr("for", "mapnik_scale")
index 7cbdb1e110b24c843dc8504c505a1363b15e0acb..377efb315b290eaf48a1c186f44aa648b56fe153 100644 (file)
@@ -18,7 +18,9 @@ $(document).ready(function () {
       iconLoading: "icon geolocate",
       strings: {
         title: I18n.t("javascripts.map.locate.title"),
-        popup: I18n.t("javascripts.map.locate.popup")
+        popup: function (options) {
+          return I18n.t("javascripts.map.locate." + options.unit + "Popup", { count: options.distance });
+        }
       }
     }).addTo(map);
 
diff --git a/app/assets/stylesheets/bootstrap.css b/app/assets/stylesheets/bootstrap.css
deleted file mode 100644 (file)
index dfe850b..0000000
+++ /dev/null
@@ -1,83 +0,0 @@
-/* Rules for bootstrap tooltips */
-
-.tooltip {
-  position: absolute;
-  display: none;
-  color: #333;
-  text-align: left;
-  font-size: 12px;
-  max-width: 250px;
-}
-
-.tooltip.in {
-  opacity: 0.8;
-  z-index: 1030;
-  height: auto;
-  display: block;
-}
-
-.tooltip.top {
-  margin-top: -10px;
-  text-align: center;
-}
-
-.tooltip.right {
-  margin-left: 10px;
-}
-
-.tooltip.bottom {
-  margin-top: 10px;
-  text-align: center;
-}
-
-.tooltip.left {
-  margin-left: -10px;
-  text-align: right;
-}
-
-.tooltip-inner {
-  display: inline-block;
-  padding: 10px;
-  font-weight: normal;
-  background-color: white;
-}
-
-.tooltip-arrow {
-  position: absolute;
-  width: 0;
-  height: 0;
-  border-color: transparent;
-  border-style: solid;
-}
-
-.tooltip.top .tooltip-arrow {
-  bottom: -5px;
-  left: 50%;
-  margin-left: -5px;
-  border-top-color: white;
-  border-width: 5px 5px 0;
-}
-
-.tooltip.right .tooltip-arrow {
-  top: 50%;
-  left: -5px;
-  margin-top: -5px;
-  border-right-color: white;
-  border-width: 5px 5px 5px 0;
-}
-
-.tooltip.left .tooltip-arrow {
-  top: 50%;
-  right: -5px;
-  margin-top: -5px;
-  border-left-color: white;
-  border-width: 5px 0 5px 5px;
-}
-
-.tooltip.bottom .tooltip-arrow {
-  top: -5px;
-  left: 50%;
-  margin-left: -5px;
-  border-bottom-color: white;
-  border-width: 0 5px 5px;
-}
index 2db397a07f8ce1332c50dbe45334bd8d12402dfb..7c6f4b36507dbf0eb7e4dca1c44592d2a3a74738 100644 (file)
   .shop.shoes::before { content: image-url('browse/shop_shoes.16.png'); }
   .shop.supermarket::before { content: image-url('browse/shop_supermarket.p.16.png'); }
 
-  .tourism.alpine_hut::before { content: image-url('browse/alpinehut.p.16.png'); }
+  .tourism.alpine_hut::before { content: image-url('browse/tourism_alpine_hut.16.png'); }
   .tourism.camp_site::before { content: image-url('browse/tourism_camp_site.16.png'); }
   .tourism.caravan_site::before { content: image-url('browse/tourism_caravan_site.16.png'); }
   .tourism.hostel::before { content: image-url('browse/tourism_hostel.16.png'); }
   .tourism.museum::before { content: image-url('browse/tourism_museum.16.png'); }
   .tourism.picnic_site::before { content: image-url('browse/tourism_picnic_site.16.png'); }
   .tourism.viewpoint::before { content: image-url('browse/view_point.p.16.png'); }
+  .tourism.wilderness_hut::before { content: image-url('browse/tourism_wilderness_hut.16.png'); }
 
   /* Ways */
 
 
   .natural.grassland::before { content: image-url('browse/grassland.png'); }
   .natural.heath::before { content: image-url('browse/heathland.png'); }
+  .natural.scrub::before { content: image-url('browse/scrub.png'); }
   .natural.water::before { content: image-url('browse/lake.png'); }
   .natural.wood::before { content: image-url('browse/wood.png'); }
 
index 40c5eaa2a8e91f4362b4852ea389bfefa1085608..a13f73d7a8ba0a2aa7228b927da1ea54e664f968 100644 (file)
 @import "parameters";
+@import "bootstrap";
 
 /* Styles common to large and small screens */
 
-/* Minimal CSS reset */
-
-html, body, ul, ol, li, form, fieldset, legend, h1, h2, h3, h4, h5, h6, p, input {
-  margin: 0;
-  padding: 0;
-  border: 0;
-  font-size:100%;
-}
-
-fieldset,img { border: 0; }
-
-legend { color: #000; }
-
-sup {
-  vertical-align: super;
-  font-size: smaller;
-}
-
-sub {
-  vertical-align: sub;
-  font-size: smaller;
-}
-
-table {
-  border-collapse: collapse;
-  border-spacing: 0;
-}
-
-li { list-style: none; }
-
-input,
-select,
-textarea,
-body { font: #{$typeheight}/#{$lineheight} "Helvetica Neue",Arial,sans-serif; }
-
-abbr, acronym {
-  text-decoration: underline dotted;
-  cursor: help;
-}
-
-strong {
-  font-weight: bold;
-}
-
-/* Micro Clearfix | Details: http://nicolasgallagher.com/micro-clearfix-hack/ */
-
-.clearfix:before,
-.clearfix:after {
-    content: " ";
-    display: table;
-}
-
-.clearfix:after {
-    clear: both;
-}
-
-/* Basic grid */
-
-.col0    { float:left; width:04.1666%; }
-.col1    { float:left; width:08.3333%; }
-.col2    { float:left; width:16.6666%; }
-.col3    { float:left; width:25.0000%; }
-.col4    { float:left; width:33.3333%; }
-.col5    { float:left; width:41.6666%; }
-.col6    { float:left; width:50.0000%; }
-.col7    { float:left; width:58.3333%; }
-.col8    { float:left; width:66.6666%; }
-.col9    { float:left; width:75.0000%; }
-.col10   { float:left; width:83.3333%; }
-.col11   { float:left; width:91.6666%; }
-.col12   { width:100%; }
-
-.margin0  { margin-left:04.1666%; }
-.margin1  { margin-left:08.3333%; }
-.margin2  { margin-left:16.6666%; }
-.margin3  { margin-left:25.0000%; }
-.margin4  { margin-left:33.3333%; }
-.margin5  { margin-left:41.6666%; }
-.margin6  { margin-left:50.0000%; }
-.margin7  { margin-left:58.3333%; }
-.margin8  { margin-left:66.6666%; }
-.margin9  { margin-left:75.0000%; }
-.margin10 { margin-left:83.3333%; }
-.margin11 { margin-left:91.6666%; }
-.margin12 { margin-left:100.0000%; }
-
 .fillL { background-color: white; }
 
 /* Default rules for the body of every page */
 
-* {
-  box-sizing: border-box;
-}
-
 body {
   font-family: 'Helvetica Neue',Arial,sans-serif;
   font-size: $typeheight;
@@ -108,23 +19,6 @@ body {
   height: 100%;
 }
 
-h1, h2, h3 {
-  font-weight: 600;
-  line-height: 1.2;
-}
-
-h4, h5 {
-  font-weight: 500;
-}
-
-h1 {
-  font-size: 18px;
-}
-
-h2, h3 {
-  font-size: 16px;
-}
-
 p > img {
   width: auto;
   max-width: 100%;
@@ -198,38 +92,10 @@ a {
   }
 }
 
-/* Rules for horizontal lines */
-
-hr {
-  border: none;
-  background-color: #ccc;
-  color: #ccc;
-  height: 1px;
-}
-
-/* General styles for tables */
-
-table {
-  width: 100%;
-  margin-bottom: $lineheight;
-  th, td {
-    text-align: left;
-    padding: $lineheight/4;
-    line-height: $lineheight;
-  }
-  th {
-    font-weight: 600;
-    vertical-align: top;
-  }
-  td {
-    vertical-align: middle;
-  }
-}
-
 /* Utility for de-emphasizing content */
 
 .deemphasize {
-  color: #999;
+  color: $darkgrey;
   a {
     color: $blue;
   }
@@ -255,7 +121,7 @@ header {
   z-index: 1001;
   font-size: 14px;
 
-  h1, nav, nav > ul, nav > ul > li, .dropdown {
+  h1, nav, nav > ul, nav > ul > li {
     display: inline-block;
   }
 
@@ -280,73 +146,55 @@ header {
   }
 
   h1 {
+    font-size: 18px;
+    font-weight: 600;
+    line-height: 1.2;
     margin: 0;
     padding-top: 15px;
 
     a {
       color: #000;
     }
-  }
-}
-
-nav.primary {
-  > ul {
-    $border: 1px solid $green;
-
-    border: $border;
-    border-radius: $border-radius;
 
-    > li {
-      border-right: $border;
-      float: left;
-      &:last-child {
-        border-right: 0;
-      }
-      > a:hover { background: lighten($green, 30%); }
-      &.current > a:hover { background: $green; }
-      &.disabled > a:hover { background: lighten($green, 38%); }
-      &.dropdown {
-        > a.tab { border-right: 1px solid lighten($green, 30%); }
-        &.current > a.tab { border-right: 1px solid lighten($green, 10%); }
-      }
+    a:hover {
+      color: #000;
     }
   }
 
-  a.tab,
-  .dropdown-toggle {
-    display: inline-block;
-    font-weight: 500;
-    color: $green;
-    padding: 5px 15px;
+  .btn {
+    font-size: 14px;
   }
+}
 
-  .dropdown-toggle {
-    padding: 5px 6px;
-  }
 
-  .caret {
-    border-top-color: $green;
-    margin-top: 10px;
+nav.primary {
+  .btn-outline-primary {
+    @include button-outline-variant($green, $white);
   }
 
-  .disabled a {
-    color: #ccc;
-    cursor: default;
+  .disabled {
+    .btn-outline-primary {
+      color: $grey;
+      cursor: default;
+
+      .caret {
+        border-top-color: $grey;
+      }
 
-    .caret {
-      border-top-color: #ccc;
+      &:hover {
+        background-color: lighten($green, 30%);
+      }
     }
   }
 
-  > ul li.current {
+  // Small tweaks to the toggle to stop the primary colour showing through
+  // when the menu is shown
+  .show > .btn-outline-primary.dropdown-toggle {
     background-color: $green;
+    border-color: $green;
 
-    .tab {
-      color: #fff;
-    }
-
-    .caret {
-      border-top-color: #fff;
+    &:focus {
+      box-shadow: 0 0 0 0.2rem fade-out($green, 0.5);
     }
   }
 }
@@ -355,102 +203,73 @@ nav.secondary {
   position: absolute;
   right: 0;
 
-  > ul {
-    vertical-align: middle;
-    a, .dropdown-toggle {
-      display: inline-block;
-      text-decoration: none;
-      color: $darkgrey;
-      padding: 5px;
-
-      &:hover { color: darken($darkgrey, 25%); }
-    }
+  .nav-link {
+    padding: 0.2rem;
+    color: $darkgrey;
   }
 
   > ul li.current a {
     color: darken($darkgrey, 25%);
   }
 
-  .user-menu {
-    $border: 1px solid $grey;
-    border: $border;
-    border-radius: $border-radius;
-    margin-left: 10px;
-
-    > li {
-      border-right: $border;
-      float: left;
-      &:last-child {
-        border-right: 0;
-
-        > a {
-          border-radius: 0 $border-radius $border-radius 0;
-        }
-      }
-      &:first-child > a { border-radius: $border-radius 0 0 $border-radius; }
-      &:hover a { background: lighten($darkgrey, 30%); }
+  .login-menu {
+    .btn-outline-secondary {
+      @include button-outline-variant($darkgrey);
     }
+  }
 
-    a {
-      padding: 5px 15px;
-
+  .user-menu {
+    .btn-outline-secondary {
+      @include button-outline-variant($darkgrey, $darkgrey, white, $darkgrey);
+      border-color: $grey;
+      &:hover {
+        border-color: $grey;
+      }
+      &:focus {
+        background-color: white;
+        box-shadow: none;
+      }
     }
-
-    &.logged-in > a {
-      padding: 0;
-      > .user-button {
-        line-height: 1.8;
-        padding: 5px 10px 3px 6px;
-        display: inline-block;
-        color: $darkgrey;
+    &.show .btn-outline-secondary {
+      background-color: white;
+      &:focus {
+        box-shadow: none;
       }
-      &:hover > .user-button { color: darken($darkgrey, 5%); }
     }
   }
 
-  .caret {
-    border-top-color: $grey;
-    margin-top: 9px;
-  }
-
   img.user_thumbnail_tiny {
     border: 0;
-    vertical-align: top;
-    margin-top: 0px;
-    margin: 4px 0 0 4px;
-    border-radius: 2px;
+    border-radius: 3px;
   }
 
   #inboxanchor {
     display: inline-block;
-    vertical-align: top;
     height: 25px;
     margin: 3px 0 3px 3px;
     background-color: lighten($grey, 10%);
     line-height: 20px;
-    border-radius: 2;
+    border-radius: 3;
   }
 
   .dropdown-menu {
-    left: auto;
-    right: -1px;
-    border-radius: 3px 0 3px 3px;
-
     .count-number {
-      float: right;
-      padding: 0 5px;
-      margin: 0;
+      font-size: 14px;
+    }
+  }
+}
+
+nav.primary, nav.secondary {
+  .dropdown-item {
+    &:hover, &:active {
+      background-color: $green;
+      color: white;
     }
   }
 }
 
 #compact-secondary-nav {
   display: none;
-  ul li a {
-    width: 100%;
-    color: #333;
-    &:hover { color: #fff; }
-  }
 }
 
 body.compact {
@@ -505,7 +324,7 @@ body.compact {
   }
 
   &.active {
-    background-color: #9ed485;
+    background-color: $vibrant-green;
   }
 
   .icon {
@@ -552,7 +371,6 @@ body.compact {
     float: left;
     width: $sidebarWidth;
     background: #fff;
-    font-size: 12px;
 
     #sidebar_loader {
       display: none;
@@ -567,12 +385,13 @@ body.compact {
 
     h2 {
       padding: $lineheight $lineheight $lineheight/2;
+      font-size: 1.5rem;
     }
 
     h3, h4 {
       margin-top: $lineheight;
       margin-bottom: $lineheight/2;
-      font-size: 13px;
+      font-size: 1.25rem;
     }
 
     .close-wrap {
@@ -692,7 +511,7 @@ body.compact {
     overflow: auto;
 
     .section {
-      border-bottom: 1px solid #DDD;
+      border-bottom: 1px solid $grey;
       padding: 10px 20px;
     }
 
@@ -702,14 +521,14 @@ body.compact {
       font-size:20px;
       line-height:10px;
       color:#222;
-      border:1px solid #ddd;
+      border:1px solid $grey;
     }
 
     .tooltip {
       opacity: 1;
-      border: 1px solid #ccc;
+      border: 1px solid $grey;
       .tooltip-arrow {
-        border-top-color: #ccc;
+        border-top-color: $grey;
       }
     }
   }
@@ -768,7 +587,7 @@ body.compact {
       font-size: 13px;
       margin-bottom: 8px;
     }
-    li.disabled { color: #999; }
+    li.disabled { color: $darkgrey; }
   }
 }
 
@@ -872,7 +691,7 @@ body.compact {
   position: relative;
   padding: $lineheight/2 $lineheight;
   // background: $offwhite;
-  // border-bottom: 1px solid #ccc;
+  // border-bottom: 1px solid $grey;
   > .close {
     float: right;
     margin-top: 2px;
@@ -935,11 +754,12 @@ header .search_forms,
 
   input[type=text].overflow {
     border-right: none;
+    border-radius: 3px 0px 0px 3px;
   }
 
   input:focus {
     outline: none;
-    box-shadow: 0px 0px 7px #9ED485;
+    box-shadow: 0px 0px 7px $vibrant-green;
   }
 
   input[type=submit].float {
@@ -1034,18 +854,13 @@ header .search_forms,
     margin-left: auto;
     margin-right: auto;
   }
-  td {
-    padding: 0 $lineheight/4 $lineheight/4 $lineheight/4;
-  }
 }
 
 /* Rules for search sidebar */
 
 #sidebar .search_results_entry {
   ul li {
-    border-bottom: $keyline;
     cursor: pointer;
-    &:first-child { border-top: $keyline; }
     &.selected { background: $list-highlight; }
   }
 
@@ -1066,6 +881,7 @@ header .search_forms,
     padding: 5px 20px 10px 15px;
     width: 100%;
     border-collapse: separate;
+    border-spacing: 0;
 }
 
 div.direction {
@@ -1085,10 +901,10 @@ p#routing_summary {
 td.instruction, td.distance {
     padding-top: $lineheight/5;
     padding-bottom: $lineheight/5;
-    border-bottom: 1px solid #DDD;
+    border-bottom: 1px solid $grey;
 }
 td.distance {
-    color: #BBB;
+    color: $darkgrey;
     text-align: right;
     font-size: x-small;
 }
@@ -1118,24 +934,15 @@ tr.turn:hover {
 
 #sidebar .changesets {
   li {
-    padding: 15px 20px;
-    border-bottom: 1px solid #ddd;
     cursor: pointer;
 
     &.selected { background: $list-highlight; }
     /* color is derived from changeset bbox fillColor in history.js */
   }
 
-  h4 {
-    margin: 0;
-    a {
-      color: #000;
-    }
-  }
-
   .comments {
     float: right;
-    color: #999;
+    color: $darkgrey;
   }
 
   .comments-0 {
@@ -1153,7 +960,7 @@ tr.turn:hover {
 #sidebar_content {
   .browse-section {
     padding: $lineheight/2 $lineheight;
-    border-bottom: 1px solid #ddd;
+    border-bottom: 1px solid $grey;
 
     h4:first-child {
       margin-top: 0;
@@ -1168,45 +975,42 @@ tr.turn:hover {
   .paginate {
     float: right;
     padding: 1px 6px;
-    border: 1px solid #eee;
+    border: 1px solid $lightgrey;
     border-radius: 3px;
   }
 
-  .paginate ul {
-    padding-left: 20px;
-  }
-
   .browse-field {
     margin-bottom: 10px;
 
     h4 {
       padding: 5px 0 5px 10px;
       font-size: 12px;
-      border: 1px solid #CCC;
+      border: 1px solid $grey;
       border-radius: 4px 4px 0 0;
-      background-color: #F6F6F6;
+      background-color: $offwhite;
     }
 
     p {
       padding: 7px 10px;
       font-size: 12px;
       background-color: #FFF;
-      border: 1px solid #CCC;
+      border: 1px solid $grey;
       border-top: 0;
       border-radius: 0 0 4px 4px;
     }
   }
 
   .browse-tag-list {
-    background-color: #F6F6F6;
-    border: 1px solid #ddd;
+    background-color: $offwhite;
+    border: 1px solid $grey;
     border-radius: 3px;
-    font-size: 12px;
     table-layout: fixed;
     border-collapse: separate;
+    border-spacing: 0;
+    width: 100%;
 
     th, td {
-      border-bottom: 1px solid #ddd;
+      border-bottom: 1px solid $grey;
     }
 
     tr:last-child th, tr:last-child td {
@@ -1223,20 +1027,20 @@ tr.turn:hover {
 
     .browse-tag-k {
       font-weight: 500;
-      background-color: #F6F6F6;
+      background-color: $offwhite;
     }
 
     .browse-tag-v {
-      border-left: 1px solid #ddd;
+      border-left: 1px solid $grey;
       background-color: #fff;
     }
 
     .colour-preview-box {
       float: right;
-      width: 12px;
-      height: 12px;
+      width: 14px;
+      height: 14px;
       margin: 4px 0px;
-      border: 1px solid rgba(0, 0, 0, .1); 
+      border: 1px solid rgba(0, 0, 0, .1);
       // add color via inline css on element: background-color: <tag value>;
     }
   }
@@ -1287,17 +1091,14 @@ tr.turn:hover {
 
   .query-results {
     display: none;
+    padding-bottom: $lineheight/2;
 
     h3 {
-      padding: $lineheight $lineheight $lineheight/2;
-      margin: 0;
+      padding: 0 $lineheight;
     }
 
     ul {
       li {
-        padding: 15px 20px;
-        border-bottom: 1px solid #ddd;
-
         &.query-result {
           cursor: pointer;
         }
@@ -1330,8 +1131,8 @@ tr.turn:hover {
   }
 
   .export_boxy {
-    background: #eee;
-    border: 1px solid #ccc;
+    background: $lightgrey;
+    border: 1px solid $grey;
     border-radius: 3px;
 
     #maxlat { margin-top: -1px; }
@@ -1393,8 +1194,6 @@ tr.turn:hover {
 
 .content-heading {
   background: $lightgrey;
-
-  h1 { font-size: 22px; }
 }
 
 .content-body {
@@ -1416,19 +1215,6 @@ tr.turn:hover {
 
 /* Overrides for pages that use new layout conventions */
 
-.users-new,
-.users-create,
-.users-terms {
-  .content-body .content-inner {
-    padding: 0;
-
-    .message {
-      margin-top: 80px;
-      padding: 20px;
-    }
-  }
-}
-
 .users-new,
 .users-create,
 .users-terms,
@@ -1488,56 +1274,11 @@ tr.turn:hover {
   position: relative;
   width: 45%;
   height: 400px;
-  border: 1px solid #ccc;
+  border: 1px solid $grey;
   margin-bottom: $lineheight;
   float: right;
 }
 
-/* Rules for the trace list shown by the traces tab etc */
-
-#trace_list {
-  font-size: $lineheight/2;
-  border-width: 0px;
-  text-align: right;
-
-  .trace_summary {
-    font-size: 12px;
-    color: gray;
-  }
-
-  .trace_pending {
-    color: red;
-  }
-
-  .trace_public {
-    color: green;
-  }
-
-  .trace_identifiable {
-    color: green;
-  }
-
-  .trace_trackable {
-    color: red;
-  }
-
-  .trace_private {
-    color: red;
-  }
-}
-
-/* Rules for the trace view */
-
-.trace-show {
-  .trace_pending {
-    color: red;
-  }
-
-  .geo {
-    display: inline;
-  }
-}
-
 /* Rules for the new trace form */
 
 #new_trace {
@@ -1551,7 +1292,7 @@ tr.turn:hover {
 /* Rules for the edit trace form */
 
 .edit_trace {
-  .form-row p {
+  .standard-form-row p {
     margin-bottom: 0px;
   }
 
@@ -1590,7 +1331,7 @@ tr.turn:hover {
 
 .activity-block {
   clear: left;
-  border-bottom: 1px solid #ccc;
+  border-bottom: 1px solid $grey;
   padding-bottom: $lineheight;
   float: left;
   h3 {
@@ -1608,10 +1349,6 @@ tr.turn:hover {
   margin-bottom: 0;
 }
 
-#friends-container .contact-activity ul {
-  margin-left: 70px;
-}
-
 .users-show {
   // Silly exception; remove when user page is redesigned.
   .content-inner {
@@ -1651,7 +1388,6 @@ tr.turn:hover {
 /* Rules for the user list */
 
 #user_list {
-  font-size: $lineheight/2;
   width: 100%;
 
   tr {
@@ -1675,7 +1411,7 @@ tr.turn:hover {
   position: relative;
   padding-top: $lineheight;
   padding-bottom: $lineheight/2;
-  border-top: 1px solid #ccc;
+  border-top: 1px solid $grey;
 
   &:first-of-type {
     margin-top: $lineheight/2;
@@ -1691,7 +1427,6 @@ tr.turn:hover {
     h2 {
       margin-top: 0;
       margin-bottom: $lineheight/2;
-      font-size: 24px;
     }
   }
 
@@ -1716,12 +1451,12 @@ tr.turn:hover {
     position: relative;
     width: 90%;
     height: 400px;
-    border: 1px solid #ccc;
+    border: 1px solid $grey;
     display: none;
     margin-bottom: $lineheight;
   }
   #newcomment {
-    border-top: 1px solid #ccc;
+    border-top: 1px solid $grey;
     padding-top: $lineheight;
     margin-top: $lineheight/2;
   }
@@ -1729,13 +1464,13 @@ tr.turn:hover {
     max-width: 740px;
   }
   .diary-comment {
-    border-top: 1px dashed #ccc;
+    border-top: 1px dashed $grey;
     padding-top: $lineheight/2;
     padding-bottom: $lineheight/2;
     &:first-child {
       margin-top: $lineheight/2;
       padding-top: $lineheight;
-      border-top: 1px solid #ccc;
+      border-top: 1px solid $grey;
     }
     &.deemphasize {
       background-color: #fee;
@@ -1776,7 +1511,7 @@ tr.turn:hover {
 
 .users-terms {
   .legale {
-    border: 1px solid #ccc;
+    border: 1px solid $grey;
     padding: $lineheight;
     margin-bottom: $lineheight;
     overflow: auto;
@@ -1805,7 +1540,7 @@ tr.turn:hover {
   position: relative;
   width: 500px;
   height: 400px;
-  border: 1px solid #ccc;
+  border: 1px solid $grey;
 }
 
 #accountForm .user_image {
@@ -1856,43 +1591,21 @@ tr.turn:hover {
 /* Rules for messages pages */
 
 .messages {
-  width: 100%;
-  border: 1px solid #ddd;
-
   input[type="submit"] {
     margin: auto;
   }
-  tbody tr {
-    border-top: 1px solid #ccc;
-  }
 
   .inbox-row {
-    background: #f8f8ff;
+    background: $offwhite;
   }
 
   .inbox-row-unread {
-    background:#CBEEA7;
+    background: #CBEEA7;
   }
 
   .right {
     float: right;
   }
-
-  tr td,
-  tr th {
-    padding: $lineheight/4;
-  }
-  p:last-child,
-  h2:last-child,
-  h3:last-child,
-  ol:last-child,
-  ul:last-child {
-    margin-bottom:0;
-  }
-  tr td {
-    height: 30px;
-    border-right: 1px solid $lightgrey;
-  }
 }
 
 .inbox-row .inbox-mark-read {
@@ -1902,7 +1615,7 @@ tr.turn:hover {
 .info-line {
   margin-bottom: $lineheight;
   padding: $lineheight/4 0px 4px 0px;
-  border-bottom: 1px solid #ccc;
+  border-bottom: 1px solid $grey;
 
   form, form div {
     display: inline;
@@ -2011,15 +1724,6 @@ tr.turn:hover {
     margin-bottom: 0px;
     padding: $lineheight/4;
   }
-
-  ul {
-    padding-left: $lineheight;
-
-    li {
-      font-size: 12px;
-      list-style: disc;
-    }
-  }
 }
 
 /* Rules for forms */
@@ -2051,11 +1755,11 @@ tr.turn:hover {
     padding-top: $lineheight;
     border-top: 1px solid $lightgrey;
   }
-  .horizontal-list .form-row {
+  .horizontal-list .standard-form-row {
     float: left;
     padding-right: 10px;
   }
-  .form-row {
+  .standard-form-row {
     margin-bottom: $lineheight/2;
   }
   .form-list {
@@ -2063,7 +1767,8 @@ tr.turn:hover {
   }
   .form-list li {
     margin-bottom: 5px;
-   }
+    list-style-type: none;
+  }
   input[type="checkbox"],
   input[type="radio"] {
     float: left;
@@ -2091,7 +1796,8 @@ input[type="password"],
 textarea {
   color: #222;
   background-color: #fff;
-  border: 1px solid #ccc;
+  border: 1px solid $grey;
+  border-radius: 3px;
   padding: 2px 5px;
   margin: 0;
   width: 200px;
@@ -2104,6 +1810,8 @@ textarea {
 textarea {
   padding: 5px;
   width: 100%;
+  min-height: 50px;
+  resize: vertical;
 }
 
 /* Rules for user images */
@@ -2111,7 +1819,7 @@ textarea {
 img.user_image {
   max-width: 100px;
   max-height: 100px;
-  border: 1px solid #ccc;
+  border: 1px solid $grey;
   margin-bottom: $lineheight;
   float: left;
   margin-right: $lineheight;
@@ -2120,7 +1828,7 @@ img.user_image {
 img.user_thumbnail {
   max-width: 50px;
   max-height: 50px;
-  border: 1px solid #ccc;
+  border: 1px solid $grey;
   margin-right: $lineheight;
 }
 
@@ -2129,7 +1837,7 @@ img.user_thumbnail_tiny {
   height: auto;
   max-width: 25px;
   max-height: 25px;
-  border: 1px solid #ccc;
+  border: 1px solid $grey;
 }
 
 /* Rules for geo microformats */
@@ -2138,19 +1846,13 @@ abbr.geo {
   border-bottom: none;
 }
 
-/* Rules for RSS buttons */
-
-.rsssmall {
-  position: relative;
-  top: 3px;
-}
-
 /* General styles for action lists / subnavs / pager navs */
 
 ul.secondary-actions {
   font-style: normal;
   margin-bottom: 0;
   margin-left: 0;
+  padding: 0;
   &.pager {
     display: inline-block;
     margin-right: 60px;
@@ -2159,7 +1861,7 @@ ul.secondary-actions {
     display: block;
     float: left;
     list-style: none;
-    border-left: 1px solid #ccc;
+    border-left: 1px solid $grey;
     padding-left: $lineheight/2;
     margin-right: $lineheight/2;
     &:first-child {
@@ -2291,16 +1993,11 @@ a.button {
   }
 }
 
-/* Rules for doing distinct colour of alternate table rows */
-
-.table0,
-.item0 {
-  background: $offwhite;
-}
+/* Customise the background colour of striped tables */
 
-.table1,
-.item1 {
-  background: #fff;
+.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 */
@@ -2314,33 +2011,15 @@ a.button {
 
 .richtext,
 .prose {
-  h1, h2 {
-    padding-bottom: $lineheight/2;
-    border-bottom: 1px dashed #cccccc;
-    margin-bottom: $lineheight/2;
-  }
-
-  h1 {
-    font-size: 24px;
-  }
-
-  h2 {
-    font-size: 18px;
-  }
-
-  h3 {
-    font-size: $typeheight;
-  }
-
   code {
     font-size: 13px;
-    background: #e8e8e8;
+    background: $lightgrey;
     padding: 2px 3px;
   }
 
   pre {
     font-size: 13px;
-    background: #e8e8e8;
+    background: $lightgrey;
     padding: 2px 3px;
     white-space: pre-wrap;
 
@@ -2361,21 +2040,7 @@ a.button {
     border-left: $lineheight solid $offwhite;
     padding-left: $lineheight;
     margin: 0;
-    color: #7E7E7E;
-  }
-
-  ul, ol {
-    padding-left: $lineheight;
-    margin-bottom: $lineheight;
-    margin-left: $lineheight;
-  }
-
-  ul > li {
-    list-style: disc;
-  }
-
-  ol > li {
-    list-style: decimal;
+    color: $darkgrey;
   }
 }
 
@@ -2434,7 +2099,7 @@ input.richtext_title[type="text"] {
     display: inline-block;
     vertical-align: top;
     margin-left: 15px;
-    background-color: #f8f8ff;
+    background-color: $offwhite;
     padding: $lineheight/2;
     width: 220px;
 
@@ -2443,7 +2108,7 @@ input.richtext_title[type="text"] {
     }
 
     h4.heading, li {
-      border-bottom: 1px solid #ccc;
+      border-bottom: 1px solid $grey;
       margin-bottom: $lineheight/4;
       padding-bottom: $lineheight/4;
     }
@@ -2472,15 +2137,7 @@ input.richtext_title[type="text"] {
 
 .note_list {
   tr.creator {
-    background-color: #eeeeee;
-  }
-
-  td {
-    padding: 3px;
-  }
-
-  p {
-    margin-bottom: 0px;
+    background-color: $offwhite;
   }
 }
 
@@ -2491,131 +2148,6 @@ input.richtext_title[type="text"] {
   height: 100%;
 }
 
-/* Rules for dropdown menus */
-
-.dropdown {
-  position: relative;
-}
-
-.dropdown-toggle {
-  *margin-bottom: -3px;
-}
-
-.dropdown-toggle:active,
-.open .dropdown-toggle {
-  outline: 0;
-}
-
-.caret {
-  display: inline-block;
-  width: 0;
-  height: 0;
-  vertical-align: top;
-  border-top: 4px solid #000000;
-  border-right: 4px solid transparent;
-  border-left: 4px solid transparent;
-  content: "";
-}
-
-.dropdown .caret {
-  margin-top: 8px;
-  margin-left: 2px;
-}
-
-.dropdown-menu {
-  position: absolute;
-  top: 100%;
-  left: -1px;
-  z-index: 1000;
-  display: none;
-  float: left;
-  min-width: 160px;
-  padding: 5px 0;
-  margin: 0;
-  list-style: none;
-  background-color: #ffffff;
-  border: 1px solid #ccc;
-  border-radius: 0 3px 3px;
-  *border-right-width: 2px;
-  *border-bottom-width: 2px;
-  box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2);
-  background-clip: padding-box;
-}
-
-.dropdown-menu.pull-right {
-  right: 0;
-  left: auto;
-}
-
-.dropdown-menu .divider {
-  *width: 100%;
-  height: 1px;
-  margin: 9px 1px;
-  *margin: -5px 0 5px;
-  overflow: hidden;
-  background-color: #e5e5e5;
-  border-bottom: 1px solid #ffffff;
-}
-
-.dropdown-menu > li > a {
-  display: block;
-  padding: 3px 10px;
-  clear: both;
-  font-weight: normal;
-  line-height: 20px;
-  color: #333333;
-  white-space: nowrap;
-}
-
-.dropdown-menu > li > a:hover,
-.dropdown-menu > li > a:focus,
-.dropdown-submenu:hover > a,
-.dropdown-submenu:focus > a {
-  color: #ffffff;
-  text-decoration: none;
-  background-color: $green;
-}
-
-.dropdown-menu > .active > a,
-.dropdown-menu > .active > a:hover,
-.dropdown-menu > .active > a:focus {
-  color: #ffffff;
-  text-decoration: none;
-  background-color: $green;
-  outline: 0;
-}
-
-.dropdown-menu > .disabled > a,
-.dropdown-menu > .disabled > a:hover,
-.dropdown-menu > .disabled > a:focus {
-  color: #999999;
-}
-
-.dropdown-menu > .disabled > a:hover,
-.dropdown-menu > .disabled > a:focus {
-  text-decoration: none;
-  cursor: default;
-  background-color: transparent;
-  background-image: none;
-}
-
-.open {
-  *z-index: 1000;
-}
-
-.open > .dropdown-menu {
-  display: block;
-}
-
-.dropdown-backdrop {
-  position: fixed;
-  top: 0;
-  right: 0;
-  bottom: 0;
-  left: 0;
-  z-index: 990;
-}
-
 /* Rules for the "Welcome" page */
 .site-welcome, .site-fixthemap {
   .center {
@@ -2689,11 +2221,6 @@ input.richtext_title[type="text"] {
     text-decoration: none;
   }
 
-  .note-box {
-    margin-top: 20px;
-    background-color: $offwhite;
-  }
-
   .icon.note {
     background-color: #333;
     border-radius: 4px;
@@ -2701,60 +2228,18 @@ input.richtext_title[type="text"] {
 }
 
 .site-about #content {
-  //background-color: #000;
-  background-color: #eee;
+  background-color: $lightgrey;
   background-position: 50% 50%;
   background-repeat: no-repeat;
   background-size: cover;
   background-attachment: fixed;
 
-  .caption {
-    max-width: 200px;
-    font: 13px/20px Helvetica, Arial, sans-serif;
-    position: fixed;
-    text-align: right;
-    right: 20px;
-    bottom: 60px;
-    text-shadow: #000 0px 1px 5px;
-    color: #eee;
-    opacity: 0.8;
-    display: none;
-  }
-
-  .caption a {
-    color: white;
-    white-space: nowrap;
-    text-decoration: none;
-  }
-
-  a.next {
-    display: block;
-    position: fixed;
-    right: 10px;
-    bottom: 10px;
-    width: 40px;
-    height: 40px;
-    border-radius: 5px;
-    text-indent: -9999px;
-    overflow: hidden;
-    background: image-url('about/sprite.png') -120px 0px no-repeat;
-    background-color: #000;
-    background-color: rgba(0, 0, 0, 0.5);
-  }
 
   .content-inner {
     position: relative;
     color: #333;
     min-width: 320px;
     max-width: 640px;
-
-    .section {
-      margin-bottom: 30px;
-    }
-
-    .section:last-child {
-      margin-bottom: 0;
-    }
   }
 
   .text {
@@ -2776,7 +2261,7 @@ input.richtext_title[type="text"] {
       font-weight: 300;
       font-size: 34px;
       span {
-        color: #76c551;
+        color: $vibrant-green;
       }
     }
 
@@ -2790,7 +2275,7 @@ input.richtext_title[type="text"] {
       background-repeat: no-repeat;
       background-image: image-url('about/osm.png');
       background-size: cover;
-      background-color: #76c551;
+      background-color: $vibrant-green;
     }
 
     .byosm {
@@ -2804,7 +2289,7 @@ input.richtext_title[type="text"] {
       font: 500 20px/24px Helvetica, Arial, sans-serif;
       white-space: nowrap;
       color: #fff;
-      background: #76c551;
+      background: $vibrant-green;
     }
 
     .byosm span {
@@ -2814,10 +2299,6 @@ input.richtext_title[type="text"] {
     }
   }
 
-  h2 {
-    margin-bottom: 10px;
-  }
-
   .icon {
     width: 30px;
     height: 30px;
@@ -2861,57 +2342,6 @@ input.richtext_title[type="text"] {
 }
 
 .read-reports {
-  background: #eee;
+  background: $lightgrey;
   opacity: 0.7;
 }
-
-.report-related-block {
-  display:inline-block;
-}
-
-.report-block {
-  width:475px;
-  float:left;
-  margin-right:100px;
-}
-
-.related-reports {
-  width: 280px;
-  float: right;
-
-  ul {
-    padding-left: $lineheight;
-    margin-bottom: 0;
-
-    li {
-      list-style: disc;
-    }
-  }
-}
-
-.issue-comments {
-  width:475px;
-}
-
-.issues-list {
-  td:nth-child(2) {
-    white-space: nowrap;
-  }
-}
-
-.report-disclaimer {
-  background: #fff1f0;
-  color: #d85030;
-  border-color: rgba(216, 80, 48, 0.3);
-  padding: 10px 20px;
-  margin-bottom: $lineheight;
-
-  ul {
-    padding-left: $lineheight;
-    margin-bottom: 0;
-
-    li {
-      list-style: disc;
-    }
-  }
-}
index 1cae5ba2f557df0ee0236ab58063e4cd7c51cd4f..90fbd74b7d035c65530e1f66dd15427d5f751bc4 100644 (file)
@@ -2,10 +2,11 @@
 $lineheight: 20px;
 $typeheight: 14px;
 
-$offwhite: #f4f4ff;
+$offwhite: #f8f8ff;
 $blue: #7092FF;
 $lightblue: #B8C5F0;
 $green: #7ebc6f;
+$vibrant-green: #76c551;
 $grey: #CCC;
 $red: red;
 $lightgrey: #EEE;
index d074f68815b458fdced15e634aa99df68cbcd730..838c9198f38157eb32f5a80b408e31d7cb85c91a 100644 (file)
@@ -1,5 +1,4 @@
 /*
  *= require ltr/common
- *= require bootstrap
  *= require ltr/small
  */
index 18d3d7c5a568c8c2d657865c703ccec36e377545..91d9fa3edba8e40bf869e5291502acdad628441f 100644 (file)
@@ -1,5 +1,4 @@
 /*
  *= require rtl/common
- *= require bootstrap
  *= require rtl/small
  */
index 12e21c7d8b8e9f52b007d4b0655820604d94f426..83b112ce7a0241367564379cc3214a68ddd412ee 100644 (file)
@@ -71,22 +71,16 @@ body.small {
         }
       }
     }
+
+    .btn-group {
+      width: 100%;
+      padding: 10px;
+    }
   }
 
   nav.secondary {
-    border-bottom: 1px solid #eee;
-
     .user-menu {
-      display: block;
       width: 100%;
-      margin-left: 0;
-      > li {
-        width: 49%;
-        > a {
-          width: 100%;
-          text-align: center;
-        }
-      }
     }
   }
 
@@ -97,34 +91,7 @@ body.small {
   .compact-hide {
     display: inline-block;
   }
-
-  &.map-layout {
-    #sidebar, #map {
-      position: relative;
-      overflow-x: hidden;
-      width: 100%;
-      height: 50%;
-    }
-
-    .overlay-sidebar {
-      #sidebar {
-        position: absolute;
-        width: 300px;
-        height: auto;
-        overflow: hidden;
-      }
-
-      #map {
-        height: 100%;
-      }
-    }
-
-    #map-ui {
-      z-index: 9999;
-      width: 100%;
-      overflow-y: scroll;
-    }
-  }
+  
 
   .overlay-sidebar #sidebar .welcome.visible {
     display: none;
@@ -182,21 +149,38 @@ body.small {
     top: auto;
   }
 
-  /* Rules for the sign-up page */
+  &.site-about #content .attr h1 {
+    font-size: 28px;
+  }
+
+}
 
-  &.user-new,
-  &.user-create {
-    .col6 {
+@media (max-width: 767.98px) {
+  body.map-layout {
+    #sidebar, #map {
+      position: relative;
+      overflow-x: hidden;
       width: 100%;
+      height: 50%;
     }
 
-    .aside {
-      display: none;
+    .overlay-sidebar {
+      #sidebar {
+        position: absolute;
+        width: 300px;
+        height: auto;
+        overflow: hidden;
+      }
+
+      #map {
+        height: 100%;
+      }
     }
-  }
 
-  &.site-about #content .attr h1 {
-    font-size: 28px;
+    #map-ui {
+      z-index: 9999;
+      width: 100%;
+      overflow-y: scroll;
+    }
   }
-
 }
index e5648989b97a9a02fa8573e7c5ba64f55c0ca153..db8982d43278237df950f2889b5078b84aef69f2 100644 (file)
@@ -226,14 +226,14 @@ module Api
       loaded_lang = "en"
 
       # Load English defaults
-      en = YAML.safe_load(File.open(Rails.root.join("config", "potlatch", "locales", "en.yml")))["en"]
+      en = YAML.safe_load(File.open(Rails.root.join("config/potlatch/locales/en.yml")))["en"]
 
       if lang == "en"
-        return [loaded_lang, en]
+        [loaded_lang, en]
       else
         # Use English as a fallback
         begin
-          other = YAML.safe_load(File.open(Rails.root.join("config", "potlatch", "locales", "#{lang}.yml")))[lang]
+          other = YAML.safe_load(File.open(Rails.root.join("config/potlatch/locales/#{lang}.yml")))[lang]
           loaded_lang = lang
         rescue StandardError
           other = en
@@ -241,7 +241,7 @@ module Api
 
         # We have to return a flat list and some of the keys won't be
         # translated (probably)
-        return [loaded_lang, en.merge(other)]
+        [loaded_lang, en.merge(other)]
       end
     end
 
@@ -876,7 +876,7 @@ module Api
     end
 
     def getlocales
-      @getlocales ||= Locale.list(Dir.glob(Rails.root.join("config", "potlatch", "locales", "*")).collect { |f| File.basename(f, ".yml") })
+      @getlocales ||= Locale.list(Dir.glob(Rails.root.join("config/potlatch/locales/*")).collect { |f| File.basename(f, ".yml") })
     end
 
     ##
@@ -906,21 +906,21 @@ module Api
     # Alternative SQL queries for getway/whichways
 
     def sql_find_ways_in_area(bbox)
-      sql = <<-SQL
-      SELECT DISTINCT current_ways.id AS wayid,current_ways.version AS version
-        FROM current_way_nodes
-      INNER JOIN current_nodes ON current_nodes.id=current_way_nodes.node_id
-      INNER JOIN current_ways  ON current_ways.id =current_way_nodes.id
-         WHERE current_nodes.visible=TRUE
-         AND current_ways.visible=TRUE
-         AND #{OSM.sql_for_area(bbox, 'current_nodes.')}
+      sql = <<~SQL
+        SELECT DISTINCT current_ways.id AS wayid,current_ways.version AS version
+          FROM current_way_nodes
+        INNER JOIN current_nodes ON current_nodes.id=current_way_nodes.node_id
+        INNER JOIN current_ways  ON current_ways.id =current_way_nodes.id
+           WHERE current_nodes.visible=TRUE
+           AND current_ways.visible=TRUE
+           AND #{OSM.sql_for_area(bbox, 'current_nodes.')}
       SQL
       ActiveRecord::Base.connection.select_all(sql).collect { |a| [a["wayid"].to_i, a["version"].to_i] }
     end
 
     def sql_find_pois_in_area(bbox)
       pois = []
-      sql = <<-SQL
+      sql = <<~SQL
         SELECT current_nodes.id,current_nodes.latitude*0.0000001 AS lat,current_nodes.longitude*0.0000001 AS lon,current_nodes.version
         FROM current_nodes
          LEFT OUTER JOIN current_way_nodes cwn ON cwn.node_id=current_nodes.id
@@ -941,7 +941,7 @@ module Api
     def sql_find_relations_in_area_and_ways(bbox, way_ids)
       # ** It would be more Potlatchy to get relations for nodes within ways
       #    during 'getway', not here
-      sql = <<-SQL
+      sql = <<~SQL
         SELECT DISTINCT cr.id AS relid,cr.version AS version
         FROM current_relations cr
         INNER JOIN current_relation_members crm ON crm.id=cr.id
@@ -949,13 +949,13 @@ module Api
          WHERE #{OSM.sql_for_area(bbox, 'cn.')}
       SQL
       unless way_ids.empty?
-        sql += <<-SQL
-         UNION
-          SELECT DISTINCT cr.id AS relid,cr.version AS version
-          FROM current_relations cr
-          INNER JOIN current_relation_members crm ON crm.id=cr.id
-           WHERE crm.member_type='Way'
-           AND crm.member_id IN (#{way_ids.join(',')})
+        sql += <<~SQL
+          UNION
+           SELECT DISTINCT cr.id AS relid,cr.version AS version
+           FROM current_relations cr
+           INNER JOIN current_relation_members crm ON crm.id=cr.id
+            WHERE crm.member_type='Way'
+            AND crm.member_id IN (#{way_ids.join(',')})
         SQL
       end
       ActiveRecord::Base.connection.select_all(sql).collect { |a| [a["relid"].to_i, a["version"].to_i] }
@@ -963,7 +963,7 @@ module Api
 
     def sql_get_nodes_in_way(wayid)
       points = []
-      sql = <<-SQL
+      sql = <<~SQL
         SELECT latitude*0.0000001 AS lat,longitude*0.0000001 AS lon,current_nodes.id,current_nodes.version
         FROM current_way_nodes,current_nodes
          WHERE current_way_nodes.id=#{wayid.to_i}
index 5f87324e0a63ddaa112e323005b94e49b306aca5..31601522877dce62e24ee81bbd91a385eededa8a 100644 (file)
@@ -61,50 +61,6 @@ module Api
       head :ok
     end
 
-    ##
-    # insert a (set of) points into a changeset bounding box. this can only
-    # increase the size of the bounding box. this is a hint that clients can
-    # set either before uploading a large number of changes, or changes that
-    # the client (but not the server) knows will affect areas further away.
-    def expand_bbox
-      # only allow POST requests, because although this method is
-      # idempotent, there is no "document" to PUT really...
-      assert_method :post
-
-      cs = Changeset.find(params[:id])
-      check_changeset_consistency(cs, current_user)
-
-      # keep an array of lons and lats
-      lon = []
-      lat = []
-
-      # the request is in pseudo-osm format... this is kind-of an
-      # abuse, maybe should change to some other format?
-      doc = XML::Parser.string(request.raw_post, :options => XML::Parser::Options::NOERROR).parse
-      doc.find("//osm/node").each do |n|
-        lon << n["lon"].to_f * GeoRecord::SCALE
-        lat << n["lat"].to_f * GeoRecord::SCALE
-      end
-
-      # add the existing bounding box to the lon-lat array
-      lon << cs.min_lon unless cs.min_lon.nil?
-      lat << cs.min_lat unless cs.min_lat.nil?
-      lon << cs.max_lon unless cs.max_lon.nil?
-      lat << cs.max_lat unless cs.max_lat.nil?
-
-      # collapse the arrays to minimum and maximum
-      cs.min_lon = lon.min.round
-      cs.min_lat = lat.min.round
-      cs.max_lon = lon.max.round
-      cs.max_lat = lat.max.round
-
-      # save the larger bounding box and return the changeset, which
-      # will include the bigger bounding box.
-      cs.save!
-      @changeset = cs
-      render "changeset"
-    end
-
     ##
     # Upload a diff in a single transaction.
     #
index b0998f7ebaa9aa6ae49f8aa125f0583f5353049e..1b5150537bccb8299a990d4e0c47b20598a7df53 100644 (file)
@@ -5,6 +5,8 @@ module Api
     before_action :check_api_readable
     around_action :api_call_handle_error, :api_call_timeout
 
+    before_action :set_request_formats
+
     # This is probably the most common call of all. It is used for getting the
     # OSM data for a specified bounding box, usually for editing. First the
     # bounding box (bbox) is checked to make sure that it is sane. All nodes
@@ -90,7 +92,10 @@ module Api
 
       response.headers["Content-Disposition"] = "attachment; filename=\"map.osm\""
       # Render the result
-      render :formats => [:xml]
+      respond_to do |format|
+        format.xml
+        format.json
+      end
     end
   end
 end
index 2962ce070ffe15ebf1cd98f43cb66adaa568c767..9204d96c0250c1901e0c031de81b6df25c6b9971 100644 (file)
@@ -13,6 +13,8 @@ module Api
     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]
+
     # Create a node from XML.
     def create
       assert_method :put
@@ -32,7 +34,10 @@ module Api
 
       if @node.visible
         # Render the result
-        render :formats => [:xml]
+        respond_to do |format|
+          format.xml
+          format.json
+        end
       else
         head :gone
       end
@@ -73,7 +78,10 @@ module Api
       @nodes = Node.find(ids)
 
       # Render the result
-      render :formats => [:xml]
+      respond_to do |format|
+        format.xml
+        format.json
+      end
     end
   end
 end
index fc9167eb382e8864aa2929ef4a69083eca7f010b..a73240e5fabc4f849780cbcb9e2dd42a26c30352 100644 (file)
@@ -294,11 +294,30 @@ module Api
           raise OSM::APIBadUserInput, "Date #{params[:to]} is in a wrong format"
         end
 
-        @notes = @notes.where(:created_at => from..to)
+        @notes = if params[:sort] == "updated_at"
+                   @notes.where(:updated_at => from..to)
+                 else
+                   @notes.where(:created_at => from..to)
+                 end
       end
 
+      # Choose the sort order
+      @notes = if params[:sort] == "created_at"
+                 if params[:order] == "oldest"
+                   @notes.order("created_at ASC")
+                 else
+                   @notes.order("created_at DESC")
+                 end
+               else
+                 if params[:order] == "oldest"
+                   @notes.order("updated_at ASC")
+                 else
+                   @notes.order("updated_at DESC")
+                 end
+               end
+
       # Find the notes we want to return
-      @notes = @notes.order("updated_at DESC").limit(result_limit).preload(:comments)
+      @notes = @notes.distinct.limit(result_limit).preload(:comments)
 
       # Render the result
       respond_to do |format|
index 862d14716f9b12e34c25e6629ada8d1b7e16b8e2..f8e42476f878c11aaa6aac42f81b5f5ddf7d5536 100644 (file)
@@ -16,6 +16,8 @@ module Api
     before_action :lookup_old_element, :except => [:history]
     before_action :lookup_old_element_versions, :only => [:history]
 
+    before_action :set_request_formats, :except => [:redact]
+
     def history
       # the .where() method used in the lookup_old_element_versions
       # call won't throw an error if no records are found, so we have
@@ -30,7 +32,10 @@ module Api
                end
 
       # Render the result
-      render :formats => [:xml]
+      respond_to do |format|
+        format.xml
+        format.json
+      end
     end
 
     def version
@@ -41,7 +46,10 @@ module Api
         response.last_modified = @old_element.timestamp
 
         # Render the result
-        render :formats => [:xml]
+        respond_to do |format|
+          format.xml
+          format.json
+        end
       end
     end
 
index ba0dd0c6bb99ae870211f19568b3ad8445dc434c..28e4a026bc47a820a5eb40faa7aa1c221e6364f6 100644 (file)
@@ -11,6 +11,8 @@ module Api
     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]
+
     def create
       assert_method :put
 
@@ -26,7 +28,10 @@ module Api
       response.last_modified = @relation.timestamp
       if @relation.visible
         # Render the result
-        render :formats => [:xml]
+        respond_to do |format|
+          format.xml
+          format.json
+        end
       else
         head :gone
       end
@@ -117,7 +122,10 @@ module Api
         @relations << relation
 
         # Render the result
-        render :formats => [:xml]
+        respond_to do |format|
+          format.xml
+          format.json
+        end
       else
         head :gone
       end
@@ -133,7 +141,10 @@ module Api
       @relations = Relation.find(ids)
 
       # Render the result
-      render :formats => [:xml]
+      respond_to do |format|
+        format.xml
+        format.json
+      end
     end
 
     def relations_for_way
@@ -160,7 +171,10 @@ module Api
       end
 
       # Render the result
-      render :formats => [:xml]
+      respond_to do |format|
+        format.xml
+        format.json
+      end
     end
   end
 end
index 39e0dff300d83078e3fffcb54b5b4d04d29a74cf..ddc5d7f430a9eff0c085bedaf40cf3a9d70a76b5 100644 (file)
@@ -9,34 +9,23 @@ module Api
 
     ##
     # return all the preferences as an XML document
-    def read
-      doc = OSM::API.new.get_xml_doc
+    def index
+      @user_preferences = current_user.preferences
 
-      prefs = current_user.preferences
-
-      el1 = XML::Node.new "preferences"
-
-      prefs.each do |pref|
-        el1 << pref.to_xml_node
-      end
-
-      doc.root << el1
-      render :xml => doc.to_s
+      render :formats => [:xml]
     end
 
     ##
     # return the value for a single preference
-    def read_one
+    def show
       pref = UserPreference.find([current_user.id, params[:preference_key]])
 
       render :plain => pref.v.to_s
     end
 
     # update the entire set of preferences
-    def update
-      old_preferences = current_user.preferences.each_with_object({}) do |preference, preferences|
-        preferences[preference.k] = preference
-      end
+    def update_all
+      old_preferences = current_user.preferences.index_by(&:k)
 
       new_preferences = {}
 
@@ -63,7 +52,7 @@ module Api
 
     ##
     # update the value of a single preference
-    def update_one
+    def update
       begin
         pref = UserPreference.find([current_user.id, params[:preference_key]])
       rescue ActiveRecord::RecordNotFound
@@ -80,7 +69,7 @@ module Api
 
     ##
     # delete a single preference
-    def delete_one
+    def destroy
       UserPreference.find([current_user.id, params[:preference_key]]).delete
 
       render :plain => ""
index 9af087d83a31fb06de92ce0a5718f9aa10225cd7..3b58f208b14e729a696a0f7e2e0ba0cd6ada631e 100644 (file)
@@ -11,6 +11,8 @@ module Api
     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]
+
     def create
       assert_method :put
 
@@ -28,7 +30,10 @@ module Api
 
       if @way.visible
         # Render the result
-        render :formats => [:xml]
+        respond_to do |format|
+          format.xml
+          format.json
+        end
       else
         head :gone
       end
@@ -75,7 +80,10 @@ module Api
         end
 
         # Render the result
-        render :formats => [:xml]
+        respond_to do |format|
+          format.xml
+          format.json
+        end
       else
         head :gone
       end
@@ -93,7 +101,10 @@ module Api
       @ways = Way.find(ids)
 
       # Render the result
-      render :formats => [:xml]
+      respond_to do |format|
+        format.xml
+        format.json
+      end
     end
 
     ##
@@ -106,7 +117,10 @@ module Api
       @ways = Way.where(:id => wayids, :visible => true)
 
       # Render the result
-      render :formats => [:xml]
+      respond_to do |format|
+        format.xml
+        format.json
+      end
     end
   end
 end
index df7cfe93be97d9ffa704db88d759be75523ffc32..478810dfc21d40448410662adf82179a2e0d60be 100644 (file)
@@ -3,6 +3,49 @@ class ApiController < ApplicationController
 
   private
 
+  ##
+  # Set allowed request formats if no explicit format has been
+  # requested via a URL suffix. Allowed formats are taken from
+  # any HTTP Accept header with XML as the default.
+  def set_request_formats
+    unless params[:format]
+      accept_header = request.headers["HTTP_ACCEPT"]
+
+      if accept_header
+        # Some clients (such asJOSM) send Accept headers which cannot be
+        # parse by Rails, for example:
+        #
+        #   Accept: text/html, image/gif, image/jpeg, *; q=.2, */*; q=.2
+        #
+        # where both "*" and ".2" as a quality do not adhere to the syntax
+        # described in RFC 7231, section 5.3.1, etc.
+        #
+        # As a workaround, and for back compatibility, default to XML format.
+        mimetypes = begin
+                      Mime::Type.parse(accept_header)
+                    rescue Mime::Type::InvalidMimeType
+                      Array(Mime[:xml])
+                    end
+
+        # Allow XML and JSON formats, and treat an all formats wildcard
+        # 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
+          elsif mime.symbol == :json then :json
+          elsif mime == "*/*" then :xml
+          end
+        end
+      else
+        # Default to XML if no accept header was sent - this includes
+        # the unit tests which don't set one by default
+        formats = Array(:xml)
+      end
+
+      request.formats = formats.compact
+    end
+  end
+
   def authorize(realm = "Web Password", errormessage = "Couldn't authenticate you")
     # make the current_user object from any auth sources we have
     setup_user_auth
@@ -12,7 +55,7 @@ class ApiController < ApplicationController
       # no auth, the user does not exist or the password was wrong
       response.headers["WWW-Authenticate"] = "Basic realm=\"#{realm}\""
       render :plain => errormessage, :status => :unauthorized
-      return false
+      false
     end
   end
 
index bee5e8169a4709d05f5d36ead2c6924a03f2db6a..f419460ee77bc97762ad6c9bc230d0718cb88c9b 100644 (file)
@@ -185,6 +185,8 @@ class ApplicationController < ActionController::Base
 
   def api_call_handle_error
     yield
+  rescue ActionController::UnknownFormat
+    head :not_acceptable
   rescue ActiveRecord::RecordNotFound => e
     head :not_found
   rescue LibXML::XML::Error, ArgumentError => e
index fc5b0ec80e37eeea2407028dfe517e068554ded3..50aa2a3c96f43838467568b2085c4aa2036fa059 100644 (file)
@@ -24,5 +24,9 @@ class ExportController < ApplicationController
     end
   end
 
-  def embed; end
+  def embed
+    append_content_security_policy_directives(
+      :frame_ancestors => %w[*]
+    )
+  end
 end
index 61f466f626a0ef94e27fc908e0b670d0f6aaac82..d136e7230a4b7baf3ccd6eebfda383843598fdc2 100644 (file)
@@ -80,6 +80,8 @@ class IssuesController < ApplicationController
   private
 
   def find_issue
-    @issue = Issue.find(params[:id])
+    @issue = Issue.visible_to(current_user).find(params[:id])
+  rescue ActiveRecord::RecordNotFound
+    head :not_found
   end
 end
index 57ac075010fd837609a4170ab96883c78ff83c3d..52fea613366684808f424ff04b094353ef7e996d 100644 (file)
@@ -75,28 +75,36 @@ class SiteController < ApplicationController
         :plugin_types => %w[application/x-shockwave-flash],
         :script_src => %w['unsafe-inline']
       )
+    elsif %w[id].include?(editor)
+      append_content_security_policy_directives(
+        :frame_src => %w[blob:]
+      )
     end
 
-    if params[:node]
-      bbox = Node.find(params[:node]).bbox.to_unscaled
-      @lat = bbox.centre_lat
-      @lon = bbox.centre_lon
-      @zoom = 18
-    elsif params[:way]
-      bbox = Way.find(params[:way]).bbox.to_unscaled
-      @lat = bbox.centre_lat
-      @lon = bbox.centre_lon
-      @zoom = 17
-    elsif params[:note]
-      note = Note.find(params[:note])
-      @lat = note.lat
-      @lon = note.lon
-      @zoom = 17
-    elsif params[:gpx] && current_user
-      trace = Trace.visible_to(current_user).find(params[:gpx])
-      @lat = trace.latitude
-      @lon = trace.longitude
-      @zoom = 16
+    begin
+      if params[:node]
+        bbox = Node.visible.find(params[:node]).bbox.to_unscaled
+        @lat = bbox.centre_lat
+        @lon = bbox.centre_lon
+        @zoom = 18
+      elsif params[:way]
+        bbox = Way.visible.find(params[:way]).bbox.to_unscaled
+        @lat = bbox.centre_lat
+        @lon = bbox.centre_lon
+        @zoom = 17
+      elsif params[:note]
+        note = Note.visible.find(params[:note])
+        @lat = note.lat
+        @lon = note.lon
+        @zoom = 17
+      elsif params[:gpx] && current_user
+        trace = Trace.visible_to(current_user).find(params[:gpx])
+        @lat = trace.latitude
+        @lon = trace.longitude
+        @zoom = 16
+      end
+    rescue ActiveRecord::RecordNotFound
+      # don't try and derive a location from a missing/deleted object
     end
   end
 
@@ -108,7 +116,9 @@ class SiteController < ApplicationController
 
   def help; end
 
-  def about; end
+  def about
+    @locale = params[:about_locale] || I18n.locale
+  end
 
   def export; end
 
index a0852d2ce2665959714c02c35c5cdc4c03504425..b800d305e03fc495f9bc9a7bdf5752a2ebd83bcf 100644 (file)
@@ -7,9 +7,9 @@ class TracesController < ApplicationController
 
   authorize_resource
 
-  before_action :check_database_writable, :only => [:new, :create, :edit, :delete]
+  before_action :check_database_writable, :only => [:new, :create, :edit, :destroy]
   before_action :offline_warning, :only => [:mine, :show]
-  before_action :offline_redirect, :only => [:new, :create, :edit, :delete, :data]
+  before_action :offline_redirect, :only => [:new, :create, :edit, :destroy, :data]
 
   # Counts and selects pages of GPX traces for various criteria (by user, tags, public etc.).
   #  target_user - if set, specifies the user to fetch traces for.  if not set will fetch all traces
@@ -184,7 +184,7 @@ class TracesController < ApplicationController
     head :not_found
   end
 
-  def delete
+  def destroy
     trace = Trace.find(params[:id])
 
     if !trace.visible?
index 345bae2616607dde518f25de11b968ae8c08380e..514b3f8ee73f41d8d2284886ac11c1dde1addf47 100644 (file)
@@ -269,7 +269,7 @@ class UsersController < ApplicationController
   def logout
     @title = t "users.logout.title"
 
-    if params[:session] == session.id
+    if request.post?
       if session[:token]
         token = UserToken.find_by(:token => session[:token])
         token&.destroy
@@ -376,7 +376,7 @@ class UsersController < ApplicationController
     @user = User.find_by(:display_name => params[:display_name])
 
     if @user &&
-       (@user.visible? || (current_user&.administrator?))
+       (@user.visible? || current_user&.administrator?)
       @title = @user.display_name
     else
       render_unknown_user params[:display_name]
index 0f2c862e6b60a212a21b541c7b76a6c921141cbe..79c6e6134a007843d0ca5e2b04d1138855dc47f6 100644 (file)
@@ -5,7 +5,7 @@ module ApplicationHelper
     if text.html_safe?
       Rinku.auto_link(text, :urls, tag_builder.tag_options(:rel => "nofollow")).html_safe
     else
-      Rinku.auto_link(text, :urls, tag_builder.tag_options(:rel => "nofollow"))
+      Rinku.auto_link(ERB::Util.h(text), :urls, tag_builder.tag_options(:rel => "nofollow")).html_safe
     end
   end
 
index bbf6f3cf72ea6a79436a9add4df38aeed80a110e..4f73eb9d7c8018f932471ac71b98e6f8f22645df 100644 (file)
@@ -21,6 +21,8 @@ module BrowseTagsHelper
       link_to h(wmc[:title]), wmc[:url], :title => t("browse.tag_details.wikimedia_commons_link", :page => wmc[:title])
     elsif url = wiki_link("tag", "#{key}=#{value}")
       link_to h(value), url, :title => t("browse.tag_details.wiki_link.tag", :key => key, :value => value)
+    elsif email = email_link(key, value)
+      link_to(h(email[:email]), email[:url], :title => t("browse.tag_details.email_link", :email => email[:email]))
     elsif phones = telephone_links(key, value)
       # similarly, telephone_links() returns an array of phone numbers
       phones = phones.map do |p|
@@ -123,6 +125,25 @@ module BrowseTagsHelper
     nil
   end
 
+  def email_link(_key, value)
+    # Does the value look like an email? eg "someone@domain.tld"
+
+    #  Uses Ruby built-in regexp to validate email.
+    #  This will not catch certain valid emails containing comments, whitespace characters,
+    #  and quoted strings.
+    #    (see: https://github.com/ruby/ruby/blob/master/lib/uri/mailto.rb)
+
+    # remove any leading and trailing whitespace
+    email = value.strip
+
+    if email.match?(URI::MailTo::EMAIL_REGEXP)
+      # add 'mailto:'' prefix
+      return { :email => email, :url => "mailto:#{email}" }
+    end
+
+    nil
+  end
+
   def telephone_links(_key, value)
     # Does it look like a global phone number? eg "+1 (234) 567-8901 "
     # or a list of alternate numbers separated by ;
diff --git a/app/mailers/application_mailer.rb b/app/mailers/application_mailer.rb
new file mode 100644 (file)
index 0000000..ead50cd
--- /dev/null
@@ -0,0 +1,2 @@
+class ApplicationMailer < ActionMailer::Base
+end
index 4509f10114bec03308da57e7744426389caf1a1b..c60dff84b9f0265cfc66137cb5cac3271529e324 100644 (file)
@@ -1,6 +1,8 @@
-class Notifier < ActionMailer::Base
+class Notifier < ApplicationMailer
   include ActionView::Helpers::AssetUrlHelper
 
+  self.delivery_job = ActionMailer::MailDeliveryJob
+
   default :from => Settings.email_from,
           :return_path => Settings.email_return_path,
           :auto_submitted => "auto-generated"
@@ -92,11 +94,12 @@ class Notifier < ActionMailer::Base
       @readurl = diary_entry_url(comment.diary_entry.user, comment.diary_entry, :anchor => "comment#{comment.id}")
       @commenturl = diary_entry_url(comment.diary_entry.user, comment.diary_entry, :anchor => "newcomment")
       @replyurl = new_message_url(comment.user, :message => { :title => "Re: #{comment.diary_entry.title}" })
-
       @author = @from_user
 
       attach_user_avatar(comment.user)
 
+      set_references("diary", comment.diary_entry)
+
       mail :from => from_address(comment.user.display_name, "c", comment.id, comment.digest, recipient.id),
            :to => recipient.email,
            :subject => I18n.t("notifier.diary_comment_notification.subject", :user => comment.user.display_name)
@@ -134,6 +137,8 @@ class Notifier < ActionMailer::Base
       @author = @commenter
       attach_user_avatar(comment.author)
 
+      set_references("note", comment.note)
+
       subject = if @owner
                   I18n.t("notifier.note_comment_notification.#{@event}.subject_own", :commenter => @commenter)
                 else
@@ -164,6 +169,8 @@ class Notifier < ActionMailer::Base
 
       attach_user_avatar(comment.author)
 
+      set_references("changeset", comment.changeset)
+
       mail :to => recipient.email, :subject => subject
     end
   end
@@ -175,7 +182,7 @@ class Notifier < ActionMailer::Base
   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"] = File.read(Rails.root.join("app/assets/images/osm_logo_30.png"))
   end
 
   def attach_user_avatar(user)
@@ -185,9 +192,9 @@ class Notifier < ActionMailer::Base
   def user_avatar_file(user)
     avatar = user&.avatar
     if avatar&.attached?
-      return avatar.variant(:resize => "50x50>").blob.download
+      avatar.variant(:resize => "50x50>").blob.download
     else
-      return File.read(Rails.root.join("app", "assets", "images", "avatar_small.png"))
+      File.read(Rails.root.join("app/assets/images/avatar_small.png"))
     end
   end
 
@@ -208,4 +215,12 @@ class Notifier < ActionMailer::Base
       Settings.email_from
     end
   end
+
+  def set_references(scope, reference_object)
+    ref = "osm-#{scope}-#{reference_object.id}@#{Settings.server_url}"
+
+    headers["X-Entity-Ref-ID"] = ref
+    headers["In-Reply-To"] = ref
+    headers["References"] = ref
+  end
 end
index 895ed61e4de8883db9940f25c056222f4d2b6965..06f754c2274ad801fe34fc6e7369867d56e4397c 100644 (file)
@@ -17,7 +17,7 @@
 #  index_acls_on_mx       (mx)
 #
 
-class Acl < ActiveRecord::Base
+class Acl < ApplicationRecord
   validates :k, :presence => true
 
   def self.match(address, options = {})
diff --git a/app/models/application_record.rb b/app/models/application_record.rb
new file mode 100644 (file)
index 0000000..10a4cba
--- /dev/null
@@ -0,0 +1,3 @@
+class ApplicationRecord < ActiveRecord::Base
+  self.abstract_class = true
+end
index 47f03c79544111eff476896c076d2cae2b6c31fb..990eae4077ae33b7b8968a9bd8542504432226c2 100644 (file)
@@ -25,7 +25,7 @@
 #  changesets_user_id_fkey  (user_id => users.id)
 #
 
-class Changeset < ActiveRecord::Base
+class Changeset < ApplicationRecord
   require "xml/libxml"
 
   belongs_to :user, :counter_cache => true
index 75b1a055b23dd1306a4469e4819d2cf531c2923d..ceb7d35597f75e816811214fbfa252eddd7470d0 100644 (file)
@@ -19,7 +19,7 @@
 #  changeset_comments_changeset_id_fkey  (changeset_id => changesets.id)
 #
 
-class ChangesetComment < ActiveRecord::Base
+class ChangesetComment < ApplicationRecord
   belongs_to :changeset
   belongs_to :author, :class_name => "User"
 
index 751029e03995c60835dc1d7eb0ab79a21fb48b3e..600ace4a6e498affc37cce96160289753fbd880e 100644 (file)
@@ -15,7 +15,7 @@
 #  changeset_tags_id_fkey  (changeset_id => changesets.id)
 #
 
-class ChangesetTag < ActiveRecord::Base
+class ChangesetTag < ApplicationRecord
   self.primary_keys = "changeset_id", "k"
 
   belongs_to :changeset
index ae95e2908a286793565915fd8f70577387df39c2..4e3dffb998aba0081a61dd56b145420f47a7f3df 100644 (file)
@@ -30,7 +30,7 @@
 #  client_applications_user_id_fkey  (user_id => users.id)
 #
 
-class ClientApplication < ActiveRecord::Base
+class ClientApplication < ApplicationRecord
   belongs_to :user
   has_many :tokens, :class_name => "OauthToken", :dependent => :delete_all
   has_many :access_tokens
@@ -39,9 +39,9 @@ class ClientApplication < ActiveRecord::Base
 
   validates :key, :presence => true, :uniqueness => true
   validates :name, :url, :secret, :presence => true
-  validates :url, :format => %r{\Ahttp(s?)://(\w+:{0,1}\w*@)?(\S+)(:[0-9]+)?(/|/([\w#!:.?+=&%@!\-/]))?}i
-  validates :support_url, :allow_blank => true, :format => %r{\Ahttp(s?)://(\w+:{0,1}\w*@)?(\S+)(:[0-9]+)?(/|/([\w#!:.?+=&%@!\-/]))?}i
-  validates :callback_url, :allow_blank => true, :format => %r{\A[a-z][a-z0-9.+-]*://(\w+:{0,1}\w*@)?(\S+)(:[0-9]+)?(/|/([\w#!:.?+=&%@!\-/]))?}i
+  validates :url, :format => /\A#{URI::DEFAULT_PARSER.make_regexp(%w[http https])}\z/
+  validates :support_url, :allow_blank => true, :format => /\A#{URI::DEFAULT_PARSER.make_regexp(%w[http https])}\z/
+  validates :callback_url, :allow_blank => true, :format => /\A#{URI::DEFAULT_PARSER.make_regexp}\z/
 
   before_validation :generate_keys, :on => :create
 
index 91533ece438f89fd9cf8440bfd5400d84ad9addf..447ee19df61050e83f229619db8968351498a62a 100644 (file)
@@ -12,6 +12,10 @@ module GeoRecord
     def to_s
       format("%.7f", self)
     end
+
+    def as_json(_)
+      format("%.7f", self).to_f
+    end
   end
 
   # This scaling factor is used to convert between the float lat/lon that is
index 4ae21be8881922e49c799ba0868e93ba0f5e77f5..05f5044c5db779b232da81bd6e3bbdf0ba31d5b5 100644 (file)
@@ -22,7 +22,7 @@
 #  diary_comments_user_id_fkey         (user_id => users.id)
 #
 
-class DiaryComment < ActiveRecord::Base
+class DiaryComment < ApplicationRecord
   belongs_to :user
   belongs_to :diary_entry
 
index 4affe8b597f1269e92839fa201ba9bc683ee93e7..4ff1eeb35e57b0e8e3890dee62e2804761634215 100644 (file)
@@ -26,7 +26,7 @@
 #  diary_entries_user_id_fkey        (user_id => users.id)
 #
 
-class DiaryEntry < ActiveRecord::Base
+class DiaryEntry < ApplicationRecord
   belongs_to :user, :counter_cache => true
   belongs_to :language, :foreign_key => "language_code"
 
index 6e9a103adafac0d1315ecd1e956ff32cfc0abc2e..ed6de79e45de20ff7fcbc648b845a9c0f12fe652 100644 (file)
@@ -15,7 +15,7 @@
 #  diary_entry_subscriptions_user_id_fkey         (user_id => users.id)
 #
 
-class DiaryEntrySubscription < ActiveRecord::Base
+class DiaryEntrySubscription < ApplicationRecord
   self.primary_keys = "user_id", "diary_entry_id"
 
   belongs_to :user
index 27b25aee1b31313674db1d1578701873c921f160..476821b47ccdb221b31b603cfa97352611425c86 100644 (file)
@@ -17,7 +17,7 @@
 #  friends_user_id_fkey         (user_id => users.id)
 #
 
-class Friendship < ActiveRecord::Base
+class Friendship < ApplicationRecord
   self.table_name = "friends"
 
   belongs_to :befriender, :class_name => "User", :foreign_key => :user_id
index f18581b5f059670f01a52836aca272b2d77265fb..14c5f8231d56b95761cea9010cdb434230974e5e 100644 (file)
@@ -30,7 +30,7 @@
 #  issues_updated_by_fkey        (updated_by => users.id)
 #
 
-class Issue < ActiveRecord::Base
+class Issue < ApplicationRecord
   belongs_to :reportable, :polymorphic => true
   belongs_to :reported_user, :class_name => "User", :foreign_key => :reported_user_id
   belongs_to :user_resolved, :class_name => "User", :foreign_key => :resolved_by
index 0841295e1760120335d2e16ed79f57b20a9a67c2..69aa8bde931b398ff18e134412ba3886a259849f 100644 (file)
@@ -20,7 +20,7 @@
 #  issue_comments_user_id_fkey   (user_id => users.id)
 #
 
-class IssueComment < ActiveRecord::Base
+class IssueComment < ApplicationRecord
   belongs_to :issue
   belongs_to :user
 
index bb1aa4bd26016758ad3d8b66d09a7527a72b8ad8..cdf85a52bc3530cb11dc0eb3e0a05ed29d7f7a56 100644 (file)
@@ -7,7 +7,7 @@
 #  native_name  :string
 #
 
-class Language < ActiveRecord::Base
+class Language < ApplicationRecord
   self.primary_key = "code"
 
   has_many :diary_entries, :foreign_key => "language"
index 4ab129e9184f6593012bacd98c3de504ca06662c..44b1d8745e6fdf8600b6ff307c789599f08c7262 100644 (file)
@@ -24,7 +24,7 @@
 #  messages_to_user_id_fkey    (to_user_id => users.id)
 #
 
-class Message < ActiveRecord::Base
+class Message < ApplicationRecord
   belongs_to :sender, :class_name => "User", :foreign_key => :from_user_id
   belongs_to :recipient, :class_name => "User", :foreign_key => :to_user_id
 
index c5df1f79e5959c3c3826b0685bf4fd0b2a71d2bd..4d48112fc6fa9361fcd268f0e17b92b2a3d14776 100644 (file)
@@ -21,7 +21,7 @@
 #  current_nodes_changeset_id_fkey  (changeset_id => changesets.id)
 #
 
-class Node < ActiveRecord::Base
+class Node < ApplicationRecord
   require "xml/libxml"
 
   include GeoRecord
@@ -200,28 +200,6 @@ class Node < ActiveRecord::Base
     save_with_history!
   end
 
-  def to_xml
-    doc = OSM::API.new.get_xml_doc
-    doc.root << to_xml_node
-    doc
-  end
-
-  def to_xml_node(changeset_cache = {}, user_display_name_cache = {})
-    el = XML::Node.new "node"
-    el["id"] = id.to_s
-
-    add_metadata_to_xml_node(el, self, changeset_cache, user_display_name_cache)
-
-    if visible?
-      el["lat"] = lat.to_s
-      el["lon"] = lon.to_s
-    end
-
-    add_tags_to_xml_node(el, node_tags)
-
-    el
-  end
-
   def tags_as_hash
     tags
   end
index 86404599b266de54e2896473cbb4abfdb0b10697..fa21b1ff6fdd60b2c813e7bd7979ee6b5a140837 100644 (file)
@@ -11,7 +11,7 @@
 #  current_node_tags_id_fkey  (node_id => current_nodes.id)
 #
 
-class NodeTag < ActiveRecord::Base
+class NodeTag < ApplicationRecord
   self.table_name = "current_node_tags"
   self.primary_keys = "node_id", "k"
 
index fa9d0b1ae6a5da75a25b02ddd6d204fe63267df8..ec57b770bbc008c923b2394e71f02e4d24549521 100644 (file)
@@ -18,7 +18,7 @@
 #  notes_updated_at_idx   (updated_at)
 #
 
-class Note < ActiveRecord::Base
+class Note < ApplicationRecord
   include GeoRecord
 
   has_many :comments, -> { left_joins(:author).where(:visible => true, :users => { :status => [nil, "active", "confirmed"] }).order(:created_at) }, :class_name => "NoteComment", :foreign_key => :note_id
index 448703ffa5f8d38471a7ecc2767f4ca41771af7b..9611cfe3af375861b865ec17582b0d278fae6c1f 100644 (file)
@@ -23,7 +23,7 @@
 #  note_comments_note_id_fkey    (note_id => notes.id)
 #
 
-class NoteComment < ActiveRecord::Base
+class NoteComment < ApplicationRecord
   belongs_to :note, :foreign_key => :note_id, :touch => true
   belongs_to :author, :class_name => "User", :foreign_key => :author_id
 
index 9d2773e8f346c5b8e6789c8a314020c7a39edd41..5f0920a96f1fd537456ff79ea0dd243a4883acda 100644 (file)
@@ -15,7 +15,7 @@
 
 # Simple store of nonces. The OAuth Spec requires that any given pair of nonce and timestamps are unique.
 # Thus you can use the same nonce with a different timestamp and viceversa.
-class OauthNonce < ActiveRecord::Base
+class OauthNonce < ApplicationRecord
   validates :timestamp, :presence => true
   validates :nonce, :presence => true, :uniqueness => { :scope => :timestamp }
 
index 5eeda48ba87ae7e13bb5ef923435f925c03bca6d..affdcdeb815b45b3c6d6ad32bf9c6526064ee45b 100644 (file)
@@ -35,7 +35,7 @@
 #  oauth_tokens_user_id_fkey                (user_id => users.id)
 #
 
-class OauthToken < ActiveRecord::Base
+class OauthToken < ApplicationRecord
   belongs_to :client_application
   belongs_to :user
 
index bdf8cb47ee32438ab875f7c5bc17d5b431fabef5..12099498e2de71edd681634fcbc64827a5e24390 100644 (file)
@@ -24,7 +24,7 @@
 #  nodes_redaction_id_fkey  (redaction_id => redactions.id)
 #
 
-class OldNode < ActiveRecord::Base
+class OldNode < ApplicationRecord
   include GeoRecord
   include ConsistencyValidations
   include ObjectMetadata
index a3e1c3aaf4a49d4a22d291ab942c7cc84b4e0f32..f2cba896cf031c7b8efbc14742e83977b197137c 100644 (file)
@@ -12,7 +12,7 @@
 #  node_tags_id_fkey  (node_id => nodes.node_id)
 #
 
-class OldNodeTag < ActiveRecord::Base
+class OldNodeTag < ApplicationRecord
   self.table_name = "node_tags"
   self.primary_keys = "node_id", "version", "k"
 
index 109f7d968da1eba835f1fbcd557048ce7cd8207b..c0025653fcaff9624279c37d13aa5abffc7ec0c5 100644 (file)
@@ -20,7 +20,7 @@
 #  relations_redaction_id_fkey  (redaction_id => redactions.id)
 #
 
-class OldRelation < ActiveRecord::Base
+class OldRelation < ApplicationRecord
   include ConsistencyValidations
   include ObjectMetadata
 
index f8d4a359f920cfca74818bb20c935b69deaf8af6..1714f3c859bc0df5d2af1d6f06bcd1151558db37 100644 (file)
@@ -18,7 +18,7 @@
 #  relation_members_id_fkey  (relation_id => relations.relation_id)
 #
 
-class OldRelationMember < ActiveRecord::Base
+class OldRelationMember < ApplicationRecord
   self.table_name = "relation_members"
   self.primary_keys = "relation_id", "version", "sequence_id"
 
index c674f708bc0214a097ccc27172a7bea0d7a2e488..d6e6e19c2dfb921a4f4621e22f9d83b08b5f86c1 100644 (file)
@@ -12,7 +12,7 @@
 #  relation_tags_id_fkey  (relation_id => relations.relation_id)
 #
 
-class OldRelationTag < ActiveRecord::Base
+class OldRelationTag < ApplicationRecord
   self.table_name = "relation_tags"
   self.primary_keys = "relation_id", "version", "k"
 
index 31e230c3869e2ee197f46a8de823834319536c5f..b515322e325cc1aed3face1f1f44166fa2f9b171 100644 (file)
@@ -20,7 +20,7 @@
 #  ways_redaction_id_fkey  (redaction_id => redactions.id)
 #
 
-class OldWay < ActiveRecord::Base
+class OldWay < ApplicationRecord
   include ConsistencyValidations
   include ObjectMetadata
 
index 836e76e47eb112e2764e6fc4c8876ab30cbdff17..d89227936aaf431b336f207b3aebcfe22538349a 100644 (file)
@@ -16,7 +16,7 @@
 #  way_nodes_id_fkey  (way_id => ways.way_id)
 #
 
-class OldWayNode < ActiveRecord::Base
+class OldWayNode < ApplicationRecord
   self.table_name = "way_nodes"
   self.primary_keys = "way_id", "version", "sequence_id"
 
index ae4ad605e70b1365f27d64c714b3085ec8f16603..90bf704b6af2c1a5be5eb7c4ea4107d488a16a85 100644 (file)
@@ -12,7 +12,7 @@
 #  way_tags_id_fkey  (way_id => ways.way_id)
 #
 
-class OldWayTag < ActiveRecord::Base
+class OldWayTag < ApplicationRecord
   self.table_name = "way_tags"
   self.primary_keys = "way_id", "version", "k"
 
index d9b2a5579c5bf3b72831649b5d4d04cc5ca41326..f4eedde0a2daddb172843d910507494261c1b4ae 100644 (file)
@@ -24,7 +24,7 @@
 # record's title and description fields, which can be
 # displayed linked from the redacted records.
 #
-class Redaction < ActiveRecord::Base
+class Redaction < ApplicationRecord
   belongs_to :user
 
   has_many :old_nodes
index dc2b71ac4193f292ea6febf9e9df8176cc606647..3f812d1b1a80c2c02e3a337524fb1cbce626650e 100644 (file)
@@ -17,7 +17,7 @@
 #  current_relations_changeset_id_fkey  (changeset_id => changesets.id)
 #
 
-class Relation < ActiveRecord::Base
+class Relation < ApplicationRecord
   require "xml/libxml"
 
   include ConsistencyValidations
@@ -121,31 +121,6 @@ class Relation < ActiveRecord::Base
     relation
   end
 
-  def to_xml
-    doc = OSM::API.new.get_xml_doc
-    doc.root << to_xml_node
-    doc
-  end
-
-  def to_xml_node(changeset_cache = {}, user_display_name_cache = {})
-    el = XML::Node.new "relation"
-    el["id"] = id.to_s
-
-    add_metadata_to_xml_node(el, self, changeset_cache, user_display_name_cache)
-
-    relation_members.each do |member|
-      member_el = XML::Node.new "member"
-      member_el["type"] = member.member_type.downcase
-      member_el["ref"] = member.member_id.to_s
-      member_el["role"] = member.member_role
-      el << member_el
-    end
-
-    add_tags_to_xml_node(el, relation_tags)
-
-    el
-  end
-
   # FIXME: is this really needed?
   def members
     @members ||= relation_members.map do |member|
index 7c399c3a88936b1769bb1657cf23c3db232d9e92..7f07dda9b82987226086f7d054e0a568d922f756 100644 (file)
@@ -17,7 +17,7 @@
 #  current_relation_members_id_fkey  (relation_id => current_relations.id)
 #
 
-class RelationMember < ActiveRecord::Base
+class RelationMember < ApplicationRecord
   self.table_name = "current_relation_members"
   self.primary_keys = "relation_id", "sequence_id"
 
index b186f505d13142bc751f28428f2f29ce8fc7b53f..ad9a932661bddc63e75eeb428fe00330c327e47b 100644 (file)
@@ -11,7 +11,7 @@
 #  current_relation_tags_id_fkey  (relation_id => current_relations.id)
 #
 
-class RelationTag < ActiveRecord::Base
+class RelationTag < ApplicationRecord
   self.table_name = "current_relation_tags"
   self.primary_keys = "relation_id", "k"
 
index 9bbf221df75c3cc0237044957321b6b372ed2da3..9afedb04ee29a68a0cabf434b549a22ff6dbd1f8 100644 (file)
@@ -21,7 +21,7 @@
 #  reports_user_id_fkey   (user_id => users.id)
 #
 
-class Report < ActiveRecord::Base
+class Report < ApplicationRecord
   belongs_to :issue, :counter_cache => true
   belongs_to :user
 
index f1cd72acd600f0ba2278b201c9427c8addaa91ae..d500784af882892ab065d8134fcfdf236c9be353 100644 (file)
@@ -25,7 +25,7 @@
 #  gpx_files_user_id_fkey  (user_id => users.id)
 #
 
-class Trace < ActiveRecord::Base
+class Trace < ApplicationRecord
   self.table_name = "gpx_files"
 
   belongs_to :user, :counter_cache => true
index 6473c943031134e87a1a9d99189a07614a9066ff..6352824fd7f08c3c50f781c95c80837c10302aab 100644 (file)
@@ -20,7 +20,7 @@
 #  gps_points_gpx_id_fkey  (gpx_id => gpx_files.id)
 #
 
-class Tracepoint < ActiveRecord::Base
+class Tracepoint < ApplicationRecord
   include GeoRecord
 
   self.table_name = "gps_points"
index 8d2f4ffface4d307c0d1013ec212781010c8879d..f13f7269fbb73625c712bcd7804a254b59661c63 100644 (file)
@@ -16,7 +16,7 @@
 #  gpx_file_tags_gpx_id_fkey  (gpx_id => gpx_files.id)
 #
 
-class Tracetag < ActiveRecord::Base
+class Tracetag < ApplicationRecord
   self.table_name = "gpx_file_tags"
 
   belongs_to :trace, :foreign_key => "gpx_id"
index 1095dc6eaccbf9e4466d4b80bc6764d81f76371b..518cb94ccf29a81223671520c23dd09d5c467d1d 100644 (file)
@@ -43,7 +43,7 @@
 #  users_home_idx                (home_tile)
 #
 
-class User < ActiveRecord::Base
+class User < ApplicationRecord
   require "xml/libxml"
 
   has_many :traces, -> { where(:visible => true) }
index 92cee16cd6e96bcc57e4718042c00a9cc7d57d59..9150bae7962b641c03e49ee7ddd888b0d57d4756 100644 (file)
@@ -24,7 +24,7 @@
 #  user_blocks_user_id_fkey       (user_id => users.id)
 #
 
-class UserBlock < ActiveRecord::Base
+class UserBlock < ApplicationRecord
   validate :moderator_permissions
   validates :reason, :characters => true
 
@@ -78,7 +78,7 @@ class UserBlock < ActiveRecord::Base
   # block. this should be caught and dealt with in the controller,
   # but i've also included it here just in case.
   def moderator_permissions
-    errors.add(:base, I18n.t("user_block.model.non_moderator_update")) if creator_id_changed? && !creator.moderator?
-    errors.add(:base, I18n.t("user_block.model.non_moderator_revoke")) unless revoker_id.nil? || revoker.moderator?
+    errors.add(:base, I18n.t("user_blocks.model.non_moderator_update")) if creator_id_changed? && !creator.moderator?
+    errors.add(:base, I18n.t("user_blocks.model.non_moderator_revoke")) if revoker_id_changed? && !revoker_id.nil? && !revoker.moderator?
   end
 end
index 583ced3c56d19ed15de8dd2a4ff6922b37312de8..d5cad3978c81f087cacee2b13f3e152aa87df506 100644 (file)
 #  user_preferences_user_id_fkey  (user_id => users.id)
 #
 
-class UserPreference < ActiveRecord::Base
+class UserPreference < ApplicationRecord
   self.primary_keys = "user_id", "k"
 
   belongs_to :user
 
   validates :user, :presence => true, :associated => true
   validates :k, :v, :length => 1..255, :characters => true
-
-  # Turn this Node in to an XML Node without the <osm> wrapper.
-  def to_xml_node
-    el1 = XML::Node.new "preference"
-    el1["k"] = k
-    el1["v"] = v
-
-    el1
-  end
 end
index f3d48cade4bcfeb53df717b509cc0ab90ac45d39..a081361a76cb6db41fe03737c313db6fc08ddc45 100644 (file)
@@ -19,7 +19,7 @@
 #  user_roles_user_id_fkey     (user_id => users.id)
 #
 
-class UserRole < ActiveRecord::Base
+class UserRole < ApplicationRecord
   belongs_to :user
   belongs_to :granter, :class_name => "User"
 
index 844357d8d0242000db462047ea6be54bd01342c1..8c9bf4aeb36de68479d11533e406f9106877da4b 100644 (file)
@@ -18,7 +18,7 @@
 #  user_tokens_user_id_fkey  (user_id => users.id)
 #
 
-class UserToken < ActiveRecord::Base
+class UserToken < ApplicationRecord
   belongs_to :user
 
   after_initialize :set_defaults
index b3466213c34de289b325718bcbdbed8c37a4a744..d0d1e2a2e43836a52018a93213a2cc463850e4a8 100644 (file)
@@ -17,7 +17,7 @@
 #  current_ways_changeset_id_fkey  (changeset_id => changesets.id)
 #
 
-class Way < ActiveRecord::Base
+class Way < ApplicationRecord
   require "xml/libxml"
 
   include ConsistencyValidations
@@ -106,44 +106,6 @@ class Way < ActiveRecord::Base
     way
   end
 
-  # Find a way given it's ID, and in a single SQL call also grab its nodes and tags
-  def to_xml
-    doc = OSM::API.new.get_xml_doc
-    doc.root << to_xml_node
-    doc
-  end
-
-  def to_xml_node(visible_nodes = nil, changeset_cache = {}, user_display_name_cache = {})
-    el = XML::Node.new "way"
-    el["id"] = id.to_s
-
-    add_metadata_to_xml_node(el, self, changeset_cache, user_display_name_cache)
-
-    # make sure nodes are output in sequence_id order
-    ordered_nodes = []
-    way_nodes.each do |nd|
-      if visible_nodes
-        # if there is a list of visible nodes then use that to weed out deleted nodes
-        ordered_nodes[nd.sequence_id] = nd.node_id.to_s if visible_nodes[nd.node_id]
-      else
-        # otherwise, manually go to the db to check things
-        ordered_nodes[nd.sequence_id] = nd.node_id.to_s if nd.node&.visible?
-      end
-    end
-
-    ordered_nodes.each do |nd_id|
-      next unless nd_id && nd_id != "0"
-
-      node_el = XML::Node.new "nd"
-      node_el["ref"] = nd_id
-      el << node_el
-    end
-
-    add_tags_to_xml_node(el, way_tags)
-
-    el
-  end
-
   def nds
     @nds ||= way_nodes.collect(&:node_id)
   end
index 0788a631c8c9c3969ea79dc84988d81fd61f58b6..0626fb2403df19b013700617421c5accfd12c296 100644 (file)
@@ -16,7 +16,7 @@
 #  current_way_nodes_node_id_fkey  (node_id => current_nodes.id)
 #
 
-class WayNode < ActiveRecord::Base
+class WayNode < ApplicationRecord
   self.table_name = "current_way_nodes"
   self.primary_keys = "way_id", "sequence_id"
 
index 6637c158535694aa017e2719f79b067913cf9527..0d32d8c41631402f7b104b6b60b881308a9ab6c5 100644 (file)
@@ -11,7 +11,7 @@
 #  current_way_tags_id_fkey  (way_id => current_ways.id)
 #
 
-class WayTag < ActiveRecord::Base
+class WayTag < ApplicationRecord
   self.table_name = "current_way_tags"
   self.primary_keys = "way_id", "k"
 
diff --git a/app/views/api/_root_attributes.json.jbuilder b/app/views/api/_root_attributes.json.jbuilder
new file mode 100644 (file)
index 0000000..d8fbef9
--- /dev/null
@@ -0,0 +1,5 @@
+json.version Settings.api_version
+json.generator Settings.generator
+json.copyright Settings.copyright_owner
+json.attribution Settings.attribution_url
+json.license Settings.license_url
diff --git a/app/views/api/map/_bounds.json.jbuilder b/app/views/api/map/_bounds.json.jbuilder
new file mode 100644 (file)
index 0000000..16fdbeb
--- /dev/null
@@ -0,0 +1,6 @@
+json.bounds do
+  json.minlat GeoRecord::Coord.new(@bounds.min_lat)
+  json.minlon GeoRecord::Coord.new(@bounds.min_lon)
+  json.maxlat GeoRecord::Coord.new(@bounds.max_lat)
+  json.maxlon GeoRecord::Coord.new(@bounds.max_lon)
+end
diff --git a/app/views/api/map/index.json.jbuilder b/app/views/api/map/index.json.jbuilder
new file mode 100644 (file)
index 0000000..7cc983a
--- /dev/null
@@ -0,0 +1,9 @@
+json.partial! "root_attributes"
+
+json.partial! "bounds"
+
+all = @nodes + @ways + @relations
+
+json.elements(all) do |obj|
+  json.partial! obj
+end
diff --git a/app/views/api/nodes/_node.json.jbuilder b/app/views/api/nodes/_node.json.jbuilder
new file mode 100644 (file)
index 0000000..e48d5f1
--- /dev/null
@@ -0,0 +1,15 @@
+json.type "node"
+json.id node.id
+if node.visible
+  json.lat GeoRecord::Coord.new(node.lat)
+  json.lon GeoRecord::Coord.new(node.lon)
+end
+json.timestamp node.timestamp.xmlschema
+json.version node.version
+json.changeset node.changeset_id
+json.user node.changeset.user.display_name
+json.uid node.changeset.user_id
+
+json.visible node.visible unless node.visible
+
+json.tags node.tags unless node.tags.empty?
diff --git a/app/views/api/nodes/index.json.jbuilder b/app/views/api/nodes/index.json.jbuilder
new file mode 100644 (file)
index 0000000..3e3ceb4
--- /dev/null
@@ -0,0 +1,5 @@
+json.partial! "api/root_attributes"
+
+json.elements(@nodes) do |node|
+  json.partial! node
+end
diff --git a/app/views/api/nodes/show.json.jbuilder b/app/views/api/nodes/show.json.jbuilder
new file mode 100644 (file)
index 0000000..9974da8
--- /dev/null
@@ -0,0 +1,5 @@
+json.partial! "api/root_attributes"
+
+json.elements([@node]) do |node|
+  json.partial! node
+end
similarity index 81%
rename from app/views/api/notes/_note.json.jsonify
rename to app/views/api/notes/_note.json.jbuilder
index b964399225350d30cf48aa7786e0c4eda41d9fb7..34f79688073249d00b7edf78cff1b05198ab77de 100644 (file)
@@ -2,7 +2,7 @@ json.type "Feature"
 
 json.geometry do
   json.type "Point"
-  json.coordinates [ note.lon.to_f, note.lat.to_f ]
+  json.coordinates [note.lon.to_f, note.lat.to_f]
 end
 
 json.properties do
@@ -16,12 +16,12 @@ json.properties do
     json.close_url close_note_url(note, :format => params[:format])
   end
 
-  json.date_created note.created_at
+  json.date_created note.created_at.to_s
   json.status note.status
-  json.closed_at note.closed_at if note.closed?
+  json.closed_at note.closed_at.to_s if note.closed?
 
   json.comments(note.comments) do |comment|
-    json.date comment.created_at
+    json.date comment.created_at.to_s
 
     if comment.author
       json.uid comment.author.id
similarity index 70%
rename from app/views/api/notes/index.json.jsonify
rename to app/views/api/notes/index.json.jbuilder
index bfc8ffcf854f3ed87c9569d05168462c8d52a725..7909391f5ae939f231121b01c958134ad0afce72 100644 (file)
@@ -1,5 +1,5 @@
 json.type "FeatureCollection"
 
 json.features(@notes) do |note|
-  json.ingest! render(note)
+  json.partial! note
 end
diff --git a/app/views/api/notes/show.json.jbuilder b/app/views/api/notes/show.json.jbuilder
new file mode 100644 (file)
index 0000000..71d9408
--- /dev/null
@@ -0,0 +1 @@
+json.partial! @note
diff --git a/app/views/api/notes/show.json.jsonify b/app/views/api/notes/show.json.jsonify
deleted file mode 100644 (file)
index 10d1272..0000000
+++ /dev/null
@@ -1 +0,0 @@
-json.ingest! render(@note)
diff --git a/app/views/api/old_nodes/_old_node.json.jbuilder b/app/views/api/old_nodes/_old_node.json.jbuilder
new file mode 100644 (file)
index 0000000..211d503
--- /dev/null
@@ -0,0 +1,15 @@
+json.type "node"
+json.id old_node.node_id
+if old_node.visible
+  json.lat GeoRecord::Coord.new(old_node.lat)
+  json.lon GeoRecord::Coord.new(old_node.lon)
+end
+json.timestamp old_node.timestamp.xmlschema
+json.version old_node.version
+json.changeset old_node.changeset_id
+json.user old_node.changeset.user.display_name
+json.uid old_node.changeset.user_id
+
+json.visible old_node.visible unless old_node.visible
+
+json.tags old_node.tags unless old_node.tags.empty?
diff --git a/app/views/api/old_nodes/history.json.jbuilder b/app/views/api/old_nodes/history.json.jbuilder
new file mode 100644 (file)
index 0000000..96e8cca
--- /dev/null
@@ -0,0 +1,5 @@
+json.partial! "api/root_attributes"
+
+json.elements(@elems) do |old_node|
+  json.partial! old_node
+end
diff --git a/app/views/api/old_nodes/version.json.jbuilder b/app/views/api/old_nodes/version.json.jbuilder
new file mode 100644 (file)
index 0000000..f63e07d
--- /dev/null
@@ -0,0 +1,5 @@
+json.partial! "api/root_attributes"
+
+json.elements([@old_element]) do |old_node|
+  json.partial! old_node
+end
diff --git a/app/views/api/old_relations/_old_relation.json.jbuilder b/app/views/api/old_relations/_old_relation.json.jbuilder
new file mode 100644 (file)
index 0000000..c52ca20
--- /dev/null
@@ -0,0 +1,19 @@
+json.type "relation"
+json.id old_relation.relation_id
+json.timestamp old_relation.timestamp.xmlschema
+json.version old_relation.version
+json.changeset old_relation.changeset_id
+json.user old_relation.changeset.user.display_name
+json.uid old_relation.changeset.user_id
+
+json.visible old_relation.visible unless old_relation.visible
+
+unless old_relation.relation_members.empty?
+  json.members(old_relation.relation_members) do |m|
+    json.type m.member_type.downcase
+    json.ref m.member_id
+    json.role m.member_role
+  end
+end
+
+json.tags old_relation.tags unless old_relation.tags.empty?
diff --git a/app/views/api/old_relations/history.json.jbuilder b/app/views/api/old_relations/history.json.jbuilder
new file mode 100644 (file)
index 0000000..311a80a
--- /dev/null
@@ -0,0 +1,5 @@
+json.partial! "api/root_attributes"
+
+json.elements(@elems) do |old_relation|
+  json.partial! old_relation
+end
diff --git a/app/views/api/old_relations/version.json.jbuilder b/app/views/api/old_relations/version.json.jbuilder
new file mode 100644 (file)
index 0000000..5b33e4b
--- /dev/null
@@ -0,0 +1,5 @@
+json.partial! "api/root_attributes"
+
+json.elements([@old_element]) do |old_relation|
+  json.partial! old_relation
+end
diff --git a/app/views/api/old_ways/_old_way.json.jbuilder b/app/views/api/old_ways/_old_way.json.jbuilder
new file mode 100644 (file)
index 0000000..b2e79f8
--- /dev/null
@@ -0,0 +1,13 @@
+json.type "way"
+json.id old_way.way_id
+json.timestamp old_way.timestamp.xmlschema
+json.version old_way.version
+json.changeset old_way.changeset_id
+json.user old_way.changeset.user.display_name
+json.uid old_way.changeset.user_id
+
+json.visible old_way.visible unless old_way.visible
+
+json.nodes old_way.nds unless old_way.nds.empty?
+
+json.tags old_way.tags unless old_way.tags.empty?
diff --git a/app/views/api/old_ways/history.json.jbuilder b/app/views/api/old_ways/history.json.jbuilder
new file mode 100644 (file)
index 0000000..b5cf80d
--- /dev/null
@@ -0,0 +1,5 @@
+json.partial! "api/root_attributes"
+
+json.elements(@elems) do |old_way|
+  json.partial! old_way
+end
diff --git a/app/views/api/old_ways/version.json.jbuilder b/app/views/api/old_ways/version.json.jbuilder
new file mode 100644 (file)
index 0000000..c664885
--- /dev/null
@@ -0,0 +1,5 @@
+json.partial! "api/root_attributes"
+
+json.elements([@old_element]) do |old_way|
+  json.partial! old_way
+end
diff --git a/app/views/api/relations/_relation.json.jbuilder b/app/views/api/relations/_relation.json.jbuilder
new file mode 100644 (file)
index 0000000..52263ce
--- /dev/null
@@ -0,0 +1,19 @@
+json.type "relation"
+json.id relation.id
+json.timestamp relation.timestamp.xmlschema
+json.version relation.version
+json.changeset relation.changeset_id
+json.user relation.changeset.user.display_name
+json.uid relation.changeset.user_id
+
+json.visible relation.visible unless relation.visible
+
+unless relation.relation_members.empty?
+  json.members(relation.relation_members) do |m|
+    json.type m.member_type.downcase
+    json.ref m.member_id
+    json.role m.member_role
+  end
+end
+
+json.tags relation.tags unless relation.tags.empty?
diff --git a/app/views/api/relations/full.json.jbuilder b/app/views/api/relations/full.json.jbuilder
new file mode 100644 (file)
index 0000000..98cbbfc
--- /dev/null
@@ -0,0 +1,7 @@
+json.partial! "api/root_attributes"
+
+all = @nodes + @ways + @relations
+
+json.elements(all) do |obj|
+  json.partial! obj
+end
diff --git a/app/views/api/relations/index.json.jbuilder b/app/views/api/relations/index.json.jbuilder
new file mode 100644 (file)
index 0000000..f170cb1
--- /dev/null
@@ -0,0 +1,5 @@
+json.partial! "api/root_attributes"
+
+json.elements(@relations) do |relation|
+  json.partial! relation
+end
diff --git a/app/views/api/relations/relations_for_node.json.jbuilder b/app/views/api/relations/relations_for_node.json.jbuilder
new file mode 100644 (file)
index 0000000..f170cb1
--- /dev/null
@@ -0,0 +1,5 @@
+json.partial! "api/root_attributes"
+
+json.elements(@relations) do |relation|
+  json.partial! relation
+end
diff --git a/app/views/api/relations/relations_for_relation.json.jbuilder b/app/views/api/relations/relations_for_relation.json.jbuilder
new file mode 100644 (file)
index 0000000..f170cb1
--- /dev/null
@@ -0,0 +1,5 @@
+json.partial! "api/root_attributes"
+
+json.elements(@relations) do |relation|
+  json.partial! relation
+end
diff --git a/app/views/api/relations/relations_for_way.json.jbuilder b/app/views/api/relations/relations_for_way.json.jbuilder
new file mode 100644 (file)
index 0000000..f170cb1
--- /dev/null
@@ -0,0 +1,5 @@
+json.partial! "api/root_attributes"
+
+json.elements(@relations) do |relation|
+  json.partial! relation
+end
diff --git a/app/views/api/relations/show.json.jbuilder b/app/views/api/relations/show.json.jbuilder
new file mode 100644 (file)
index 0000000..7f85d0f
--- /dev/null
@@ -0,0 +1,5 @@
+json.partial! "api/root_attributes"
+
+json.elements([@relation]) do |relation|
+  json.partial! relation
+end
diff --git a/app/views/api/user_preferences/_user_preference.xml.builder b/app/views/api/user_preferences/_user_preference.xml.builder
new file mode 100644 (file)
index 0000000..ae830be
--- /dev/null
@@ -0,0 +1,6 @@
+attrs = {
+  "k" => user_preference.k,
+  "v" => user_preference.v
+}
+
+xml.preference(attrs)
diff --git a/app/views/api/user_preferences/index.xml.builder b/app/views/api/user_preferences/index.xml.builder
new file mode 100644 (file)
index 0000000..0a852b2
--- /dev/null
@@ -0,0 +1,7 @@
+xml.instruct!
+
+xml.osm(OSM::API.new.xml_root_attributes) do |osm|
+  osm.preferences do |preferences|
+    preferences << (render(@user_preferences) || "")
+  end
+end
diff --git a/app/views/api/ways/_way.json.jbuilder b/app/views/api/ways/_way.json.jbuilder
new file mode 100644 (file)
index 0000000..11e796b
--- /dev/null
@@ -0,0 +1,13 @@
+json.type "way"
+json.id way.id
+json.timestamp way.timestamp.xmlschema
+json.version way.version
+json.changeset way.changeset_id
+json.user way.changeset.user.display_name
+json.uid way.changeset.user_id
+
+json.visible way.visible unless way.visible
+
+json.nodes way.nodes.ids unless way.nodes.ids.empty?
+
+json.tags way.tags unless way.tags.empty?
diff --git a/app/views/api/ways/full.json.jbuilder b/app/views/api/ways/full.json.jbuilder
new file mode 100644 (file)
index 0000000..bebad5e
--- /dev/null
@@ -0,0 +1,7 @@
+json.partial! "api/root_attributes"
+
+all = @nodes + [@way]
+
+json.elements(all) do |obj|
+  json.partial! obj
+end
diff --git a/app/views/api/ways/index.json.jbuilder b/app/views/api/ways/index.json.jbuilder
new file mode 100644 (file)
index 0000000..19e59cf
--- /dev/null
@@ -0,0 +1,5 @@
+json.partial! "api/root_attributes"
+
+json.elements(@ways) do |way|
+  json.partial! way
+end
diff --git a/app/views/api/ways/show.json.jbuilder b/app/views/api/ways/show.json.jbuilder
new file mode 100644 (file)
index 0000000..acb93c1
--- /dev/null
@@ -0,0 +1,5 @@
+json.partial! "api/root_attributes"
+
+json.elements([@way]) do |way|
+  json.partial! way
+end
diff --git a/app/views/api/ways/ways_for_node.json.jbuilder b/app/views/api/ways/ways_for_node.json.jbuilder
new file mode 100644 (file)
index 0000000..19e59cf
--- /dev/null
@@ -0,0 +1,5 @@
+json.partial! "api/root_attributes"
+
+json.elements(@ways) do |way|
+  json.partial! way
+end
index 1c8f1c5da4293105c06200e2b29647b8395a7d05..669e0fe6dee4358452740c2540c041080d4cb0c7 100644 (file)
@@ -1,6 +1,6 @@
 <h4>
   <% if common_details.changeset.tags['comment'].present? %>
-    <%= linkify(h(common_details.changeset.tags["comment"])) %>
+    <%= linkify(common_details.changeset.tags["comment"]) %>
   <% else %>
     <%= t "browse.no_comment" %>
   <% end %>
@@ -15,7 +15,7 @@
 
 <div class="details">
   <%= t "browse.version" %>
-  #<%= h(common_details.version) %>
+  #<%= common_details.version %>
   &middot;
   <%= t "browse.in_changeset" %>
   #<%= link_to common_details.changeset_id, :action => :changeset, :id => common_details.changeset_id %>
index b3cb90cec2aa4a7d05b705c1c2745b6f59237a23..06220a97ddb9267b471e2aaea31a86273b0a5463 100644 (file)
@@ -1,7 +1,7 @@
-<li><%= linked_name = link_to h(printable_name(containing_relation.relation)), :action => "relation", :id => containing_relation.relation.id.to_s
+<li><%= linked_name = link_to printable_name(containing_relation.relation), :action => "relation", :id => containing_relation.relation.id.to_s
         if containing_relation.member_role.blank?
-          raw t ".entry", :relation_name => linked_name
+          t ".entry_html", :relation_name => linked_name
         else
-          raw t ".entry_role", :relation_name => linked_name, :relation_role => h(containing_relation.member_role)
+          t ".entry_role_html", :relation_name => linked_name, :relation_role => containing_relation.member_role
         end %>
 </li>
index 52502ad4b42c2e82deeb1470e3522cf9db37d0bc..6acc5e01ed9e5f0e5cbc99fe7dc7bb1002cd8adb 100644 (file)
@@ -12,7 +12,7 @@
 
     <% unless node.ways.empty? and node.containing_relation_members.empty? %>
       <h4><%= t "browse.part_of" %></h4>
-      <ul>
+      <ul class="list-unstyled">
         <% node.ways.uniq.each do |way| %>
           <li><%= link_to printable_name(way), { :action => "way", :id => way.id.to_s }, { :class => link_class("way", way), :title => link_title(way) } %></li>
         <% end %>
index 452556364530c1c16ca77a578f665e81ad61b002..b54581b8bda2e34f0bcf480cd6683112b99f418a 100644 (file)
 
     <% unless relation.containing_relation_members.empty? %>
       <h4><%= t "browse.part_of" %></h4>
-      <ul><%= render :partial => "containing_relation", :collection => relation.containing_relation_members.uniq %></ul>
+      <ul class="list-unstyled"><%= render :partial => "containing_relation", :collection => relation.containing_relation_members.uniq %></ul>
     <% end %>
 
     <% unless relation.relation_members.empty? %>
       <h4><%= t ".members" %></h4>
-      <ul><%= render :partial => "relation_member", :collection => relation.relation_members %></ul>
+      <ul class="list-unstyled"><%= render :partial => "relation_member", :collection => relation.relation_members %></ul>
     <% end %>
   </div>
 <% end %>
index bb37bdf210a5c7ee89ee6c792e9e01a37af474c2..51b2bb3e6dd05cc288f38c1cda03a64a029a4c4a 100644 (file)
@@ -3,8 +3,8 @@
    type_str = t ".type." + relation_member.member_type.downcase %>
 <li class="<%= member_class %>">
   <%= if relation_member.member_role.blank?
-        raw t ".entry", :type => type_str, :name => linked_name
+        t ".entry_html", :type => type_str, :name => linked_name
       else
-        raw t ".entry_role", :type => type_str, :name => linked_name, :role => h(relation_member.member_role)
+        t ".entry_role_html", :type => type_str, :name => linked_name, :role => relation_member.member_role
       end %>
 </li>
index ed206c59bcc7399f803a2bca50e1882ccff569c1..137d529ff2a1296831a1c44d0c0be00d1dbd8cb6 100644 (file)
 
     <% unless way.containing_relation_members.empty? %>
       <h4><%= t "browse.part_of" %></h4>
-      <ul>
+      <ul class="list-unstyled">
         <%= render :partial => "containing_relation", :collection => way.containing_relation_members.uniq %>
       </ul>
     <% end %>
 
     <% unless way.way_nodes.empty? %>
       <h4><%= t ".nodes" %></h4>
-      <ul>
+      <ul class="list-unstyled">
         <% way.way_nodes.each do |wn| %>
           <li>
             <%= link_to printable_name(wn.node), { :action => "node", :id => wn.node_id.to_s }, { :class => link_class("node", wn.node), :title => link_title(wn.node), :rel => link_follow(wn.node) } %>
             <% related_ways = wn.node.ways.reject { |w| w.id == wn.way_id } %>
             <% if related_ways.size > 0 then %>
-              (<%= raw t ".also_part_of", :count => related_ways.size, :related_ways => related_ways.map { |w| link_to(printable_name(w), { :action => "way", :id => w.id.to_s }, { :class => link_class("way", w), :title => link_title(w) }) }.to_sentence %>)
+              (<%= t ".also_part_of_html", :count => related_ways.size, :related_ways => to_sentence(related_ways.map { |w| link_to(printable_name(w), { :action => "way", :id => w.id.to_s }, { :class => link_class("way", w), :title => link_title(w) }) }) %>)
             <% end %>
           </li>
         <% end %>
index 9ad35b2a7d5c71bba58a14854a49a01f56edf7c2..3c41dd83fefd32a482b43e07568e6cdcdb4ce286 100644 (file)
@@ -6,7 +6,7 @@
 </h2>
 
 <div class="browse-section">
-  <h4><%= linkify(h(@changeset.tags["comment"].to_s.presence || t("browse.no_comment"))) %></h4>
+  <h6><%= linkify(@changeset.tags["comment"].to_s.presence || t("browse.no_comment")) %></h6>
   <div class="details"><%= changeset_details(@changeset) %></div>
 
   <%= render :partial => "tag_details", :object => @changeset.tags.except("comment") %>
@@ -30,7 +30,7 @@
   <% if @comments.length > 0 %>
     <div class='changeset-comments'>
       <form action="#">
-        <ul>
+        <ul class="list-unstyled">
           <% @comments.each do |comment| %>
             <% if comment.visible %>
               <li id="c<%= comment.id %>">
@@ -38,7 +38,7 @@
                   <%= t(".commented_by",
                         :when => friendly_date_ago(comment.created_at),
                         :exact_time => l(comment.created_at),
-                        :user => link_to(h(comment.author.display_name), user_path(comment.author))).html_safe %>
+                        :user => link_to(comment.author.display_name, user_path(comment.author))).html_safe %>
                   <% if current_user and current_user.moderator? %>
                     — <span class="action-button deemphasize" data-comment-id="<%= comment.id %>" data-method="POST" data-url="<%= changeset_comment_hide_url(comment.id) %>"><%= t("javascripts.changesets.show.hide_comment") %></span>
                   <% end %>
@@ -51,7 +51,7 @@
                   <%= t(".hidden_commented_by",
                         :when => friendly_date_ago(comment.created_at),
                         :exact_time => l(comment.created_at),
-                        :user => link_to(h(comment.author.display_name), user_path(comment.author))).html_safe %>
+                        :user => link_to(comment.author.display_name, user_path(comment.author))).html_safe %>
                   — <span class="action-button deemphasize" data-comment-id="<%= comment.id %>" data-method="POST" data-url="<%= changeset_comment_unhide_url(comment.id) %>"><%= t("javascripts.changesets.show.unhide_comment") %></span>
                  </small>
                 <%= comment.body.to_html %>
@@ -89,7 +89,7 @@
       <%= type_and_paginated_count("way", @way_pages) %>
       <%= render :partial => "paging_nav", :locals => { :pages => @way_pages, :page_param => "way_page" } %>
     </h4>
-    <ul>
+    <ul class="list-unstyled">
       <% @ways.each do |way| %>
         <li><%= link_to printable_name(way, true), { :action => "way", :id => way.way_id.to_s }, { :class => link_class("way", way), :title => link_title(way) } %></li>
       <% end %>
       <%= type_and_paginated_count("relation", @relation_pages) %>
       <%= render :partial => "paging_nav", :locals => { :pages => @relation_pages, :page_param => "relation_page" } %>
     </h4>
-    <ul>
+    <ul class="list-unstyled">
       <% @relations.each do |relation| %>
         <li><%= link_to printable_name(relation, true), { :action => "relation", :id => relation.relation_id.to_s }, { :class => link_class("relation", relation), :title => link_title(relation) } %></li>
       <% end %>
       <%= type_and_paginated_count("node", @node_pages) %>
       <%= render :partial => "paging_nav", :locals => { :pages => @node_pages, :page_param => "node_page" } %>
     </h4>
-    <ul>
+    <ul class="list-unstyled">
       <% @nodes.each do |node| %>
         <li><%= link_to printable_name(node, true), { :action => "node", :id => node.node_id.to_s }, { :class => link_class("node", node), :title => link_title(node), :rel => link_follow(node) } %></li>
       <% end %>
index 6c94b0a9dfd50aa8dab39c5ec33611c29c4abd2e..75e54f0b2c89c1aa4962808841b9e57bfbdc5f4e 100644 (file)
@@ -1,8 +1,8 @@
-<% set_title(t("browse.#{@type}.title", :name => printable_name(@feature))) %>
+<% set_title(t("browse.#{@type}.title_html", :name => printable_name(@feature))) %>
 
 <h2>
   <a class="geolink" href="<%= root_path %>"><span class="icon close"></span></a>
-  <%= raw t("browse.#{@type}.title", :name => printable_name(@feature)) %>
+  <%= t("browse.#{@type}.title_html", :name => printable_name(@feature)) %>
 </h2>
 
 <%= render :partial => @type, :object => @feature %>
index 0a2c1811c0d72c64465a163f2570c43ac17c9388..faa88359e6b9ced2911915dd7b37a0437b9dfb4c 100644 (file)
@@ -1,8 +1,8 @@
-<% set_title(t("browse.#{@type}.history_title", :name => printable_name(@feature))) %>
+<% set_title(t("browse.#{@type}.history_title_html", :name => printable_name(@feature))) %>
 
 <h2>
   <a class="geolink" href="<%= root_path %>"><span class="icon close"></span></a>
-  <%= raw t("browse.#{@type}.history_title", :name => printable_name(@feature)) %>
+  <%= t("browse.#{@type}.history_title_html", :name => printable_name(@feature)) %>
 </h2>
 
 <%= render :partial => @type, :collection => @feature.send("old_#{@type}s").reverse %>
index f68dfbe2eae5bfc2a8bd1cbb5778214604075d0f..884f95d57914013481a76688b41e11d5110be888 100644 (file)
@@ -12,7 +12,7 @@
   </div>
 
   <div class="details" data-coordinates="<%= @note.lat %>,<%= @note.lon %>" data-status="<%= @note.status %>">
-    <%= note_event("open", @note.created_at, @note.author) %>
+    <%= note_event("opened", @note.created_at, @note.author) %>
     <% if @note.status == "closed" %>
       <br />
       <%= note_event(@note.status, @note.closed_at, @note_comments.last.author) %>
@@ -29,7 +29,7 @@
 
   <% if @note_comments.length > 1 %>
     <div class='note-comments'>
-      <ul>
+      <ul class="list-unstyled">
         <% @note_comments[1..-1].each do |comment| %>
           <li id="c<%= comment.id %>">
             <small class='deemphasize'><%= note_event(comment.event, comment.created_at, comment.author) %></small>
index ea40a00dbcf7ee91189efd95447bf3edbe0b0034..ad6fdddb53e360068c1c598c3ed30299f3b505ea 100644 (file)
 <div id="query-nearby" class="query-results">
   <h3><%= t(".nearby") %></h3>
   <%= image_tag "searching.gif", :class => "loader" %>
-  <ul class="query-results-list"></ul>
+  <div>
+    <ul class="query-results-list list-group list-group-flush"></ul>
+  </div>
 </div>
 
 <div id="query-isin" class="query-results">
   <h3><%= t(".enclosing") %></h3>
   <%= image_tag "searching.gif", :class => "loader" %>
-  <ul class="query-results-list"></ul>
+  <div>
+    <ul class="query-results-list list-group list-group-flush"></ul>
+  </div>
 </div>
index 56726e2d9a51eee471f8e503cad51dc43fb762de..f1173671e67375564ade610bde17be67a9de8db6 100644 (file)
      }
    end %>
 
-<%= content_tag "li", :id => "changeset_#{changeset.id}", :data => { :changeset => changeset_data } do %>
-  <h4>
-    <a class="changeset_id" href="<%= changeset_path(changeset) %>">
+<%= content_tag "li", :id => "changeset_#{changeset.id}", :data => { :changeset => changeset_data }, :class => "list-group-item" do %>
+  <h6>
+    <a class="changeset_id text-dark" href="<%= changeset_path(changeset) %>">
       <%= changeset.tags["comment"].to_s.presence || t("browse.no_comment") %>
     </a>
-  </h4>
+  </h6>
   <div class="comments comments-<%= changeset.comments.length %>">
     <%= changeset.comments.length %>
     <span class="icon note grey"></span>
index b1fbd6992aa14c3ddd4d2e9c65a7c9bc9621093f..2bbae2cc277a9cd4d1172319351e4261de8065ed 100644 (file)
@@ -24,9 +24,9 @@ atom_feed(:language => I18n.locale, :schema_date => 2009,
                  :type => "application/osmChange+xml"
 
       if !changeset.tags.empty? && changeset.tags.key?("comment")
-        entry.title t("browse.changeset.feed.title_comment", :id => h(changeset.id), :comment => h(changeset.tags["comment"])), :type => "html"
+        entry.title t("browse.changeset.feed.title_comment", :id => changeset.id, :comment => changeset.tags["comment"]), :type => "html"
       else
-        entry.title t("browse.changeset.feed.title", :id => h(changeset.id))
+        entry.title t("browse.changeset.feed.title", :id => changeset.id)
       end
 
       if changeset.user.data_public?
@@ -51,7 +51,7 @@ atom_feed(:language => I18n.locale, :schema_date => 2009,
             table.tr do |tr|
               tr.th t("browse.changeset.belongs_to")
               tr.td do |td|
-                td.a h(changeset.user.display_name), :href => user_url(changeset.user, :only_path => false)
+                td.a changeset.user.display_name, :href => user_url(changeset.user, :only_path => false)
               end
             end
           end
@@ -62,7 +62,7 @@ atom_feed(:language => I18n.locale, :schema_date => 2009,
                 td.table :cellpadding => "0" do |tag_table|
                   changeset.tags.sort.each do |tag|
                     tag_table.tr do |tag_tr|
-                      tag_tr.td << "#{h(tag[0])} = #{linkify(h(tag[1]))}"
+                      tag_tr.td << "#{tag[0]} = #{linkify(tag[1])}"
                     end
                   end
                 end
index 1e3daa081e4cf3acc8dffced758d8f970d3fe338..527a0a23280839ee129c2769de87dd59054d1315 100644 (file)
@@ -1,5 +1,5 @@
 <% if @changesets.present? %>
-  <ol class="changesets">
+  <ol class="changesets list-group list-group-flush">
     <%= render @changesets %>
   </ol>
 <% if @changesets.size == 20 -%>
index 639ac9a191f170e055db979142739c8f179dbb07..db48478263968d0103adc44727c3706bdfabe036 100644 (file)
@@ -1,6 +1,6 @@
 <div class="clearfix diary-comment<%= " deemphasize" unless diary_comment.visible? %>">
   <%= user_thumbnail diary_comment.user %>
-  <p class="deemphasize comment-heading" id="comment<%= diary_comment.id %>"><%= raw(t(".comment_from", :link_user => (link_to h(diary_comment.user.display_name), user_path(diary_comment.user)), :comment_created_at => link_to(l(diary_comment.created_at, :format => :friendly), :anchor => "comment#{diary_comment.id}"))) %>
+  <p class="deemphasize comment-heading" id="comment<%= diary_comment.id %>"><%= t(".comment_from_html", :link_user => (link_to diary_comment.user.display_name, user_path(diary_comment.user)), :comment_created_at => link_to(l(diary_comment.created_at, :format => :friendly), :anchor => "comment#{diary_comment.id}")) %>
     <% if current_user and diary_comment.user.id != current_user.id %>
       | <%= report_link(t(".report"), diary_comment) %>
     <% end %>
index ef983ebde5b511f8a8d2ab57675cccdf5ee57b0f..b0c464718011160ada4c5990be1faf64e3849f2d 100644 (file)
@@ -4,10 +4,10 @@
       <%= user_thumbnail diary_entry.user %>
     <% end %>
 
-    <h2><%= link_to h(diary_entry.title), diary_entry_path(diary_entry.user, diary_entry) %></h2>
+    <h2><%= link_to diary_entry.title, diary_entry_path(diary_entry.user, diary_entry) %></h2>
 
     <small class='deemphasize'>
-      <%= raw(t(".posted_by", :link_user => (link_to h(diary_entry.user.display_name), user_path(diary_entry.user)), :created => l(diary_entry.created_at, :format => :blog), :language_link => (link_to h(diary_entry.language.name), :controller => "diary_entries", :action => "index", :display_name => nil, :language => diary_entry.language_code))) %>
+      <%= t(".posted_by_html", :link_user => (link_to diary_entry.user.display_name, user_path(diary_entry.user)), :created => l(diary_entry.created_at, :format => :blog), :language_link => (link_to diary_entry.language.name, :controller => "diary_entries", :action => "index", :display_name => nil, :language => diary_entry.language_code)) %>
     </small>
 
   </div>
index 0d8f7ef5ba85811b5e921beb9ac8941c1ba0993f..1b04b15197d970e6a90422b2ddd0e9df5302f915 100644 (file)
@@ -1,14 +1,14 @@
 <div class="diary_entry standard-form">
   <fieldset>
-    <div class='form-row'>
+    <div class='standard-form-row'>
       <label class="standard-label"><%= t ".subject" -%></label>
       <%= f.text_field :title, :class => "richtext_title" %>
     </div>
-    <div class='form-row'>
+    <div class='standard-form-row'>
       <label class="standard-label"><%= t ".body" -%></label>
       <%= richtext_area :diary_entry, :body, :cols => 80, :rows => 20, :format => @diary_entry.body_format %>
     </div>
-    <div class='form-row'>
+    <div class='standard-form-row'>
       <label class="standard-label"><%= t ".language" -%></label>
       <%= f.collection_select :language_code, Language.order(:english_name), :code, :name %>
   </div>
@@ -16,7 +16,7 @@
   <fieldset class='location'>
     <label class="standard-label"><%= t ".location" -%></label>
     <%= content_tag "div", "", :id => "map", :data => { :lat => @lat, :lon => @lon, :zoom => @zoom } %>
-    <div class='form-row clearfix'>
+    <div class='standard-form-row clearfix'>
       <div class='form-column'>
         <label class="secondary standard-label"><%= t ".latitude" -%></label>
         <%= f.text_field :latitude, :size => 20, :id => "latitude" %>
index 56dba30b875d81d9f37eab7654c323167ee61ae9..ec8c3dfda8e7535ac419171d0910171bd96abd51 100644 (file)
@@ -2,15 +2,16 @@
   <h1><%= t(".has_commented_on", :display_name => @user.display_name) %></h1>
 <% end %>
 
-<table class="messages" width="100%">
-  <tr>
-    <th width="25%"><%= t ".post" %></th>
-    <th width="25%"><%= t ".when" %></th>
-    <th width="50%"><%= t ".comment" %></th>
-  </tr>
+<table class="table table-striped" width="100%">
+  <thead>
+    <tr>
+      <th width="25%"><%= t ".post" %></th>
+      <th width="25%"><%= t ".when" %></th>
+      <th width="50%"><%= t ".comment" %></th>
+    </tr>
+  </thead>
   <% @comments.each do |comment| -%>
-  <% cl = cycle("table0", "table1") %>
-  <tr class="<%= cl %><%= " deemphasize" unless comment.visible? %>">
+  <tr class="<%= "deemphasize" unless comment.visible? %>">
     <td width="25%"><%= link_to comment.diary_entry.title, diary_entry_path(comment.diary_entry.user, comment.diary_entry) %></td>
     <td width="25%"><span title="<%= l comment.created_at, :format => :friendly %>"><%= time_ago_in_words(comment.created_at, :scope => :'datetime.distance_in_words_ago') %></span></td>
     <td width="50%" class="richtext"><%= comment.body.to_html %></td>
index acf9ac17c7f558a443bd0bde51f58227ead71372..32a4e1e0fb7154f32f2f8b8d0789f84e122a8968 100644 (file)
@@ -3,7 +3,7 @@
     <% if @user %>
       <%= user_image @user %>
     <% end %>
-    <h1><%= h(@title) %></h1>
+    <h1><%= @title %></h1>
 
     <ul class='secondary-actions clearfix'>
       <% unless params[:friends] or params[:nearby] -%>
 
       <% if @user %>
         <% if @user == current_user %>
-          <div>
-            <li><%= link_to image_tag("new.png", :class => "small_icon", :border => 0) + t(".new"), new_diary_entry_path, :title => t(".new_title") %></li>
-          </div>
+          <li><%= link_to image_tag("new.png", :class => "small_icon") + t(".new"), new_diary_entry_path, :title => t(".new_title") %></li>
         <% end %>
       <% else %>
         <% if current_user %>
-          <div>
-            <li><%= link_to image_tag("new.png", :class => "small_icon", :border => 0) + t(".new"), new_diary_entry_path, :title => t(".new_title") %></li>
-          </div>
+          <li><%= link_to image_tag("new.png", :class => "small_icon") + t(".new"), new_diary_entry_path, :title => t(".new_title") %></li>
         <% end %>
       <% end %>
     </ul>
index 1cc1e1c5363212c303fd8eb6aefdb4c51d1f0b92..d3a0f70b267a5cfbc409e896aed35ddb14448ad5 100644 (file)
@@ -1,7 +1,7 @@
 <% content_for :heading do %>
   <div id="userinformation">
     <%= user_image @entry.user %>
-    <h2><%= link_to t(".user_title", :user => h(@entry.user.display_name)), :action => :index %></h2>
+    <h2><%= link_to t(".user_title", :user => @entry.user.display_name), :action => :index %></h2>
     <p><%= rss_link_to :action => :rss, :display_name => @entry.user.display_name %></p>
   </div>
 <% end %>
@@ -29,7 +29,7 @@
       <div class="diary-subscribe-buttons"><%= link_to t("javascripts.changesets.show.subscribe"), diary_entry_subscribe_path(:display_name => @entry.user.display_name, :id => @entry.id), :method => :post, :class => :button %></div>
     <% end %>
   <% else %>
-    <h3 id="newcomment"><%= raw t(".login_to_leave_a_comment", :login_link => link_to(t(".login"), :controller => "users", :action => "login", :referer => request.fullpath)) %></h3>
+    <h3 id="newcomment"><%= t(".login_to_leave_a_comment_html", :login_link => link_to(t(".login"), :controller => "users", :action => "login", :referer => request.fullpath)) %></h3>
   <% end %>
 </div>
 
index deec4ed3939665f725c89133a3a73d75fbcd6f7c..94f19d2feec9960627c916edf0d6216b013793ef 100644 (file)
@@ -1,9 +1,13 @@
 <% if @results.empty? %>
-  <p class="search_results_entry inner12"><%= t ".no_results" %></p>
+  <ul class="list-group list-group-flush">
+    <li class="list-group-item">
+      <%= t ".no_results" %>
+    </li>
+  </ul>
 <% else %>
-  <ul class='results-list'>
+  <ul class='results-list list-group list-group-flush'>
     <% @results.each do |result| %>
-      <li><p class="inner12 search_results_entry clearfix"><%= result_to_html(result) %></p></li>
+      <li class="list-group-item search_results_entry"><%= result_to_html(result) %></li>
     <% end %>
   </ul>
   <% if @more_params %>
index 3eb9561a2d2105a7d3ec0443de412f3739532dc6..13144d568eda6306a3f0345278669f9c2fa3f6a0 100644 (file)
@@ -5,7 +5,7 @@
     <%= t("site.sidebar.search_results") %>
 </h2>
 <% @sources.each do |source| %>
-  <h4 class="inner12"><%= raw(t(".title.#{source}")) %></h4>
+  <h4 class="inner12"><%= t(".title.#{source}_html") %></h4>
   <div class="search_results_entry" data-href="<%= url_for @params.merge(:action => "search_#{source}") %>">
     <%= image_tag "searching.gif", :class => "loader" %>
   </div>
index 7ff0948db5e961fa53379ebea39e50c166aeb901..54932cc56b4f8f1e3b3905d4cd7760d7993ae470 100644 (file)
@@ -1,15 +1,15 @@
 <div class="issue-comments">
   <% comments.each do |comment| %>
     <div class="comment">
-      <div style="float:left">
+      <div class="float-left">
         <%= link_to user_thumbnail(comment.user), user_path(comment.user) %>
       </div>
-      <b> <%= link_to comment.user.display_name, user_path(comment.user) %> </b> <br />
-      <%= comment.body %>
+      <p class="text-muted mb-0">
+        <%= t ".comment_from_html", :user_link => link_to(comment.user.display_name, user_path(comment.user)),
+                                    :comment_created_at => l(comment.created_at.to_datetime, :format => :friendly) %>
+      </p>
+      <p><%= comment.body %></p>
     </div>
-    <span class="deemphasize">
-      <%= t(".created_at", :datetime => l(comment.created_at.to_datetime, :format => :friendly)) %>
-    </span>
     <hr>
   <% end %>
 </div>
index 1a43a73c7372d21333f78c48b453cf3fb3cf2efb..143f2d887106cb996406f62358b4d4e8f3429465 100644 (file)
@@ -1,16 +1,14 @@
 <% reports.each do |report| %>
   <div class="report">
-    <div style="float:left">
+    <div class="float-left">
       <%= link_to user_thumbnail(report.user), user_path(report.user) %>
     </div>
-    <%= t ".reported_by_html", :category => report.category, :user => link_to(report.user.display_name, user_path(report.user)) %>
-    <br />
-    <span class="deemphasize">
-      <%= t(".updated_at", :datetime => l(report.updated_at.to_datetime, :format => :friendly)) %>
-    </span>
-    <br />
-    <%= report.details %>
-    <br />
+    <p class="text-muted mb-0">
+      <%= t ".reported_by_html", :category => report.category,
+                                 :user => link_to(report.user.display_name, user_path(report.user)),
+                                 :updated_at => l(report.updated_at.to_datetime, :format => :friendly) %>
+    </p>
+    <p><%= report.details %></p>
   </div>
   <hr>
 <% end %>
index 1c45f41246395d9eb97d4598fef1df7db910ee1b..1f3ca3118f54b824278ab1dc09b979b6c8899a40 100644 (file)
@@ -18,7 +18,7 @@
 
 <br />
 
-<table class="issues-list">
+<table class="table table-sm">
   <thead>
     <tr>
       <th><%= t ".status" %></th>
@@ -32,7 +32,7 @@
     <% @issues.each do |issue| %>
       <tr>
         <td><%= t ".states.#{issue.status}" %></td>
-        <td><%= link_to t(".reports_count", :count => issue.reports_count), issue %></td>
+        <td class="text-nowrap"><%= link_to t(".reports_count", :count => issue.reports_count), issue %></td>
         <td><%= link_to reportable_title(issue.reportable), reportable_url(issue.reportable) %></td>
         <td><%= link_to issue.reported_user.display_name, user_path(issue.reported_user) if issue.reported_user %></td>
         <td>
index 3e2f5ef488e34202519d9fe760e6191c14f12216..5efa6a1bb776b754cb86129622fec24faac88fbf 100644 (file)
@@ -19,9 +19,8 @@
 <p><%= link_to t(".reopen"), reopen_issue_url(@issue), :method => :post if @issue.may_reopen? %></p>
 <% end %>
 
-<div class="report-related-block">
-
-  <div class="report-block">
+<div class="row">
+  <div class="col-md-8">
     <h3><%= t ".reports_of_this_issue" %></h3>
 
     <% if @read_reports.present? %>
       <%= render "reports", :reports => @unread_reports %>
     </div>
     <% end %>
-    <br />
   </div>
 
   <% if @issue.reported_user %>
-    <div class="related-reports">
+    <div class="col-md-4">
       <h3><%= t ".other_issues_against_this_user" %></h3>
       <% if @related_issues.count > 1 %>
         <ul>
index 8b04dac91d465f44844ffe7145519c9375c70732..0f39c4a47f3f7d0df5f415422e4fc749ff5d1615 100644 (file)
@@ -1,7 +1,7 @@
 <% if flash[:error] %>
   <div class="flash error">
     <picture>
-      <source srcset="<%= image_path "notice.svg" %>" type="image/svg+xml"></source>
+      <source srcset="<%= image_path "notice.svg" %>" type="image/svg+xml" />
       <%= image_tag("notice.png", :srcset => image_path("notice.svg"), :class => "small_icon", :border => 0) %>
     </picture>
     <div class="message"><%= flash[:error] %></div>
index c95cc4a75d447375c544c3b38d1a5a22e1dd6a4f..06823e18f81f1debcd658cbb2e0c61f8a55159f6 100644 (file)
@@ -2,7 +2,7 @@
   <h1>
     <a href="<%= root_path %>" class="geolink">
       <picture>
-        <source srcset="<%= image_path "osm_logo.svg" %>" type="image/svg+xml"></source>
+        <source srcset="<%= image_path "osm_logo.svg" %>" type="image/svg+xml" />
         <%= image_tag "osm_logo.png", :srcset => image_path("osm_logo.svg"), :alt => t("layouts.logo.alt_text"), :class => "logo" %>
       </picture>
       <%= t "layouts.project_name.h1" %>
   <a href="#" id="menu-icon"></a>
   <nav class='primary'>
     <%= content_for :header %>
-    <ul>
-      <li id="edit_tab" class="dropdown <%= current_page_class(edit_path) %>">
+    <div class="btn-group">
+      <div id="edit_tab" class="btn-group <%= current_page_class(edit_path) %>">
         <%= link_to t("layouts.edit"),
                     edit_path,
-                    :class => "tab geolink editlink",
+                    :class => "btn btn-outline-primary geolink editlink",
                     :id => "editanchor",
                     :data => { :editor => preferred_editor } %>
-        <a class='dropdown-toggle' data-toggle='dropdown' href='#'><b class="caret"></b></a>
+        <a class='btn btn-outline-primary dropdown-toggle dropdown-toggle-split' data-toggle='dropdown' href='#'></a>
         <ul class='dropdown-menu'>
           <% Editors::RECOMMENDED_EDITORS.each do |editor| %>
             <li>
               <%= link_to t("layouts.edit_with", :editor => t("editor.#{editor}.description")),
                           edit_path(:editor => editor),
                           :data => { :editor => editor },
-                          :class => "geolink editlink" %>
+                          :class => "geolink editlink dropdown-item" %>
             </li>
           <% end %>
         </ul>
-      </li>
-      <li id="history_tab" class="<%= current_page_class(history_path) %>">
-        <%= link_to t("layouts.history"), history_path, :class => "tab geolink" %>
-      </li>
-      <li id="export_tab" class="<%= current_page_class(export_path) %>">
-        <%= link_to t("layouts.export"), export_path, :class => "tab geolink" %>
-      </li>
-    </ul>
+      </div>
+      <%= link_to t("layouts.history"), history_path, :class => "btn btn-outline-primary geolink flex-grow-1 current_page_class(history_path)", :id => "history_tab" %>
+      <%= link_to t("layouts.export"), export_path, :class => "btn btn-outline-primary geolink current_page_class(export_path)", :id => "export_tab" %>
+    </div>
   </nav>
   <nav class='secondary'>
-    <ul>
+    <ul class='mx-1 px-0'>
       <% if can? :index, Issue %>
-        <li class="compact-hide <%= current_page_class(issues_path) %>">
-          <%= link_to issues_path(:status => "open") do %>
+        <li class="compact-hide nav-item <%= current_page_class(issues_path) %>">
+          <%= link_to issues_path(:status => "open"), :class => "nav-link" do %>
             <%= t("layouts.issues") %>
             <%= open_issues_count %>
           <% end -%>
         </li>
       <% end %>
-      <li class="compact-hide <%= current_page_class(traces_path) %>"><%= link_to t("layouts.gps_traces"), traces_path %></li>
-      <li class="compact-hide <%= current_page_class(diary_entries_path) %>"><%= link_to t("layouts.user_diaries"), diary_entries_path %></li>
-      <li class="compact-hide <%= current_page_class(copyright_path) %>"><%= link_to t("layouts.copyright"), copyright_path %></li>
-      <li class="compact-hide <%= current_page_class(help_path) %>"><%= link_to t("layouts.help"), help_path %></li>
-      <li class="compact-hide <%= current_page_class(about_path) %>"><%= link_to t("layouts.about"), about_path %></li>
-      <li id="compact-secondary-nav" class="dropdown">
-        <a class="dropdown-toggle" data-toggle="dropdown" href="#"><%= t "layouts.more" %> <b class="caret"></b></a>
+      <li class="compact-hide nav-item <%= current_page_class(traces_path) %>">
+        <%= link_to t("layouts.gps_traces"), traces_path, :class => "nav-link" %>
+      </li>
+      <li class="compact-hide nav-item <%= current_page_class(diary_entries_path) %>">
+        <%= link_to t("layouts.user_diaries"), diary_entries_path, :class => "nav-link" %>
+      </li>
+      <li class="compact-hide nav-item <%= current_page_class(copyright_path) %>">
+        <%= link_to t("layouts.copyright"), copyright_path, :class => "nav-link" %>
+      </li>
+      <li class="compact-hide nav-item <%= current_page_class(help_path) %>">
+        <%= link_to t("layouts.help"), help_path, :class => "nav-link" %>
+      </li>
+      <li class="compact-hide nav-item <%= current_page_class(about_path) %>">
+        <%= link_to t("layouts.about"), about_path, :class => "nav-link" %>
+      </li>
+      <li id="compact-secondary-nav" class="dropdown nav-item">
+        <a class="dropdown-toggle nav-link" data-toggle="dropdown" href="#"><%= t "layouts.more" %></a>
         <ul class="dropdown-menu">
           <% if Settings.status != "database_offline" && can?(:index, Issue) %>
             <li class="<%= current_page_class(issues_path) %>">
-              <%= link_to issues_path(:status => "open") do %>
-                <%= open_issues_count %>
+              <%= link_to issues_path(:status => "open"), :class => "dropdown-item" do %>
                 <%= t("layouts.issues") %>
+                <%= open_issues_count %>
               <% end -%>
             </li>
           <% end %>
-          <li class="<%= current_page_class(traces_path) %>"><%= link_to t("layouts.gps_traces"), traces_path %></li>
-          <li class="<%= current_page_class(diary_entries_path) %>"><%= link_to t("layouts.user_diaries"), diary_entries_path %></li>
-          <li class="<%= current_page_class(copyright_path) %>"><%= link_to t("layouts.copyright"), copyright_path %></li>
-          <li class="<%= current_page_class(help_path) %>"><%= link_to t("layouts.help"), help_path %></li>
-          <li class="<%= current_page_class(about_path) %>"><%= link_to t("layouts.about"), about_path %></li>
+          <li class="<%= current_page_class(traces_path) %>"><%= link_to t("layouts.gps_traces"), traces_path, :class => "dropdown-item" %></li>
+          <li class="<%= current_page_class(diary_entries_path) %>"><%= link_to t("layouts.user_diaries"), diary_entries_path, :class => "dropdown-item" %></li>
+          <li class="<%= current_page_class(copyright_path) %>"><%= link_to t("layouts.copyright"), copyright_path, :class => "dropdown-item" %></li>
+          <li class="<%= current_page_class(help_path) %>"><%= link_to t("layouts.help"), help_path, :class => "dropdown-item" %></li>
+          <li class="<%= current_page_class(about_path) %>"><%= link_to t("layouts.about"), about_path, :class => "dropdown-item" %></li>
         </ul>
       </li>
     </ul>
     <% if current_user && current_user.id %>
-      <div class='dropdown user-menu logged-in'>
-        <a class='dropdown-toggle' data-toggle='dropdown' href="#">
+      <div class='d-inline-flex dropdown user-menu logged-in clearfix'>
+        <a class='dropdown-toggle btn btn-outline-secondary pl-2 py-1 flex-grow-1' data-toggle='dropdown' href="#">
           <%= user_thumbnail_tiny(current_user, :width => 25, :height => 25) %>
           <%= render :partial => "layouts/inbox" %>
           <span class="user-button">
             <span class='username'>
               <%= current_user.display_name %>
             </span>
-            <b class="caret"></b>