Merge 2b5af63895
into 432fa57e61
This commit is contained in:
commit
21e8e29d99
12 changed files with 354 additions and 2 deletions
|
@ -54,6 +54,7 @@ class Ability
|
||||||
can [:read, :create, :destroy], UserMute
|
can [:read, :create, :destroy], UserMute
|
||||||
|
|
||||||
if user.moderator?
|
if user.moderator?
|
||||||
|
can :manage, ChangesetTag
|
||||||
can [:hide, :unhide], [DiaryEntry, DiaryComment]
|
can [:hide, :unhide], [DiaryEntry, DiaryComment]
|
||||||
can [:read, :resolve, :ignore, :reopen], Issue
|
can [:read, :resolve, :ignore, :reopen], Issue
|
||||||
can :create, IssueComment
|
can :create, IssueComment
|
||||||
|
|
40
app/controllers/changeset_tags_controller.rb
Normal file
40
app/controllers/changeset_tags_controller.rb
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
class ChangesetTagsController < ApplicationController
|
||||||
|
layout "site"
|
||||||
|
|
||||||
|
before_action :authorize_web
|
||||||
|
before_action :set_locale
|
||||||
|
before_action :check_database_readable
|
||||||
|
|
||||||
|
authorize_resource
|
||||||
|
|
||||||
|
def show
|
||||||
|
@changeset = Changeset.find(params[:changeset_id])
|
||||||
|
rescue ActiveRecord::RecordNotFound
|
||||||
|
render :action => "changeset_not_found", :status => :not_found
|
||||||
|
end
|
||||||
|
|
||||||
|
def destroy
|
||||||
|
begin
|
||||||
|
@changeset = Changeset.find(params[:changeset_id])
|
||||||
|
rescue ActiveRecord::RecordNotFound
|
||||||
|
render :action => "changeset_not_found", :status => :not_found
|
||||||
|
return
|
||||||
|
end
|
||||||
|
begin
|
||||||
|
@key = Base64.urlsafe_decode64(params[:base64_key].to_s)
|
||||||
|
rescue ArgumentError
|
||||||
|
render :action => "invalid_tag", :status => :not_found
|
||||||
|
return
|
||||||
|
end
|
||||||
|
begin
|
||||||
|
@changeset_tag = ChangesetTag.find([params[:changeset_id], @key])
|
||||||
|
rescue ActiveRecord::RecordNotFound
|
||||||
|
render :action => "tag_not_found", :status => :not_found
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
@changeset_tag.delete
|
||||||
|
flash[:notice] = t ".success", :k => @changeset_tag.k, :v => @changeset_tag.v
|
||||||
|
redirect_to changeset_tags_path(@changeset)
|
||||||
|
end
|
||||||
|
end
|
12
app/views/changeset_tags/_tag.html.erb
Normal file
12
app/views/changeset_tags/_tag.html.erb
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
<tr>
|
||||||
|
<th class='py-1 border-secondary-subtle table-secondary fw-normal' dir='auto'><%= format_key(tag[0]) %></th>
|
||||||
|
<td class='py-1 border-secondary-subtle border-start' dir='auto'><%= format_value(tag[0], tag[1]) %></td>
|
||||||
|
<td class='py-1 border-secondary-subtle text-end'>
|
||||||
|
<%= button_to t(".delete"),
|
||||||
|
changeset_tags_path(@changeset),
|
||||||
|
:method => :delete,
|
||||||
|
:params => { :base64_key => Base64.urlsafe_encode64(tag[0]) },
|
||||||
|
:data => { :confirm => t(".confirm_delete", :k => tag[0], :v => tag[1]) },
|
||||||
|
:class => "btn btn-sm btn-danger" %>
|
||||||
|
</td>
|
||||||
|
</tr>
|
7
app/views/changeset_tags/changeset_not_found.html.erb
Normal file
7
app/views/changeset_tags/changeset_not_found.html.erb
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
<% content_for :heading do %>
|
||||||
|
<h1><%= t(".heading") %></h1>
|
||||||
|
<% end %>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<p><%= t ".body", :id => params[:changeset_id] %>
|
||||||
|
</div>
|
7
app/views/changeset_tags/invalid_tag.html.erb
Normal file
7
app/views/changeset_tags/invalid_tag.html.erb
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
<% content_for :heading do %>
|
||||||
|
<h1><%= t(".heading") %></h1>
|
||||||
|
<% end %>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<p><%= t ".body" %>
|
||||||
|
</div>
|
19
app/views/changeset_tags/show.html.erb
Normal file
19
app/views/changeset_tags/show.html.erb
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
<% content_for :heading do %>
|
||||||
|
<h1><%= t ".heading" %></h1>
|
||||||
|
<% end %>
|
||||||
|
|
||||||
|
<div class="alert alert-warning">
|
||||||
|
<%= t ".comment_warning" %>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<h2><%= t ".changeset_html", :id => link_to(@changeset.id, @changeset) %></h2>
|
||||||
|
|
||||||
|
<p><%= changeset_details(@changeset) %></p>
|
||||||
|
|
||||||
|
<% unless @changeset.tags.empty? %>
|
||||||
|
<div class='mb-3 border border-secondary-subtle rounded overflow-hidden'>
|
||||||
|
<table class='mb-0 table align-middle'>
|
||||||
|
<%= render :partial => "tag", :collection => @changeset.tags.sort %>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
<% end %>
|
7
app/views/changeset_tags/tag_not_found.html.erb
Normal file
7
app/views/changeset_tags/tag_not_found.html.erb
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
<% content_for :heading do %>
|
||||||
|
<h1><%= t(".heading") %></h1>
|
||||||
|
<% end %>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<p><%= t ".body", :id => params[:changeset_id], :k => @key %>
|
||||||
|
</div>
|
|
@ -111,9 +111,13 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class='secondary-actions'>
|
<div class='secondary-actions'>
|
||||||
<%= link_to t(".changesetxml"), api_changeset_path(@changeset) %>
|
<%= link_to t(".changesetxml"), api_changeset_path(@changeset), :class => "text-nowrap" %>
|
||||||
·
|
·
|
||||||
<%= link_to t(".osmchangexml"), api_changeset_download_path(@changeset) %>
|
<%= link_to t(".osmchangexml"), api_changeset_download_path(@changeset), :class => "text-nowrap" %>
|
||||||
|
<% if can? :manage, ChangesetTag %>
|
||||||
|
·
|
||||||
|
<%= link_to t(".manage_tags"), changeset_tags_path(@changeset), :class => "text-nowrap" %>
|
||||||
|
<% end %>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<% if @next_by_user || @prev_by_user %>
|
<% if @next_by_user || @prev_by_user %>
|
||||||
|
|
|
@ -474,6 +474,25 @@ en:
|
||||||
title_particular: "OpenStreetMap changeset #%{changeset_id} discussion"
|
title_particular: "OpenStreetMap changeset #%{changeset_id} discussion"
|
||||||
timeout:
|
timeout:
|
||||||
sorry: "Sorry, the list of changeset comments you requested took too long to retrieve."
|
sorry: "Sorry, the list of changeset comments you requested took too long to retrieve."
|
||||||
|
changeset_tags:
|
||||||
|
show:
|
||||||
|
heading: Manage changeset tags
|
||||||
|
changeset_html: "Changeset: %{id}"
|
||||||
|
comment_warning: Remember to add a comment to the changeset after modifying its tags. This ensures that the changes you made are replicated.
|
||||||
|
tag:
|
||||||
|
delete: "Delete"
|
||||||
|
confirm_delete: "Delete the tag %{k}=%{v} ?"
|
||||||
|
changeset_not_found:
|
||||||
|
heading: Changeset does not exist
|
||||||
|
body: "Sorry, changeset #%{id} could not be found."
|
||||||
|
invalid_tag:
|
||||||
|
heading: Invalid changeset tag
|
||||||
|
body: "Sorry, the requested tag key cannot be decoded."
|
||||||
|
tag_not_found:
|
||||||
|
heading: Changeset tag does not exist
|
||||||
|
body: "Sorry, tag %{k} could not be found in changeset #%{id}."
|
||||||
|
destroy:
|
||||||
|
success: Tag %{k}=%{v} deleted successfully.
|
||||||
changesets:
|
changesets:
|
||||||
changeset:
|
changeset:
|
||||||
comments:
|
comments:
|
||||||
|
@ -521,6 +540,7 @@ en:
|
||||||
comment: "Comment"
|
comment: "Comment"
|
||||||
changesetxml: "Changeset XML"
|
changesetxml: "Changeset XML"
|
||||||
osmchangexml: "osmChange XML"
|
osmchangexml: "osmChange XML"
|
||||||
|
manage_tags: "Manage changeset tags"
|
||||||
paging_nav:
|
paging_nav:
|
||||||
nodes: "Nodes (%{count})"
|
nodes: "Nodes (%{count})"
|
||||||
nodes_paginated: "Nodes (%{x}-%{y} of %{count})"
|
nodes_paginated: "Nodes (%{x}-%{y} of %{count})"
|
||||||
|
|
|
@ -151,6 +151,8 @@ OpenStreetMap::Application.routes.draw do
|
||||||
namespace :changeset_comments, :as => :comments, :path => :comments do
|
namespace :changeset_comments, :as => :comments, :path => :comments do
|
||||||
resource :feed, :only => :show, :defaults => { :format => "rss" }
|
resource :feed, :only => :show, :defaults => { :format => "rss" }
|
||||||
end
|
end
|
||||||
|
|
||||||
|
resource :tags, :controller => "changeset_tags", :only => [:show, :destroy]
|
||||||
end
|
end
|
||||||
get "/changeset/:id/subscribe", :id => /\d+/, :to => redirect(:path => "/changeset/%{id}/subscription")
|
get "/changeset/:id/subscribe", :id => /\d+/, :to => redirect(:path => "/changeset/%{id}/subscription")
|
||||||
get "/changeset/:id/unsubscribe", :id => /\d+/, :to => redirect(:path => "/changeset/%{id}/subscription")
|
get "/changeset/:id/unsubscribe", :id => /\d+/, :to => redirect(:path => "/changeset/%{id}/subscription")
|
||||||
|
|
206
test/controllers/changeset_tags_controller_test.rb
Normal file
206
test/controllers/changeset_tags_controller_test.rb
Normal file
|
@ -0,0 +1,206 @@
|
||||||
|
require "test_helper"
|
||||||
|
|
||||||
|
class ChangesetTagsControllerTest < ActionDispatch::IntegrationTest
|
||||||
|
def test_routes
|
||||||
|
assert_routing(
|
||||||
|
{ :path => "/changeset/1/tags", :method => :get },
|
||||||
|
{ :controller => "changeset_tags", :action => "show", :changeset_id => "1" }
|
||||||
|
)
|
||||||
|
assert_routing(
|
||||||
|
{ :path => "/changeset/1/tags", :method => :delete },
|
||||||
|
{ :controller => "changeset_tags", :action => "destroy", :changeset_id => "1" }
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_show_success
|
||||||
|
changeset = create(:changeset)
|
||||||
|
moderator_user = create(:moderator_user)
|
||||||
|
|
||||||
|
session_for(moderator_user)
|
||||||
|
|
||||||
|
get changeset_tags_path(changeset)
|
||||||
|
assert_response :success
|
||||||
|
|
||||||
|
assert_dom ".content-body" do
|
||||||
|
assert_dom "h2", :text => "Changeset: #{changeset.id}" do
|
||||||
|
assert_dom "a[href='#{changeset_path(changeset)}']"
|
||||||
|
end
|
||||||
|
|
||||||
|
assert_dom "a[href='#{user_path(changeset.user)}']"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_show_success_1_tag
|
||||||
|
changeset = create(:changeset)
|
||||||
|
create(:changeset_tag, :changeset => changeset, :k => "tested-tag-key", :v => "tested-tag-value")
|
||||||
|
moderator_user = create(:moderator_user)
|
||||||
|
|
||||||
|
session_for(moderator_user)
|
||||||
|
|
||||||
|
get changeset_tags_path(changeset)
|
||||||
|
assert_response :success
|
||||||
|
|
||||||
|
assert_dom ".content-body" do
|
||||||
|
assert_dom "tbody tr", :count => 1 do |rows|
|
||||||
|
check_tag_table_row rows[0], changeset, "tested-tag-key", "tested-tag-value"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_show_success_2_tags
|
||||||
|
changeset = create(:changeset)
|
||||||
|
create(:changeset_tag, :changeset => changeset, :k => "tested-1st-tag-key", :v => "tested-1st-tag-value")
|
||||||
|
create(:changeset_tag, :changeset => changeset, :k => "tested-2nd-tag-key", :v => "tested-2nd-tag-value")
|
||||||
|
moderator_user = create(:moderator_user)
|
||||||
|
|
||||||
|
session_for(moderator_user)
|
||||||
|
|
||||||
|
get changeset_tags_path(changeset)
|
||||||
|
assert_response :success
|
||||||
|
|
||||||
|
assert_dom ".content-body" do
|
||||||
|
assert_dom "tbody tr", :count => 2 do |rows|
|
||||||
|
check_tag_table_row rows[0], changeset, "tested-1st-tag-key", "tested-1st-tag-value"
|
||||||
|
check_tag_table_row rows[1], changeset, "tested-2nd-tag-key", "tested-2nd-tag-value"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_show_success_empty_tag
|
||||||
|
changeset = create(:changeset)
|
||||||
|
create(:changeset_tag, :changeset => changeset, :k => "", :v => "")
|
||||||
|
moderator_user = create(:moderator_user)
|
||||||
|
|
||||||
|
session_for(moderator_user)
|
||||||
|
|
||||||
|
get changeset_tags_path(changeset)
|
||||||
|
assert_response :success
|
||||||
|
|
||||||
|
assert_dom ".content-body" do
|
||||||
|
assert_dom "tbody tr", :count => 1 do |rows|
|
||||||
|
check_tag_table_row rows[0], changeset, "", ""
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_show_fail_no_changeset
|
||||||
|
moderator_user = create(:moderator_user)
|
||||||
|
|
||||||
|
session_for(moderator_user)
|
||||||
|
|
||||||
|
get changeset_tags_path(999111)
|
||||||
|
assert_response :not_found
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_show_fail_not_logged_in
|
||||||
|
changeset = create(:changeset)
|
||||||
|
|
||||||
|
get changeset_tags_path(changeset)
|
||||||
|
assert_redirected_to login_path(:referer => changeset_tags_path(changeset))
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_show_fail_not_moderator
|
||||||
|
changeset = create(:changeset)
|
||||||
|
user = create(:user)
|
||||||
|
|
||||||
|
session_for(user)
|
||||||
|
|
||||||
|
get changeset_tags_path(changeset)
|
||||||
|
assert_redirected_to :controller => :errors, :action => :forbidden
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_destroy_success
|
||||||
|
changeset = create(:changeset)
|
||||||
|
create(:changeset_tag, :changeset => changeset, :k => "tested-1st-tag-key", :v => "tested-1st-tag-value")
|
||||||
|
create(:changeset_tag, :changeset => changeset, :k => "tested-2nd-tag-key", :v => "tested-2nd-tag-value")
|
||||||
|
other_changeset = create(:changeset)
|
||||||
|
create(:changeset_tag, :changeset => other_changeset, :k => "tested-1st-tag-key", :v => "tested-1st-tag-value")
|
||||||
|
moderator_user = create(:moderator_user)
|
||||||
|
|
||||||
|
session_for(moderator_user)
|
||||||
|
|
||||||
|
assert_difference "ChangesetTag.count", -1 do
|
||||||
|
delete changeset_tags_path(changeset, :base64_key => Base64.urlsafe_encode64("tested-1st-tag-key"))
|
||||||
|
assert_redirected_to changeset_tags_path(changeset)
|
||||||
|
end
|
||||||
|
assert_equal({ "tested-2nd-tag-key" => "tested-2nd-tag-value" }, changeset.tags)
|
||||||
|
assert_match(/tested-1st-tag-key=tested-1st-tag-value deleted successfully/, flash[:notice])
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_destroy_success_empty_tag
|
||||||
|
changeset = create(:changeset)
|
||||||
|
create(:changeset_tag, :changeset => changeset, :k => "", :v => "")
|
||||||
|
moderator_user = create(:moderator_user)
|
||||||
|
|
||||||
|
session_for(moderator_user)
|
||||||
|
|
||||||
|
assert_difference "ChangesetTag.count", -1 do
|
||||||
|
delete changeset_tags_path(changeset, :base64_key => Base64.urlsafe_encode64(""))
|
||||||
|
assert_redirected_to changeset_tags_path(changeset)
|
||||||
|
end
|
||||||
|
assert_empty changeset.tags
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_destroy_fail_no_changeset
|
||||||
|
moderator_user = create(:moderator_user)
|
||||||
|
|
||||||
|
session_for(moderator_user)
|
||||||
|
|
||||||
|
delete changeset_tags_path(999111, :base64_key => Base64.urlsafe_encode64("nope"))
|
||||||
|
assert_response :not_found
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_destroy_fail_invalid_key_encoding
|
||||||
|
changeset = create(:changeset)
|
||||||
|
moderator_user = create(:moderator_user)
|
||||||
|
|
||||||
|
session_for(moderator_user)
|
||||||
|
|
||||||
|
delete changeset_tags_path(changeset, :base64_key => "ZnJvbV9jb")
|
||||||
|
assert_response :not_found
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_destroy_fail_no_key
|
||||||
|
changeset = create(:changeset)
|
||||||
|
moderator_user = create(:moderator_user)
|
||||||
|
|
||||||
|
session_for(moderator_user)
|
||||||
|
|
||||||
|
delete changeset_tags_path(changeset, :base64_key => Base64.urlsafe_encode64("tested-missing-tag"))
|
||||||
|
assert_response :not_found
|
||||||
|
|
||||||
|
assert_dom ".content-body", :text => /tested-missing-tag could not be found/
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_destroy_fail_not_logged_in
|
||||||
|
changeset = create(:changeset)
|
||||||
|
create(:changeset_tag, :changeset => changeset, :k => "tested-tag-key", :v => "tested-tag-value")
|
||||||
|
|
||||||
|
delete changeset_tags_path(changeset, :base64_key => Base64.urlsafe_encode64("tested-tag-key"))
|
||||||
|
assert_response :forbidden
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_destroy_fail_not_moderator
|
||||||
|
changeset = create(:changeset)
|
||||||
|
create(:changeset_tag, :changeset => changeset, :k => "tested-tag-key", :v => "tested-tag-value")
|
||||||
|
user = create(:user)
|
||||||
|
|
||||||
|
session_for(user)
|
||||||
|
|
||||||
|
delete changeset_tags_path(changeset, :base64_key => Base64.urlsafe_encode64("tested-tag-key"))
|
||||||
|
assert_redirected_to :controller => :errors, :action => :forbidden
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def check_tag_table_row(row, changeset, key, value)
|
||||||
|
assert_dom row, "th", :text => key
|
||||||
|
assert_dom row, "td", :text => value
|
||||||
|
assert_dom row, "td form[action='#{changeset_tags_path(changeset)}']" do
|
||||||
|
assert_dom "input[type='hidden'][name='base64_key']" do
|
||||||
|
assert_dom "> @value", Base64.urlsafe_encode64(key)
|
||||||
|
end
|
||||||
|
assert_dom "button", :text => "Delete"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -334,6 +334,33 @@ class ChangesetsControllerTest < ActionDispatch::IntegrationTest
|
||||||
assert_dom "a[href='#{changeset_path changeset5}']", :count => 1
|
assert_dom "a[href='#{changeset_path changeset5}']", :count => 1
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def test_show_no_manage_tags_link_for_anonymous_users
|
||||||
|
changeset = create(:changeset)
|
||||||
|
|
||||||
|
sidebar_browse_check :changeset_path, changeset.id, "changesets/show"
|
||||||
|
assert_dom ".secondary-actions a[href='#{changeset_tags_path changeset}']", :count => 0
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_show_no_manage_tags_link_for_regular_users
|
||||||
|
changeset = create(:changeset)
|
||||||
|
user = create(:user)
|
||||||
|
|
||||||
|
session_for(user)
|
||||||
|
|
||||||
|
sidebar_browse_check :changeset_path, changeset.id, "changesets/show"
|
||||||
|
assert_dom ".secondary-actions a[href='#{changeset_tags_path changeset}']", :count => 0
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_show_manage_tags_link
|
||||||
|
changeset = create(:changeset)
|
||||||
|
moderator_user = create(:moderator_user)
|
||||||
|
|
||||||
|
session_for(moderator_user)
|
||||||
|
|
||||||
|
sidebar_browse_check :changeset_path, changeset.id, "changesets/show"
|
||||||
|
assert_dom ".secondary-actions a[href='#{changeset_tags_path changeset}']", :count => 1
|
||||||
|
end
|
||||||
|
|
||||||
##
|
##
|
||||||
# This should display the last 20 non-empty changesets
|
# This should display the last 20 non-empty changesets
|
||||||
def test_feed
|
def test_feed
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue