]> git.openstreetmap.org Git - rails.git/commitdiff
Add importer role that can be associated with higher rate limits
authorTom Hughes <tom@compton.nu>
Sun, 29 Oct 2023 15:34:12 +0000 (15:34 +0000)
committerTom Hughes <tom@compton.nu>
Thu, 2 Nov 2023 08:58:12 +0000 (08:58 +0000)
12 files changed:
app/assets/images/roles/blank_importer.png [new file with mode: 0644]
app/assets/images/roles/blank_importer.svg [new file with mode: 0644]
app/assets/images/roles/importer.png [new file with mode: 0644]
app/assets/images/roles/importer.svg [new file with mode: 0644]
app/models/user.rb
app/models/user_role.rb
config/initializers/migrate.rb
config/locales/en.yml
db/migrate/20231029151516_add_importer_role.rb [new file with mode: 0644]
db/structure.sql
test/factories/user.rb
test/helpers/user_roles_helper_test.rb

diff --git a/app/assets/images/roles/blank_importer.png b/app/assets/images/roles/blank_importer.png
new file mode 100644 (file)
index 0000000..1eb8118
Binary files /dev/null and b/app/assets/images/roles/blank_importer.png differ
diff --git a/app/assets/images/roles/blank_importer.svg b/app/assets/images/roles/blank_importer.svg
new file mode 100644 (file)
index 0000000..d4e53ec
--- /dev/null
@@ -0,0 +1,65 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+   xmlns:dc="http://purl.org/dc/elements/1.1/"
+   xmlns:cc="http://creativecommons.org/ns#"
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+   xmlns:svg="http://www.w3.org/2000/svg"
+   xmlns="http://www.w3.org/2000/svg"
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+   id="svg6042"
+   version="1.1"
+   inkscape:version="0.48.2 r9819"
+   width="20"
+   height="20"
+   sodipodi:docname="blank_moderator.svg"
+   inkscape:export-filename="/Users/saman/work_repos/openstreetmap-website/app/assets/images/roles/blank_moderator.png"
+   inkscape:export-xdpi="90"
+   inkscape:export-ydpi="90">
+  <metadata
+     id="metadata6048">
+    <rdf:RDF>
+      <cc:Work
+         rdf:about="">
+        <dc:format>image/svg+xml</dc:format>
+        <dc:type
+           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+        <dc:title></dc:title>
+      </cc:Work>
+    </rdf:RDF>
+  </metadata>
+  <defs
+     id="defs6046" />
+  <sodipodi:namedview
+     pagecolor="#ffffff"
+     bordercolor="#666666"
+     borderopacity="1"
+     objecttolerance="10"
+     gridtolerance="10"
+     guidetolerance="10"
+     inkscape:pageopacity="0"
+     inkscape:pageshadow="2"
+     inkscape:window-width="1264"
+     inkscape:window-height="776"
+     id="namedview6044"
+     showgrid="false"
+     inkscape:zoom="1"
+     inkscape:cx="10"
+     inkscape:cy="10"
+     inkscape:window-x="0"
+     inkscape:window-y="0"
+     inkscape:window-maximized="0"
+     inkscape:current-layer="svg6042" />
+  <path
+     inkscape:connector-curvature="0"
+     style="color:#000000;fill:#38e13a;fill-opacity:1;fill-rule:nonzero;stroke:#38e13a;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
+     d="M 10,2 8.125,8 2,8 6.96875,11.71875 5,18 10,14 15,18 13.03125,11.71875 18,8 11.875,8 10,2 z"
+     id="path4709" />
+  <path
+     inkscape:connector-curvature="0"
+     style="color:#000000;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
+     d="M 10,2 8.125,8 2,8 6.96875,11.71875 5,18 10,14 15,18 13.03125,11.71875 18,8 11.875,8 10,2 z"
+     id="path5684" />
+</svg>
diff --git a/app/assets/images/roles/importer.png b/app/assets/images/roles/importer.png
new file mode 100644 (file)
index 0000000..6711729
Binary files /dev/null and b/app/assets/images/roles/importer.png differ
diff --git a/app/assets/images/roles/importer.svg b/app/assets/images/roles/importer.svg
new file mode 100644 (file)
index 0000000..449787a
--- /dev/null
@@ -0,0 +1,71 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+   xmlns:dc="http://purl.org/dc/elements/1.1/"
+   xmlns:cc="http://creativecommons.org/ns#"
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+   xmlns:svg="http://www.w3.org/2000/svg"
+   xmlns="http://www.w3.org/2000/svg"
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+   id="svg4678"
+   version="1.1"
+   inkscape:version="0.48.2 r9819"
+   width="20"
+   height="20"
+   sodipodi:docname="moderator.svg"
+   inkscape:export-filename="/Users/saman/work_repos/openstreetmap-website/app/assets/images/roles/moderator.png"
+   inkscape:export-xdpi="90"
+   inkscape:export-ydpi="90">
+  <metadata
+     id="metadata4684">
+    <rdf:RDF>
+      <cc:Work
+         rdf:about="">
+        <dc:format>image/svg+xml</dc:format>
+        <dc:type
+           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+        <dc:title></dc:title>
+      </cc:Work>
+    </rdf:RDF>
+  </metadata>
+  <defs
+     id="defs4682" />
+  <sodipodi:namedview
+     pagecolor="#ffffff"
+     bordercolor="#666666"
+     borderopacity="1"
+     objecttolerance="10"
+     gridtolerance="10"
+     guidetolerance="10"
+     inkscape:pageopacity="0"
+     inkscape:pageshadow="2"
+     inkscape:window-width="1574"
+     inkscape:window-height="831"
+     id="namedview4680"
+     showgrid="false"
+     inkscape:zoom="1"
+     inkscape:cx="9.1260993"
+     inkscape:cy="11.531765"
+     inkscape:window-x="0"
+     inkscape:window-y="0"
+     inkscape:window-maximized="0"
+     inkscape:current-layer="svg4678">
+    <inkscape:grid
+       type="xygrid"
+       id="grid4707" />
+  </sodipodi:namedview>
+  <path
+     style="color:#000000;fill:#38e13a;fill-opacity:1;fill-rule:nonzero;stroke:#38e13a;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
+     d="m 10,2 5,16 -5,-4 -5,4 z"
+     id="path4709"
+     inkscape:connector-curvature="0"
+     sodipodi:nodetypes="ccccc" />
+  <path
+     style="color:#000000;fill:#38e13a;fill-opacity:1;fill-rule:nonzero;stroke:#38e13a;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
+     d="m 2,8 16,0 -8,6 z"
+     id="path5479"
+     inkscape:connector-curvature="0"
+     sodipodi:nodetypes="cccc" />
+</svg>
index f804f4666b8c28a8e38f64a35837af2dcec65a29..3d74b3933ec2c7eaabdf49585ff8ba0206b8fa0d 100644 (file)
@@ -290,6 +290,12 @@ class User < ApplicationRecord
     role? "administrator"
   end
 
+  ##
+  # returns true if the user has the importer role, false otherwise
+  def importer?
+    role? "importer"
+  end
+
   ##
   # returns true if the user has the requested role
   def role?(role)
index a081361a76cb6db41fe03737c313db6fc08ddc45..332848e420ffec509d270267e81f281ee4502bef 100644 (file)
@@ -23,7 +23,7 @@ class UserRole < ApplicationRecord
   belongs_to :user
   belongs_to :granter, :class_name => "User"
 
-  ALL_ROLES = %w[administrator moderator].freeze
+  ALL_ROLES = %w[administrator moderator importer].freeze
 
   validates :role, :inclusion => ALL_ROLES, :uniqueness => { :scope => :user_id }
 end
index 0667e3346b01d03a5b15d3af5c5bce09722125b7..43dd446a3fdf4e3b485681a3f77a4c3d72b2c4a5 100644 (file)
@@ -51,6 +51,10 @@ if defined?(ActiveRecord::ConnectionAdapters::AbstractAdapter)
           execute "DROP TYPE #{enumeration_name}"
         end
 
+        def add_enumeration_value(enumeration_name, value)
+          execute "ALTER TYPE #{enumeration_name} ADD VALUE '#{value}'"
+        end
+
         def rename_enumeration(old_name, new_name)
           old_name = quote_table_name(old_name)
           new_name = quote_table_name(new_name)
index 158f19ac4dc5f9834ea74a0ccb9f5a6be2f0c71a..ce84600d2870cf6a681d544ca5963e053faee832 100644 (file)
@@ -2739,12 +2739,15 @@ en:
       role:
         administrator: "This user is an administrator"
         moderator: "This user is a moderator"
+        importer: "This user is a importer"
         grant:
           administrator: "Grant administrator access"
           moderator: "Grant moderator access"
+          importer: "Grant importer access"
         revoke:
           administrator: "Revoke administrator access"
           moderator: "Revoke moderator access"
+          importer: "Revoke importer access"
       block_history: "Active Blocks"
       moderator_history: "Blocks Given"
       comments: "Comments"
diff --git a/db/migrate/20231029151516_add_importer_role.rb b/db/migrate/20231029151516_add_importer_role.rb
new file mode 100644 (file)
index 0000000..f3fcdb8
--- /dev/null
@@ -0,0 +1,9 @@
+class AddImporterRole < ActiveRecord::Migration[7.1]
+  def up
+    add_enumeration_value :user_role_enum, "importer"
+  end
+
+  def down
+    raise ActiveRecord::IrreversibleMigration
+  end
+end
index 17f2696667d343f796e7b9917e1278bcf4f82f18..f7f3293265b2602523a1be18fdea1cf6bdbde681 100644 (file)
@@ -91,7 +91,8 @@ CREATE TYPE public.nwr_enum AS ENUM (
 
 CREATE TYPE public.user_role_enum AS ENUM (
     'administrator',
-    'moderator'
+    'moderator',
+    'importer'
 );
 
 
@@ -3437,6 +3438,7 @@ INSERT INTO "schema_migrations" (version) VALUES
 ('23'),
 ('22'),
 ('21'),
+('20231029151516'),
 ('20231010194809'),
 ('20231007141103'),
 ('20230830115220'),
index cdc606cf1d66f6087514156b5192fc6e55eb3238..166461637662609dc6bdb58c08cb87f3c9e17ddc 100644 (file)
@@ -47,6 +47,12 @@ FactoryBot.define do
       end
     end
 
+    factory :importer_user do
+      after(:create) do |user, _evaluator|
+        create(:user_role, :role => "importer", :user => user)
+      end
+    end
+
     factory :moderator_user do
       after(:create) do |user, _evaluator|
         create(:user_role, :role => "moderator", :user => user)
index 7708d51150d9903b3b11755da34a5a3a45988b74..ba51dd14f4b60a3181fafc6078e3d63da29387dc 100644 (file)
@@ -9,17 +9,27 @@ class UserRolesHelperTest < ActionView::TestCase
     icon = role_icon(current_user, "moderator")
     assert_dom_equal "", icon
 
+    icon = role_icon(current_user, "importer")
+    assert_dom_equal "", icon
+
     icon = role_icon(create(:moderator_user), "moderator")
     expected = <<~HTML.delete("\n")
       <img srcset="/images/roles/moderator.svg" border="0" alt="This user is a moderator" title="This user is a moderator" src="/images/roles/moderator.png" width="20" height="20" />
     HTML
     assert_dom_equal expected, icon
+
+    icon = role_icon(create(:importer_user), "importer")
+    expected = <<~HTML.delete("\n")
+      <img srcset="/images/roles/importer.svg" border="0" alt="This user is a importer" title="This user is a importer" src="/images/roles/importer.png" width="20" height="20" />
+    HTML
+    assert_dom_equal expected, icon
   end
 
   def test_role_icon_administrator
     self.current_user = create(:administrator_user)
 
     user = create(:user)
+
     icon = role_icon(user, "moderator")
     expected = <<~HTML.delete("\n")
       <a data-confirm="Are you sure you want to grant the role `moderator&#39; to the user `#{user.display_name}&#39;?" rel="nofollow" data-method="post" href="/user/#{ERB::Util.u(user.display_name)}/role/moderator/grant">
@@ -28,7 +38,16 @@ class UserRolesHelperTest < ActionView::TestCase
     HTML
     assert_dom_equal expected, icon
 
+    icon = role_icon(user, "importer")
+    expected = <<~HTML.delete("\n")
+      <a data-confirm="Are you sure you want to grant the role `importer&#39; to the user `#{user.display_name}&#39;?" rel="nofollow" data-method="post" href="/user/#{ERB::Util.u(user.display_name)}/role/importer/grant">
+      <img srcset="/images/roles/blank_importer.svg" border="0" alt="Grant importer access" title="Grant importer access" src="/images/roles/blank_importer.png" width="20" height="20" />
+      </a>
+    HTML
+    assert_dom_equal expected, icon
+
     moderator_user = create(:moderator_user)
+
     icon = role_icon(moderator_user, "moderator")
     expected = <<~HTML.delete("\n")
       <a data-confirm="Are you sure you want to revoke the role `moderator&#39; from the user `#{moderator_user.display_name}&#39;?" rel="nofollow" data-method="post" href="/user/#{ERB::Util.u(moderator_user.display_name)}/role/moderator/revoke">
@@ -36,6 +55,32 @@ class UserRolesHelperTest < ActionView::TestCase
       </a>
     HTML
     assert_dom_equal expected, icon
+
+    icon = role_icon(user, "importer")
+    expected = <<~HTML.delete("\n")
+      <a data-confirm="Are you sure you want to grant the role `importer&#39; to the user `#{user.display_name}&#39;?" rel="nofollow" data-method="post" href="/user/#{ERB::Util.u(user.display_name)}/role/importer/grant">
+      <img srcset="/images/roles/blank_importer.svg" border="0" alt="Grant importer access" title="Grant importer access" src="/images/roles/blank_importer.png" width="20" height="20" />
+      </a>
+    HTML
+    assert_dom_equal expected, icon
+
+    importer_user = create(:importer_user)
+
+    icon = role_icon(user, "moderator")
+    expected = <<~HTML.delete("\n")
+      <a data-confirm="Are you sure you want to grant the role `moderator&#39; to the user `#{user.display_name}&#39;?" rel="nofollow" data-method="post" href="/user/#{ERB::Util.u(user.display_name)}/role/moderator/grant">
+      <img srcset="/images/roles/blank_moderator.svg" border="0" alt="Grant moderator access" title="Grant moderator access" src="/images/roles/blank_moderator.png" width="20" height="20" />
+      </a>
+    HTML
+    assert_dom_equal expected, icon
+
+    icon = role_icon(importer_user, "importer")
+    expected = <<~HTML.delete("\n")
+      <a data-confirm="Are you sure you want to revoke the role `importer&#39; from the user `#{importer_user.display_name}&#39;?" rel="nofollow" data-method="post" href="/user/#{ERB::Util.u(importer_user.display_name)}/role/importer/revoke">
+      <img srcset="/images/roles/importer.svg" border="0" alt="Revoke importer access" title="Revoke importer access" src="/images/roles/importer.png" width="20" height="20" />
+      </a>
+    HTML
+    assert_dom_equal expected, icon
   end
 
   def test_role_icons_normal
@@ -50,10 +95,17 @@ class UserRolesHelperTest < ActionView::TestCase
     HTML
     assert_dom_equal expected, icons
 
+    icons = role_icons(create(:importer_user))
+    expected = <<~HTML.delete("\n")
+      <img srcset="/images/roles/importer.svg" border="0" alt="This user is a importer" title="This user is a importer" src="/images/roles/importer.png" width="20" height="20" />
+    HTML
+    assert_dom_equal expected, icons
+
     icons = role_icons(create(:super_user))
     expected = <<~HTML.delete("\n")
       <img srcset="/images/roles/administrator.svg" border="0" alt="This user is an administrator" title="This user is an administrator" src="/images/roles/administrator.png" width="20" height="20" />
       <img srcset="/images/roles/moderator.svg" border="0" alt="This user is a moderator" title="This user is a moderator" src="/images/roles/moderator.png" width="20" height="20" />
+      <img srcset="/images/roles/importer.svg" border="0" alt="This user is a importer" title="This user is a importer" src="/images/roles/importer.png" width="20" height="20" />
     HTML
     assert_dom_equal expected, icons
   end
@@ -70,6 +122,9 @@ class UserRolesHelperTest < ActionView::TestCase
       <a data-confirm="Are you sure you want to grant the role `moderator&#39; to the user `#{user.display_name}&#39;?" rel="nofollow" data-method="post" href="/user/#{ERB::Util.u(user.display_name)}/role/moderator/grant">
       <img srcset="/images/roles/blank_moderator.svg" border="0" alt="Grant moderator access" title="Grant moderator access" src="/images/roles/blank_moderator.png" width="20" height="20" />
       </a>
+      <a data-confirm="Are you sure you want to grant the role `importer&#39; to the user `#{user.display_name}&#39;?" rel="nofollow" data-method="post" href="/user/#{ERB::Util.u(user.display_name)}/role/importer/grant">
+      <img srcset="/images/roles/blank_importer.svg" border="0" alt="Grant importer access" title="Grant importer access" src="/images/roles/blank_importer.png" width="20" height="20" />
+      </a>
     HTML
     assert_dom_equal expected, icons
 
@@ -82,6 +137,24 @@ class UserRolesHelperTest < ActionView::TestCase
       <a data-confirm="Are you sure you want to revoke the role `moderator&#39; from the user `#{moderator_user.display_name}&#39;?" rel="nofollow" data-method="post" href="/user/#{ERB::Util.u(moderator_user.display_name)}/role/moderator/revoke">
       <img srcset="/images/roles/moderator.svg" border="0" alt="Revoke moderator access" title="Revoke moderator access" src="/images/roles/moderator.png" width="20" height="20" />
       </a>
+      <a data-confirm="Are you sure you want to grant the role `importer&#39; to the user `#{moderator_user.display_name}&#39;?" rel="nofollow" data-method="post" href="/user/#{ERB::Util.u(moderator_user.display_name)}/role/importer/grant">
+      <img srcset="/images/roles/blank_importer.svg" border="0" alt="Grant importer access" title="Grant importer access" src="/images/roles/blank_importer.png" width="20" height="20" />
+      </a>
+    HTML
+    assert_dom_equal expected, icons
+
+    importer_user = create(:importer_user)
+    icons = role_icons(importer_user)
+    expected = <<~HTML.delete("\n")
+      <a data-confirm="Are you sure you want to grant the role `administrator&#39; to the user `#{importer_user.display_name}&#39;?" rel="nofollow" data-method="post" href="/user/#{ERB::Util.u(importer_user.display_name)}/role/administrator/grant">
+      <img srcset="/images/roles/blank_administrator.svg" border="0" alt="Grant administrator access" title="Grant administrator access" src="/images/roles/blank_administrator.png" width="20" height="20" />
+      </a>
+      <a data-confirm="Are you sure you want to grant the role `moderator&#39; to the user `#{importer_user.display_name}&#39;?" rel="nofollow" data-method="post" href="/user/#{ERB::Util.u(importer_user.display_name)}/role/moderator/grant">
+      <img srcset="/images/roles/blank_moderator.svg" border="0" alt="Grant moderator access" title="Grant moderator access" src="/images/roles/blank_moderator.png" width="20" height="20" />
+      </a>
+      <a data-confirm="Are you sure you want to revoke the role `importer&#39; from the user `#{importer_user.display_name}&#39;?" rel="nofollow" data-method="post" href="/user/#{ERB::Util.u(importer_user.display_name)}/role/importer/revoke">
+      <img srcset="/images/roles/importer.svg" border="0" alt="Revoke importer access" title="Revoke importer access" src="/images/roles/importer.png" width="20" height="20" />
+      </a>
     HTML
     assert_dom_equal expected, icons
 
@@ -94,6 +167,9 @@ class UserRolesHelperTest < ActionView::TestCase
       <a data-confirm="Are you sure you want to revoke the role `moderator&#39; from the user `#{super_user.display_name}&#39;?" rel="nofollow" data-method="post" href="/user/#{ERB::Util.u(super_user.display_name)}/role/moderator/revoke">
       <img srcset="/images/roles/moderator.svg" border="0" alt="Revoke moderator access" title="Revoke moderator access" src="/images/roles/moderator.png" width="20" height="20" />
       </a>
+      <a data-confirm="Are you sure you want to revoke the role `importer&#39; from the user `#{super_user.display_name}&#39;?" rel="nofollow" data-method="post" href="/user/#{ERB::Util.u(super_user.display_name)}/role/importer/revoke">
+      <img srcset="/images/roles/importer.svg" border="0" alt="Revoke importer access" title="Revoke importer access" src="/images/roles/importer.png" width="20" height="20" />
+      </a>
     HTML
     assert_dom_equal expected, icons
   end