]> git.openstreetmap.org Git - rails.git/commitdiff
Merge remote-tracking branch 'upstream/pull/5652'
authorTom Hughes <tom@compton.nu>
Thu, 27 Feb 2025 20:28:47 +0000 (20:28 +0000)
committerTom Hughes <tom@compton.nu>
Thu, 27 Feb 2025 20:28:47 +0000 (20:28 +0000)
65 files changed:
.github/workflows/danger.yml
.github/workflows/lint.yml
.github/workflows/tests.yml
.rubocop.yml
Dockerfile
Gemfile
Gemfile.lock
INSTALL.md
app/abilities/api_ability.rb
app/controllers/api/changeset_comments/visibilities_controller.rb [new file with mode: 0644]
app/controllers/api/changeset_comments_controller.rb
app/controllers/api/changesets/downloads_controller.rb [new file with mode: 0644]
app/controllers/api/changesets_controller.rb
app/controllers/notes_controller.rb
app/controllers/users/lists_controller.rb
app/helpers/note_helper.rb
app/helpers/svg_helper.rb
app/models/note.rb
app/views/api/changeset_comments/visibilities/create.json.jbuilder [new file with mode: 0644]
app/views/api/changeset_comments/visibilities/create.xml.builder [new file with mode: 0644]
app/views/api/changeset_comments/visibilities/destroy.json.jbuilder [new file with mode: 0644]
app/views/api/changeset_comments/visibilities/destroy.xml.builder [new file with mode: 0644]
app/views/api/changesets/downloads/show.xml.builder [moved from app/views/api/changesets/download.xml.builder with 55% similarity]
app/views/changesets/index.atom.builder
app/views/changesets/show.html.erb
app/views/notes/index.html.erb
app/views/notes/show.html.erb
app/views/users/lists/show.html.erb
bin/setup
config/initializers/cors.rb
config/locales/be.yml
config/locales/cs.yml
config/locales/cy.yml
config/locales/da.yml
config/locales/de.yml
config/locales/en.yml
config/locales/eo.yml
config/locales/fr.yml
config/locales/gl.yml
config/locales/it.yml
config/locales/ko.yml
config/locales/lb.yml
config/locales/nl.yml
config/locales/pl.yml
config/locales/ru.yml
config/locales/skr-arab.yml
config/locales/sl.yml
config/locales/sr.yml
config/locales/sv.yml
config/locales/th.yml
config/locales/tr.yml
config/locales/uk.yml
config/locales/xmf.yml
config/locales/zh-CN.yml
config/routes.rb
lib/osm.rb
test/abilities/api_capability_test.rb
test/controllers/api/changeset_comments/visibilities_controller_test.rb [new file with mode: 0644]
test/controllers/api/changeset_comments_controller_test.rb
test/controllers/api/changesets/downloads_controller_test.rb [new file with mode: 0644]
test/controllers/api/changesets_controller_test.rb
test/controllers/api/relations_controller_test.rb
test/controllers/notes_controller_test.rb
test/controllers/users/lists_controller_test.rb
yarn.lock

index 66c2aca5113f870efe89bfbb72290a4d3e032dac..8c6680bdc998d3176422c6729b3121c55b0006a4 100644 (file)
@@ -19,7 +19,7 @@ jobs:
       - name: Setup ruby
         uses: ruby/setup-ruby@v1
         with:
-          ruby-version: 3.1
+          ruby-version: 3.2
           rubygems: 3.4.10
           bundler-cache: true
       - name: Create base branch
index 8383068b3f08d9e8893a1692334f51f55f254cdd..433b523f7c866f12defc6717a2807d26fb77dfdc 100644 (file)
@@ -6,7 +6,7 @@ concurrency:
   group: ${{ github.workflow }}-${{ github.head_ref || github.ref }}
   cancel-in-progress: true
 env:
-  ruby: '3.1'
+  ruby: '3.2'
 jobs:
   rubocop:
     name: RuboCop
index d1cf73eaefc7d239a7fe5af072007ee702f3d236..841155bf643da3dcdee31b4ae284211dd9ed122a 100644 (file)
@@ -10,7 +10,7 @@ jobs:
     name: Ruby ${{ matrix.ruby }}
     strategy:
       matrix:
-        ruby: ['3.1', '3.2', '3.3', '3.4']
+        ruby: ['3.2', '3.3', '3.4']
     runs-on: ubuntu-latest
     env:
       RAILS_ENV: test
index eedbf47e0d341311dc2abd723cef53a1ef5b803b..64a8d0a0f04509b6dad14174ab882718d524f452 100644 (file)
@@ -12,7 +12,7 @@ require:
   - ./.rubocop/specific_action_names.rb
 
 AllCops:
-  TargetRubyVersion: 3.1
+  TargetRubyVersion: 3.2
   NewCops: enable
   Exclude:
     - 'vendor/**/*'
@@ -108,6 +108,10 @@ Style/MixinUsage:
     - 'bin/setup'
     - 'bin/update'
 
+Style/RaiseArgs:
+  Exclude:
+    - 'lib/osm.rb'
+
 Style/StringLiterals:
   EnforcedStyle: double_quotes
 
@@ -129,7 +133,6 @@ Rails/SpecificActionNames:
     - app/controllers/**/*.rb
   Exclude:
     # This is a todo list, but is currently too long for `rubocop --auto-gen-config`
-    - 'app/controllers/api/changeset_comments_controller.rb'
     - 'app/controllers/api/changesets_controller.rb'
     - 'app/controllers/api/notes_controller.rb'
     - 'app/controllers/api/user_preferences_controller.rb'
index dae25be3fcc3b848d15b02844f3687aaecbf7914..5df13d717e9cd655eec5c639315e282061725b43 100644 (file)
@@ -1,4 +1,4 @@
-FROM debian:bookworm
+FROM ruby:3.2-bookworm
 
 ENV DEBIAN_FRONTEND=noninteractive
 
diff --git a/Gemfile b/Gemfile
index 2765b1ae71fe39b643993629b70adc69dc0bec7a..660ff7f1a2adba56990d17b7b5454c02d5f5b2e5 100644 (file)
--- a/Gemfile
+++ b/Gemfile
@@ -55,7 +55,6 @@ gem "bootstrap_form", "~> 5.0"
 gem "cancancan"
 gem "config"
 gem "delayed_job_active_record"
-gem "dry-schema", "< 1.14.0" # see https://github.com/openstreetmap/openstreetmap-website/issues/5482
 gem "dry-validation"
 gem "frozen_record"
 gem "http_accept_language", "~> 2.1.1"
@@ -139,9 +138,6 @@ gem "image_processing"
 # Used to validate widths
 gem "unicode-display_width"
 
-# Lock some modules to old versions for ruby 3.1 support
-gem "zeitwerk", "< 2.7"
-
 # Gems useful for development
 group :development do
   gem "better_errors"
index c5f6f4b796538c69d49992ce0037c41db4c16b0a..66f09585dc94479228d5f84e667273ba884a8815 100644 (file)
@@ -92,17 +92,17 @@ GEM
     autoprefixer-rails (10.4.19.0)
       execjs (~> 2)
     aws-eventstream (1.3.1)
-    aws-partitions (1.1051.0)
-    aws-sdk-core (3.218.1)
+    aws-partitions (1.1054.0)
+    aws-sdk-core (3.219.0)
       aws-eventstream (~> 1, >= 1.3.0)
       aws-partitions (~> 1, >= 1.992.0)
       aws-sigv4 (~> 1.9)
       base64
       jmespath (~> 1, >= 1.6.1)
-    aws-sdk-kms (1.98.0)
+    aws-sdk-kms (1.99.0)
       aws-sdk-core (~> 3, >= 3.216.0)
       aws-sigv4 (~> 1.5)
-    aws-sdk-s3 (1.181.0)
+    aws-sdk-s3 (1.182.0)
       aws-sdk-core (~> 3, >= 3.216.0)
       aws-sdk-kms (~> 1)
       aws-sigv4 (~> 1.5)
@@ -231,13 +231,13 @@ GEM
       concurrent-ruby (~> 1.0)
       dry-core (~> 1.1)
       zeitwerk (~> 2.6)
-    dry-schema (1.13.4)
+    dry-schema (1.14.0)
       concurrent-ruby (~> 1.0)
       dry-configurable (~> 1.0, >= 1.0.1)
-      dry-core (~> 1.0, < 2)
-      dry-initializer (~> 3.0)
-      dry-logic (>= 1.4, < 2)
-      dry-types (>= 1.7, < 2)
+      dry-core (~> 1.1)
+      dry-initializer (~> 3.2)
+      dry-logic (~> 1.5)
+      dry-types (~> 1.8)
       zeitwerk (~> 2.6)
     dry-types (1.8.2)
       bigdecimal (~> 3.0)
@@ -246,11 +246,11 @@ GEM
       dry-inflector (~> 1.0)
       dry-logic (~> 1.4)
       zeitwerk (~> 2.6)
-    dry-validation (1.10.0)
+    dry-validation (1.11.1)
       concurrent-ruby (~> 1.0)
-      dry-core (~> 1.0, < 2)
-      dry-initializer (~> 3.0)
-      dry-schema (>= 1.12, < 2)
+      dry-core (~> 1.1)
+      dry-initializer (~> 3.2)
+      dry-schema (~> 1.14)
       zeitwerk (~> 2.6)
     erb_lint (0.9.0)
       activesupport
@@ -372,7 +372,7 @@ GEM
     marcel (1.0.4)
     matrix (0.4.2)
     maxminddb (0.1.22)
-    mini_magick (5.1.2)
+    mini_magick (5.2.0)
       benchmark
       logger
     mini_mime (1.1.5)
@@ -579,7 +579,7 @@ GEM
       lint_roller (~> 1.1)
       rubocop (>= 1.72.1, < 2.0)
       rubocop-ast (>= 1.38.0, < 2.0)
-    rubocop-rails (2.30.1)
+    rubocop-rails (2.30.2)
       activesupport (>= 4.2.0)
       lint_roller (~> 1.1)
       rack (>= 1.1)
@@ -632,7 +632,7 @@ GEM
       actionpack (>= 6.1)
       activesupport (>= 6.1)
       sprockets (>= 3.0.0)
-    stringio (3.1.3)
+    stringio (3.1.5)
     strong_migrations (1.8.0)
       activerecord (>= 5.2)
     teaspoon (1.4.0)
@@ -658,7 +658,7 @@ GEM
       i18n (>= 0.8.0)
       simpleidn
     vendorer (0.2.0)
-    version_gem (1.1.4)
+    version_gem (1.1.6)
     webmock (3.25.0)
       addressable (>= 2.8.0)
       crack (>= 0.3.2)
@@ -671,7 +671,7 @@ GEM
     websocket-extensions (0.1.5)
     xpath (3.2.0)
       nokogiri (~> 1.8)
-    zeitwerk (2.6.18)
+    zeitwerk (2.7.2)
 
 PLATFORMS
   ruby
@@ -708,7 +708,6 @@ DEPENDENCIES
   doorkeeper
   doorkeeper-i18n
   doorkeeper-openid_connect
-  dry-schema (< 1.14.0)
   dry-validation
   erb_lint
   factory_bot_rails
@@ -782,7 +781,6 @@ DEPENDENCIES
   validates_email_format_of (>= 1.5.1)
   vendorer
   webmock
-  zeitwerk (< 2.7)
 
 BUNDLED WITH
    2.5.22
index 8667fb5129161efca38295559202e64fd86475b4..cb54cb5433af361824a542220ae8850636ea36db 100644 (file)
@@ -22,7 +22,7 @@ of packages required before you can get the various gems installed.
 
 ## Minimum requirements
 
-* Ruby 3.1+
+* Ruby 3.2+
 * PostgreSQL 13+
 * Bundler (see note below about [developer Ruby setup](#rbenv))
 * Javascript Runtime
index edf051faeb8f324b1a1767bbafeff07149dd222b..ef852b69fe72f48cde9434c74235f54fd0fff0a4 100644 (file)
@@ -10,7 +10,7 @@ class ApiAbility
       can [:read, :feed, :search], Note
       can :create, Note unless user
 
-      can [:read, :download], Changeset
+      can :read, Changeset
       can :read, ChangesetComment
       can :read, Tracepoint
       can :read, User
@@ -40,7 +40,7 @@ class ApiAbility
         end
 
         if user.moderator?
-          can [:destroy, :restore], ChangesetComment if scopes.include?("write_changeset_comments")
+          can [:create, :destroy], :changeset_comment_visibility if scopes.include?("write_changeset_comments")
 
           can :destroy, Note if scopes.include?("write_notes")
 
diff --git a/app/controllers/api/changeset_comments/visibilities_controller.rb b/app/controllers/api/changeset_comments/visibilities_controller.rb
new file mode 100644 (file)
index 0000000..397872c
--- /dev/null
@@ -0,0 +1,60 @@
+module Api
+  module ChangesetComments
+    class VisibilitiesController < ApiController
+      before_action :check_api_writable
+      before_action :authorize
+
+      authorize_resource :class => :changeset_comment_visibility
+
+      before_action :set_request_formats
+
+      ##
+      # Sets visible flag on comment to true
+      def create
+        # Check the arguments are sane
+        raise OSM::APIBadUserInput, "No id was given" unless params[:changeset_comment_id]
+
+        # Extract the arguments
+        changeset_comment_id = params[:changeset_comment_id].to_i
+
+        # Find the changeset
+        comment = ChangesetComment.find(changeset_comment_id)
+
+        # Unhide the comment
+        comment.update(:visible => true)
+
+        # Return a copy of the updated changeset
+        @changeset = comment.changeset
+
+        respond_to do |format|
+          format.xml
+          format.json
+        end
+      end
+
+      ##
+      # Sets visible flag on comment to false
+      def destroy
+        # Check the arguments are sane
+        raise OSM::APIBadUserInput, "No id was given" unless params[:changeset_comment_id]
+
+        # Extract the arguments
+        changeset_comment_id = params[:changeset_comment_id].to_i
+
+        # Find the changeset
+        comment = ChangesetComment.find(changeset_comment_id)
+
+        # Hide the comment
+        comment.update(:visible => false)
+
+        # Return a copy of the updated changeset
+        @changeset = comment.changeset
+
+        respond_to do |format|
+          format.xml
+          format.json
+        end
+      end
+    end
+  end
+end
index 808ac97ea3418257445177caeb8615cc2574ff78..2f4a361baa47b58f5f44f9ca0aa6db45359a403e 100644 (file)
@@ -24,16 +24,16 @@ module Api
     # Add a comment to a changeset
     def create
       # Check the arguments are sane
-      raise OSM::APIBadUserInput, "No id was given" unless params[:id]
+      raise OSM::APIBadUserInput, "No id was given" unless params[:changeset_id]
       raise OSM::APIBadUserInput, "No text was given" if params[:text].blank?
       raise OSM::APIRateLimitExceeded if rate_limit_exceeded?
 
       # Extract the arguments
-      id = params[:id].to_i
+      changeset_id = params[:changeset_id].to_i
       body = params[:text]
 
       # Find the changeset and check it is valid
-      changeset = Changeset.find(id)
+      changeset = Changeset.find(changeset_id)
       raise OSM::APIChangesetNotYetClosedError, changeset if changeset.open?
 
       # Add a comment to the changeset
@@ -59,56 +59,6 @@ module Api
       end
     end
 
-    ##
-    # Sets visible flag on comment to false
-    def destroy
-      # Check the arguments are sane
-      raise OSM::APIBadUserInput, "No id was given" unless params[:id]
-
-      # Extract the arguments
-      id = params[:id].to_i
-
-      # Find the changeset
-      comment = ChangesetComment.find(id)
-
-      # Hide the comment
-      comment.update(:visible => false)
-
-      # Return a copy of the updated changeset
-      @changeset = comment.changeset
-      render "api/changesets/show"
-
-      respond_to do |format|
-        format.xml
-        format.json
-      end
-    end
-
-    ##
-    # Sets visible flag on comment to true
-    def restore
-      # Check the arguments are sane
-      raise OSM::APIBadUserInput, "No id was given" unless params[:id]
-
-      # Extract the arguments
-      id = params[:id].to_i
-
-      # Find the changeset
-      comment = ChangesetComment.find(id)
-
-      # Unhide the comment
-      comment.update(:visible => true)
-
-      # Return a copy of the updated changeset
-      @changeset = comment.changeset
-      render "api/changesets/show"
-
-      respond_to do |format|
-        format.xml
-        format.json
-      end
-    end
-
     private
 
     ##
diff --git a/app/controllers/api/changesets/downloads_controller.rb b/app/controllers/api/changesets/downloads_controller.rb
new file mode 100644 (file)
index 0000000..1abc9e4
--- /dev/null
@@ -0,0 +1,75 @@
+module Api
+  module Changesets
+    class DownloadsController < ApiController
+      before_action :setup_user_auth
+
+      authorize_resource :changeset
+
+      before_action :set_request_formats
+
+      ##
+      # download the changeset as an osmChange document.
+      #
+      # to make it easier to revert diffs it would be better if the osmChange
+      # format were reversible, i.e: contained both old and new versions of
+      # modified elements. but it doesn't at the moment...
+      #
+      # this method cannot order the database changes fully (i.e: timestamp and
+      # version number may be too coarse) so the resulting diff may not apply
+      # to a different database. however since changesets are not atomic this
+      # behaviour cannot be guaranteed anyway and is the result of a design
+      # choice.
+      def show
+        changeset = Changeset.find(params[:changeset_id])
+
+        # get all the elements in the changeset which haven't been redacted
+        # and stick them in a big array.
+        elements = if show_redactions?
+                     [changeset.old_nodes,
+                      changeset.old_ways,
+                      changeset.old_relations].flatten
+                   else
+                     [changeset.old_nodes.unredacted,
+                      changeset.old_ways.unredacted,
+                      changeset.old_relations.unredacted].flatten
+                   end
+
+        # sort the elements by timestamp and version number, as this is the
+        # almost sensible ordering available. this would be much nicer if
+        # global (SVN-style) versioning were used - then that would be
+        # unambiguous.
+        elements.sort_by! { |e| [e.timestamp, e.version] }
+
+        # generate an output element for each operation. note: we avoid looking
+        # at the history because it is simpler - but it would be more correct to
+        # check these assertions.
+        @created = []
+        @modified = []
+        @deleted = []
+
+        elements.each do |elt|
+          if elt.version == 1
+            # first version, so it must be newly-created.
+            @created << elt
+          elsif elt.visible
+            # must be a modify
+            @modified << elt
+          else
+            # if the element isn't visible then it must have been deleted
+            @deleted << elt
+          end
+        end
+
+        respond_to do |format|
+          format.xml
+        end
+      end
+
+      private
+
+      def show_redactions?
+        current_user&.moderator? && params[:show_redactions] == "true"
+      end
+    end
+  end
+end
index 3df7b75cea752aeb11a0681b728377d6f6a0d0e0..7f0ee1276752d8b986ff3ab0d7b274c6ae657886 100644 (file)
@@ -63,7 +63,7 @@ module Api
       @changeset = Changeset.find(params[:id])
       if params[:include_discussion].presence
         @comments = @changeset.comments
-        @comments = @comments.unscope(:where => :visible) if params[:show_hidden_comments].presence && can?(:restore, ChangesetComment)
+        @comments = @comments.unscope(:where => :visible) if params[:show_hidden_comments].presence && can?(:create, :changeset_comment_visibility)
         @comments = @comments.includes(:author)
       end
 
@@ -130,58 +130,6 @@ module Api
       end
     end
 
-    ##
-    # download the changeset as an osmChange document.
-    #
-    # to make it easier to revert diffs it would be better if the osmChange
-    # format were reversible, i.e: contained both old and new versions of
-    # modified elements. but it doesn't at the moment...
-    #
-    # this method cannot order the database changes fully (i.e: timestamp and
-    # version number may be too coarse) so the resulting diff may not apply
-    # to a different database. however since changesets are not atomic this
-    # behaviour cannot be guaranteed anyway and is the result of a design
-    # choice.
-    def download
-      changeset = Changeset.find(params[:id])
-
-      # get all the elements in the changeset which haven't been redacted
-      # and stick them in a big array.
-      elements = [changeset.old_nodes.unredacted,
-                  changeset.old_ways.unredacted,
-                  changeset.old_relations.unredacted].flatten
-
-      # sort the elements by timestamp and version number, as this is the
-      # almost sensible ordering available. this would be much nicer if
-      # global (SVN-style) versioning were used - then that would be
-      # unambiguous.
-      elements.sort_by! { |e| [e.timestamp, e.version] }
-
-      # generate an output element for each operation. note: we avoid looking
-      # at the history because it is simpler - but it would be more correct to
-      # check these assertions.
-      @created = []
-      @modified = []
-      @deleted = []
-
-      elements.each do |elt|
-        if elt.version == 1
-          # first version, so it must be newly-created.
-          @created << elt
-        elsif elt.visible
-          # must be a modify
-          @modified << elt
-        else
-          # if the element isn't visible then it must have been deleted
-          @deleted << elt
-        end
-      end
-
-      respond_to do |format|
-        format.xml
-      end
-    end
-
     ##
     # updates a changeset's tags. none of the changeset's attributes are
     # user-modifiable, so they will be ignored.
index 5d817c9c6cf3f525d4669de10174332978f3e6f0..fbeb01b76425b890f4b69dd603ddf96e255abf8c 100644 (file)
@@ -43,7 +43,7 @@ class NotesController < ApplicationController
 
     @note_includes_anonymous = @note.author.nil? || @note_comments.find { |comment| comment.author.nil? }
 
-    @note_comments = @note_comments.drop(1) if @note.author.nil? || @note.author.active?
+    @note_comments = @note_comments.drop(1) if @note_comments.first&.event == "opened"
   rescue ActiveRecord::RecordNotFound
     render :template => "browse/not_found", :status => :not_found
   end
index a2f35e9b213a8f5ea91a4a8ecbbe76ca1e889c97..7e3fa2a32801cb70854c8e4fc8b3f0dea9d70d66 100644 (file)
@@ -13,10 +13,11 @@ module Users
     ##
     # display a list of users matching specified criteria
     def show
-      @params = params.permit(:status, :ip, :before, :after)
+      @params = params.permit(:status, :username, :ip, :before, :after)
 
       users = User.all
       users = users.where(:status => @params[:status]) if @params[:status].present?
+      users = users.where("LOWER(email) = LOWER(?) OR LOWER(NORMALIZE(display_name, NFKC)) = LOWER(NORMALIZE(?, NFKC))", @params[:username], @params[:username]) if @params[:username].present?
       users = users.where("creation_address <<= ?", @params[:ip]) if @params[:ip].present?
 
       @users_count = users.limit(501).count
index 0ba5032288b7c7b7ebb9803a00fc230506372775..7a75a4ff53c22ea2b0da5816978e03bf8846ea1a 100644 (file)
@@ -1,9 +1,11 @@
 module NoteHelper
   include ActionView::Helpers::TranslationHelper
 
-  def note_description(author, description)
+  def note_description(author, description, first_comment)
     if !author.nil? && author.status == "deleted"
       RichText.new("text", t("notes.show.description_when_author_is_deleted"))
+    elsif first_comment&.event != "opened"
+      RichText.new("text", t("notes.show.description_when_there_is_no_opening_comment"))
     else
       description
     end
index b04ab1b0d56439a7b921c341bc2a4e731c365e12..7fb6b4321fcafe96deb631d9fa4699b224779d3e 100644 (file)
@@ -5,12 +5,12 @@ module SvgHelper
     tag.svg path_tag, :width => 16, :height => 16
   end
 
-  def previous_page_svg_tag(**options)
-    adjacent_page_svg_tag(dir == "rtl" ? 1 : -1, **options)
+  def previous_page_svg_tag(**)
+    adjacent_page_svg_tag(dir == "rtl" ? 1 : -1, **)
   end
 
-  def next_page_svg_tag(**options)
-    adjacent_page_svg_tag(dir == "rtl" ? -1 : 1, **options)
+  def next_page_svg_tag(**)
+    adjacent_page_svg_tag(dir == "rtl" ? -1 : 1, **)
   end
 
   def key_svg_tag(**options)
index b7215d6f7706084318ecefcd845d303771f14fc9..0a1f4abd7fed5594c0560180d5c77163a4066a02 100644 (file)
@@ -95,7 +95,7 @@ class Note < ApplicationRecord
   # Return the note's description, derived from the first comment
   def description
     if user_ip.nil? && user_id.nil?
-      all_comments.first.body
+      all_comments.first.body if all_comments.first&.event == "opened"
     else
       RichText.new("text", super)
     end
@@ -104,7 +104,7 @@ class Note < ApplicationRecord
   # Return the note's author object, derived from the first comment
   def author
     if user_ip.nil? && user_id.nil?
-      all_comments.first.author
+      all_comments.first.author if all_comments.first&.event == "opened"
     else
       super
     end
diff --git a/app/views/api/changeset_comments/visibilities/create.json.jbuilder b/app/views/api/changeset_comments/visibilities/create.json.jbuilder
new file mode 100644 (file)
index 0000000..c245cd1
--- /dev/null
@@ -0,0 +1,5 @@
+json.partial! "api/root_attributes"
+
+json.changeset do
+  json.partial! "api/changesets/changeset", :changeset => @changeset
+end
diff --git a/app/views/api/changeset_comments/visibilities/create.xml.builder b/app/views/api/changeset_comments/visibilities/create.xml.builder
new file mode 100644 (file)
index 0000000..84892f7
--- /dev/null
@@ -0,0 +1,5 @@
+xml.instruct! :xml, :version => "1.0"
+
+xml.osm(OSM::API.new.xml_root_attributes) do |osm|
+  osm << render(:partial => "api/changesets/changeset", :object => @changeset)
+end
diff --git a/app/views/api/changeset_comments/visibilities/destroy.json.jbuilder b/app/views/api/changeset_comments/visibilities/destroy.json.jbuilder
new file mode 100644 (file)
index 0000000..c245cd1
--- /dev/null
@@ -0,0 +1,5 @@
+json.partial! "api/root_attributes"
+
+json.changeset do
+  json.partial! "api/changesets/changeset", :changeset => @changeset
+end
diff --git a/app/views/api/changeset_comments/visibilities/destroy.xml.builder b/app/views/api/changeset_comments/visibilities/destroy.xml.builder
new file mode 100644 (file)
index 0000000..84892f7
--- /dev/null
@@ -0,0 +1,5 @@
+xml.instruct! :xml, :version => "1.0"
+
+xml.osm(OSM::API.new.xml_root_attributes) do |osm|
+  osm << render(:partial => "api/changesets/changeset", :object => @changeset)
+end
similarity index 55%
rename from app/views/api/changesets/download.xml.builder
rename to app/views/api/changesets/downloads/show.xml.builder
index 1e400cd9feceef34b4d857e31fa0a6d6497418c9..88e05b587c4c08600cabef92efdf8770169c1453 100644 (file)
@@ -3,17 +3,17 @@ xml.instruct! :xml, :version => "1.0"
 xml.osmChange(OSM::API.new.xml_root_attributes) do |osm|
   @created.each do |elt|
     osm.create do |create|
-      create << render(elt)
+      create << render(:partial => "api/#{elt.to_partial_path}", :object => elt)
     end
   end
   @modified.each do |elt|
     osm.modify do |modify|
-      modify << render(elt)
+      modify << render(:partial => "api/#{elt.to_partial_path}", :object => elt)
     end
   end
   @deleted.each do |elt|
     osm.delete do |delete|
-      delete << render(elt)
+      delete << render(:partial => "api/#{elt.to_partial_path}", :object => elt)
     end
   end
 end
index c8ffe1a81c973a852af251c1147fa4e8d024094d..6556f12bfe1641f54d79522abfb71510a2d3fb9b 100644 (file)
@@ -18,10 +18,10 @@ atom_feed(:language => I18n.locale, :schema_date => 2009,
   @changesets.each do |changeset|
     feed.entry(changeset, :updated => changeset.closed_at, :id => changeset_url(changeset.id, :only_path => false)) do |entry|
       entry.link :rel => "alternate",
-                 :href => changeset_show_url(changeset, :only_path => false),
+                 :href => api_changeset_url(changeset, :only_path => false),
                  :type => "application/osm+xml"
       entry.link :rel => "alternate",
-                 :href => changeset_download_url(changeset, :only_path => false),
+                 :href => api_changeset_download_url(changeset, :only_path => false),
                  :type => "application/osmChange+xml"
 
       if !changeset.tags.empty? && changeset.tags.key?("comment")
index 167bcb5cb807d5fc2d147a049853b2284125d25b..70b1877b5a0bb4789001e5d2ef9ebdb4a470f502 100644 (file)
@@ -49,8 +49,8 @@
               —
               <%= tag.button t(".#{comment.visible ? 'hide' : 'unhide'}_comment"),
                              :class => "btn btn-sm small btn-link link-secondary p-0 align-baseline",
-                             :data => { :method => "POST",
-                                        :url => comment.visible ? changeset_comment_hide_url(comment) : changeset_comment_unhide_url(comment) } %>
+                             :data => { :method => comment.visible ? "DELETE" : "POST",
+                                        :url => api_changeset_comment_visibility_path(comment) } %>
             <% end %>
           </small>
           <div class="mx-2">
@@ -81,7 +81,7 @@
                          :name => "comment",
                          :disabled => true,
                          :data => { :method => "POST",
-                                    :url => changeset_comment_url(@changeset) } %>
+                                    :url => api_changeset_changeset_comments_path(@changeset) } %>
         </div>
       </form>
     <% else %>
 </div>
 
 <div class='secondary-actions'>
-  <%= link_to(t(".changesetxml"), :controller => "api/changesets", :action => "show") %>
+  <%= link_to t(".changesetxml"), api_changeset_path(@changeset) %>
   &middot;
-  <%= link_to(t(".osmchangexml"), :controller => "api/changesets", :action => "download") %>
+  <%= link_to t(".osmchangexml"), api_changeset_download_path(@changeset) %>
 </div>
 
 <% if @next_by_user || @prev_by_user %>
index 0b39234a93e61f2eaf222b0e221906f48bf8c567..4bd7d499d3da7acec77a8996842a450e662c5e26 100644 (file)
@@ -48,7 +48,7 @@
       </td>
       <td><%= link_to note.id, note %></td>
       <td><%= note_author(note.author) %></td>
-      <td><%= note_description(note.author, note.description).to_html %></td>
+      <td><%= note_description(note.author, note.description, current_user&.moderator? ? note.comments.unscope(:where => :visible).first : note.comments.first).to_html %></td>
       <td><%= friendly_date_ago(note.created_at) %></td>
       <td><%= friendly_date_ago(note.updated_at) %></td>
     </tr>
index b65926b5f9df335fd7abbcfda573cfef93ad8c8c..d0dcdeb1b9417354c01f5e9830011b8d980a876b 100644 (file)
@@ -5,7 +5,7 @@
 <div>
   <h4><%= t(".description") %></h4>
   <div class="overflow-hidden ms-2">
-    <%= note_description(@note.author, @note.description).to_html %>
+    <%= note_description(@note.author, @note.description, current_user&.moderator? ? @note.comments.unscope(:where => :visible).first : @note.comments.first).to_html %>
   </div>
 
   <div class="details" data-coordinates="<%= @note.lat %>,<%= @note.lon %>" data-status="<%= @note.status %>">
index dd037c7af6c3a13dee647bf9ba3e077ca658be7e..c3d15d18afe6656385195801474c89a299f5f4cb 100644 (file)
                      :data => { :behavior => "category_dropdown" },
                      :class => "form-select" %>
     </div>
+    <div class="mb-3 col-md">
+      <%= text_field_tag :username,
+                         params[:username],
+                         :placeholder => t(".name_or_email"),
+                         :autocomplete => "on",
+                         :class => "form-control" %>
+    </div>
     <div class="mb-3 col-md">
       <%= text_field_tag :ip,
                          params[:ip],
index ff6a47532311f69c591687838c96795d49608cde..ced1ba3708f5024f01e48454ba2f15d9eac89a82 100755 (executable)
--- a/bin/setup
+++ b/bin/setup
@@ -4,8 +4,8 @@ require "fileutils"
 APP_ROOT = File.expand_path("..", __dir__)
 APP_NAME = "openstreetmap".freeze
 
-def system!(*args)
-  system(*args, :exception => true)
+def system!(*)
+  system(*, :exception => true)
 end
 
 FileUtils.chdir APP_ROOT do
index e7b813b73f788fb7bf4cac947007c2ff0154340d..710e2c00950e9e6c568bec3e57238d775462b8aa 100644 (file)
@@ -30,5 +30,6 @@ Rails.application.config.middleware.insert_before 0, OpenStreetMap::Cors do
     resource "/diary/*/rss", :headers => :any, :methods => [:get]
     resource "/trace/*/data", :headers => :any, :methods => [:get]
     resource "/user/*/diary/rss", :headers => :any, :methods => [:get]
+    resource "/rails/active_storage/*", :headers => :any, :methods => [:get]
   end
 end
index c2631f5f080dc08b2632bf09e06cef90749795f0..c32740cbc4e366f91a9b3dde6318bf3cb4c78476 100644 (file)
@@ -292,6 +292,7 @@ be:
   accounts:
     show:
       title: Рэдагаваць уліковы запіс
+      my_account: Мой уліковы запіс
       current email address: Бягучы адрас электроннай пошты
       external auth: Знешняя Аўтэнтыфікацыя
       openid:
@@ -373,6 +374,7 @@ be:
           Калі ласка, прайдзіце па спасылцы і азнаёмцеся з тэкстам.'
         read_tou: Я згаджаюся з Умовамі выкарыстання
         continue: Працягнуць
+        cancel: Скасаваць
         you need to accept or decline: Калі ласка, прачытайце, а потым згадзіцеся
           ці адхіліце новыя ўмовы супрацоўніцтва для працягу.
         legale_select: 'Краіна пражывання:'
index d56e455889a26eecc40b0cce06cd33aab57edc9f..6d556b02020b81d7a6101e8c65a92d0815869762 100644 (file)
@@ -138,6 +138,7 @@ cs:
         allow_write_prefs: měnit jejich uživatelské nastavení
         allow_write_diary: vytvářet deníkové záznamy a komentáře
         allow_write_api: upravovat mapu
+        allow_write_changeset_comments: komentovat sady změn
         allow_read_gpx: číst jejich soukromé GPS stopy
         allow_write_gpx: nahrávat GPS stopy
         allow_write_notes: měnit poznámky
@@ -319,6 +320,7 @@ cs:
   accounts:
     show:
       title: Upravit účet
+      my_account: Můj účet
       current email address: Stávající e-mailová adresa
       external auth: Externí autentizace
       openid:
@@ -333,6 +335,9 @@ cs:
         agreed_with_pd: Také jste prohlásili, že své editace považujete za volné dílo.
         link: https://wiki.openstreetmap.org/wiki/Cs:Open_Database_License/Contributor_Terms?uselang=cs
         link text: co to znamená?
+        not_agreed_with_pd: Neprohlásili jste, že své editace považujete za volné
+          dílo.
+        pd_link_text: prohlásit
       save changes button: Uložit změny
       delete_account: Smazat účet…
     go_public:
@@ -414,6 +419,18 @@ cs:
           pro přispěvatele. Podrobnější informace najdete na %{terms_declined_link}.
         terms_declined_link: této wikistránce
         terms_declined_url: http://wiki.openstreetmap.org/wiki/CS:Contributor_Terms_Declined?uselang=cs
+    pd_declarations:
+      show:
+        title: Považujte mé příspěvky za volné dílo
+        consider_pd: Považuji své příspěvky za volné dílo
+        consider_pd_why: Proč bych mohl chtít, aby moje příspěvky byly volným dílem?
+        confirm: Potvrdit
+      create:
+        successfully_declared: Úspěšně jste prohlásili, že své příspěvky považujete
+          za volné dílo.
+        already_declared: Již dříve jste prohlásili, že své příspěvky považujete za
+          volné dílo.
+        did_not_confirm: Nepotvrdili jste, že považujete své příspěvky za volné dílo.
   browse:
     deleted_ago_by_html: Smazáno %{time_ago} uživatelem %{user}
     edited_ago_by_html: Upraveno %{time_ago} uživatelem %{user}
@@ -489,6 +506,7 @@ cs:
     start_rjs:
       feature_warning: Načítá se %{num_features} prvků, což může váš prohlížeč zpomalit
         či zablokovat. Určitě chcete tato data zobrazit?
+      feature_error: 'Nepodařilo se načíst prvky: %{message}'
       load_data: Nahrát data
       loading: Načítá se…
     tag_details:
@@ -1055,6 +1073,7 @@ cs:
           bridleway: Koňská stezka
           bus_guideway: Autobusová dráha
           bus_stop: Autobusová zastávka
+          busway: Trasa metrobusu
           construction: Silnice ve výstavbě
           corridor: Koridor
           crossing: Přechod
@@ -1745,6 +1764,9 @@ cs:
     offline_flash:
       osm_offline: Databáze OpenStreetMap je momentálně kvůli probíhající neodkladné
         údržbě mimo provoz.
+      osm_read_only: Databáze OpenStreetMap je momentálně kvůli probíhající neodkladné
+        údržbě pouze pro čtení.
+      expected_restore_html: Očekáváme se, že služby budou obnoveny v %{time}.
       announcement: Oznámení si můžete přečíst zde.
   user_mailer:
     diary_comment_notification:
@@ -2813,10 +2835,12 @@ cs:
       write_prefs: Měnit preference uživatele
       write_diary: Vytvářet deníkové záznamy a komentáře
       write_api: Upravovat mapu
+      write_changeset_comments: Komentovat sady změn
       read_gpx: Číst soukromé GPS stopy
       write_gpx: Nahrávat GPS stopy
       write_notes: Měnit poznámky
       write_redactions: Skrývat mapová data
+      write_blocks: Vytvářet a rušit blokování uživatelů
       read_email: Přečíst e-mailovou adresu uživatele
       consume_messages: Číst, aktualizovat stav a mazat zprávy uživatelů
       send_messages: Posílat soukromé zprávy jiným uživatelům
@@ -2925,6 +2949,7 @@ cs:
       my notes: Moje poznámky k mapě
       my messages: Moje zprávy
       my profile: Můj profil
+      my_account: Můj účet
       my comments: Moje komentáře
       my_preferences: Moje preference
       my_dashboard: Moje nástěnka
@@ -3006,6 +3031,16 @@ cs:
       show:
         title: Uživatelé
         heading: Uživatelé
+        select_status: Vyberte stav
+        states:
+          pending: Čekající
+          active: Aktivní
+          confirmed: Potvrzený
+          suspended: Pozastavený
+          deleted: Smazaný
+        name_or_email: Jméno nebo e-mail
+        ip_address: IP adresa
+        search: Hledat
       page:
         found_users:
           one: Nalezen %{count} uživatel
@@ -3220,6 +3255,7 @@ cs:
       closed_title: 'Vyřešená poznámka #%{note_name}'
       hidden_title: 'Skrytá poznámka #%{note_name}'
       description_when_author_is_deleted: smazáno
+      description_when_there_is_no_opening_comment: neznámé
       event_opened_by_html: Vytvořeno uživatelem %{user} %{time_ago}
       event_opened_by_anonymous_html: Vytvořeno anonymním uživatelem %{time_ago}
       event_commented_by_html: Komentář od uživatele %{user} %{time_ago}
@@ -3458,6 +3494,8 @@ cs:
           ninth: "9."
           tenth: "10."
       time: Čas
+      download: Stáhnout trasu jako GeoJSON
+      filename: trasa
     query:
       node: Uzel
       way: Cesta
@@ -3472,6 +3510,9 @@ cs:
       show_address: Zobrazit adresu
       query_features: Průzkum prvků
       centre_map: Zde vystředit mapu
+    home:
+      marker_title: Poloha mého domova
+      not_set: Ve vašem účtu není nastavena poloha domova
   redactions:
     edit:
       heading: Upravit redakci
index 3fbdb3b194ba3b8f2d390e88cc2ab0dd29939633..c26712ca14e9d39ac07ba18e6b175188ce15c8a1 100644 (file)
@@ -90,6 +90,7 @@ cy:
         allow_write_prefs: addasu eu dewisiadau defnyddiwr
         allow_write_diary: creu cofnodion dyddiadur a sylwadau
         allow_write_api: golygu'r map
+        allow_write_changeset_comments: rhoi sylwadau ar grwpiau newid
         allow_read_gpx: darllen eu harllwybrau GPS
         allow_write_gpx: uwchlwytho olion GPS
         allow_write_notes: addasu nodiadau
@@ -292,9 +293,11 @@ cy:
   accounts:
     show:
       title: Golygu cyfrif
+      my_account: Fy Nghyfrif
       current email address: Cyfeiriad E-bost Cyfredol
       external auth: Dilysu Allanol
       openid:
+        link: https://wiki.openstreetmap.org/wiki/OpenID
         link text: beth yw hwn?
       contributor terms:
         heading: Telerau Cyfranwyr
@@ -304,7 +307,11 @@ cy:
           newydd.
         agreed_with_pd: Rydych hefyd wedi datgan eich bod yn ystyried bod eich golygiadau
           yn y Parth Cyhoeddus.
+        link: https://osmfoundation.org/wiki/Licence/Contributor_Terms
         link text: beth yw hwn?
+        not_agreed_with_pd: Nid ydych wedi datgan eich bod yn ystyried bod eich golygiadau
+          yn y Parth Cyhoeddus.
+        pd_link_text: datgan
       save changes button: Cadw Newidiadau
       delete_account: Dileu Cyfrif...
     go_public:
@@ -338,6 +345,8 @@ cy:
           a lleoliad cartref yn cael eu dileu.
         delete_display_name: Bydd eich enw defnyddiwr yn cael ei ddileu, a gellir
           ei ailddefnyddio gan gyfrifon eraill.
+        retain_caveats: 'Fodd bynnag, bydd rhywfaint o wybodaeth amdanoch yn cael
+          ei chadw ar OpenStreetMap, hyd yn oed ar ôl i''ch cyfrif gael ei ddileu:'
         retain_edits: Bydd eich golygiadau i'r gronfa ddata mapiau, os ydynt yn bodoli,
           yn cael eu cadw.
         retain_traces: Cedwir unrhyw olion rydych chi wedi uwchlwytho os ydynt yn
@@ -374,7 +383,14 @@ cy:
         terms_declined_url: https://wiki.openstreetmap.org/wiki/Contributor_Terms_Declined
     pd_declarations:
       show:
+        title: Ystyried bod fy nghyfraniadau yn y Parth Cyhoeddus
+        consider_pd: Ystyriaf fod fy nghyfraniadau yn y Parth Cyhoeddus
+        consider_pd_why: Pam fyddwn am gael fy nghyfraniadau yn y Parth Cyhoeddus?
         consider_pd_why_url: https://osmfoundation.org/wiki/Licence_and_Legal_FAQ/Why_would_I_want_my_contributions_to_be_public_domain
+        confirm: Cadarnhau
+      create:
+        successfully_declared: Rydych wedi datgan yn llwyddiannus eich bod yn ystyried
+          bod eich golygiadau yn y Parth Cyhoeddus.
   browse:
     deleted_ago_by_html: Dilëwyd %{time_ago} gan %{user}
     edited_ago_by_html: Golygwyd %{time_ago} gan %{user}
@@ -444,7 +460,7 @@ cy:
       title: Heb ei Ganfod
     timeout:
       title: Gwall Goramser
-      sorry: Sori, cymerodd yn rhy hir i adalw data math %{type} gyda'r id %{id}.
+      sorry: 'Mae''n ddrwg gennym, cymerodd rhy hir i nôl data %{type} #%{id}.'
       type:
         node: nod
         way: llwybr
@@ -462,6 +478,7 @@ cy:
     start_rjs:
       feature_warning: Wrthi'n llwytho %{num_features} nodwedd, a all arafu neu chwalu
         eich porwr. Ydych chi wir eisiau gweld y data?
+      feature_error: 'Ni ellid llwytho nodweddion: %{message}'
       load_data: Llwytho Data
       loading: Wrthi'n llwytho...
     tag_details:
@@ -492,29 +509,37 @@ cy:
       view_redacted_data: Gweld Data Wedi'i Gorchuddio
       view_redaction_message: Gweld Neges Orchuddio
   nodes:
+    not_found_message:
+      sorry: 'Mae''n ddrwg gennym, ni ellid dod o hyd i nod #%{id}.'
     timeout:
-      sorry: Sori, cymerodd yn rhy hir i adalw data'r nod gyda'r id %{id}.
+      sorry: 'Mae''n ddrwg gennym, cymerodd rhy hir i nôl data nod #%{id}.'
   old_nodes:
     not_found_message:
-      sorry: 'Sori, ni ellir canfod fersiwn %{version} o''r nod #%{id}.'
+      sorry: 'Mae''n ddrwg gennym, ni ellid dod o hyd i fersiwn %{version} o nod #%{id}.'
     timeout:
-      sorry: Sori, cymerodd yn rhy hir i adalw hanes y nod gyda'r id %{id}.
+      sorry: 'Mae''n ddrwg gennym, cymerodd rhy hir i nôl hanes nod #%{id}.'
   ways:
+    not_found_message:
+      sorry: 'Mae''n ddrwg gennym, ni ellid dod o hyd i lwybr #%{id}.'
     timeout:
-      sorry: Sori, cymerodd yn rhy hir i adalw data'r llwybr gyda'r id %{id}.
+      sorry: 'Mae''n ddrwg gennym, cymerodd rhy hir i nôl data llwybr #%{id}.'
   old_ways:
     not_found_message:
-      sorry: 'Sori, ni ellir canfod fersiwn %{version} o lwybr #%{id}.'
+      sorry: 'Mae''n ddrwg gennym, ni ellid dod o hyd i fersiwn %{version} o lwybr
+        #%{id}.'
     timeout:
-      sorry: Sori, cymerodd yn rhy hir i adalw hanes y llwybr gyda'r id %{id}.
+      sorry: 'Mae''n ddrwg gennym, cymerodd rhy hir i nôl hanes llwybr #%{id}.'
   relations:
+    not_found_message:
+      sorry: 'Mae''n ddrwg gennym, ni ellid dod o hyd i berthynas #%{id}.'
     timeout:
-      sorry: Sori, cymerodd yn rhy hir i adalw data'r perthynas gyda'r id %{id}.
+      sorry: 'Mae''n ddrwg gennym, cymerodd rhy hir i nôl data perthynas #%{id}.'
   old_relations:
     not_found_message:
-      sorry: 'Sori, ni ellir canfod fersiwn %{version} o''r perthynas #%{id}.'
+      sorry: 'Mae''n ddrwg gennym, ni ellid dod o hyd i fersiwn %{version} o berthynas
+        #%{id}.'
     timeout:
-      sorry: Sori, cymerodd yn rhy hir i adalw hanes y perthynas gyda'r id %{id}.
+      sorry: 'Mae''n ddrwg gennym, cymerodd rhy hir i nôl hanes perthynas #%{id}.'
   changeset_comments:
     feeds:
       comment:
@@ -524,8 +549,8 @@ cy:
         title_all: Trafodaeth grŵp newid OpenStreetMap
         title_particular: Trafodaeth grŵp newid OpenStreetMap %{changeset_id}
       timeout:
-        sorry: Sori, cymerodd yn rhy hir i adalw rhestr o sylwadau grŵp newid y gofynnoch
-          amdanynt.
+        sorry: Mae'n ddrwg gennym, cymerodd rhy hir i nôl rhestr o sylwadau grŵp newid
+          y gofynnoch amdanynt.
   changesets:
     changeset:
       no_edits: (dim golygiadau)
@@ -534,6 +559,7 @@ cy:
       title: Grwpiau newid
       title_user: Grwpiau newid gan %{user}
       title_user_link_html: Grwpiau newid gan %{user_link}
+      title_followed: Grwpiau newid gan bobl rydych yn eu dilyn
       title_nearby: Grwpiau newid gan ddefnyddwyr gerllaw
       empty: Heb ganfod grwpiau newid.
       empty_area: Heb ganfod grwpiau newid yn yr ardal hon.
@@ -576,9 +602,11 @@ cy:
       ways_paginated: Llwybrau (%{x}-%{y} o %{count})
       relations: Perthnasau (%{count})
       relations_paginated: Perthnasau (%{x}-%{y} o %{count})
+    not_found_message:
+      sorry: 'Mae''n ddrwg gennym, ni ellid dod o hyd i grŵp newid #%{id}.'
     timeout:
-      sorry: Sori, cymerodd y rhestr o grwpiau newid y gofynnoch amdanynt rhy hir
-        i'w hadalw.
+      sorry: Mae'n ddrwg gennym, cymerodd rhy hir i nôl rhestr o grŵp newid y gofynnoch
+        amdanynt.
   changeset_subscriptions:
     show:
       subscribe:
@@ -592,8 +620,8 @@ cy:
       created_by_html: Crëwyd gan %{link_user} ar %{created}.
     no_such_entry:
       heading: 'Dim cofnod gyda''r id: %{id}'
-      body: Mae'n ddrwg gennym, nid oes grŵp newid gyda'r id %{id}. Gwiriwch eich
-        sillafu, neu efallai bod y ddolen rydych chi wedi ei chlicio arni'n anghywir.
+      body: Mae'n ddrwg gennym, nid oes grŵp newid gyda'r cyfeirnod %{id}. Gwiriwch
+        eich sillafu, neu efallai bod y ddolen rydych chi wedi ei chlicio arni'n anghywir.
   dashboards:
     contact:
       km away: '%{count}km i ffwrdd'
@@ -649,9 +677,9 @@ cy:
     no_such_entry:
       title: Dim cofnod dyddiadur
       heading: 'Dim cofnod gyda''r id: %{id}'
-      body: Mae'n ddrwg gennym, nid oes cofnod dyddiadur neu sylw gyda'r id %{id}.
-        Gwiriwch eich sillafu, neu efallai bod y ddolen rydych chi wedi ei chlicio
-        arni'n anghywir.
+      body: Mae'n ddrwg gennym, nid oes cofnod dyddiadur neu sylw gyda'r cyfeirnod
+        %{id}. Gwiriwch eich sillafu, neu efallai bod y ddolen rydych chi wedi ei
+        chlicio arni'n anghywir.
     diary_entry:
       posted_by_html: Postiwyd gan %{link_user} ar %{created} yn %{language_link}
       updated_at_html: Diweddarwyd ddiwethaf ar %{updated}.
@@ -745,10 +773,15 @@ cy:
         heading: Ydych chi am ddilyn %{user}?
         button: Dilyn Defnyddiwr
       unfollow:
+        heading: Ydych chi am ddad-ddilyn %{user}?
         button: Dad-ddilyn Defnyddiwr
     create:
       success: Rydych nawr yn dilyn %{name}!
+      failed: Mae'n ddrwg gennym, mae eich cais i ddilyn %{name} wedi methu.
       already_followed: Rydych chi eisoes yn dilyn %{name}.
+    destroy:
+      success: Rydych chi wedi dad-ddilyn %{name}.
+      not_followed: Nid ydych yn dilyn %{name}.
   geocoder:
     search:
       title:
@@ -1001,6 +1034,7 @@ cy:
           bridleway: Llwybr Ceffyl
           bus_guideway: Lon Bysiau
           bus_stop: Safle Bws
+          busway: Ffordd Fws
           construction: Priffordd yn cael ei Adeiladu
           corridor: Coridor
           crossing: Croesfan
@@ -1163,6 +1197,8 @@ cy:
           track: Trac Rhedeg
           water_park: Parc Dŵr
           "yes": Hamdden
+        lock:
+          "yes": Loc
         man_made:
           adit: Adit
           advertising: Hysbysebu
@@ -1509,6 +1545,9 @@ cy:
           reservoir: Cronfa Ddŵr
           basin: Basn Dwr
           fishpond: Pwll Pysgod
+          lagoon: Lagŵn
+          wastewater: Dŵr Gwastraff
+          lock: Loc
         waterway:
           artificial: Dyfrffyrdd Artiffisial
           boatyard: Iard Gychod
@@ -1564,6 +1603,7 @@ cy:
       reports: Adroddiadau
       last_updated: Diweddarwyd ddiwethaf
       last_updated_time_ago_user_html: '%{time_ago} gan %{user}'
+      reporting_users: Defnyddiwyr sydd wedi rhoi gwybod
       reports_count:
         zero: '%{count} Adroddiad'
         one: '%{count} Adroddiad'
@@ -1571,6 +1611,7 @@ cy:
         few: '%{count} Adroddiad'
         many: '%{count} Adroddiad'
         other: '%{count} Adroddiad'
+      more_reporters: a %{count} arall
       reported_item: Eitem a roddwyd gwybod
       states:
         ignored: Anwybyddwyd
@@ -1609,6 +1650,8 @@ cy:
       reopened: Mae statws y mater wedi'i osod i 'Agored'
     comments:
       comment_from_html: Sylw gan %{user_link} ar %{comment_created_at}
+      reassign_to_moderators: Ailbennu Mater i Gymedrolwyr
+      reassign_to_administrators: Ailbennu Mater i Weinyddwyr
     reports:
       reported_by_html: Adroddwyd fel %{category} gan %{user} ar %{updated_at}
     helper:
@@ -1686,6 +1729,13 @@ cy:
     communities: Cymunedau
     learn_more: Dysgu Rhagor
     more: Rhagor
+    offline_flash:
+      osm_offline: Mae cronfa ddata OpenStreetMap all-lein ar hyn o bryd er mwyn gwneud
+        gwaith cynnal a chadw hanfodol.
+      osm_read_only: Nid yw'n bosib golygu cronfa ddata OpenStreetMap ar hyn o bryd
+        tra bod gwaith cynnal a chadw hanfodol yn digwydd.
+      expected_restore_html: Disgwylir i wasanaethau gael eu hadfer ymhen %{time}.
+      announcement: Gallwch ddarllen y cyhoeddiad yma.
   user_mailer:
     diary_comment_notification:
       description: 'Cofnod Dyddiadur OpenStreetMap #%{id}'
@@ -1707,8 +1757,11 @@ cy:
     follow_notification:
       hi: Helo %{to_user},
       subject: '[OpenStreetMap] Mae %{user} wedi eich dilyn'
+      followed_you: Mae %{user} bellach yn eich dilyn ar OpenStreetMap.
       see_their_profile: 'Gallwch weld eu proffil yma: %{userurl}.'
       see_their_profile_html: 'Gallwch weld eu proffil yma: %{userurl}.'
+      follow_them: Gallwch hefyd ddilyn y defnyddiwr hwn yn %{followurl}.
+      follow_them_html: Gallwch hefyd ddilyn y defnyddiwr hwn yn %{followurl}.
     gpx_details:
       details: 'Manylion eich ffeil:'
       filename: Enw ffeil
@@ -1832,7 +1885,7 @@ cy:
     no_such_message:
       title: Dim neges o'r fath
       heading: Dim neges o'r fath
-      body: Sori, nid oes neges gyda'r id yno.
+      body: Mae'n ddrwg gennym, nid oes neges gyda'r cyfeirnod yno.
     show:
       title: Darllen neges
       reply_button: Ateb
@@ -1977,7 +2030,7 @@ cy:
       login_button: Mewngofnodi
       with external: neu fewngofnodi gyda thrydydd parti
       or: neu
-      auth failure: Mae'n ddrwg gennym, ni ellir mewngofnodi gyda'r manylion hynny.
+      auth failure: Mae'n ddrwg gennym, ni ellid mewngofnodi gyda'r manylion hynny.
     destroy:
       title: Allgofnodi
       heading: Allgofnodi o OpenStreetMap
@@ -2237,6 +2290,8 @@ cy:
     not_public_flash:
       not_public: Nid ydych wedi gosod eich golygiadau i fod yn gyhoeddus.
       user_page_link: tudalen defnyddiwr
+      anon_edits_html: (%{link})
+      anon_edits_link: https://wiki.openstreetmap.org/wiki/Disabling_anonymous_edits
       anon_edits_link_text: Gweld pam.
     edit:
       id_not_configured: iD heb ei ffurfweddu
@@ -2504,8 +2559,8 @@ cy:
       trace_uploaded: Mae eich ffeil GPX wedi'i huwchlwytho ac yn aros i gael ei chynnwys
         yn y gronfa ddata. Bydd hyn fel arfer yn digwydd o fewn hanner awr, a bydd
         e-bost yn cael ei anfon atoch ar ôl cwblhau.
-      upload_failed: Mae'n ddrwg gennym, methodd eich uwchlwythiad GPX. Mae gweinyddwr
-        wedi cael gwybod am y gwall. Ceisiwch eto.
+      upload_failed: Mae'n ddrwg gennym, mae eich uwchlwythiad GPX wedi methu. Mae
+        gweinyddwr wedi cael gwybod am y gwall. Ceisiwch eto.
       traces_waiting:
         zero: Mae gennych %{count} ôl yn aros i'w uwchlwytho. Arhoswch i'r rhain orffen
           cyn uwchlwytho rhagor, er mwyn osgoi rhwystro'r ciw i ddefnyddwyr eraill.
@@ -2669,10 +2724,12 @@ cy:
       write_prefs: Addasu dewisiadau defnyddwyr
       write_diary: Creu cofnodion dyddiadur a sylwadau
       write_api: Golygu'r map
+      write_changeset_comments: Rhoi sylwadau ar grwpiau newid
       read_gpx: Darllen olion GPS preifat
       write_gpx: Uwchlwytho olion GPS
       write_notes: Addasu nodiadau
       write_redactions: Gorchuddio data map
+      write_blocks: Creu a dirymu blociau defnyddwyr
       read_email: Darllen cyfeiriad e-bost defnyddwyr
       consume_messages: Darllen, diweddaru statws a dileu negeseuon defnyddiwr
       send_messages: Anfon negeseuon preifat at ddefnyddwyr eraill
@@ -2697,6 +2754,8 @@ cy:
       confirm_delete: Dileu'r ap hwn?
       client_id: ID Cleient
       client_secret: Cyfrinach Cleient
+      client_secret_warning: Gwnewch yn siŵr eich bod yn cadw'r gyfrinach hon - ni
+        fydd ar gael i chi eto
       permissions: Caniatadau
       redirect_uris: Ailgyfeirio URIs
   oauth2_authorizations:
@@ -2752,6 +2811,7 @@ cy:
       my notes: Nodiadau
       my messages: Negeseuon
       my profile: Proffil
+      my_account: Fy Nghyfrif
       my comments: Sylwadau
       my_preferences: Dewisiadau
       my_dashboard: Dangosfwrdd
@@ -2836,6 +2896,16 @@ cy:
       show:
         title: Defnyddwyr
         heading: Defnyddwyr
+        select_status: Dewiswch Statws
+        states:
+          pending: Arfaethedig
+          active: Gweithredol
+          confirmed: Cadarnhawyd
+          suspended: Wedi'u hatal
+          deleted: Wedi'u dileu
+        name_or_email: Enw neu Gyfeiriad E-bost
+        ip_address: Cyfeiriad IP
+        search: Chwilio
       page:
         found_users:
           zero: Canfuwyd %{count} defnyddiwr
@@ -2852,8 +2922,9 @@ cy:
         summary_no_ip_html: '%{name} wedi''i greu ar %{date}'
     comments:
       index:
+        heading_html: Sylwadau %{user}
         changesets: Grwpiau newid
-        diary_entries: Cofnodion dyddiadur
+        diary_entries: Cofnodion Dyddiadur
         no_comments: Dim sylwadau
     changeset_comments:
       index:
@@ -2872,9 +2943,11 @@ cy:
     suspended:
       title: Cyfrif wedi'i atal
       heading: Cyfrif wedi'i atal
-      support: cymorth
+      support: chymorth
       automatically_suspended: Mae'n ddrwg gennym, mae eich cyfrif wedi'i atal yn
         awtomatig oherwydd gweithgarwch amheus.
+      contact_support_html: Bydd y penderfyniad hwn yn cael ei adolygu gan weinyddwr
+        yn fuan, neu gallwch gysylltu â %{support_link} os hoffech drafod hyn.
     auth_failure:
       no_authorization_code: Dim cod awdurdodi
       invalid_scope: Sgop annilys
@@ -3034,6 +3107,8 @@ cy:
       open_title: 'Nodyn heb ei ddatrys #%{note_name}'
       closed_title: 'Nodyn wedi''i ddatrys #%{note_name}'
       hidden_title: 'Nodyn cudd #%{note_name}'
+      description_when_author_is_deleted: wedi'i ddileu
+      description_when_there_is_no_opening_comment: anhysbys
       event_opened_by_html: Crëwyd gan %{user} %{time_ago}
       event_opened_by_anonymous_html: Crëwyd gan berson ddienw %{time_ago}
       event_commented_by_html: Sylw gan %{user} %{time_ago}
@@ -3067,11 +3142,25 @@ cy:
         wybod i fapwyr eraill fel y gallwn ei ddatrys. Symudwch y marciwr i'r safle
         cywir ac ysgrifennwch nodyn i esbonio'r broblem.
       anonymous_warning_html: Nid ydych chi wedi mewngofnodi. %{log_in} neu %{sign_up}
-        os ydych chi eisiau derbyn diweddariadau am eich nodyn.
+        os ydych am gael diweddariadau ar gyfer eich nodyn a helpu mapwyr eraill i'w
+        ddatrys.
       anonymous_warning_log_in: Mewngofnodwch
       anonymous_warning_sign_up: gofrestrwch
+      counter_warning_html: Rydych chi eisoes wedi creu o leiaf %{x_anonymous_notes},
+        sy'n wych i'r gymuned, diolch! Nawr rydym yn eich annog i %{contribute_by_yourself},
+        nid yw mor gymhleth â hynny, a %{community_can_help}.
+      x_anonymous_notes:
+        zero: '%{count} nodyn dienw'
+        one: '%{count} nodyn dienw'
+        two: '%{count} nodyn dienw'
+        few: '%{count} nodyn dienw'
+        many: '%{count} nodyn dienw'
+        other: '%{count} nodyn dienw'
       counter_warning_guide_link:
+        text: gyfrannu eich hun
         url: https://wiki.openstreetmap.org/wiki/Beginners%27_guide
+      counter_warning_forum_link:
+        text: gall y gymuned eich helpu
       advice: Mae eich nodyn yn gyhoeddus a gellid ei ddefnyddio i ddiweddaru'r map,
         felly peidiwch ag ysgrifennu gwybodaeth bersonol, na gwybodaeth o fapiau hawlfreintiedig
         neu gyfeiriaduron.
@@ -3084,6 +3173,8 @@ cy:
       showing_page: Tudalen %{page}
       next: Nesaf
       previous: Cynt
+    not_found_message:
+      sorry: 'Mae''n ddrwg gennym, ni ellid dod o hyd i nodyn #%{id}.'
   javascripts:
     close: Cau
     share:
@@ -3187,7 +3278,7 @@ cy:
       distance_km: '%{distance}km'
       errors:
         no_route: Ni ellir dod o hyd i'r llwybr rhwng y ddau le.
-        no_place: Ymddiheuriadau - ni ellir canfod '%{place}'.
+        no_place: Mae'n ddrwg gennym - ni ellid dod o hyd i '%{place}'.
       instructions:
         continue_without_exit: Parhau ar %{name}
         slight_right_without_exit: Ychydig i'r dde i %{name}
@@ -3263,6 +3354,8 @@ cy:
           ninth: 9fed
           tenth: 10fed
       time: Amser
+      download: Lawrlwytho'r llwybr fel GeoJSON
+      filename: ffordd
     query:
       node: Nod
       way: Llwybr
@@ -3277,6 +3370,9 @@ cy:
       show_address: Dangos cyfeiriad
       query_features: Ymholiad nodweddion
       centre_map: Canoli'r map yma
+    home:
+      marker_title: Fy lleoliad cartref
+      not_set: Nid yw lleoliad cartref wedi'i osod ar gyfer eich cyfrif
   redactions:
     edit:
       heading: Golygu Gorchuddiad
index 5a1df55c95eff24becb6ad21f8dffb5ba28dadb4..ec46ad5e6caadaca9142dfca4ba4c8b54d1e2eaa 100644 (file)
@@ -291,7 +291,7 @@ da:
           fælleseje/uden ophavsret.
         link text: hvad er dette?
         not_agreed_with_pd: Du har ikke erklæret, at du anser dine ændringer for at
-          være i det offentlige domæne, dvs. fælleseje/uden ophavsret.
+          være i det offentlige domæne (Public Domain), dvs. fælleseje/uden ophavsret.
         pd_link_text: erklær
       save changes button: Gem ændringer
       delete_account: Slet konto
@@ -380,8 +380,8 @@ da:
       show:
         title: Betragt mine bidrag for at være i det offentlige domæne
         consider_pd: Jeg anser mine bidrag for at være i det offentlige domæne
-        consider_pd_why: Hvorfor skulle jeg gerne have, at mine bidrag er i det offentlige
-          domæne?
+        consider_pd_why: Hvorfor skulle jeg have et ønske om, at mine bidrag er i
+          det offentlige domæne?
         confirm: Bekræft
       create:
         successfully_declared: Du har erklæret, at du anser dine redigeringer for
@@ -2978,6 +2978,7 @@ da:
           confirmed: Bekræftet
           suspended: Suspenderet
           deleted: Slettet
+        name_or_email: Navn eller e-mail
         ip_address: IP-adresse
         search: Søg
       page:
@@ -3189,6 +3190,7 @@ da:
       closed_title: 'Løst bemærkning #%{note_name}'
       hidden_title: 'Skjult bemærkning #%{note_name}'
       description_when_author_is_deleted: slettet
+      description_when_there_is_no_opening_comment: ukendt
       event_opened_by_html: Oprettet af %{user} %{time_ago}
       event_opened_by_anonymous_html: Oprettet af anonym %{time_ago}
       event_commented_by_html: Kommentar fra %{user} %{time_ago}
@@ -3423,6 +3425,8 @@ da:
           ninth: "9."
           tenth: "10."
       time: Tid
+      download: Download rute som GeoJSON
+      filename: rute
     query:
       node: Punkt
       way: Vej
index 88021a200765bafc93342dc9ae061a03e0612fa2..0a1be26ba3ad0394864f04552670f7ae8a42b204 100644 (file)
@@ -72,6 +72,7 @@
 # Author: Manfredbrandl
 # Author: Markobr
 # Author: MarkusHD
+# Author: Marwin H.H.
 # Author: McDutchie
 # Author: Mcandri13
 # Author: Mcliquid
@@ -3138,6 +3139,7 @@ de:
           confirmed: Bestätigt
           suspended: Ausgesetzt
           deleted: Gelöscht
+        name_or_email: Name oder E-Mail
         ip_address: IP-Adresse
         search: Suchen
       page:
@@ -3352,6 +3354,7 @@ de:
       closed_title: Erledigter Hinweis Nr. %{note_name}
       hidden_title: Versteckter Hinweis Nr. %{note_name}
       description_when_author_is_deleted: gelöscht
+      description_when_there_is_no_opening_comment: unbekannt
       event_opened_by_html: Erstellt von %{user} %{time_ago}
       event_opened_by_anonymous_html: Erstellt von anonym %{time_ago}
       event_commented_by_html: Kommentar von %{user} %{time_ago}
@@ -3591,6 +3594,8 @@ de:
           ninth: neunte
           tenth: zehnte
       time: Zeit
+      download: Route als GeoJSON herunterladen
+      filename: route
     query:
       node: Knoten
       way: Linie
index 3a82aab3909b280ce105f7bf34ec78862d6d4500..b56fb6410c6af3c6460180e2d7921518d247ce7c 100644 (file)
@@ -2905,6 +2905,7 @@ en:
           confirmed: Confirmed
           suspended: Suspended
           deleted: Deleted
+        name_or_email: Name or Email
         ip_address: IP Address
         search: Search
       page:
@@ -3103,6 +3104,7 @@ en:
       closed_title: "Resolved note #%{note_name}"
       hidden_title: "Hidden note #%{note_name}"
       description_when_author_is_deleted: "deleted"
+      description_when_there_is_no_opening_comment: "unknown"
       event_opened_by_html: "Created by %{user} %{time_ago}"
       event_opened_by_anonymous_html: "Created by anonymous %{time_ago}"
       event_commented_by_html: "Comment from %{user} %{time_ago}"
index 4cbc7734fe357ec0a1e54763bf3f101d6f737ffd..e3239f50289083fc2a6f3af1c99052be74b1f3f4 100644 (file)
@@ -370,6 +370,13 @@ eo:
           havaĵo.
         consider_pd_why: Kiel vi volus, ke miaj kontribuaĵoj estu publikaj?
         confirm: Konfirmi
+      create:
+        successfully_declared: Vi sukcese deklaris, por ke viaj redaktoj estu en la
+          publika havaĵo.
+        already_declared: Vi jam deklaris, por ke viaj redaktoj estu en la publika
+          havaĵo.
+        did_not_confirm: Vi malkonsentis, por ke viaj redaktoj estu en la publika
+          havaĵo.
   browse:
     deleted_ago_by_html: Forigita %{time_ago} de %{user}
     edited_ago_by_html: Redaktita %{time_ago} de %{user}
@@ -1009,6 +1016,7 @@ eo:
           bridleway: Ĉevalvojo
           bus_guideway: Aŭtobus-trako
           bus_stop: Haltejo aŭtobusa
+          busway: Aŭtobusa vojo
           construction: Vojo konstruata
           corridor: Koridoro
           crossing: Trapasejo
@@ -1579,6 +1587,7 @@ eo:
       reports: Raportoj
       last_updated: Laste aktualigita
       last_updated_time_ago_user_html: '%{time_ago} de %{user}'
+      reporting_users: Raportantaj uzantoj
       reports_count:
         one: '%{count} raporto'
         other: '%{count} raportoj'
@@ -1617,6 +1626,8 @@ eo:
       reopened: Problemo estas malfermita
     comments:
       comment_from_html: Komento fare de %{user_link} je %{comment_created_at}
+      reassign_to_moderators: Reasigni problemon al kontrolantoj
+      reassign_to_administrators: Reasigni problemon al administrantoj
     reports:
       reported_by_html: Raportita kiel %{category} fare de %{user} je %{updated_at}
     helper:
@@ -1692,6 +1703,13 @@ eo:
     communities: Komunumoj
     learn_more: Ekscii pli
     more: Pli
+    offline_flash:
+      osm_offline: La OpenStreetMap-datumbazo estas nuntempe eksterreta pro necesaj
+        laboroj de prizorgado.
+      osm_read_only: La OpenStreetMap-datumbazo estas nuntempe en nurlega reĝimo pro
+        necesaj laboroj de prizorgado.
+      expected_restore_html: La servoj probable estos restarigitaj je %{time}.
+      announcement: Vi povas legi la afiŝon tie ĉi.
   user_mailer:
     diary_comment_notification:
       description: 'OpenStreetMap: taglibra afiŝo %{id}'
@@ -2729,10 +2747,12 @@ eo:
       write_prefs: modifi preferojn de uzanto
       write_diary: afiŝi en taglibro kaj komenti
       write_api: redakti la mapon
+      write_changeset_comments: komenti ŝanĝarojn
       read_gpx: legi privatajn GPS-spurojn
       write_gpx: alŝuti GPS-spurojn
       write_notes: modifi rimarkojn
       write_redactions: Redakti map-datumojn
+      write_blocks: krei kaj nuligi blokadojn al uzantoj
       read_email: legi retpoŝtan adreson de uzanto
       consume_messages: legi, ŝanĝi staton kaj forigi mesaĝojn de uzanto
       send_messages: sendi privatajn mesaĝojn al aliaj uzantoj
@@ -2839,6 +2859,7 @@ eo:
       my notes: Miaj rimarkoj
       my messages: Mesaĝoj
       my profile: Profilo
+      my_account: Mia konto
       my comments: Miaj komentoj
       my_preferences: Preferoj
       my_dashboard: Panelo
@@ -2916,6 +2937,16 @@ eo:
       show:
         title: Uzantoj
         heading: Uzantoj
+        select_status: Elekti staton
+        states:
+          pending: pritraktata
+          active: aktiva
+          confirmed: konfirmita
+          suspended: blokita
+          deleted: forigita
+        name_or_email: Nomo aŭ retpoŝta adreso
+        ip_address: IP-adreso
+        search: Serĉi
       page:
         found_users:
           one: '%{count} trovita uzanto'
@@ -2929,8 +2960,8 @@ eo:
     comments:
       index:
         heading_html: Komentoj de %{user}
-        changesets: pri ŝanĝaroj
-        diary_entries: pri taglibraj afiŝoj
+        changesets: Ŝanĝaroj
+        diary_entries: Taglibroj
         no_comments: Neniu komento
     changeset_comments:
       index:
@@ -2947,8 +2978,8 @@ eo:
         when: Kiam
         comment: Komento
     suspended:
-      title: Konto haltigita
-      heading: Konto haltigita
+      title: Konto blokita
+      heading: Konto blokita
       support: subtena teamo
       automatically_suspended: Bedaŭrinde via konto estas aŭtomate blokita pro suspektinda
         agado.
@@ -3116,6 +3147,8 @@ eo:
       open_title: 'Nesolvita rimarko #%{note_name}'
       closed_title: 'Solvita rimarko #%{note_name}'
       hidden_title: 'Kaŝita rimarko #%{note_name}'
+      description_when_author_is_deleted: forigita
+      description_when_there_is_no_opening_comment: nekonata
       event_opened_by_html: Kreita de %{user} %{time_ago}
       event_opened_by_anonymous_html: Anonime kreita %{time_ago}
       event_commented_by_html: Komento de %{time_ago} de %{user}
@@ -3353,6 +3386,8 @@ eo:
           ninth: 9-an
           tenth: 10-an
       time: Tempo
+      download: Elŝuti kurson kiel GeoJSON
+      filename: kurso
     query:
       node: Nodo
       way: Linio
@@ -3367,6 +3402,9 @@ eo:
       show_address: Montri adreson
       query_features: Informoj pri objektoj
       centre_map: Centrigi mapon ĉi tien
+    home:
+      marker_title: Mia hejmloko
+      not_set: Hejmloko ne estas agordita por via konto
   redactions:
     edit:
       heading: Redakti korekton
index 1f8595fb38124260311f54d4def5cba553b1608b..1fb42e2e2f63f4271a43a5f7c762ff6240b1eafa 100644 (file)
@@ -204,6 +204,7 @@ fr:
         allow_write_prefs: modifier les préférences de l’utilisateur
         allow_write_diary: créer des entrées du journal et des commentaires
         allow_write_api: modifier la carte
+        allow_write_changeset_comments: commenter les changements
         allow_read_gpx: lire ses traces GPS privées
         allow_write_gpx: téléverser des traces GPS
         allow_write_notes: modifier les notes
index 4e4c627e1843b022ccd1048b7f7d4dc0161a7d55..205d37635c9e0c49436609f537e9ade07f559a91 100644 (file)
@@ -263,9 +263,11 @@ gl:
   accounts:
     show:
       title: Editar a conta
+      my_account: A miña conta
       current email address: Enderezo de correo electrónico actual
       external auth: Autenticación externa
       openid:
+        link: https://wiki.openstreetmap.org/wiki/OpenID
         link text: que é isto?
       contributor terms:
         heading: Termos do colaborador
@@ -275,7 +277,11 @@ gl:
           do colaborador.
         agreed_with_pd: Tamén declaraches que consideras que as túas edicións pertencen
           ó dominio público.
+        link: https://osmfoundation.org/wiki/Licence/Contributor_Terms
         link text: que é isto?
+        not_agreed_with_pd: Non declaraches que consideras que as túas edicións pertencen
+          ó dominio público.
+        pd_link_text: declárao
       save changes button: Gardar as modificacións
       delete_account: Borrar a conta...
     go_public:
@@ -362,7 +368,19 @@ gl:
         terms_declined_url: https://wiki.openstreetmap.org/wiki/Contributor_Terms_Declined
     pd_declarations:
       show:
+        title: Considerar as miñas contribucións de dominio público
+        consider_pd: Considero que as miñas contribucións son de dominio público
+        consider_pd_why: Por que querería que as miñas contribucións sexan de dominio
+          público?
         consider_pd_why_url: https://osmfoundation.org/wiki/Licence_and_Legal_FAQ/Why_would_I_want_my_contributions_to_be_public_domain
+        confirm: Confirmar
+      create:
+        successfully_declared: Declaraches que consideras que as túas edicións pertencen
+          ao dominio público.
+        already_declared: Xa declaraches que consideras que as túas edicións pertencen
+          ao dominio público.
+        did_not_confirm: Non confirmaches que consideras que as túas edicións pertencen
+          ao dominio público.
   browse:
     deleted_ago_by_html: Borrado %{time_ago} por %{user}
     edited_ago_by_html: Editado %{time_ago} por %{user}
@@ -2038,7 +2056,7 @@ gl:
       failure: Non foi posíbel actualizar o perfil.
   sessions:
     new:
-      tab_title: Acceder ao sistema
+      tab_title: Iniciar a sesión
       login_to_authorize_html: Inicia sesión no OpenStreetMap para acceder a %{client_app_name}.
       email or username: Enderezo de correo electrónico ou nome de usuario
       password: Contrasinal
@@ -2799,6 +2817,7 @@ gl:
       write_gpx: Subir pistas GPS
       write_notes: Modificar notas
       write_redactions: Censurar datos do mapa
+      write_blocks: Crear e revogar bloqueos de usuarios
       read_email: Ver os enderezos de correo electrónico dos usuarios
       consume_messages: Ver, actualizar o estado e borrar mensaxes dos usuarios
       send_messages: Enviar mensaxes privadas aos usuarios
@@ -2907,6 +2926,7 @@ gl:
       my notes: As miñas notas do mapa
       my messages: As miñas mensaxes
       my profile: O meu perfil
+      my_account: A miña conta
       my comments: Os meus comentarios
       my_preferences: As miñas preferencias
       my_dashboard: O meu panel de control
@@ -2987,6 +3007,16 @@ gl:
       show:
         title: Usuarios
         heading: Usuarios
+        select_status: Seleccionar estado
+        states:
+          pending: Pendente
+          active: Activo
+          confirmed: Confirmado
+          suspended: Suspendido
+          deleted: Borrado
+        name_or_email: Nome ou correo electrónico
+        ip_address: Enderezo IP
+        search: Procurar
       page:
         found_users:
           one: Atopouse %{count} usuario
@@ -3191,6 +3221,7 @@ gl:
       closed_title: Nota resolta n.º %{note_name}
       hidden_title: Nota agochada n.º %{note_name}
       description_when_author_is_deleted: eliminado
+      description_when_there_is_no_opening_comment: descoñecido
       event_opened_by_html: Creado por %{user} %{time_ago}
       event_opened_by_anonymous_html: Creado por un usuario anónimo %{time_ago}
       event_commented_by_html: Comentario de %{user} %{time_ago}
@@ -3426,6 +3457,8 @@ gl:
           ninth: 9.ª
           tenth: 10.ª
       time: Tempo
+      download: Descargar a ruta como GeoJSON
+      filename: ruta
     query:
       node: Nó
       way: Vía
@@ -3440,6 +3473,9 @@ gl:
       show_address: Amosar enderezo
       query_features: Consultar elementos
       centre_map: Centrar o mapa aquí
+    home:
+      marker_title: Localización da miña casa
+      not_set: A localización da casa non está configurada para a túa conta
   redactions:
     edit:
       heading: Editar a censura
index e027f941daea2a614eadc83e50cfa13ed616ebf2..805b32b5f673fcb1ca46944d8667851cb45ca7fc 100644 (file)
@@ -308,7 +308,7 @@ it:
   accounts:
     show:
       title: Modifica profilo
-      my_account: Il mio profilo
+      my_account: La mia utenza
       current email address: Indirizzo email attuale
       external auth: Autenticazione esterna
       openid:
@@ -2954,7 +2954,7 @@ it:
       my notes: Le mie note
       my messages: I miei messaggi
       my profile: Il mio profilo
-      my_account: Il mio profilo
+      my_account: La mia utenza
       my comments: I miei commenti
       my_preferences: Preferenze
       my_dashboard: La mia dashboard
@@ -3042,6 +3042,7 @@ it:
           confirmed: Confermato
           suspended: Sospeso
           deleted: Cancellato
+        name_or_email: Nome o e-mail
         ip_address: Indirizzo IP
         search: Cerca
       page:
@@ -3250,6 +3251,7 @@ it:
       closed_title: 'Nota risolta #%{note_name}'
       hidden_title: 'Nota nascosta #%{note_name}'
       description_when_author_is_deleted: cancellato
+      description_when_there_is_no_opening_comment: sconosciuto
       event_opened_by_html: Creata da %{user} %{time_ago}
       event_opened_by_anonymous_html: Creata da anonimo %{time_ago}
       event_commented_by_html: Commento da %{user} %{time_ago}
@@ -3483,6 +3485,8 @@ it:
           ninth: 9ª
           tenth: 10ª
       time: Tempo
+      download: Scarica l'itinerario come GeoJson
+      filename: itinerario
     query:
       node: Nodo
       way: Percorso
index 6786b32f3c647513421aa85a2a9ef385db75dd0b..7d1d0c4de19f1e8a838dd932872184e42b9c531b 100644 (file)
@@ -2654,6 +2654,7 @@ ko:
           confirmed: 확인됨
           suspended: 정지됨
           deleted: 삭제됨
+        name_or_email: 이름 또는 이메일
         ip_address: IP 주소
         search: 검색
       page:
@@ -2823,6 +2824,7 @@ ko:
       open_title: '해결되지 않은 참고 #%{note_name}'
       closed_title: '해결된 참고 #%{note_name}'
       hidden_title: '숨겨진 참고 #%{note_name}'
+      description_when_there_is_no_opening_comment: 알 수 없음
       event_opened_by_html: '%{time_ago} %{user}님이 생성함'
       event_opened_by_anonymous_html: '%{time_ago} 익명의 사용자가 생성함'
       event_commented_by_html: '%{time_ago} %{user}님의 댓글'
index 4e7eb351e11be30083fa5b8e91b47ca66583491d..5f853bda6a8ae4678a43ca76601252f1422284bc 100644 (file)
@@ -2342,6 +2342,7 @@ lb:
       my notes: Meng Notizen
       my messages: Meng Messagen
       my profile: Mäi Profil
+      my_account: Mäi Benotzerkont
       my comments: Meng Bemierkungen
       my_preferences: Meng Benotzerastellungen
       my_dashboard: Meng Iwwersiichtssäit
@@ -2396,6 +2397,7 @@ lb:
           active: Aktiv
           confirmed: Confirméiert
           deleted: Geläscht
+        name_or_email: Numm oder E-Mail
         ip_address: IP-Adress
         search: Sichen
       page:
@@ -2497,6 +2499,7 @@ lb:
       closed_title: 'Geléisten Hiweis #%{note_name}'
       hidden_title: Verstoppt Notiz N° %{note_name}
       description_when_author_is_deleted: geläscht
+      description_when_there_is_no_opening_comment: onbekannt
       report: Dësen Hiweis mellen
       discussion: Diskussioun
       subscribe: Abonéieren
@@ -2621,6 +2624,8 @@ lb:
           ninth: néngt
           tenth: zéngt
       time: Zäit
+      download: Streck als GeoJSON eroflueden
+      filename: Streck
     query:
       way: Wee
       relation: Relatioun
index e647046a425b3c5f412a2e9105f169b7eaa82e0f..554a4f0615c4ad618b48702b4750d62312f6216c 100644 (file)
@@ -2763,8 +2763,8 @@ nl:
         niet te onderschijven, maar moet ze wel gezien hebben.
     settings_menu:
       account_settings: Accountinstellingen
-      oauth2_applications: OAuth 2 toepassingen
-      oauth2_authorizations: OAuth 2 autorisaties
+      oauth2_applications: OAuth 2-toepassingen
+      oauth2_authorizations: OAuth 2-autorisaties
       muted_users: Gedempte gebruikers
     auth_providers:
       openid_url: OpenID-URL
index bc1c78f334859f6aa6dd16da898d0454381793b4..7541d4c9db290bb0a1377ce7d64a41e14725b9be 100644 (file)
@@ -539,6 +539,7 @@ pl:
     start_rjs:
       feature_warning: Wczytywanie %{num_features} obiektów, może spowolnić lub zawiesić
         przeglądarkę. Wyświetlić te dane?
+      feature_error: 'Błąd przy ładowaniu obiektów: %{message}'
       load_data: Wczytaj dane
       loading: Wczytywanie...
     tag_details:
@@ -1684,11 +1685,13 @@ pl:
       reports: Zgłoszenia
       last_updated: Ostatnia aktualizacja
       last_updated_time_ago_user_html: '%{time_ago} przez %{user}'
+      reporting_users: Raportowanie użytkowników
       reports_count:
         one: 1 zgłoszenie
         few: '%{count} zgłoszenia'
         many: '%{count} zgłoszeń'
         other: '%{count} zgłoszenia'
+      more_reporters: i jeszcze %{count}
       reported_item: Zgłoszony element
       states:
         ignored: Zignorowano
@@ -1726,6 +1729,8 @@ pl:
       reopened: Status sprawy został ustawiony na „Otwarta”
     comments:
       comment_from_html: Komentarz od %{user_link} z %{comment_created_at}
+      reassign_to_moderators: Przydziel sprawę moderatorom
+      reassign_to_administrators: Przydziel sprawę administratorom
     reports:
       reported_by_html: Zgłoszone %{updated_at} jako %{category} przez %{user}
     helper:
@@ -1806,6 +1811,13 @@ pl:
     communities: Społeczności
     learn_more: Dowiedz się więcej
     more: Więcej
+    offline_flash:
+      osm_offline: Baza danych OpenStreetMap jest w trybie offline na czas ważnych
+        zadań administracyjnych, które są w tym momencie wykonywane.
+      osm_read_only: Baza danych OpenStreetMap jest w trybie tylko do odczytu na czas
+        ważnych zadań administracyjnych, które są w tym momencie wykonywane.
+      expected_restore_html: Przywrócenie usług spodziewane jest w ciągu %{time}.
+      announcement: Ogłoszenie można przeczytać tutaj.
   user_mailer:
     diary_comment_notification:
       description: 'Wpis dziennika OpenStreetMap #%{id}'
@@ -3121,6 +3133,10 @@ pl:
       show:
         title: Użytkownicy
         heading: Użytkownicy
+        select_status: Wybierz status
+        name_or_email: Nazwa lub e-mail
+        ip_address: Adres IP
+        search: Szukaj
       page:
         found_users:
           one: Znaleziono %{count} użytkownika
@@ -3588,6 +3604,8 @@ pl:
           ninth: "9."
           tenth: "10."
       time: Czas
+      download: Pobierz trasę jako GeoJSON
+      filename: trasa
     query:
       node: Węzeł
       way: Linia
@@ -3602,6 +3620,9 @@ pl:
       show_address: Pokaż adres
       query_features: Wyświetl dane obiektów
       centre_map: Wycentruj mapę tutaj
+    home:
+      marker_title: Położenie domu
+      not_set: Położenie domu nie jest ustawione w twoim profilu
   redactions:
     edit:
       heading: Edytuj poprawkę
index ac27335ed94a8d6a58a6bf804243bdac258612df..9c0c9daee4ec7390b6cb23df2c977f1e34e7e456 100644 (file)
@@ -15,6 +15,7 @@
 # Author: Anton Khorev
 # Author: Ashed
 # Author: Banonotit
+# Author: Borealex
 # Author: BushmanK
 # Author: Butko
 # Author: CM3X
 # Author: XnL
 # Author: Yuri Nazarov
 # Author: Yurik
+# Author: Yurina Tatiana
 # Author: Yuryleb
 # Author: Zverik
 # Author: Александр Сигачёв
@@ -204,6 +206,7 @@ ru:
         allow_write_prefs: изменять пользовательские настройки
         allow_write_diary: создавать записи в дневнике и комментарии
         allow_write_api: редактировать карту
+        allow_write_changeset_comments: комментировать пакеты правок
         allow_read_gpx: читать частные GPS-треки
         allow_write_gpx: загружать GPS-треки
         allow_write_notes: исправлять заметки
@@ -384,6 +387,7 @@ ru:
   accounts:
     show:
       title: Изменить учетную запись
+      my_account: Мой аккаунт
       current email address: Текущий адрес электронной почты
       external auth: 'Внешняя аутентификация:'
       openid:
@@ -399,6 +403,9 @@ ru:
           в общественном достоянии.
         link: https://openstreetmap.org/wiki/RU:Open_Database_License/Contributor_Terms?uselang=ru
         link text: что это?
+        not_agreed_with_pd: Вы не заявили, что считаете свои правки находящимися в
+          общественном достоянии.
+        pd_link_text: объявить
       save changes button: Сохранить изменения
       delete_account: Удалить учётную запись…
     go_public:
@@ -572,6 +579,7 @@ ru:
     start_rjs:
       feature_warning: Необходимо загрузить %{num_features} объектов, что может замедлить
         ваш браузер. Вы уверены, что хотите просмотреть эти данные?
+      feature_error: 'Не удалось загрузить объекты: %{message}'
       load_data: Загрузить данные
       loading: Загружается…
     tag_details:
@@ -734,6 +742,8 @@ ru:
       nearby users: Другие ближайшие пользователи
       no nearby users: Пока нет других пользователей, которые признают, что занимаются
         составлением карты поблизости.
+      followed_changesets: пакеты правок
+      followed_diaries: дневники
       nearby_changesets: пакеты правок соседей
       nearby_diaries: дневники соседей
   diary_entries:
@@ -2754,7 +2764,7 @@ ru:
     scopes:
       read_prefs: Прочитать настройки пользователя
       write_prefs: Изменить настройки пользователя
-      write_diary: Создавать записи в дневнике, комментировать и заводить друзей
+      write_diary: Создавать записи в дневнике и комментарии
       write_api: Изменить карту
       read_gpx: Читать частные GPS-треки
       write_gpx: Загрузить GPS-треки
@@ -2856,6 +2866,7 @@ ru:
       my notes: Мои заметки
       my messages: Мои сообщения
       my profile: Мой профиль
+      my_account: Мой аккаунт
       my comments: Мои комментарии
       my_preferences: Мои предпочтения
       my_dashboard: Мой пульт
@@ -2921,9 +2932,11 @@ ru:
         heading: Пользователи
         states:
           pending: В ожидании
+          active: Активен
           confirmed: Подтверждено
           suspended: Приостановлено
           deleted: Удалено
+        name_or_email: Имя или адрес эл. почты
         ip_address: IP-адрес
         search: Поиск
       page:
@@ -2935,9 +2948,13 @@ ru:
         summary_no_ip_html: '%{name} создан %{date}'
     comments:
       index:
+        heading_html: Комментарии пользователя %{user}
+        changesets: Пакеты правок
         diary_entries: Записи в дневнике
+        no_comments: Нету комментариев
     changeset_comments:
       page:
+        changeset: Пакет правок
         when: Когда
         comment: Комментарий
     diary_comments:
@@ -2945,6 +2962,8 @@ ru:
         title: Комментарии к записям в дневнике, добавленные пользователем %{user}
       page:
         post: Сообщение
+        when: Когда
+        comment: Комментарий
     suspended:
       title: Учётная запись приостановлена
       heading: Учётная запись приостановлена
@@ -3363,6 +3382,7 @@ ru:
       query_features: Что здесь?
       centre_map: Центрировать карту
     home:
+      marker_title: Мой домашний адрес
       not_set: Установка места нахождения не установлена для вашей учётной записи
   redactions:
     edit:
index bcc03a0a28841d9a217f0e3b03f3f432009bf27c..2f2a9dee300ec6c4809abab3ece7708a093a2a9a 100644 (file)
@@ -4,6 +4,7 @@
 # Author: Ajeje Brazorf
 # Author: Saraiki
 # Author: Sayam Asjad
+# Author: Umar Shahid
 ---
 skr-arab:
   html:
@@ -1422,6 +1423,7 @@ skr-arab:
           eighth: ٨واں
           ninth: ٩واں
           tenth: ١٠واں
+      download: روٹ کوں GeoJSON دے طور تے ڈاؤن لوڈ کرو۔
   redactions:
     show:
       confirm: بھلا تہاکوں پک ہے؟
index 74388e3bc76e6ed01bc217827798c1cb9204bf7a..d5286ff01a3a234268c0206c839fbcc610ef0bcc 100644 (file)
@@ -1708,7 +1708,7 @@ sl:
           ste jo dodali. Opomba je v bližini %{place}.'
         commented_note_html: '%{commenter} je spet aktiviral_a opombo na zemljevidu,
           ki ste jo dodali. Opomba je v bližini %{place}.'
-      details: Več podatkov o opombi lahko najdete na %{url}.
+      details: Več podatkov o opombi je na voljo na %{url}.
       details_html: Na opombo lahko odgovorite ali izveste več o njej na %{url}.
     changeset_comment_notification:
       description: Nabor sprememb OpenStreetMap št. %{id}
@@ -2918,6 +2918,7 @@ sl:
       status: Stanje
     navigation:
       block: 'Blokiraj #%{id}'
+      new_block: Novo blokiranje
   user_mutes:
     index:
       title: Utišani uporabniki
index c23d9b28adea575606c96c690ffc95dd018499da..bdd30c03e1074a7f9cabef5c80272a5a54c734e9 100644 (file)
@@ -2204,6 +2204,7 @@ sr:
         heading: Корисници
         states:
           deleted: Обрисано
+        name_or_email: Име или имејл
         ip_address: IP адреса
         search: Претрага
       page:
@@ -2333,6 +2334,7 @@ sr:
       closed_title: Решена белешка бр. %{note_name}
       hidden_title: Скривена белешка бр. %{note_name}
       description_when_author_is_deleted: обрисано
+      description_when_there_is_no_opening_comment: непознато
       event_opened_by_html: Направио %{user} %{time_ago}
       report: пријави ову белешку
       anonymous_warning: Ова белешка садржи коментаре анонимних корисника које би
index 52d7cb2a281fa9cff7309164f59b1677ad3eee9e..cfcbad6f909e25c0231d7ee77783158352319044 100644 (file)
@@ -138,6 +138,7 @@ sv:
         allow_write_prefs: ändra deras användaralternativ
         allow_write_diary: skapa dagboksinlägg och kommentarer
         allow_write_api: ändra kartan
+        allow_write_changeset_comments: kommentar på ändringsuppsättning
         allow_read_gpx: läs deras privata GPS-spår
         allow_write_gpx: ladda upp GPS-spår
         allow_write_notes: ändra anteckningar
@@ -292,6 +293,7 @@ sv:
   accounts:
     show:
       title: Redigera konto
+      my_account: Mitt konto
       current email address: Nuvarande e-postadress
       external auth: Extern autentisering
       openid:
@@ -305,6 +307,9 @@ sv:
         agreed_with_pd: Du har också förklarat att du anser att dina redigeringar
           är inom Public Domain.
         link text: vad är detta?
+        not_agreed_with_pd: Du har inte förklarat att du anser att dina redigeringar
+          är inom Public Domain.
+        pd_link_text: förklara
       save changes button: Spara ändringar
       delete_account: Radera konto...
     go_public:
@@ -389,6 +394,15 @@ sv:
         terms_declined_html: Vi beklagar att du bestämt dig för att inte acceptera
           de nya användarvillkoren. För mer information, se %{terms_declined_link}.
         terms_declined_link: denna wikisida
+    pd_declarations:
+      show:
+        title: Anse att mina bidrag är i public domain
+        consider_pd: Jag anser mina bidrag vara i public domain
+        consider_pd_why: Varför skulle jag vilja ha mina bidrag i public domain?
+        confirm: Bekräfta
+      create:
+        successfully_declared: Du har förklarat att du anser att dina redigeringar
+          är inom public domain.
   browse:
     deleted_ago_by_html: Raderades %{time_ago} av %{user}
     edited_ago_by_html: Redigerades %{time_ago} av %{user}
@@ -2893,6 +2907,7 @@ sv:
       my notes: Mina kartanteckningar
       my messages: Mina meddelanden
       my profile: Min profil
+      my_account: Mitt konto
       my comments: Mina kommentarer
       my_preferences: Mina alternativ
       my_dashboard: Min kontrollpanel
@@ -2972,6 +2987,15 @@ sv:
       show:
         title: Användare
         heading: Användare
+        select_status: Välj status
+        states:
+          pending: Pågående
+          active: Aktiv
+          confirmed: Bekräftad
+          suspended: Uppskjutet
+          deleted: Raderad
+        ip_address: IP-adress
+        search: Sök
       page:
         found_users:
           one: '%{count} användare hittade'
@@ -3180,6 +3204,7 @@ sv:
       closed_title: 'Avklarad anteckning #%{note_name}'
       hidden_title: 'Dold anteckning #%{note_name}'
       description_when_author_is_deleted: raderad
+      description_when_there_is_no_opening_comment: okänd
       event_opened_by_html: Skapades av %{user} %{time_ago}
       event_opened_by_anonymous_html: Skapades av anonym %{time_ago}
       event_commented_by_html: Kommenterades från %{user} %{time_ago}
@@ -3408,6 +3433,8 @@ sv:
           ninth: 9:e
           tenth: 10:e
       time: Tid
+      download: Ladda ner rutt som GeoJSON
+      filename: rutt
     query:
       node: Nod
       way: Sträcka
@@ -3422,6 +3449,9 @@ sv:
       show_address: Visa adress
       query_features: Undersök kartobjekt
       centre_map: Centrera kartan här
+    home:
+      marker_title: Min hemort
+      not_set: Hemort har inte angetts för ditt konto
   redactions:
     edit:
       heading: Redigera maskering
index 2c244ac8f6cf5ce90da546b7a312bbce3314dc26..6b5b396b19a477b532b38e9a5f5674739b3b31c8 100644 (file)
@@ -242,6 +242,7 @@ th:
   accounts:
     show:
       title: แก้ไขบัญชี
+      my_account: บัญชีของฉัน
       current email address: ที่อยู่อีเมลปัจจุบัน
       external auth: การยืนยันตัวตนด้วยบริการภายนอก
       openid:
@@ -253,6 +254,7 @@ th:
         review link text: โปรดเข้าลิงก์นี้ตามความสะดวกของคุณเพื่อตรวจสอบและยอมรับข้อกำหนดของผู้ร่วมให้ข้อมูลใหม่
         agreed_with_pd: คุณยังได้ประกาศด้วยว่า คุณถือว่าการแก้ไขของคุณถือว่าเป็นสาธารณสมบัติ
         link text: นี่คืออะไร?
+        pd_link_text: ประกาศ
       save changes button: บันทึกการเปลี่ยนแปลง
       delete_account: ลบบัญชี...
     go_public:
@@ -300,6 +302,7 @@ th:
         contributor_terms_explain: ข้อตกลงนี้ใช้บังคับกับข้อกำหนดสำหรับการมีส่วนร่วมในปัจจุบันและในอนาคตของคุณ
         read_ct: ฉันได้อ่านและยอมรับเงื่อนไขของผู้มีส่วนร่วมให้ข้อมูลข้างต้นแล้ว
         read_tou: ข้าพเจ้าได้อ่านและเห็นด้วยในข้อกำหนดการใช้งาน
+        informal_translations: การแปลแบบไม่เป็นทางการ
         continue: ถัดไป
         cancel: ยกเลิก
         you need to accept or decline: กรุณาตรวจทานและเลือกยอมรับหรือไม่ยอมรับข้อกำหนดของผู้ร่วมให้ข้อมูลเพื่อไปต่อ
@@ -310,6 +313,9 @@ th:
           rest_of_world: พื้นที่อื่น ๆ ในโลก
       update:
         terms accepted: ขอบคุณสำหรับการตอบรับเงื่อนไขผู้ร่วมให้ข้อมูล!
+    pd_declarations:
+      show:
+        confirm: ยืนยัน
   browse:
     deleted_ago_by_html: ลบเมื่อ %{time_ago} โดย %{user}
     edited_ago_by_html: แก้ไขเมื่อ %{time_ago} โดย %{user}
@@ -961,6 +967,8 @@ th:
           wayside_shrine: ศาลเจ้าริมทาง
           wreck: ซากปรักหักพัง
           "yes": สถานที่ประวัติศาสตร์
+        information:
+          office: สำนักงานบริการนักท่องเที่ยว
         junction:
           "yes": ทางแยก
         landuse:
@@ -1373,6 +1381,13 @@ th:
           building_passage: ทางลอดใต้อาคาร
           culvert: ท่อระบายน้ำ
           "yes": อุโมงค์
+        water:
+          lake: ทะเลสาบ
+          pond: สระน้ำ
+          reservoir: อ่างเก็บน้ำ
+          lagoon: ลากูน
+          wastewater: น้ำเสีย
+          oxbow: ทะเลสาบรูปแอก
         waterway:
           artificial: ทางน้ำที่มนุษย์สร้าง
           boatyard: ที่จอดเรือ
@@ -1787,6 +1802,11 @@ th:
         เพิ่มข้อมูล หรือกระทำการให้เกิดสิ่งอื่นใดใหม่ขึ้นต่องานที่มีนี้ ท่านสามารถแจกจ่ายได้แต่เพียงภายใต้สัญญาอนุญาตเดียวกันเท่านั้น
         โปรดดู%{copyright_license_link}สำหรับลายละเอียดเพิ่มเติม
       legal_title: ข้อกำหนดทางกฎหมาย
+      legal_1_1_terms_of_use: ข้อกำหนดการใช้งาน
+      legal_1_1_aup: นโยบายการใช้งานที่ยอมรับได้
+      legal_1_1_privacy_policy: นโยบายความเป็นส่วนตัว
+      legal_2_1_html: โปรดไปที่ %{contact_the_osmf_link} หากคุณมีปัญหาหรือข้อสงสัยเกี่ยวกับการอนุญาตให้ใช้งาน
+        ลิขสิทธิ์ หรือข้อสงสัยด้านกฎหมายอื่น ๆ
       partners_title: องค์กรพันธมิตร
     copyright:
       title: ลิขสิทธิ์และสัญญาอนุญาต
@@ -1802,6 +1822,7 @@ th:
         native_link: รุ่นภาษาTHIS_LANGUAGE_NAME_HERE
         mapping_link: เริ่มการทำแผนที่
       legal_babble:
+        introduction_1_open_data: ข้อมูลเปิดเผย
         introduction_2_html: คุณสามารถคัดลอก แจกจ่าย ถ่ายทอด และดัดแปลงข้อมูลของเราได้อย่างเสรี
           ตราบใดที่คุณให้อ้างอิงเครดิต OpenStreetMap และผู้ร่วมให้ข้อมูล ถ้าคุณแก้ไข
           หรือสร้างข้อมูลต่อจากข้อมูลของเรา คุณสามารถเผยแพร่ผลลัพธ์ภายใต้สัญญาเดิมเท่านั้น
@@ -1810,6 +1831,7 @@ th:
         credit_1_html: |-
           เราจำเป็นต้องให้ท่านแสดงที่มาหรือคำขอบคุณแก่ &ldquo;&copy; ผู้ร่วมสร้างสรรค์ OpenStreetMap
           &rdquo;.
+        credit_3_attribution_guidelines: แนวทางการระบุแหล่งที่มา
         attribution_example:
           alt: ตัวอย่างการอ้างอิง OpenStreetMap บนหน้าเว็บ
           title: ตัวอย่างการอ้างอิง
@@ -1823,11 +1845,23 @@ th:
         contributors_at_stadt_wien: เมืองเวียนนา
         contributors_at_land_vorarlberg: แลนด์ ฟอร์อาร์ลแบร์ค
         contributors_at_cc_by_at_with_amendments: CC BY AT with amendments
+        contributors_cz_czechia: เช็กเกีย
+        contributors_fi_finland: ฟินแลนด์
+        contributors_fr_france: ฝรั่งเศส
+        contributors_hr_croatia: โครเอเชีย
+        contributors_si_slovenia: สโลวีเนีย
+        contributors_si_gu: หน่วยงานสำรวจและจัดทำแผนที่
+        contributors_si_mkgp: กระทรวงการเกษตร ป่าไม้ และอาหาร
+        contributors_es_credit_html: '%{spain}: ประกอบด้วยข้อมูลที่มาจากสถาบันภูมิศาสตร์แห่งชาติสเปน
+          (%{ign_link}) และระบบการทำแผนที่แห่งชาติสเปน (%{scne_link}) ซึ่งได้รับอนุญาตให้ทำซ้ำภายใต้สัญญา
+          %{cc_by_link}'
+        contributors_es_spain: สเปน
         contributors_footer_2_html: การรวมข้อมูลใน OpenStreetMap ไม่ได้หมายความว่าผู้ให้บริการข้อมูลดั้งเดิมจะรับรอง
           OpenStreetMap รับประกัน หรือรับผิดชอบใด ๆ
         infringement_title_html: การละเมิดลิขสิทธิ์
         infringement_1_html: ผู้ร่วมให้ข้อมูล OSM จะถูกเตือนไม่ให้เพิ่มข้อมูลจากแหล่งข้อมูลที่มีลิขสิทธิ์
           (เช่น Google Maps หรือแผนที่แบบพิมพ์) โดยไม่ได้รับอนุญาตอย่างชัดเจนจากเจ้าของลิขสิทธิ์
+        trademarks_title: เครื่องหมายการค้า
     index:
       js_1: ท่านกำลังใช้เบราว์เซอร์ที่ไม่รองรับจาวาสคริปต์ หรือท่านปิดใช้งานจาวาสคริปต์
       js_2: OpenStreetMap ใช้จาวาสคริปต์ในการแสดงแผนที่
@@ -1922,6 +1956,9 @@ th:
           taxiway: ทางขับเครื่องบิน
           apron: โรงซ่อมบำรุงเครื่องบิน
           admin: ขอบเขตการปกครอง
+          capital: เมืองหลวง
+          city: เมือง
+          orchard: สวนผลไม้
           forest: ป่า
           wood: ไม้
           golf: สนามกอล์ฟ
@@ -1934,18 +1971,25 @@ th:
           heathland: ทุ่งไม้พุ่ม
           lake: ทะเลสาบ
           reservoir: อ่างเก็บน้ำ
+          glacier: ธารน้ำแข็ง
+          wetland: พื้นที่ชุ่มน้ำ
           farm: ไร่นา
           brownfield: พื้นที่อุตสาหกรรมเดิม
           cemetery: สุสาน
           allotments: ที่ดินแบ่งใช้
           pitch: ลานกีฬา
           centre: ศูนย์กีฬา
+          beach: ชายหาด
           reserve: พื้นที่สงวนธรรมชาติ
           military: เขตทหาร
           school: โรงเรียน
           university: มหาวิทยาลัย
+          hospital: โรงพยาบาล
           building: อาคารสำคัญ
           station: สถานีรถไฟ
+          railway_halt: ที่หยุดรถไฟ
+          subway_station: สถานีรถไฟใต้ดิน
+          tram_stop: ที่หยุดรถราง
           summit: ยอดเขา
           peak: ยอดเขา
           tunnel: เส้นประ = อุโมงค์
@@ -1953,7 +1997,9 @@ th:
           private: พื้นที่ส่วนบุคคล
           destination: การเข้าถึงที่จุดปลายทาง
           construction: ถนนกำลังก่อสร้าง
+          bus_stop: ป้ายหยุดรถประจำทาง
           bicycle_shop: ร้านขายและเช่าจักรยาน
+          bicycle_rental: บริการให้เช่าจักรยาน
           bicycle_parking: ที่จอดจักรยาน
           toilets: ห้องน้ำ
     welcome:
index ac8c50251dd04d2b93baca27b1a90b303c241e49..6834d54ca0aa652dcc51ca6ea68fae7bb8e8acf3 100644 (file)
@@ -24,6 +24,7 @@
 # Author: Katpatuka
 # Author: Khalvar
 # Author: Kumkumuk
+# Author: Leo
 # Author: LuCKY
 # Author: Makina88
 # Author: Mavrikant
@@ -137,7 +138,7 @@ tr:
         support_url: Destek Bağlantısı
         allow_read_prefs: kullanıcı tercihlerini okuyun
         allow_write_prefs: kullanıcı tercihlerini değiştir
-        allow_write_diary: günlük girdiler, yorumlar oluşturun ve arkadaş edinin
+        allow_write_diary: günlük kayıtları ve yorumları oluşturun
         allow_write_api: haritayı değiştir
         allow_read_gpx: özel GPS izlerini oku
         allow_write_gpx: GPS izlerini yükle
index 07a7b690a3fafb2657c5f685b8098fe282220bf9..80545d79b35b5ef93994175f4b396318d34f6980 100644 (file)
@@ -135,6 +135,7 @@ uk:
         allow_write_prefs: змінювати налаштування
         allow_write_diary: створювати записи у щоденнику та залишати коментарі
         allow_write_api: змінювати мапу
+        allow_write_changeset_comments: коментувати набори змін
         allow_read_gpx: отримувати приватні GPS-треки.
         allow_write_gpx: надсилати GPS-треки на сервер
         allow_write_notes: змінювати нотатки
@@ -317,6 +318,7 @@ uk:
   accounts:
     show:
       title: Редагувати обліковий запис
+      my_account: Обліковий запис
       current email address: Поточна адреса електронної пошти
       external auth: Стороння автентифікація
       openid:
@@ -332,6 +334,8 @@ uk:
           Надбанням.
         link: https://wiki.openstreetmap.org/wiki/Uk:Open_Database_License/Contributor_Terms
         link text: що це?
+        not_agreed_with_pd: Ви не заявили, що ви вважаєте свій внесок Суспільним Надбанням.
+        pd_link_text: заявляю
       save changes button: Зберегти зміни
       delete_account: Видалити обліковий запис…
     go_public:
@@ -417,7 +421,18 @@ uk:
         terms_declined_url: https://wiki.openstreetmap.org/wiki/Uk:Contributor_Terms_Declined
     pd_declarations:
       show:
+        title: Вважайте, що мій внесок є Суспільним Надбанням
+        consider_pd: Я вважаю, що мій внесок є суспільним надбанням
+        consider_pd_why: Чому я мав(ла) би хотіти, щоб мій внесок став Суспільним
+          Надбанням?
         consider_pd_why_url: https://wiki.openstreetmap.org/wiki/Uk:Чому_я_хочу,_щоб_мої_внески_були_суспільним_надбанням
+        confirm: Підтвердити
+      create:
+        successfully_declared: Ви заявили, що вважаєте, що ваші правки є Суспільним
+          Надбанням.
+        already_declared: Ви вже заявили, що вважаєте, що ваші правки є Суспільним
+          Надбанням.
+        did_not_confirm: Ви не підтвердили, що вважаєте свої правки Суспільним Надбанням.
   browse:
     deleted_ago_by_html: Вилучив(ла) %{time_ago} %{user}
     edited_ago_by_html: Змінено %{user} %{time_ago}
@@ -1069,6 +1084,7 @@ uk:
           bridleway: Дорога для їзди верхи
           bus_guideway: Рейковий автобус
           bus_stop: Автобусна зупинка
+          busway: Дорога для автобусів
           construction: Будівництво автомагістралі
           corridor: Коридор
           crossing: Перехід
@@ -1641,6 +1657,7 @@ uk:
       reports: Скарги
       last_updated: Останнє оновлення
       last_updated_time_ago_user_html: '%{user} %{time_ago}'
+      reporting_users: Повідомлення про користувачів
       reports_count:
         one: '%{count} Скарга'
         few: '%{count} Скарги'
@@ -1683,6 +1700,8 @@ uk:
       reopened: Статус проблеми був змінений на 'Відкрито'
     comments:
       comment_from_html: Коментар від %{user_link}, %{comment_created_at}
+      reassign_to_moderators: Перепризначити звернення Модераторам
+      reassign_to_administrators: Перепризначити звернення Адміністраторам
     reports:
       reported_by_html: Повідомлено як %{category}, %{user}, %{updated_at}
     helper:
@@ -1761,6 +1780,13 @@ uk:
     communities: Спільноти
     learn_more: Докладніше
     more: Більше
+    offline_flash:
+      osm_offline: База даних OpenStreetMap наразі працює в режимі офлайн, поки проводяться
+        роботи з технічного обслуговування.
+      osm_read_only: База даних OpenStreetMap наразі працює в режимі тільки для читання,
+        поки проводяться роботи з технічного обслуговування.
+      expected_restore_html: Очікується, що роботу буде відновлено через %{time}.
+      announcement: Ознайомитися з оголошенням можна тут.
   user_mailer:
     diary_comment_notification:
       description: 'Запис у щоденнику OpenStreetMap #%{id}'
@@ -2097,7 +2123,7 @@ uk:
       failure: Неможливо зберегти оновлення профілю.
   sessions:
     new:
-      tab_title: Ð\9bаÑ\81каво Ð¿Ñ\80оÑ\81имо
+      tab_title: Ð\92Ñ\85Ñ\96д
       login_to_authorize_html: Увійдіть до OpenStreetMap, щоб отримати доступ до %{client_app_name}.
       email or username: Ел. пошта або прізвисько
       password: Пароль
@@ -2381,6 +2407,8 @@ uk:
       not_public_description_html: 'Ви не можете більше анонімно редагувати мапу.
         Ви можете зробити ваші редагування загальнодоступними тут: %{user_page}.'
       user_page_link: сторінка учасника
+      anon_edits_html: '%{link}'
+      anon_edits_link: https://wiki.openstreetmap.org/wiki/Uk:Анонімне_редагування
       anon_edits_link_text: З’ясувати в чому справа.
     edit:
       id_not_configured: iD не був налаштований
@@ -2783,8 +2811,8 @@ uk:
         погоджуватись, але ви повинні переглянути їх.
     settings_menu:
       account_settings: Налаштування облікового запису
-      oauth2_applications: Ð·астосунки OAuth 2
-      oauth2_authorizations: Ð°вторизації OAuth 2
+      oauth2_applications: Ð\97астосунки OAuth 2
+      oauth2_authorizations: Ð\90вторизації OAuth 2
       muted_users: Стишені учасники
     auth_providers:
       openid_url: OpenID URL
@@ -2838,10 +2866,12 @@ uk:
       write_prefs: Змінювати налаштування
       write_diary: Створювати записи у щоденнику та залишати коментарі
       write_api: Змінювати мапу
+      write_changeset_comments: Коментувати набори змін
       read_gpx: Отримувати приватні GPS-треки
       write_gpx: Завантажувати GPS-треки
       write_notes: Змінювати нотатки
       write_redactions: Виконувати очищення даних
+      write_blocks: Створювати та скасовувати блокування користувачів
       read_email: Отримувати адресу е-пошти
       consume_messages: Прочитати, оновити статус та видалити повідомлення користувача
       send_messages: Надіслати приватне повідомлення іншим користувачам
@@ -2893,7 +2923,7 @@ uk:
       title: Авторизовані застосунки
       application: Застосунок
       permissions: Дозволи
-      last_authorized: Ð\9eÑ\81Ñ\82аннÑ\96й Ð°Ð²Ñ\82оÑ\80изований
+      last_authorized: Ð\92 Ð¾Ñ\81Ñ\82аннÑ\94 Ð°Ð²Ñ\82оÑ\80изовано
       no_applications_html: У вас ще немає застосунків авторизованих через %{oauth2}.
     application:
       revoke: Відкликати доступ
@@ -2953,6 +2983,7 @@ uk:
       my notes: Мої нотатки
       my messages: Повідомлення
       my profile: Профіль
+      my_account: Обліковий запис
       my comments: Мої коментарі
       my_preferences: Вподобання
       my_dashboard: Інфо панель
@@ -3035,6 +3066,16 @@ uk:
       show:
         title: Учасники
         heading: Учасники
+        select_status: Обрати статус
+        states:
+          pending: В очікуванні
+          active: Активний
+          confirmed: Підтверджено
+          suspended: Призупинено
+          deleted: Вилучено
+        name_or_email: Імʼя або електронна пошта
+        ip_address: IP-адреса
+        search: Пошук
       page:
         found_users:
           one: знайдено %{count} користувача
@@ -3254,6 +3295,8 @@ uk:
       open_title: 'Неопрацьована нотатка #%{note_name}'
       closed_title: 'Опрацьована нотатка #%{note_name}'
       hidden_title: 'Прихована нотатка #%{note_name}'
+      description_when_author_is_deleted: вилучено
+      description_when_there_is_no_opening_comment: невідомо
       event_opened_by_html: Створив(ла) %{user} %{time_ago}
       event_opened_by_anonymous_html: Створено анонімом %{time_ago}
       event_commented_by_html: Коментар від %{user} %{time_ago}
@@ -3494,6 +3537,8 @@ uk:
           ninth: 9й
           tenth: 10й
       time: Час
+      download: Завантажити маршрут як GeoJSON
+      filename: маршрут
     query:
       node: Точка
       way: Лінія
@@ -3508,6 +3553,9 @@ uk:
       show_address: Показати адресу
       query_features: Отримати об’єкти
       centre_map: Центрувати мапу тут
+    home:
+      marker_title: Моє розташування
+      not_set: Для вашого облікового запису не вказано його розташування
   redactions:
     edit:
       heading: Змінити редакцію
index 0e9def5d5de0d5ce513db9f4238e4603316abd96..791b4d8af2ff8504d177287b711767c14a1c0662 100644 (file)
@@ -19,14 +19,14 @@ xmf:
         create: კომენტარი
       diary_entry:
         create: გუმობჟინაფა
-        update: á\83\92á\83\9dá\83\90á\83®á\83\90á\83\9aá\83\90á\83¤
+        update: á\83\9bá\83\9dá\83\90á\83®á\83\90á\83\9aá\83\94á\83\91
       issue_comment:
         create: კომენტარიშ გეძინა
       message:
         create: ჯღონუა
       oauth2_application:
         create: რეგისტრაცია
-        update: á\83\92á\83\9dá\83\90á\83®á\83\90á\83\9aá\83\90á\83¤
+        update: á\83\9bá\83\9dá\83\90á\83®á\83\90á\83\9aá\83\94á\83\91
       redaction:
         create: მიშათინუაშ გოჭყაფა
         update: მიშათინუაშ ჩუალა
@@ -37,6 +37,10 @@ xmf:
         create: ბლოკირაფაშ გოჭყაფა
         update: ბლოკირაფაშ გოახალაფა
   activerecord:
+    errors:
+      models:
+        user_mute:
+          is_already_muted: უკვე აკორანგვილი რე
     models:
       acl: ხემეჭირნაფაშ კონტროლიშ ერკებული
       changeset: თირაფეფიშ პაკეტი
@@ -50,6 +54,7 @@ xmf:
       message: გინაფა
       node: ჭურჭული
       node_tag: ჭურჭულიშ ხინტკი
+      note: ხვილაფა
       old_node: ჯვეში ჭურჭული
       old_node_tag: ჭურჭულიშ ჯვეში ხინტკი
       old_relation: ჯვეში ურთიართობა
@@ -83,6 +88,7 @@ xmf:
         allow_write_diary: დღარეფიშ დინნაჭარეფიშ გოჭყაფა, კომენტირაფა დო მაჸალეეფიშ
           გეძინა
         allow_write_api: რუკაშ თირუა
+        allow_write_changeset_comments: თირუეფიშ პაკეტეფიშ კომენტირება
         allow_read_gpx: კერზო GPS-ტრეკეფიშ კითხირი
         allow_write_gpx: GPS-ტრეკეფიშ ეხარგუა
         allow_write_notes: მოღანკუეფიშ მიშათინუა
@@ -259,8 +265,27 @@ xmf:
         retain_email: თქვან ელ-ფოშტა დიჩუალუაფუ.
         confirm_delete: დასურო გონებჷნანო?
         cancel: გოუქვაფა
+    terms:
+      show:
+        title: აპიჯალეფი
+        heading: წოროხანდაშ აპიჯალეფი
+        heading_ct: წოროხანდაშ აპიჯალეფი
+        continue: გაგჷნძორაფა
+        cancel: გოუქვაფა
+        legale_names:
+          france: საფრანგეთი
+          italy: იტალია
+          rest_of_world: დოსკილადირი მოსოფელი
+      terms_declined_flash:
+        terms_declined_link: თენა რე ვიკი ხასჷლა
+    pd_declarations:
+      show:
+        confirm: დოდასურება
   browse:
+    deleted_ago_by_html: დილასჷ %{time_ago} მახვარებუქ %{user}
+    edited_ago_by_html: დარედაქტირჷ %{time_ago} მახვარებუქ %{user}
     version: ვერსია
+    redacted_version: ვერსიაშ რედაქტირაფა
     in_changeset: თირაფეფიშ პაკეტი
     anonymous: ანონიმი
     no_comment: (უკომენტარე)
@@ -334,6 +359,7 @@ xmf:
       wikimedia_commons_link: ელემენტი %{page} ვიკიოწკარუეს
       telephone_link: რეკუა %{phone_number}
       colour_preview: ფერი %{colour_value} გიწოთოლორაფა
+      email_link: ელექტრონული ფოშტა %{email}
     query:
       title: მუ რე თაქ?
       introduction: უახოლაში ობიექტეფიშ ოგორალო ქეგუნჭირით რუკა.
@@ -347,6 +373,8 @@ xmf:
         title_html: 'ღოზიშ ისტორია: %{name}'
       relation:
         title_html: 'ურთიართობაშ ისტორია: %{name}'
+    actions:
+      view_redacted_data: რედაქტირებული მუნაჩემეფშა გინოჯინა
   changeset_comments:
     feeds:
       comment:
@@ -384,6 +412,11 @@ xmf:
       join_discussion: გემშართით სისტემაშა, თიშენ ნამჷ-და, სხუნუას ქაკათათინ
       still_open: თირუეფიშ პაკეტი დიო ღილე რე. სხუნუა ხემიოჭირინაფუ იჸი, მუჭო თირუეფიშ
         პაკეტი დიკილუნ.
+      subscribe: გიშაჭარუა
+      unsubscribe: გიშაჭარუაშ გოუქვაფა
+      hide_comment: ტყობინაფა
+      unhide_comment: ძირაფა
+      comment: კომენტირება
       changesetxml: თირუეფიშ პაკეტიშ XML
       osmchangexml: osmChange XML
     paging_nav:
@@ -427,6 +460,8 @@ xmf:
     show:
       title: '%{user}შ დღარი | %{title}'
       user_title: '%{user}შ დღარი'
+      subscribe: გიშაჭარუა
+      unsubscribe: გიშაჭარუაშ გოუქვაფა
       leave_a_comment: კომენტარიშ დოტება
       login_to_leave_a_comment_html: '%{login_link}, კომენტარიშ დატებელო'
       login: მიშულა
@@ -458,14 +493,28 @@ xmf:
         title: OpenStreetMap დღარიშ დინნაჭარეფი გეჸვენჯი ნინაშა %{language_name}
       all:
         title: OpenStreetMap დღარიშ დინნაჭარეფი
+  doorkeeper:
+    flash:
+      applications:
+        create:
+          notice: აპლიკაცია რეგისტრირებული რე.
+    scopes:
+      profile: თქვანი ანგარიშიშ ინფორმაციაშ ძირაფა
   errors:
     contact:
       contact: კონტაქტი
+    bad_request:
+      title: ჩილათერი მოთხირი
+    forbidden:
+      title: ჭირინაფა გოხურგელი
     internal_server_error:
       title: აპლიკაციაშ ჩილათა
     not_found:
       title: ფაილქ ვეგორჷ
   geocoder:
+    search:
+      title:
+        latlon: დინოხოლენი
     search_osm_nominatim:
       prefix:
         aerialway:
@@ -484,6 +533,7 @@ xmf:
           terminal: აეროპორტიშ ტერმინალი
           windsock: ბორიამაძირაფალი
         amenity:
+          animal_shelter: ჩხოლარეფიშ ხვაშია
           arts_centre: ხელუანაფაშ ცენტრი
           atm: ბანკომატი
           bank: ბანკი
@@ -492,6 +542,7 @@ xmf:
           bench: დახუნალი
           bicycle_parking: ველოსიპედეფიშ დგჷმილი
           bicycle_rental: ველოსპიედეფიშ გაქირაფა
+          bicycle_repair_station: ველოსიპეტეფიშ აკანწყუალი დგჷმილი
           biergarten: ლუდიშ ბაღი
           blood_bank: ზისხირიშ ბანკი
           boat_rental: ნიშეფიშ გაქირება
@@ -531,7 +582,10 @@ xmf:
           kindergarten: საბაღანო ბაღი
           language_school: ნინაშ სკოლა
           library: ბიბლიოთეკა
+          loading_dock: ოხარგუე დოკი
+          love_hotel: ჸოროფაშ სასუმარო
           marketplace: ბაზარი
+          mobile_money_agent: მობილური ფარაშ აგენტი
           monastery: მონასტერი
           money_transfer: ფარაშ ჯღონუა
           motorcycle_parking: მოტოციკლეტიშ პარკირაფა
@@ -624,7 +678,9 @@ xmf:
           ruins: აკოცჷმილი დგჷმილი
           school: სკოლა
           service: ონინალე დგჷმილი
+          stable: ცხენსაბეკი
           static_caravan: ქერვანი
+          sty: ორეჯე
           temple: ოხიდა
           terrace: აკიბი
           train_station: მახინწალიშ დგჷმილი
@@ -632,6 +688,7 @@ xmf:
           warehouse: ოწკარუე
           "yes": ნოდგჷმი
         club:
+          scout: სკაუტეფიშ ბაზა
           sport: სპორტული კლუბი
           "yes": კლუბი
         craft:
index e9f454f7e418d536347a4dca624d454c0ab70803..2c67e5237ce66bf3a1d175bb56d85b83cc26af6b 100644 (file)
@@ -3158,6 +3158,8 @@ zh-CN:
           ninth: 第9
           tenth: 第10
       time: 时间
+      download: 以GeoJSON格式下载路线
+      filename: 路线
     query:
       node: 节点
       way: 路径
index 3029a418f824e38c1811b2cba90e8c413058ddce..3f4a12bd782e058360439c44e3c3ac30bdb15c48 100644 (file)
@@ -18,22 +18,24 @@ OpenStreetMap::Application.routes.draw do
     get "permissions" => "permissions#show"
 
     post "changeset/:id/upload" => "changesets#upload", :as => :changeset_upload, :id => /\d+/
-    get "changeset/:id/download" => "changesets#download", :as => :changeset_download, :id => /\d+/
-    get "changeset/:id" => "changesets#show", :as => :changeset_show, :id => /\d+/
     post "changeset/:id/subscribe" => "changesets#subscribe", :as => :api_changeset_subscribe, :id => /\d+/
     post "changeset/:id/unsubscribe" => "changesets#unsubscribe", :as => :api_changeset_unsubscribe, :id => /\d+/
-    put "changeset/:id" => "changesets#update", :id => /\d+/
     put "changeset/:id/close" => "changesets#close", :as => :changeset_close, :id => /\d+/
-    post "changeset/:id/comment" => "changeset_comments#create", :as => :changeset_comment, :id => /\d+/
-    post "changeset/comment/:id/hide" => "changeset_comments#destroy", :as => :changeset_comment_hide, :id => /\d+/
-    post "changeset/comment/:id/unhide" => "changeset_comments#restore", :as => :changeset_comment_unhide, :id => /\d+/
   end
 
   namespace :api, :path => "api/0.6" do
     resources :changesets, :only => [:index, :create]
+    resources :changesets, :path => "changeset", :id => /\d+/, :only => [:show, :update] do
+      resource :download, :module => :changesets, :only => :show
+      resources :changeset_comments, :path => "comment", :only => :create
+    end
     put "changeset/create" => "changesets#create", :as => nil
 
-    resources :changeset_comments, :only => :index
+    resources :changeset_comments, :id => /\d+/, :only => :index do
+      resource :visibility, :module => :changeset_comments, :only => [:create, :destroy]
+    end
+    post "changeset/comment/:changeset_comment_id/unhide" => "changeset_comments/visibilities#create", :changeset_comment_id => /\d+/, :as => nil
+    post "changeset/comment/:changeset_comment_id/hide" => "changeset_comments/visibilities#destroy", :changeset_comment_id => /\d+/, :as => nil
 
     resources :nodes, :only => [:index, :create]
     resources :nodes, :path => "node", :id => /\d+/, :only => [:show, :update, :destroy] do
index 6710ee88e02b947c1294bb97d309e0d1e60b4118..b0c314cbc8258c2b3010377776c4990434229d99 100644 (file)
@@ -506,10 +506,10 @@ module OSM
   end
 
   # Parse a float, raising a specified exception on failure
-  def self.parse_float(str, klass, *args)
+  def self.parse_float(str, klass, *)
     Float(str)
   rescue StandardError
-    raise klass.new(*args)
+    raise klass.new(*)
   end
 
   # Construct a random token of a given length
index 58c8f7fe7141f95fee59b393beb65fc74dbe1d87..0945b6290045a444b6c35e163a968819fd6a88cc 100644 (file)
@@ -8,9 +8,9 @@ class ChangesetCommentApiCapabilityTest < ActiveSupport::TestCase
     scopes = Set.new
     ability = ApiAbility.new user, scopes
 
-    [:create, :destroy, :restore].each do |action|
-      assert ability.cannot? action, ChangesetComment
-    end
+    assert ability.cannot? :create, ChangesetComment
+    assert ability.cannot? :create, :changeset_comment_visibility
+    assert ability.cannot? :destroy, :changeset_comment_visibility
   end
 
   test "as a normal user with write_changeset_comments scope" do
@@ -18,13 +18,9 @@ class ChangesetCommentApiCapabilityTest < ActiveSupport::TestCase
     scopes = Set.new %w[write_changeset_comments]
     ability = ApiAbility.new user, scopes
 
-    [:destroy, :restore].each do |action|
-      assert ability.cannot? action, ChangesetComment
-    end
-
-    [:create].each do |action|
-      assert ability.can? action, ChangesetComment
-    end
+    assert ability.can? :create, ChangesetComment
+    assert ability.cannot? :create, :changeset_comment_visibility
+    assert ability.cannot? :destroy, :changeset_comment_visibility
   end
 
   test "as a moderator without scopes" do
@@ -32,9 +28,9 @@ class ChangesetCommentApiCapabilityTest < ActiveSupport::TestCase
     scopes = Set.new
     ability = ApiAbility.new user, scopes
 
-    [:create, :destroy, :restore].each do |action|
-      assert ability.cannot? action, ChangesetComment
-    end
+    assert ability.cannot? :create, ChangesetComment
+    assert ability.cannot? :create, :changeset_comment_visibility
+    assert ability.cannot? :destroy, :changeset_comment_visibility
   end
 
   test "as a moderator with write_changeset_comments scope" do
@@ -42,9 +38,9 @@ class ChangesetCommentApiCapabilityTest < ActiveSupport::TestCase
     scopes = Set.new %w[write_changeset_comments]
     ability = ApiAbility.new user, scopes
 
-    [:create, :destroy, :restore].each do |action|
-      assert ability.can? action, ChangesetComment
-    end
+    assert ability.can? :create, ChangesetComment
+    assert ability.can? :create, :changeset_comment_visibility
+    assert ability.can? :destroy, :changeset_comment_visibility
   end
 end
 
diff --git a/test/controllers/api/changeset_comments/visibilities_controller_test.rb b/test/controllers/api/changeset_comments/visibilities_controller_test.rb
new file mode 100644 (file)
index 0000000..1e8b562
--- /dev/null
@@ -0,0 +1,253 @@
+require "test_helper"
+
+module Api
+  module ChangesetComments
+    class VisibilitiesControllerTest < ActionDispatch::IntegrationTest
+      ##
+      # test all routes which lead to this controller
+      def test_routes
+        assert_routing(
+          { :path => "/api/0.6/changeset_comments/1/visibility", :method => :post },
+          { :controller => "api/changeset_comments/visibilities", :action => "create", :changeset_comment_id => "1" }
+        )
+        assert_routing(
+          { :path => "/api/0.6/changeset_comments/1/visibility.json", :method => :post },
+          { :controller => "api/changeset_comments/visibilities", :action => "create", :changeset_comment_id => "1", :format => "json" }
+        )
+        assert_routing(
+          { :path => "/api/0.6/changeset_comments/1/visibility", :method => :delete },
+          { :controller => "api/changeset_comments/visibilities", :action => "destroy", :changeset_comment_id => "1" }
+        )
+        assert_routing(
+          { :path => "/api/0.6/changeset_comments/1/visibility.json", :method => :delete },
+          { :controller => "api/changeset_comments/visibilities", :action => "destroy", :changeset_comment_id => "1", :format => "json" }
+        )
+
+        assert_recognizes(
+          { :controller => "api/changeset_comments/visibilities", :action => "create", :changeset_comment_id => "1" },
+          { :path => "/api/0.6/changeset/comment/1/unhide", :method => :post }
+        )
+        assert_recognizes(
+          { :controller => "api/changeset_comments/visibilities", :action => "create", :changeset_comment_id => "1", :format => "json" },
+          { :path => "/api/0.6/changeset/comment/1/unhide.json", :method => :post }
+        )
+        assert_recognizes(
+          { :controller => "api/changeset_comments/visibilities", :action => "destroy", :changeset_comment_id => "1" },
+          { :path => "/api/0.6/changeset/comment/1/hide", :method => :post }
+        )
+        assert_recognizes(
+          { :controller => "api/changeset_comments/visibilities", :action => "destroy", :changeset_comment_id => "1", :format => "json" },
+          { :path => "/api/0.6/changeset/comment/1/hide.json", :method => :post }
+        )
+      end
+
+      def test_create_by_unauthorized
+        comment = create(:changeset_comment, :visible => false)
+
+        post api_changeset_comment_visibility_path(comment)
+
+        assert_response :unauthorized
+        assert_not comment.reload.visible
+      end
+
+      def test_create_by_normal_user
+        comment = create(:changeset_comment, :visible => false)
+        auth_header = bearer_authorization_header
+
+        post api_changeset_comment_visibility_path(comment), :headers => auth_header
+
+        assert_response :forbidden
+        assert_not comment.reload.visible
+      end
+
+      def test_create_on_missing_comment
+        auth_header = bearer_authorization_header create(:moderator_user)
+
+        post api_changeset_comment_visibility_path(999111), :headers => auth_header
+
+        assert_response :not_found
+      end
+
+      def test_create_without_required_scope
+        comment = create(:changeset_comment, :visible => false)
+        auth_header = bearer_authorization_header create(:moderator_user), :scopes => %w[read_prefs]
+
+        post api_changeset_comment_visibility_path(comment), :headers => auth_header
+
+        assert_response :forbidden
+        assert_not comment.reload.visible
+      end
+
+      def test_create_with_write_changeset_comments_scope
+        comment = create(:changeset_comment, :visible => false)
+        auth_header = bearer_authorization_header create(:moderator_user), :scopes => %w[write_changeset_comments]
+
+        post api_changeset_comment_visibility_path(comment), :headers => auth_header
+
+        check_successful_response_xml(comment, :comment_visible => true)
+      end
+
+      def test_create_with_write_changeset_comments_scope_json
+        comment = create(:changeset_comment, :visible => false)
+        auth_header = bearer_authorization_header create(:moderator_user), :scopes => %w[write_changeset_comments]
+
+        post api_changeset_comment_visibility_path(comment, :format => "json"), :headers => auth_header
+
+        check_successful_response_json(comment, :comment_visible => true)
+      end
+
+      def test_create_with_write_api_scope
+        comment = create(:changeset_comment, :visible => false)
+        auth_header = bearer_authorization_header create(:moderator_user), :scopes => %w[write_api]
+
+        post api_changeset_comment_visibility_path(comment), :headers => auth_header
+
+        check_successful_response_xml(comment, :comment_visible => true)
+      end
+
+      def test_create_with_write_api_scope_json
+        comment = create(:changeset_comment, :visible => false)
+        auth_header = bearer_authorization_header create(:moderator_user), :scopes => %w[write_api]
+
+        post api_changeset_comment_visibility_path(comment, :format => "json"), :headers => auth_header
+
+        check_successful_response_json(comment, :comment_visible => true)
+      end
+
+      def test_create_at_legacy_route
+        comment = create(:changeset_comment, :visible => false)
+        auth_header = bearer_authorization_header create(:moderator_user), :scopes => %w[write_api]
+
+        post "/api/0.6/changeset/comment/#{comment.id}/unhide", :headers => auth_header
+
+        check_successful_response_xml(comment, :comment_visible => true)
+      end
+
+      def test_create_at_legacy_route_json
+        comment = create(:changeset_comment, :visible => false)
+        auth_header = bearer_authorization_header create(:moderator_user), :scopes => %w[write_api]
+
+        post "/api/0.6/changeset/comment/#{comment.id}/unhide.json", :headers => auth_header
+
+        check_successful_response_json(comment, :comment_visible => true)
+      end
+
+      def test_destroy_by_unauthorized
+        comment = create(:changeset_comment)
+
+        delete api_changeset_comment_visibility_path(comment)
+
+        assert_response :unauthorized
+        assert comment.reload.visible
+      end
+
+      def test_destroy_by_normal_user
+        comment = create(:changeset_comment)
+        auth_header = bearer_authorization_header
+
+        delete api_changeset_comment_visibility_path(comment), :headers => auth_header
+
+        assert_response :forbidden
+        assert comment.reload.visible
+      end
+
+      def test_destroy_on_missing_comment
+        auth_header = bearer_authorization_header create(:moderator_user)
+
+        delete api_changeset_comment_visibility_path(999111), :headers => auth_header
+
+        assert_response :not_found
+      end
+
+      def test_destroy_without_required_scope
+        comment = create(:changeset_comment)
+        auth_header = bearer_authorization_header create(:moderator_user), :scopes => %w[read_prefs]
+
+        delete api_changeset_comment_visibility_path(comment), :headers => auth_header
+
+        assert_response :forbidden
+        assert comment.reload.visible
+      end
+
+      def test_destroy_with_write_changeset_comments_scope
+        comment = create(:changeset_comment)
+        auth_header = bearer_authorization_header create(:moderator_user), :scopes => %w[write_changeset_comments]
+
+        delete api_changeset_comment_visibility_path(comment), :headers => auth_header
+
+        check_successful_response_xml(comment, :comment_visible => false)
+      end
+
+      def test_destroy_with_write_changeset_comments_scope_json
+        comment = create(:changeset_comment)
+        auth_header = bearer_authorization_header create(:moderator_user), :scopes => %w[write_changeset_comments]
+
+        delete api_changeset_comment_visibility_path(comment, :format => "json"), :headers => auth_header
+
+        check_successful_response_json(comment, :comment_visible => false)
+      end
+
+      def test_destroy_with_write_api_scope
+        comment = create(:changeset_comment)
+        auth_header = bearer_authorization_header create(:moderator_user), :scopes => %w[write_api]
+
+        delete api_changeset_comment_visibility_path(comment), :headers => auth_header
+
+        check_successful_response_xml(comment, :comment_visible => false)
+      end
+
+      def test_destroy_with_write_api_scope_json
+        comment = create(:changeset_comment)
+        auth_header = bearer_authorization_header create(:moderator_user), :scopes => %w[write_api]
+
+        delete api_changeset_comment_visibility_path(comment, :format => "json"), :headers => auth_header
+
+        check_successful_response_json(comment, :comment_visible => false)
+      end
+
+      def test_destroy_at_legacy_route
+        comment = create(:changeset_comment)
+        auth_header = bearer_authorization_header create(:moderator_user), :scopes => %w[write_api]
+
+        post "/api/0.6/changeset/comment/#{comment.id}/hide", :headers => auth_header
+
+        check_successful_response_xml(comment, :comment_visible => false)
+      end
+
+      def test_destroy_at_legacy_route_json
+        comment = create(:changeset_comment)
+        auth_header = bearer_authorization_header create(:moderator_user), :scopes => %w[write_api]
+
+        post "/api/0.6/changeset/comment/#{comment.id}/hide.json", :headers => auth_header
+
+        check_successful_response_json(comment, :comment_visible => false)
+      end
+
+      private
+
+      def check_successful_response_xml(comment, comment_visible:)
+        assert_response :success
+        assert_equal "application/xml", response.media_type
+        assert_dom "osm", 1 do
+          assert_dom "> changeset", 1 do
+            assert_dom "> @id", comment.changeset_id.to_s
+            assert_dom "> @comments_count", comment_visible ? "1" : "0"
+          end
+        end
+
+        assert_equal comment_visible, comment.reload.visible
+      end
+
+      def check_successful_response_json(comment, comment_visible:)
+        assert_response :success
+        assert_equal "application/json", response.media_type
+        js = ActiveSupport::JSON.decode(@response.body)
+        assert_not_nil js["changeset"]
+        assert_equal comment.changeset_id, js["changeset"]["id"]
+        assert_equal comment_visible ? 1 : 0, js["changeset"]["comments_count"]
+
+        assert_equal comment_visible, comment.reload.visible
+      end
+    end
+  end
+end
index 72463ed676e3ae39044e8473da70929260354909..b16ea4502120a02e5be5024a38ade2b631467d7e 100644 (file)
@@ -15,27 +15,11 @@ module Api
       )
       assert_routing(
         { :path => "/api/0.6/changeset/1/comment", :method => :post },
-        { :controller => "api/changeset_comments", :action => "create", :id => "1" }
+        { :controller => "api/changeset_comments", :action => "create", :changeset_id => "1" }
       )
       assert_routing(
         { :path => "/api/0.6/changeset/1/comment.json", :method => :post },
-        { :controller => "api/changeset_comments", :action => "create", :id => "1", :format => "json" }
-      )
-      assert_routing(
-        { :path => "/api/0.6/changeset/comment/1/hide", :method => :post },
-        { :controller => "api/changeset_comments", :action => "destroy", :id => "1" }
-      )
-      assert_routing(
-        { :path => "/api/0.6/changeset/comment/1/hide.json", :method => :post },
-        { :controller => "api/changeset_comments", :action => "destroy", :id => "1", :format => "json" }
-      )
-      assert_routing(
-        { :path => "/api/0.6/changeset/comment/1/unhide", :method => :post },
-        { :controller => "api/changeset_comments", :action => "restore", :id => "1" }
-      )
-      assert_routing(
-        { :path => "/api/0.6/changeset/comment/1/unhide.json", :method => :post },
-        { :controller => "api/changeset_comments", :action => "restore", :id => "1", :format => "json" }
+        { :controller => "api/changeset_comments", :action => "create", :changeset_id => "1", :format => "json" }
       )
     end
 
@@ -81,35 +65,35 @@ module Api
 
     def test_create_by_unauthorized
       assert_no_difference "ChangesetComment.count" do
-        post changeset_comment_path(create(:changeset, :closed), :text => "This is a comment")
+        post api_changeset_changeset_comments_path(create(:changeset, :closed), :text => "This is a comment")
         assert_response :unauthorized
       end
     end
 
     def test_create_on_missing_changeset
       assert_no_difference "ChangesetComment.count" do
-        post changeset_comment_path(999111, :text => "This is a comment"), :headers => bearer_authorization_header
+        post api_changeset_changeset_comments_path(999111, :text => "This is a comment"), :headers => bearer_authorization_header
         assert_response :not_found
       end
     end
 
     def test_create_on_open_changeset
       assert_no_difference "ChangesetComment.count" do
-        post changeset_comment_path(create(:changeset), :text => "This is a comment"), :headers => bearer_authorization_header
+        post api_changeset_changeset_comments_path(create(:changeset), :text => "This is a comment"), :headers => bearer_authorization_header
         assert_response :conflict
       end
     end
 
     def test_create_without_text
       assert_no_difference "ChangesetComment.count" do
-        post changeset_comment_path(create(:changeset, :closed)), :headers => bearer_authorization_header
+        post api_changeset_changeset_comments_path(create(:changeset, :closed)), :headers => bearer_authorization_header
         assert_response :bad_request
       end
     end
 
     def test_create_with_empty_text
       assert_no_difference "ChangesetComment.count" do
-        post changeset_comment_path(create(:changeset, :closed), :text => ""), :headers => bearer_authorization_header
+        post api_changeset_changeset_comments_path(create(:changeset, :closed), :text => ""), :headers => bearer_authorization_header
         assert_response :bad_request
       end
     end
@@ -120,7 +104,7 @@ module Api
       changeset = create(:changeset, :closed)
 
       assert_difference "ChangesetComment.count", 0 do
-        post changeset_comment_path(changeset), :params => { :text => "This is a comment" }, :headers => auth_header
+        post api_changeset_changeset_comments_path(changeset), :params => { :text => "This is a comment" }, :headers => auth_header
         assert_response :forbidden
       end
     end
@@ -131,7 +115,7 @@ module Api
       changeset = create(:changeset, :closed)
 
       assert_difference "ChangesetComment.count", 0 do
-        post changeset_comment_path(changeset), :params => { :text => "This is a comment" }, :headers => auth_header
+        post api_changeset_changeset_comments_path(changeset), :params => { :text => "This is a comment" }, :headers => auth_header
         assert_response :forbidden
       end
     end
@@ -142,7 +126,7 @@ module Api
       changeset = create(:changeset, :closed)
 
       assert_difference "ChangesetComment.count", 1 do
-        post changeset_comment_path(changeset), :params => { :text => "This is a comment" }, :headers => auth_header
+        post api_changeset_changeset_comments_path(changeset), :params => { :text => "This is a comment" }, :headers => auth_header
         assert_response :success
       end
 
@@ -159,7 +143,7 @@ module Api
       changeset = create(:changeset, :closed)
 
       assert_difference "ChangesetComment.count", 1 do
-        post changeset_comment_path(changeset), :params => { :text => "This is a comment" }, :headers => auth_header
+        post api_changeset_changeset_comments_path(changeset), :params => { :text => "This is a comment" }, :headers => auth_header
         assert_response :success
       end
 
@@ -177,7 +161,7 @@ module Api
       assert_difference "ChangesetComment.count", 1 do
         assert_no_difference "ActionMailer::Base.deliveries.size" do
           perform_enqueued_jobs do
-            post changeset_comment_path(changeset, :text => "This is a comment"), :headers => auth_header
+            post api_changeset_changeset_comments_path(changeset, :text => "This is a comment"), :headers => auth_header
             assert_response :success
           end
         end
@@ -193,7 +177,7 @@ module Api
       assert_difference "ChangesetComment.count", 1 do
         assert_no_difference "ActionMailer::Base.deliveries.size" do
           perform_enqueued_jobs do
-            post changeset_comment_path(changeset, :text => "This is a comment"), :headers => auth_header
+            post api_changeset_changeset_comments_path(changeset, :text => "This is a comment"), :headers => auth_header
             assert_response :success
           end
         end
@@ -209,7 +193,7 @@ module Api
       assert_difference "ChangesetComment.count", 1 do
         assert_no_difference "ActionMailer::Base.deliveries.size" do
           perform_enqueued_jobs do
-            post changeset_comment_path(changeset, :text => "This is a comment"), :headers => auth_header
+            post api_changeset_changeset_comments_path(changeset, :text => "This is a comment"), :headers => auth_header
             assert_response :success
           end
         end
@@ -226,7 +210,7 @@ module Api
       assert_difference "ChangesetComment.count", 1 do
         assert_difference "ActionMailer::Base.deliveries.size", 1 do
           perform_enqueued_jobs do
-            post changeset_comment_path(changeset, :text => "This is a comment"), :headers => auth_header
+            post api_changeset_changeset_comments_path(changeset, :text => "This is a comment"), :headers => auth_header
             assert_response :success
           end
         end
@@ -250,7 +234,7 @@ module Api
       assert_difference "ChangesetComment.count", 1 do
         assert_difference "ActionMailer::Base.deliveries.size", 2 do
           perform_enqueued_jobs do
-            post changeset_comment_path(changeset, :text => "This is a comment"), :headers => auth_header
+            post api_changeset_changeset_comments_path(changeset, :text => "This is a comment"), :headers => auth_header
             assert_response :success
           end
         end
@@ -277,13 +261,13 @@ module Api
 
       assert_difference "ChangesetComment.count", Settings.initial_changeset_comments_per_hour do
         1.upto(Settings.initial_changeset_comments_per_hour) do |count|
-          post changeset_comment_path(changeset, :text => "Comment #{count}"), :headers => auth_header
+          post api_changeset_changeset_comments_path(changeset, :text => "Comment #{count}"), :headers => auth_header
           assert_response :success
         end
       end
 
       assert_no_difference "ChangesetComment.count" do
-        post changeset_comment_path(changeset, :text => "One comment too many"), :headers => auth_header
+        post api_changeset_changeset_comments_path(changeset, :text => "One comment too many"), :headers => auth_header
         assert_response :too_many_requests
       end
     end
@@ -299,13 +283,13 @@ module Api
 
       assert_difference "ChangesetComment.count", Settings.max_changeset_comments_per_hour do
         1.upto(Settings.max_changeset_comments_per_hour) do |count|
-          post changeset_comment_path(changeset, :text => "Comment #{count}"), :headers => auth_header
+          post api_changeset_changeset_comments_path(changeset, :text => "Comment #{count}"), :headers => auth_header
           assert_response :success
         end
       end
 
       assert_no_difference "ChangesetComment.count" do
-        post changeset_comment_path(changeset, :text => "One comment too many"), :headers => auth_header
+        post api_changeset_changeset_comments_path(changeset, :text => "One comment too many"), :headers => auth_header
         assert_response :too_many_requests
       end
     end
@@ -321,13 +305,13 @@ module Api
 
       assert_difference "ChangesetComment.count", Settings.initial_changeset_comments_per_hour / 2 do
         1.upto(Settings.initial_changeset_comments_per_hour / 2) do |count|
-          post changeset_comment_path(changeset, :text => "Comment #{count}"), :headers => auth_header
+          post api_changeset_changeset_comments_path(changeset, :text => "Comment #{count}"), :headers => auth_header
           assert_response :success
         end
       end
 
       assert_no_difference "ChangesetComment.count" do
-        post changeset_comment_path(changeset, :text => "One comment too many"), :headers => auth_header
+        post api_changeset_changeset_comments_path(changeset, :text => "One comment too many"), :headers => auth_header
         assert_response :too_many_requests
       end
     end
@@ -342,131 +326,17 @@ module Api
 
       assert_difference "ChangesetComment.count", Settings.moderator_changeset_comments_per_hour do
         1.upto(Settings.moderator_changeset_comments_per_hour) do |count|
-          post changeset_comment_path(changeset, :text => "Comment #{count}"), :headers => auth_header
+          post api_changeset_changeset_comments_path(changeset, :text => "Comment #{count}"), :headers => auth_header
           assert_response :success
         end
       end
 
       assert_no_difference "ChangesetComment.count" do
-        post changeset_comment_path(changeset, :text => "One comment too many"), :headers => auth_header
+        post api_changeset_changeset_comments_path(changeset, :text => "One comment too many"), :headers => auth_header
         assert_response :too_many_requests
       end
     end
 
-    def test_hide_by_unauthorized
-      comment = create(:changeset_comment)
-
-      post changeset_comment_hide_path(comment)
-
-      assert_response :unauthorized
-      assert comment.reload.visible
-    end
-
-    def test_hide_by_normal_user
-      comment = create(:changeset_comment)
-      auth_header = bearer_authorization_header
-
-      post changeset_comment_hide_path(comment), :headers => auth_header
-
-      assert_response :forbidden
-      assert comment.reload.visible
-    end
-
-    def test_hide_missing_comment
-      auth_header = bearer_authorization_header create(:moderator_user)
-
-      post changeset_comment_hide_path(999111), :headers => auth_header
-
-      assert_response :not_found
-    end
-
-    def test_hide_without_required_scope
-      comment = create(:changeset_comment)
-      auth_header = bearer_authorization_header create(:moderator_user), :scopes => %w[read_prefs]
-
-      post changeset_comment_hide_path(comment), :headers => auth_header
-
-      assert_response :forbidden
-      assert comment.reload.visible
-    end
-
-    def test_hide_with_write_changeset_comments_scope
-      comment = create(:changeset_comment)
-      auth_header = bearer_authorization_header create(:moderator_user), :scopes => %w[write_changeset_comments]
-
-      post changeset_comment_hide_path(comment), :headers => auth_header
-
-      assert_response :success
-      assert_not comment.reload.visible
-    end
-
-    def test_hide_with_write_api_scope
-      comment = create(:changeset_comment)
-      auth_header = bearer_authorization_header create(:moderator_user), :scopes => %w[write_api]
-
-      post changeset_comment_hide_path(comment), :headers => auth_header
-
-      assert_response :success
-      assert_not comment.reload.visible
-    end
-
-    def test_unhide_by_unauthorized
-      comment = create(:changeset_comment, :visible => false)
-
-      post changeset_comment_unhide_path(comment)
-
-      assert_response :unauthorized
-      assert_not comment.reload.visible
-    end
-
-    def test_unhide_by_normal_user
-      comment = create(:changeset_comment, :visible => false)
-      auth_header = bearer_authorization_header
-
-      post changeset_comment_unhide_path(comment), :headers => auth_header
-
-      assert_response :forbidden
-      assert_not comment.reload.visible
-    end
-
-    def test_unhide_missing_comment
-      auth_header = bearer_authorization_header create(:moderator_user)
-
-      post changeset_comment_unhide_path(999111), :headers => auth_header
-
-      assert_response :not_found
-    end
-
-    def test_unhide_without_required_scope
-      comment = create(:changeset_comment, :visible => false)
-      auth_header = bearer_authorization_header create(:moderator_user), :scopes => %w[read_prefs]
-
-      post changeset_comment_unhide_path(comment), :headers => auth_header
-
-      assert_response :forbidden
-      assert_not comment.reload.visible
-    end
-
-    def test_unhide_with_write_changeset_comments_scope
-      comment = create(:changeset_comment, :visible => false)
-      auth_header = bearer_authorization_header create(:moderator_user), :scopes => %w[write_changeset_comments]
-
-      post changeset_comment_unhide_path(comment), :headers => auth_header
-
-      assert_response :success
-      assert comment.reload.visible
-    end
-
-    def test_unhide_with_write_api_scope
-      comment = create(:changeset_comment, :visible => false)
-      auth_header = bearer_authorization_header create(:moderator_user), :scopes => %w[write_api]
-
-      post changeset_comment_unhide_path(comment), :headers => auth_header
-
-      assert_response :success
-      assert comment.reload.visible
-    end
-
     private
 
     ##
diff --git a/test/controllers/api/changesets/downloads_controller_test.rb b/test/controllers/api/changesets/downloads_controller_test.rb
new file mode 100644 (file)
index 0000000..9eda6ec
--- /dev/null
@@ -0,0 +1,309 @@
+require "test_helper"
+
+module Api
+  module Changesets
+    class DownloadsControllerTest < ActionDispatch::IntegrationTest
+      ##
+      # test all routes which lead to this controller
+      def test_routes
+        assert_routing(
+          { :path => "/api/0.6/changeset/1/download", :method => :get },
+          { :controller => "api/changesets/downloads", :action => "show", :changeset_id => "1" }
+        )
+      end
+
+      def test_show_empty
+        changeset = create(:changeset)
+
+        get api_changeset_download_path(changeset)
+
+        assert_response :success
+        assert_dom "osmChange[version='#{Settings.api_version}'][generator='#{Settings.generator}']" do
+          assert_dom "create", 0
+          assert_dom "modify", 0
+          assert_dom "delete", 0
+        end
+      end
+
+      def test_show_created_elements
+        changeset = create(:changeset)
+        old_node1 = create(:old_node, :changeset => changeset, :version => 1, :latitude => (60.12345 * OldNode::SCALE).to_i, :longitude => (30.54321 * OldNode::SCALE).to_i)
+        create(:old_node_tag, :old_node => old_node1, :k => "highway", :v => "crossing")
+        create(:old_node_tag, :old_node => old_node1, :k => "crossing", :v => "marked")
+        old_node2 = create(:old_node, :changeset => changeset, :version => 1, :latitude => (60.23456 * OldNode::SCALE).to_i, :longitude => (30.65432 * OldNode::SCALE).to_i)
+        create(:old_node_tag, :old_node => old_node2, :k => "highway", :v => "traffic_signals")
+        old_way = create(:old_way, :changeset => changeset, :version => 1)
+        create(:old_way_tag, :old_way => old_way, :k => "highway", :v => "secondary")
+        create(:old_way_tag, :old_way => old_way, :k => "name", :v => "Some Street")
+        create(:old_way_node, :old_way => old_way, :node => old_node1.current_node, :sequence_id => 1)
+        create(:old_way_node, :old_way => old_way, :node => old_node2.current_node, :sequence_id => 2)
+        old_relation = create(:old_relation, :changeset => changeset, :version => 1)
+        create(:old_relation_tag, :old_relation => old_relation, :k => "type", :v => "restriction")
+        create(:old_relation_member, :old_relation => old_relation, :member => old_way.current_way, :member_role => "from", :sequence_id => 1)
+        create(:old_relation_member, :old_relation => old_relation, :member => old_node2.current_node, :member_role => "via", :sequence_id => 2)
+
+        get api_changeset_download_path(changeset)
+
+        assert_response :success
+        assert_dom "osmChange[version='#{Settings.api_version}'][generator='#{Settings.generator}']" do
+          assert_dom "create", 4 do
+            assert_dom "node", 2
+            assert_dom "node[id='#{old_node1.node_id}']", 1 do
+              assert_dom "> @version", "1"
+              assert_dom "> @visible", "true"
+              assert_dom "tag", 2
+              assert_dom "tag[k='highway'][v='crossing']"
+              assert_dom "tag[k='crossing'][v='marked']"
+              assert_dom "> @lat", "60.1234500"
+              assert_dom "> @lon", "30.5432100"
+            end
+            assert_dom "node[id='#{old_node2.node_id}']", 1 do
+              assert_dom "> @version", "1"
+              assert_dom "> @visible", "true"
+              assert_dom "tag", 1
+              assert_dom "tag[k='highway'][v='traffic_signals']"
+              assert_dom "> @lat", "60.2345600"
+              assert_dom "> @lon", "30.6543200"
+            end
+            assert_dom "way", 1
+            assert_dom "way[id='#{old_way.way_id}']", 1 do
+              assert_dom "> @version", "1"
+              assert_dom "> @visible", "true"
+              assert_dom "tag", 2
+              assert_dom "tag[k='highway'][v='secondary']"
+              assert_dom "tag[k='name'][v='Some Street']"
+              assert_dom "nd", 2 do |dom_nds|
+                assert_dom dom_nds[0], "> @ref", old_node1.node_id.to_s
+                assert_dom dom_nds[1], "> @ref", old_node2.node_id.to_s
+              end
+            end
+            assert_dom "relation", 1
+            assert_dom "relation[id='#{old_relation.relation_id}']", 1 do
+              assert_dom "> @version", "1"
+              assert_dom "> @visible", "true"
+              assert_dom "tag", 1
+              assert_dom "tag[k='type'][v='restriction']"
+              assert_dom "member", 2 do |dom_members|
+                assert_dom dom_members[0], "> @type", "way"
+                assert_dom dom_members[0], "> @ref", old_way.way_id.to_s
+                assert_dom dom_members[0], "> @role", "from"
+                assert_dom dom_members[1], "> @type", "node"
+                assert_dom dom_members[1], "> @ref", old_node2.node_id.to_s
+                assert_dom dom_members[1], "> @role", "via"
+              end
+            end
+          end
+        end
+      end
+
+      def test_show_edited_elements
+        changeset = create(:changeset)
+        old_node1 = create(:old_node, :changeset => changeset, :version => 2, :latitude => (60.12345 * OldNode::SCALE).to_i, :longitude => (30.54321 * OldNode::SCALE).to_i)
+        create(:old_node_tag, :old_node => old_node1, :k => "highway", :v => "crossing")
+        create(:old_node_tag, :old_node => old_node1, :k => "crossing", :v => "marked")
+        old_node2 = create(:old_node, :changeset => changeset, :version => 2, :latitude => (60.23456 * OldNode::SCALE).to_i, :longitude => (30.65432 * OldNode::SCALE).to_i)
+        create(:old_node_tag, :old_node => old_node2, :k => "highway", :v => "traffic_signals")
+        old_way = create(:old_way, :changeset => changeset, :version => 2)
+        create(:old_way_tag, :old_way => old_way, :k => "highway", :v => "secondary")
+        create(:old_way_tag, :old_way => old_way, :k => "name", :v => "Some Street")
+        create(:old_way_node, :old_way => old_way, :node => old_node1.current_node, :sequence_id => 1)
+        create(:old_way_node, :old_way => old_way, :node => old_node2.current_node, :sequence_id => 2)
+        old_relation = create(:old_relation, :changeset => changeset, :version => 2)
+        create(:old_relation_tag, :old_relation => old_relation, :k => "type", :v => "restriction")
+        create(:old_relation_member, :old_relation => old_relation, :member => old_way.current_way, :member_role => "from", :sequence_id => 1)
+        create(:old_relation_member, :old_relation => old_relation, :member => old_node2.current_node, :member_role => "via", :sequence_id => 2)
+
+        get api_changeset_download_path(changeset)
+
+        assert_response :success
+        assert_dom "osmChange[version='#{Settings.api_version}'][generator='#{Settings.generator}']" do
+          assert_dom "modify", 4 do
+            assert_dom "node", 2
+            assert_dom "node[id='#{old_node1.node_id}']", 1 do
+              assert_dom "> @version", "2"
+              assert_dom "> @visible", "true"
+              assert_dom "tag", 2
+              assert_dom "tag[k='highway'][v='crossing']"
+              assert_dom "tag[k='crossing'][v='marked']"
+              assert_dom "> @lat", "60.1234500"
+              assert_dom "> @lon", "30.5432100"
+            end
+            assert_dom "node[id='#{old_node2.node_id}']", 1 do
+              assert_dom "> @version", "2"
+              assert_dom "> @visible", "true"
+              assert_dom "tag", 1
+              assert_dom "tag[k='highway'][v='traffic_signals']"
+              assert_dom "> @lat", "60.2345600"
+              assert_dom "> @lon", "30.6543200"
+            end
+            assert_dom "way", 1
+            assert_dom "way[id='#{old_way.way_id}']", 1 do
+              assert_dom "> @version", "2"
+              assert_dom "> @visible", "true"
+              assert_dom "tag", 2
+              assert_dom "tag[k='highway'][v='secondary']"
+              assert_dom "tag[k='name'][v='Some Street']"
+              assert_dom "nd", 2 do |dom_nds|
+                assert_dom dom_nds[0], "> @ref", old_node1.node_id.to_s
+                assert_dom dom_nds[1], "> @ref", old_node2.node_id.to_s
+              end
+            end
+            assert_dom "relation", 1
+            assert_dom "relation[id='#{old_relation.relation_id}']", 1 do
+              assert_dom "> @version", "2"
+              assert_dom "> @visible", "true"
+              assert_dom "tag", 1
+              assert_dom "tag[k='type'][v='restriction']"
+              assert_dom "member", 2 do |dom_members|
+                assert_dom dom_members[0], "> @type", "way"
+                assert_dom dom_members[0], "> @ref", old_way.way_id.to_s
+                assert_dom dom_members[0], "> @role", "from"
+                assert_dom dom_members[1], "> @type", "node"
+                assert_dom dom_members[1], "> @ref", old_node2.node_id.to_s
+                assert_dom dom_members[1], "> @role", "via"
+              end
+            end
+          end
+        end
+      end
+
+      def test_show_deleted_elements
+        changeset = create(:changeset)
+        old_node1 = create(:old_node, :changeset => changeset, :version => 3, :visible => false)
+        old_node2 = create(:old_node, :changeset => changeset, :version => 3, :visible => false)
+        old_way = create(:old_way, :changeset => changeset, :version => 3, :visible => false)
+        old_relation = create(:old_relation, :changeset => changeset, :version => 3, :visible => false)
+
+        get api_changeset_download_path(changeset)
+
+        assert_response :success
+        assert_dom "osmChange[version='#{Settings.api_version}'][generator='#{Settings.generator}']" do
+          assert_dom "delete", 4 do
+            assert_dom "node", 2
+            assert_dom "node[id='#{old_node1.node_id}']", 1 do
+              assert_dom "> @version", "3"
+              assert_dom "> @visible", "false"
+            end
+            assert_dom "node[id='#{old_node2.node_id}']", 1 do
+              assert_dom "> @version", "3"
+              assert_dom "> @visible", "false"
+            end
+            assert_dom "way", 1
+            assert_dom "way[id='#{old_way.way_id}']", 1 do
+              assert_dom "> @version", "3"
+              assert_dom "> @visible", "false"
+            end
+            assert_dom "relation", 1
+            assert_dom "relation[id='#{old_relation.relation_id}']", 1 do
+              assert_dom "> @version", "3"
+              assert_dom "> @visible", "false"
+            end
+          end
+        end
+      end
+
+      def test_show_should_sort_by_timestamp
+        changeset = create(:changeset)
+        node1 = create(:old_node, :version => 2, :timestamp => "2020-02-01", :changeset => changeset)
+        node0 = create(:old_node, :version => 2, :timestamp => "2020-01-01", :changeset => changeset)
+
+        get api_changeset_download_path(changeset)
+
+        assert_response :success
+        assert_dom "modify", :count => 2 do |modify|
+          assert_dom modify[0], ">node", :count => 1 do |node|
+            assert_dom node, ">@id", node0.node_id.to_s
+          end
+          assert_dom modify[1], ">node", :count => 1 do |node|
+            assert_dom node, ">@id", node1.node_id.to_s
+          end
+        end
+      end
+
+      def test_show_should_sort_by_version
+        changeset = create(:changeset)
+        node1 = create(:old_node, :version => 3, :timestamp => "2020-01-01", :changeset => changeset)
+        node0 = create(:old_node, :version => 2, :timestamp => "2020-01-01", :changeset => changeset)
+
+        get api_changeset_download_path(changeset)
+
+        assert_response :success
+        assert_dom "modify", :count => 2 do |modify|
+          assert_dom modify[0], ">node", :count => 1 do |node|
+            assert_dom node, ">@id", node0.node_id.to_s
+          end
+          assert_dom modify[1], ">node", :count => 1 do |node|
+            assert_dom node, ">@id", node1.node_id.to_s
+          end
+        end
+      end
+
+      ##
+      # check that the changeset download for a changeset with a redacted
+      # element in it doesn't contain that element.
+      def test_show_redacted
+        check_redacted do |changeset|
+          get api_changeset_download_path(changeset)
+        end
+      end
+
+      def test_show_redacted_unauthorized
+        check_redacted do |changeset|
+          get api_changeset_download_path(changeset, :show_redactions => "true")
+        end
+      end
+
+      def test_show_redacted_normal_user
+        auth_header = bearer_authorization_header
+
+        check_redacted do |changeset|
+          get api_changeset_download_path(changeset, :show_redactions => "true"), :headers => auth_header
+        end
+      end
+
+      def test_show_redacted_moderator_without_show_redactions
+        auth_header = bearer_authorization_header create(:moderator_user)
+
+        check_redacted do |changeset|
+          get api_changeset_download_path(changeset), :headers => auth_header
+        end
+      end
+
+      def test_show_redacted_moderator
+        auth_header = bearer_authorization_header create(:moderator_user)
+
+        check_redacted(:redacted_included => true) do |changeset|
+          get api_changeset_download_path(changeset, :show_redactions => "true"), :headers => auth_header
+        end
+      end
+
+      private
+
+      def check_redacted(redacted_included: false)
+        redaction = create(:redaction)
+        changeset = create(:changeset)
+        node = create(:node, :with_history, :version => 2, :changeset => changeset)
+        node_v1 = node.old_nodes.find_by(:version => 1)
+        node_v1.redact!(redaction)
+        way = create(:way, :with_history, :version => 2, :changeset => changeset)
+        way_v1 = way.old_ways.find_by(:version => 1)
+        way_v1.redact!(redaction)
+        relation = create(:relation, :with_history, :version => 2, :changeset => changeset)
+        relation_v1 = relation.old_relations.find_by(:version => 1)
+        relation_v1.redact!(redaction)
+
+        yield changeset
+
+        assert_response :success
+        assert_dom "osmChange", 1 do
+          assert_dom "node[id='#{node.id}'][version='1']", redacted_included ? 1 : 0
+          assert_dom "node[id='#{node.id}'][version='2']", 1
+          assert_dom "way[id='#{way.id}'][version='1']", redacted_included ? 1 : 0
+          assert_dom "way[id='#{way.id}'][version='2']", 1
+          assert_dom "relation[id='#{relation.id}'][version='1']", redacted_included ? 1 : 0
+          assert_dom "relation[id='#{relation.id}'][version='2']", 1
+        end
+      end
+    end
+  end
+end
index e73459a36d0693a64c897e0c76054ea2eb92737d..87deb3bdfcb2c1b064eb8b7be3ed8c4fd5b9836b 100644 (file)
@@ -17,14 +17,6 @@ module Api
         { :path => "/api/0.6/changesets", :method => :post },
         { :controller => "api/changesets", :action => "create" }
       )
-      assert_routing(
-        { :path => "/api/0.6/changeset/1/upload", :method => :post },
-        { :controller => "api/changesets", :action => "upload", :id => "1" }
-      )
-      assert_routing(
-        { :path => "/api/0.6/changeset/1/download", :method => :get },
-        { :controller => "api/changesets", :action => "download", :id => "1" }
-      )
       assert_routing(
         { :path => "/api/0.6/changeset/1", :method => :get },
         { :controller => "api/changesets", :action => "show", :id => "1" }
@@ -33,6 +25,14 @@ module Api
         { :path => "/api/0.6/changeset/1.json", :method => :get },
         { :controller => "api/changesets", :action => "show", :id => "1", :format => "json" }
       )
+      assert_routing(
+        { :path => "/api/0.6/changeset/1", :method => :put },
+        { :controller => "api/changesets", :action => "update", :id => "1" }
+      )
+      assert_routing(
+        { :path => "/api/0.6/changeset/1/upload", :method => :post },
+        { :controller => "api/changesets", :action => "upload", :id => "1" }
+      )
       assert_routing(
         { :path => "/api/0.6/changeset/1/subscribe", :method => :post },
         { :controller => "api/changesets", :action => "subscribe", :id => "1" }
@@ -49,10 +49,6 @@ module Api
         { :path => "/api/0.6/changeset/1/unsubscribe.json", :method => :post },
         { :controller => "api/changesets", :action => "unsubscribe", :id => "1", :format => "json" }
       )
-      assert_routing(
-        { :path => "/api/0.6/changeset/1", :method => :put },
-        { :controller => "api/changesets", :action => "update", :id => "1" }
-      )
       assert_routing(
         { :path => "/api/0.6/changeset/1/close", :method => :put },
         { :controller => "api/changesets", :action => "close", :id => "1" }
@@ -406,7 +402,7 @@ module Api
     def test_show
       changeset = create(:changeset)
 
-      get changeset_show_path(changeset)
+      get api_changeset_path(changeset)
       assert_response :success, "cannot get first changeset"
 
       assert_dom "osm[version='#{Settings.api_version}'][generator='#{Settings.generator}']", 1
@@ -414,7 +410,7 @@ module Api
         assert_dom "> discussion", 0
       end
 
-      get changeset_show_path(changeset), :params => { :include_discussion => true }
+      get api_changeset_path(changeset, :include_discussion => true)
       assert_response :success, "cannot get first changeset with comments"
 
       assert_dom "osm[version='#{Settings.api_version}'][generator='#{Settings.generator}']", 1
@@ -429,7 +425,7 @@ module Api
       changeset = create(:changeset, :closed)
       comment1, comment2, comment3 = create_list(:changeset_comment, 3, :changeset_id => changeset.id)
 
-      get changeset_show_path(changeset), :params => { :include_discussion => true }
+      get api_changeset_path(changeset, :include_discussion => true)
       assert_response :success, "cannot get closed changeset with comments"
 
       assert_dom "osm[version='#{Settings.api_version}'][generator='#{Settings.generator}']", 1 do
@@ -451,7 +447,7 @@ module Api
       comment2.update(:visible => false)
       changeset.reload
 
-      get changeset_show_path(changeset), :params => { :include_discussion => true }
+      get api_changeset_path(changeset, :include_discussion => true)
       assert_response :success, "cannot get closed changeset with comments"
 
       assert_dom "osm[version='#{Settings.api_version}'][generator='#{Settings.generator}']", 1
@@ -467,7 +463,7 @@ module Api
       end
 
       # one hidden comment not included because no permissions
-      get changeset_show_path(changeset), :params => { :include_discussion => true, :show_hidden_comments => true }
+      get api_changeset_path(changeset, :include_discussion => true, :show_hidden_comments => true)
       assert_response :success, "cannot get closed changeset with comments"
 
       assert_dom "osm[version='#{Settings.api_version}'][generator='#{Settings.generator}']", 1
@@ -486,8 +482,7 @@ module Api
       # one hidden comment shown to moderators
       moderator_user = create(:moderator_user)
       auth_header = bearer_authorization_header moderator_user
-      get changeset_show_path(changeset), :params => { :include_discussion => true, :show_hidden_comments => true },
-                                          :headers => auth_header
+      get api_changeset_path(changeset, :include_discussion => true, :show_hidden_comments => true), :headers => auth_header
       assert_response :success, "cannot get closed changeset with comments"
 
       assert_dom "osm[version='#{Settings.api_version}'][generator='#{Settings.generator}']", 1
@@ -510,7 +505,7 @@ module Api
       create(:changeset_tag, :changeset => changeset, :k => "created_by", :v => "JOSM/1.5 (18364)")
       create(:changeset_tag, :changeset => changeset, :k => "comment", :v => "changeset comment")
 
-      get changeset_show_path(changeset)
+      get api_changeset_path(changeset)
 
       assert_response :success
       assert_dom "osm[version='#{Settings.api_version}'][generator='#{Settings.generator}']", 1
@@ -524,7 +519,7 @@ module Api
     def test_show_json
       changeset = create(:changeset)
 
-      get changeset_show_path(changeset), :params => { :format => "json" }
+      get api_changeset_path(changeset, :format => "json")
       assert_response :success, "cannot get first changeset"
 
       js = ActiveSupport::JSON.decode(@response.body)
@@ -538,7 +533,7 @@ module Api
       assert_equal changeset.user.id, js["changeset"]["uid"]
       assert_equal changeset.user.display_name, js["changeset"]["user"]
 
-      get changeset_show_path(changeset), :params => { :format => "json", :include_discussion => true }
+      get api_changeset_path(changeset, :format => "json", :include_discussion => true)
       assert_response :success, "cannot get first changeset with comments"
 
       js = ActiveSupport::JSON.decode(@response.body)
@@ -559,7 +554,7 @@ module Api
       changeset = create(:changeset, :closed)
       comment0, comment1, comment2 = create_list(:changeset_comment, 3, :changeset_id => changeset.id)
 
-      get changeset_show_path(changeset), :params => { :format => "json", :include_discussion => true }
+      get api_changeset_path(changeset, :format => "json", :include_discussion => true)
       assert_response :success, "cannot get closed changeset with comments"
 
       js = ActiveSupport::JSON.decode(@response.body)
@@ -579,7 +574,7 @@ module Api
       comment1.update(:visible => false)
       changeset.reload
 
-      get changeset_show_path(changeset), :params => { :format => "json", :include_discussion => true }
+      get api_changeset_path(changeset, :format => "json", :include_discussion => true)
       assert_response :success, "cannot get closed changeset with comments"
 
       js = ActiveSupport::JSON.decode(@response.body)
@@ -594,7 +589,7 @@ module Api
       assert js["changeset"]["comments"][1]["visible"]
 
       # one hidden comment not included because no permissions
-      get changeset_show_path(changeset), :params => { :format => "json", :include_discussion => true, :show_hidden_comments => true }
+      get api_changeset_path(changeset, :format => "json", :include_discussion => true, :show_hidden_comments => true)
       assert_response :success, "cannot get closed changeset with comments"
 
       js = ActiveSupport::JSON.decode(@response.body)
@@ -612,8 +607,7 @@ module Api
       # one hidden comment shown to moderators
       moderator_user = create(:moderator_user)
       auth_header = bearer_authorization_header moderator_user
-      get changeset_show_path(changeset), :params => { :format => "json", :include_discussion => true, :show_hidden_comments => true },
-                                          :headers => auth_header
+      get api_changeset_path(changeset, :format => "json", :include_discussion => true, :show_hidden_comments => true), :headers => auth_header
       assert_response :success, "cannot get closed changeset with comments"
 
       js = ActiveSupport::JSON.decode(@response.body)
@@ -635,7 +629,7 @@ module Api
       create(:changeset_tag, :changeset => changeset, :k => "created_by", :v => "JOSM/1.5 (18364)")
       create(:changeset_tag, :changeset => changeset, :k => "comment", :v => "changeset comment")
 
-      get changeset_show_path(changeset, :format => "json")
+      get api_changeset_path(changeset, :format => "json")
 
       assert_response :success
       js = ActiveSupport::JSON.decode(@response.body)
@@ -653,7 +647,7 @@ module Api
       changeset = create(:changeset, :min_lat => (-5 * GeoRecord::SCALE).round, :min_lon => (5 * GeoRecord::SCALE).round,
                                      :max_lat => (15 * GeoRecord::SCALE).round, :max_lon => (12 * GeoRecord::SCALE).round)
 
-      get changeset_show_path(changeset), :params => { :format => "json" }
+      get api_changeset_path(changeset, :format => "json")
       assert_response :success, "cannot get first changeset"
 
       js = ActiveSupport::JSON.decode(@response.body)
@@ -668,7 +662,7 @@ module Api
     # check that a changeset that doesn't exist returns an appropriate message
     def test_show_not_found
       [0, -32, 233455644, "afg", "213"].each do |id|
-        get changeset_show_path(id)
+        get api_changeset_path(id)
         assert_response :not_found, "should get a not found"
       rescue ActionController::UrlGenerationError => e
         assert_match(/No route matches/, e.to_s)
@@ -2186,7 +2180,7 @@ module Api
       assert_response :success,
                       "can't upload multiple versions of an element in a diff: #{@response.body}"
 
-      get changeset_download_path(changeset_id)
+      get api_changeset_download_path(changeset_id)
       assert_response :success
 
       assert_select "osmChange", 1
@@ -2244,7 +2238,7 @@ module Api
       assert_response :success,
                       "can't upload a diff from JOSM: #{@response.body}"
 
-      get changeset_download_path(changeset_id)
+      get api_changeset_download_path(changeset_id)
       assert_response :success
 
       assert_select "osmChange", 1
@@ -2299,7 +2293,7 @@ module Api
       assert_response :success,
                       "can't upload multiple versions of an element in a diff: #{@response.body}"
 
-      get changeset_download_path(changeset_id)
+      get api_changeset_download_path(changeset_id)
       assert_response :success
 
       assert_select "osmChange", 1
@@ -2312,63 +2306,6 @@ module Api
       assert_select "osmChange>modify>way", 1
     end
 
-    def test_changeset_download
-      changeset = create(:changeset)
-      node = create(:node, :with_history, :version => 1, :changeset => changeset)
-      tag = create(:old_node_tag, :old_node => node.old_nodes.find_by(:version => 1))
-      node2 = create(:node, :with_history, :version => 1, :changeset => changeset)
-      _node3 = create(:node, :with_history, :deleted, :version => 1, :changeset => changeset)
-      _relation = create(:relation, :with_history, :version => 1, :changeset => changeset)
-      _relation2 = create(:relation, :with_history, :deleted, :version => 1, :changeset => changeset)
-
-      get changeset_download_path(changeset)
-
-      assert_response :success
-
-      # FIXME: needs more assert_select tests
-      assert_select "osmChange[version='#{Settings.api_version}'][generator='#{Settings.generator}']" do
-        assert_select "create", :count => 5
-        assert_select "create>node[id='#{node.id}'][visible='#{node.visible?}'][version='#{node.version}']" do
-          assert_select "tag[k='#{tag.k}'][v='#{tag.v}']"
-        end
-        assert_select "create>node[id='#{node2.id}']"
-      end
-    end
-
-    test "sorts downloaded elements by timestamp" do
-      changeset = create(:changeset)
-      node1 = create(:old_node, :version => 2, :timestamp => "2020-02-01", :changeset => changeset)
-      node0 = create(:old_node, :version => 2, :timestamp => "2020-01-01", :changeset => changeset)
-
-      get changeset_download_path(changeset)
-      assert_response :success
-      assert_dom "modify", :count => 2 do |modify|
-        assert_dom modify[0], ">node", :count => 1 do |node|
-          assert_dom node, ">@id", node0.node_id.to_s
-        end
-        assert_dom modify[1], ">node", :count => 1 do |node|
-          assert_dom node, ">@id", node1.node_id.to_s
-        end
-      end
-    end
-
-    test "sorts downloaded elements by version" do
-      changeset = create(:changeset)
-      node1 = create(:old_node, :version => 3, :timestamp => "2020-01-01", :changeset => changeset)
-      node0 = create(:old_node, :version => 2, :timestamp => "2020-01-01", :changeset => changeset)
-
-      get changeset_download_path(changeset)
-      assert_response :success
-      assert_dom "modify", :count => 2 do |modify|
-        assert_dom modify[0], ">node", :count => 1 do |node|
-          assert_dom node, ">@id", node0.node_id.to_s
-        end
-        assert_dom modify[1], ">node", :count => 1 do |node|
-          assert_dom node, ">@id", node1.node_id.to_s
-        end
-      end
-    end
-
     ##
     # check that the bounding box of a changeset gets updated correctly
     # FIXME: This should really be moded to a integration test due to the with_controller
@@ -2392,7 +2329,7 @@ module Api
       end
 
       # get the bounding box back from the changeset
-      get changeset_show_path(changeset_id)
+      get api_changeset_path(changeset_id)
       assert_response :success, "Couldn't read back changeset."
       assert_select "osm>changeset[min_lon='0.1000000']", 1
       assert_select "osm>changeset[max_lon='0.1000000']", 1
@@ -2407,7 +2344,7 @@ module Api
       end
 
       # get the bounding box back from the changeset
-      get changeset_show_path(changeset_id)
+      get api_changeset_path(changeset_id)
       assert_response :success, "Couldn't read back changeset for the second time."
       assert_select "osm>changeset[min_lon='0.1000000']", 1
       assert_select "osm>changeset[max_lon='0.2000000']", 1
@@ -2422,7 +2359,7 @@ module Api
       end
 
       # get the bounding box back from the changeset
-      get changeset_show_path(changeset_id)
+      get api_changeset_path(changeset_id)
       assert_response :success, "Couldn't read back changeset for the third time."
       assert_select "osm>changeset[min_lon='0.1000000']", 1
       assert_select "osm>changeset[max_lon='0.3000000']", 1
@@ -2446,17 +2383,17 @@ module Api
       new_changeset.find("//osm/changeset").first << new_tag
 
       # try without any authorization
-      put changeset_show_path(private_changeset), :params => new_changeset.to_s
+      put api_changeset_path(private_changeset), :params => new_changeset.to_s
       assert_response :unauthorized
 
       # try with the wrong authorization
       auth_header = bearer_authorization_header
-      put changeset_show_path(private_changeset), :params => new_changeset.to_s, :headers => auth_header
+      put api_changeset_path(private_changeset), :params => new_changeset.to_s, :headers => auth_header
       assert_response :conflict
 
       # now this should get an unauthorized
       auth_header = bearer_authorization_header private_user
-      put changeset_show_path(private_changeset), :params => new_changeset.to_s, :headers => auth_header
+      put api_changeset_path(private_changeset), :params => new_changeset.to_s, :headers => auth_header
       assert_require_public_data "user with their data non-public, shouldn't be able to edit their changeset"
 
       ## Now try with the public user
@@ -2467,17 +2404,17 @@ module Api
       new_changeset.find("//osm/changeset").first << new_tag
 
       # try without any authorization
-      put changeset_show_path(changeset), :params => new_changeset.to_s
+      put api_changeset_path(changeset), :params => new_changeset.to_s
       assert_response :unauthorized
 
       # try with the wrong authorization
       auth_header = bearer_authorization_header
-      put changeset_show_path(changeset), :params => new_changeset.to_s, :headers => auth_header
+      put api_changeset_path(changeset), :params => new_changeset.to_s, :headers => auth_header
       assert_response :conflict
 
       # now this should work...
       auth_header = bearer_authorization_header user
-      put changeset_show_path(changeset), :params => new_changeset.to_s, :headers => auth_header
+      put api_changeset_path(changeset), :params => new_changeset.to_s, :headers => auth_header
       assert_response :success
 
       assert_select "osm>changeset[id='#{changeset.id}']", 1
@@ -2498,7 +2435,7 @@ module Api
       new_tag["v"] = "testing"
       new_changeset.find("//osm/changeset").first << new_tag
 
-      put changeset_show_path(changeset), :params => new_changeset.to_s, :headers => auth_header
+      put api_changeset_path(changeset), :params => new_changeset.to_s, :headers => auth_header
       assert_response :conflict
     end
 
@@ -2566,25 +2503,6 @@ module Api
                  "element limit.")
     end
 
-    ##
-    # check that the changeset download for a changeset with a redacted
-    # element in it doesn't contain that element.
-    def test_diff_download_redacted
-      changeset = create(:changeset)
-      node = create(:node, :with_history, :version => 2, :changeset => changeset)
-      node_v1 = node.old_nodes.find_by(:version => 1)
-      node_v1.redact!(create(:redaction))
-
-      get changeset_download_path(changeset)
-      assert_response :success
-
-      assert_select "osmChange", 1
-      # this changeset contains the node in versions 1 & 2, but 1 should
-      # be hidden.
-      assert_select "osmChange node[id='#{node.id}']", 1
-      assert_select "osmChange node[id='#{node.id}'][version='1']", 0
-    end
-
     ##
     # test subscribe success
     def test_subscribe_success
index f585c5c2cf6dda76c32d2a73e34890f941715d2e..a4c8522e82f081be9f99f9bda034cd31106fb9b1 100644 (file)
@@ -1064,7 +1064,7 @@ module Api
 
       # now download the changeset to check its bounding box
       with_controller(Api::ChangesetsController.new) do
-        get changeset_show_path(changeset_id)
+        get api_changeset_path(changeset_id)
         assert_response :success, "can't re-read changeset for modify test"
         assert_select "osm>changeset", 1, "Changeset element doesn't exist in #{@response.body}"
         assert_select "osm>changeset[id='#{changeset_id}']", 1, "Changeset id=#{changeset_id} doesn't exist in #{@response.body}"
index a70bb32cd7789d33ae680e2d7a52693aaae68667..0185c67413141d954b88951e220800e72518ed24 100644 (file)
@@ -164,7 +164,7 @@ class NotesControllerTest < ActionDispatch::IntegrationTest
   def test_read_note_suspended_opener_and_comment
     note = create(:note)
     create(:note_comment, :note => note, :author => create(:user, :suspended))
-    create(:note_comment, :note => note)
+    create(:note_comment, :note => note, :event => "commented")
 
     sidebar_browse_check :note_path, note.id, "notes/show"
     assert_select "div.note-comments ul li", :count => 1
index b683107d9a61b30fe257728d74bebbff413fbc32..724c43222b61a84869a1f7787919b8c45153c623 100644 (file)
@@ -27,12 +27,14 @@ module Users
       user = create(:user)
       moderator_user = create(:moderator_user)
       administrator_user = create(:administrator_user)
-      _suspended_user = create(:user, :suspended)
-      _ip_user = create(:user, :creation_address => "1.2.3.4")
+      suspended_user = create(:user, :suspended)
+      name_user = create(:user, :display_name => "Test User")
+      email_user = create(:user, :email => "test@example.com")
+      ip_user = create(:user, :creation_address => "1.2.3.4")
 
-      # There are now 7 users - the five above, plus two extra "granters" for the
+      # There are now 9 users - the 7 above, plus two extra "granters" for the
       # moderator_user and administrator_user
-      assert_equal 7, User.count
+      assert_equal 9, User.count
 
       # Shouldn't work when not logged in
       get users_list_path
@@ -57,19 +59,55 @@ module Users
       get users_list_path
       assert_response :success
       assert_template :show
-      assert_select "table#user_list tbody tr", :count => 7
+      assert_select "table#user_list tbody tr", :count => 9
 
       # Should be able to limit by status
       get users_list_path, :params => { :status => "suspended" }
       assert_response :success
       assert_template :show
-      assert_select "table#user_list tbody tr", :count => 1
+      assert_select "table#user_list tbody tr", :count => 1 do
+        assert_select "a[href='#{user_path(suspended_user)}']", :count => 1
+      end
+
+      # Should be able to limit by name
+      get users_list_path, :params => { :username => "Test User" }
+      assert_response :success
+      assert_template :show
+      assert_select "table#user_list tbody tr", :count => 1 do
+        assert_select "a[href='#{user_path(name_user)}']", :count => 1
+      end
+
+      # Should be able to limit by name ignoring case
+      get users_list_path, :params => { :username => "test user" }
+      assert_response :success
+      assert_template :show
+      assert_select "table#user_list tbody tr", :count => 1 do
+        assert_select "a[href='#{user_path(name_user)}']", :count => 1
+      end
+
+      # Should be able to limit by email
+      get users_list_path, :params => { :username => "test@example.com" }
+      assert_response :success
+      assert_template :show
+      assert_select "table#user_list tbody tr", :count => 1 do
+        assert_select "a[href='#{user_path(email_user)}']", :count => 1
+      end
+
+      # Should be able to limit by email ignoring case
+      get users_list_path, :params => { :username => "TEST@example.com" }
+      assert_response :success
+      assert_template :show
+      assert_select "table#user_list tbody tr", :count => 1 do
+        assert_select "a[href='#{user_path(email_user)}']", :count => 1
+      end
 
       # Should be able to limit by IP address
       get users_list_path, :params => { :ip => "1.2.3.4" }
       assert_response :success
       assert_template :show
-      assert_select "table#user_list tbody tr", :count => 1
+      assert_select "table#user_list tbody tr", :count => 1 do
+        assert_select "a[href='#{user_path(ip_user)}']", :count => 1
+      end
     end
 
     def test_show_paginated
index 71a706e4590dd048c5a612aa0eeb4454abe80f96..bc2be6e82c0072db70dbdbd3bdabbc30774a2450 100644 (file)
--- a/yarn.lock
+++ b/yarn.lock
   resolved "https://registry.yarnpkg.com/@eslint-community/regexpp/-/regexpp-4.12.1.tgz#cfc6cffe39df390a3841cde2abccf92eaa7ae0e0"
   integrity sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==
 
-"@eslint/config-array@^0.19.0":
-  version "0.19.0"
-  resolved "https://registry.yarnpkg.com/@eslint/config-array/-/config-array-0.19.0.tgz#3251a528998de914d59bb21ba4c11767cf1b3519"
-  integrity sha512-zdHg2FPIFNKPdcHWtiNT+jEFCHYVplAXRDlQDyqy0zGx/q2parwh7brGJSiTxRk/TSMkbM//zt/f5CHgyTyaSQ==
+"@eslint/config-array@^0.19.2":
+  version "0.19.2"
+  resolved "https://registry.yarnpkg.com/@eslint/config-array/-/config-array-0.19.2.tgz#3060b809e111abfc97adb0bb1172778b90cb46aa"
+  integrity sha512-GNKqxfHG2ySmJOBSHg7LxeUx4xpuCoFjacmlCoYWEbaPXLwvfIjixRI12xCQZeULksQb23uiA8F40w5TojpV7w==
   dependencies:
-    "@eslint/object-schema" "^2.1.4"
+    "@eslint/object-schema" "^2.1.6"
     debug "^4.3.1"
     minimatch "^3.1.2"
 
-"@eslint/core@^0.10.0":
-  version "0.10.0"
-  resolved "https://registry.yarnpkg.com/@eslint/core/-/core-0.10.0.tgz#23727063c21b335f752dbb3a16450f6f9cbc9091"
-  integrity sha512-gFHJ+xBOo4G3WRlR1e/3G8A6/KZAH6zcE/hkLRCZTi/B9avAG365QhFA8uOGzTMqgTghpn7/fSnscW++dpMSAw==
+"@eslint/core@^0.12.0":
+  version "0.12.0"
+  resolved "https://registry.yarnpkg.com/@eslint/core/-/core-0.12.0.tgz#5f960c3d57728be9f6c65bd84aa6aa613078798e"
+  integrity sha512-cmrR6pytBuSMTaBweKoGMwu3EiHiEC+DoyupPmlZ0HxBJBtIxwe+j/E4XPIKNx+Q74c8lXKPwYawBf5glsTkHg==
   dependencies:
     "@types/json-schema" "^7.0.15"
 
-"@eslint/core@^0.11.0":
-  version "0.11.0"
-  resolved "https://registry.yarnpkg.com/@eslint/core/-/core-0.11.0.tgz#7a9226e850922e42cbd2ba71361eacbe74352a12"
-  integrity sha512-DWUB2pksgNEb6Bz2fggIy1wh6fGgZP4Xyy/Mt0QZPiloKKXerbqq9D3SBQTlCRYOrcRPu4vuz+CGjwdfqxnoWA==
-  dependencies:
-    "@types/json-schema" "^7.0.15"
-
-"@eslint/eslintrc@^3.2.0":
-  version "3.2.0"
-  resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-3.2.0.tgz#57470ac4e2e283a6bf76044d63281196e370542c"
-  integrity sha512-grOjVNN8P3hjJn/eIETF1wwd12DdnwFDoyceUJLYYdkpbwq3nLi+4fqrTAONx7XDALqlL220wC/RHSC/QTI/0w==
+"@eslint/eslintrc@^3.3.0":
+  version "3.3.0"
+  resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-3.3.0.tgz#96a558f45842989cca7ea1ecd785ad5491193846"
+  integrity sha512-yaVPAiNAalnCZedKLdR21GOGILMLKPyqSLWaAjQFvYA2i/ciDi8ArYVr69Anohb6cH2Ukhqti4aFnYyPm8wdwQ==
   dependencies:
     ajv "^6.12.4"
     debug "^4.3.2"
     minimatch "^3.1.2"
     strip-json-comments "^3.1.1"
 
-"@eslint/js@9.20.0":
-  version "9.20.0"
-  resolved "https://registry.yarnpkg.com/@eslint/js/-/js-9.20.0.tgz#7421bcbe74889fcd65d1be59f00130c289856eb4"
-  integrity sha512-iZA07H9io9Wn836aVTytRaNqh00Sad+EamwOVJT12GTLw1VGMFV/4JaME+JjLtr9fiGaoWgYnS54wrfWsSs4oQ==
+"@eslint/js@9.21.0":
+  version "9.21.0"
+  resolved "https://registry.yarnpkg.com/@eslint/js/-/js-9.21.0.tgz#4303ef4e07226d87c395b8fad5278763e9c15c08"
+  integrity sha512-BqStZ3HX8Yz6LvsF5ByXYrtigrV5AXADWLAGc7PH/1SxOb7/FIYYMszZZWiUou/GB9P2lXWk2SV4d+Z8h0nknw==
 
-"@eslint/object-schema@^2.1.4":
-  version "2.1.4"
-  resolved "https://registry.yarnpkg.com/@eslint/object-schema/-/object-schema-2.1.4.tgz#9e69f8bb4031e11df79e03db09f9dbbae1740843"
-  integrity sha512-BsWiH1yFGjXXS2yvrf5LyuoSIIbPrGUWob917o+BTKuZ7qJdxX8aJLRxs1fS9n6r7vESrq1OUqb68dANcFXuQQ==
+"@eslint/object-schema@^2.1.6":
+  version "2.1.6"
+  resolved "https://registry.yarnpkg.com/@eslint/object-schema/-/object-schema-2.1.6.tgz#58369ab5b5b3ca117880c0f6c0b0f32f6950f24f"
+  integrity sha512-RBMg5FRL0I0gs51M/guSAj5/e14VQ4tpZnQNWwuDT66P14I43ItmPfIZRhO9fUVIPOAQXU47atlywZ/czoqFPA==
 
-"@eslint/plugin-kit@^0.2.5":
-  version "0.2.5"
-  resolved "https://registry.yarnpkg.com/@eslint/plugin-kit/-/plugin-kit-0.2.5.tgz#ee07372035539e7847ef834e3f5e7b79f09e3a81"
-  integrity sha512-lB05FkqEdUg2AA0xEbUz0SnkXT1LcCTa438W4IWTUh4hdOnVbQyOJ81OrDXsJk/LSiJHubgGEFoR5EHq1NsH1A==
+"@eslint/plugin-kit@^0.2.7":
+  version "0.2.7"
+  resolved "https://registry.yarnpkg.com/@eslint/plugin-kit/-/plugin-kit-0.2.7.tgz#9901d52c136fb8f375906a73dcc382646c3b6a27"
+  integrity sha512-JubJ5B2pJ4k4yGxaNLdbjrnk9d/iDz6/q8wOilpIowd6PJPgaxCuHBnBszq7Ce2TyMrywm5r4PnKm6V3iiZF+g==
   dependencies:
-    "@eslint/core" "^0.10.0"
+    "@eslint/core" "^0.12.0"
     levn "^0.4.1"
 
 "@humanfs/core@^0.19.1":
   resolved "https://registry.yarnpkg.com/@humanwhocodes/retry/-/retry-0.3.1.tgz#c72a5c76a9fbaf3488e231b13dc52c0da7bab42a"
   integrity sha512-JBxkERygn7Bv/GbN5Rv8Ul6LVknS+5Bp6RgDC/O8gEBU/yeH5Ui5C/OlWrTb6qct7LjjfT6Re2NxB0ln0yYybA==
 
-"@humanwhocodes/retry@^0.4.1":
-  version "0.4.1"
-  resolved "https://registry.yarnpkg.com/@humanwhocodes/retry/-/retry-0.4.1.tgz#9a96ce501bc62df46c4031fbd970e3cc6b10f07b"
-  integrity sha512-c7hNEllBlenFTHBky65mhq8WD2kbN9Q6gk0bTk8lSBvc554jpXSkST1iePudpt7+A/AQvuHs9EMqjHDXMY1lrA==
+"@humanwhocodes/retry@^0.4.2":
+  version "0.4.2"
+  resolved "https://registry.yarnpkg.com/@humanwhocodes/retry/-/retry-0.4.2.tgz#1860473de7dfa1546767448f333db80cb0ff2161"
+  integrity sha512-xeO57FpIu4p1Ri3Jq/EXq4ClRm86dVF2z/+kvFnyqVYRavTZmaFaUBbWCOuuTh0o/g7DSsk6kc2vrS4Vl5oPOQ==
 
 "@stylistic/eslint-plugin-js@^4.0.0":
-  version "4.0.1"
-  resolved "https://registry.yarnpkg.com/@stylistic/eslint-plugin-js/-/eslint-plugin-js-4.0.1.tgz#c55e59ad2f00914f9eb103ba9e7be00aff7715dd"
-  integrity sha512-2EGKM6WHnZSidWKCu6ePJCqdpgWiEU1Bt26ktWEfTpCmRP+2vRQ6ViK8X6DLwu4+F0zPLy/Txe2HhI3qJFUvqA==
+  version "4.1.0"
+  resolved "https://registry.yarnpkg.com/@stylistic/eslint-plugin-js/-/eslint-plugin-js-4.1.0.tgz#cb3fcc6bef022f1af8245a6cda404d290358c260"
+  integrity sha512-YOe+dChNoR26JVVt+7BjyebsPIQF05OLNmHCXivq8lLZ4ZeVs4+wXaW+pREVboDiAaSRznauAdAU8f+iQouw6Q==
   dependencies:
     eslint-visitor-keys "^4.2.0"
     espree "^10.3.0"
@@ -280,20 +273,20 @@ eslint-visitor-keys@^4.2.0:
   integrity sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw==
 
 eslint@^9.0.0:
-  version "9.20.1"
-  resolved "https://registry.yarnpkg.com/eslint/-/eslint-9.20.1.tgz#923924c078f5226832449bac86662dd7e53c91d6"
-  integrity sha512-m1mM33o6dBUjxl2qb6wv6nGNwCAsns1eKtaQ4l/NPHeTvhiUPbtdfMyktxN4B3fgHIgsYh1VT3V9txblpQHq+g==
+  version "9.21.0"
+  resolved "https://registry.yarnpkg.com/eslint/-/eslint-9.21.0.tgz#b1c9c16f5153ff219791f627b94ab8f11f811591"
+  integrity sha512-KjeihdFqTPhOMXTt7StsDxriV4n66ueuF/jfPNC3j/lduHwr/ijDwJMsF+wyMJethgiKi5wniIE243vi07d3pg==
   dependencies:
     "@eslint-community/eslint-utils" "^4.2.0"
     "@eslint-community/regexpp" "^4.12.1"
-    "@eslint/config-array" "^0.19.0"
-    "@eslint/core" "^0.11.0"
-    "@eslint/eslintrc" "^3.2.0"
-    "@eslint/js" "9.20.0"
-    "@eslint/plugin-kit" "^0.2.5"
+    "@eslint/config-array" "^0.19.2"
+    "@eslint/core" "^0.12.0"
+    "@eslint/eslintrc" "^3.3.0"
+    "@eslint/js" "9.21.0"
+    "@eslint/plugin-kit" "^0.2.7"
     "@humanfs/node" "^0.16.6"
     "@humanwhocodes/module-importer" "^1.0.1"
-    "@humanwhocodes/retry" "^0.4.1"
+    "@humanwhocodes/retry" "^0.4.2"
     "@types/estree" "^1.0.6"
     "@types/json-schema" "^7.0.15"
     ajv "^6.12.4"