feat: 🎸 methods and db changes to publish single map and batch-publish multiple maps (no interface yet) (#1772)

* feat: 🎸 publish single map and batch-publish multiple maps

* style: 💄 implemented code style suggestions & refactored routes

* refactor: 💡 discarded authors' object and used count instead

* refactor: 💡 made method names more readable
This commit is contained in:
PeculiarE
2022-07-01 12:47:14 +01:00
committed by GitHub
parent 833eb3718f
commit 0d2e31119d
11 changed files with 264 additions and 40 deletions

View File

@@ -53,7 +53,7 @@ class ApplicationController < ActionController::Base
def logged_in_as(roles, action)
unless current_user && roles.any? { |role| current_user.role == role }
flash[:error] = "Only #{roles.collect { |role| role.pluralize }.join(' and ')} can #{action}."
flash[:error] = "Only #{roles.collect(&:pluralize).join(" and ")} can #{action}."
redirect_to('/' + '?_=' + Time.now.to_i.to_s)
end
end

View File

@@ -1,28 +1,36 @@
class SpamController < ApplicationController
module ModerationGuards
def spam_check(map)
def check_and_spam(map)
# check and spam only unspammed maps
map.spam unless map.status == Map::Status::BANNED
end
def ban_check(map)
def check_and_ban(map)
# check and ban only unbanned non-anonymous authors
map.user.ban unless map.anonymous? || map.user.status == User::Status::BANNED
end
def check_and_publish(map)
# check and publish only spammed or moderated maps
map.publish unless map.status == Map::Status::NORMAL
end
def check_and_unban(map)
# check and unban only banned non-anonymous authors
map.user.unban unless map.anonymous? || map.user.status != User::Status::BANNED
end
end
include ModerationGuards
require 'set'
before_action :require_login
before_action { logged_in_as(['admin', 'moderator'], 'moderate maps and users') }
def spam_map
@map = Map.find(params[:id])
if spam_check(@map)
if check_and_spam(@map)
notice_text = 'Map marked as spam.'
notice_text.chop! << ' and author banned.' if ban_check(@map)
notice_text.chop! << ' and author banned.' if check_and_ban(@map)
else
notice_text = 'Map already marked as spam.'
end
@@ -30,17 +38,43 @@ class SpamController < ApplicationController
redirect_back(fallback_location: root_path)
end
def batch_spam_map
def batch_spam_maps
spammed_maps = 0
banned_authors = Set.new
banned_authors = 0
params[:ids].split(',').uniq.each do |id|
map = Map.find(id)
if spam_check(map)
if check_and_spam(map)
spammed_maps += 1
banned_authors << map.user.id if ban_check(map)
banned_authors += 1 if check_and_ban(map)
end
end
flash[:notice] = helpers.pluralize(spammed_maps, 'map') + ' spammed and ' + helpers.pluralize(banned_authors.size, 'author') + ' banned.'
flash[:notice] = helpers.pluralize(spammed_maps, 'map') + ' spammed and ' + helpers.pluralize(banned_authors, 'author') + ' banned.'
redirect_back(fallback_location: root_path)
end
def publish_map
@map = Map.find(params[:id])
if check_and_publish(@map)
notice_text = 'Map published.'
notice_text.chop! << ' and author unbanned.' if check_and_unban(@map)
else
notice_text = 'Map already published.'
end
flash[:notice] = notice_text
redirect_back(fallback_location: root_path)
end
def batch_publish_maps
published_maps = 0
unbanned_authors = 0
params[:ids].split(',').uniq.each do |id|
map = Map.find(id)
if check_and_publish(map)
published_maps += 1
unbanned_authors += 1 if check_and_unban(map)
end
end
flash[:notice] = helpers.pluralize(published_maps, 'map') + ' published and ' + helpers.pluralize(unbanned_authors, 'author') + ' unbanned.'
redirect_back(fallback_location: root_path)
end
end

View File

@@ -299,4 +299,8 @@ class Map < ApplicationRecord
def spam
update!(status: Status::BANNED)
end
def publish
update!(status: Status::NORMAL)
end
end

View File

@@ -65,6 +65,10 @@ class User < ApplicationRecord
end
def ban
update!(status: Status::BANNED)
update!({ status: Status::BANNED, status_updated_at: Time.now })
end
def unban
update!({ status: Status::NORMAL, status_updated_at: Time.now })
end
end

View File

@@ -123,8 +123,15 @@ Mapknitter::Application.routes.draw do
get '/warps/:map/:file(.:format)', to: redirect('https://archive.publiclab.org/warps/%{map}/%{file}.%{format}')
patch 'moderate/spam_map/:id' => 'spam#spam_map', as: 'spam_map'
patch 'moderate/batch_spam_map/:ids' => 'spam#batch_spam_map', as: 'batch_spam_map'
scope 'moderate', module: 'spam' do
%w(spam_map publish_map).each do |action|
patch action + '/:id', action: action, as: action
end
%w(batch_spam_maps batch_publish_maps).each do |action|
patch action + '/:ids', action: action, as: action
end
end
# See how all your routes lay out with 'rails routes'

View File

@@ -0,0 +1,5 @@
class AddStatusUpdateTimeToUsers < ActiveRecord::Migration[5.2]
def change
add_column :users, :status_updated_at, :datetime, null: true
end
end

View File

@@ -10,7 +10,7 @@
#
# It's strongly recommended that you check this file into your version control system.
ActiveRecord::Schema.define(version: 2022_06_19_221926) do
ActiveRecord::Schema.define(version: 2022_06_28_190519) do
create_table "annotations", options: "ENGINE=InnoDB DEFAULT CHARSET=utf8", force: :cascade do |t|
t.integer "map_id"
@@ -124,6 +124,7 @@ ActiveRecord::Schema.define(version: 2022_06_19_221926) do
t.string "remember_token", limit: 40
t.datetime "remember_token_expires_at"
t.integer "status", default: 1, null: false
t.datetime "status_updated_at"
t.index ["login"], name: "index_users_on_login", unique: true
end

View File

@@ -6,8 +6,14 @@ class SpamControllerTest < ActionController::TestCase
@maps = [maps(:cubbon)]
end
def custom_setup
@map_ids = @maps.collect { |map| map['id'] }.join(',')
def custom_setup(spam_maps)
if spam_maps
@maps.collect { |map|
map.spam
map.user.ban
}
end
@map_ids = @maps.collect(&:id).join(',')
end
test 'should not moderate a map if user not logged in' do
@@ -20,9 +26,9 @@ class SpamControllerTest < ActionController::TestCase
end
test 'should not moderate maps if user is not an admin or a moderator' do
custom_setup
custom_setup(false)
session[:user_id] = 1
patch(:batch_spam_map, params: { ids: @map_ids })
patch(:batch_spam_maps, params: { ids: @map_ids })
assert_equal 'Only admins and moderators can moderate maps and users.', flash[:error]
assert_redirected_to ('/' + '?_=' + Time.now.to_i.to_s)
@@ -82,9 +88,9 @@ class SpamControllerTest < ActionController::TestCase
test 'should batch-spam maps and ban non-anonymous authors' do
@maps << maps(:village)
custom_setup
custom_setup(false)
session[:user_id] = 2
patch(:batch_spam_map, params: { ids: @map_ids })
patch(:batch_spam_maps, params: { ids: @map_ids })
assert_equal @maps.length, 2
assert_equal @maps.uniq.length, 2
@@ -96,9 +102,9 @@ class SpamControllerTest < ActionController::TestCase
test 'should batch-spam maps and not ban anonymous authors' do
@maps << maps(:yaya)
custom_setup
custom_setup(false)
session[:user_id] = 2
patch(:batch_spam_map, params: { ids: @map_ids })
patch(:batch_spam_maps, params: { ids: @map_ids })
assert_equal @maps.length, 2
assert_equal @maps.uniq.length, 2
@@ -110,9 +116,9 @@ class SpamControllerTest < ActionController::TestCase
test 'should not batch-spam a duplicate map' do
@maps << maps(:cubbon)
custom_setup
custom_setup(false)
session[:user_id] = 2
patch(:batch_spam_map, params: { ids: @map_ids })
patch(:batch_spam_maps, params: { ids: @map_ids })
assert_equal @maps.length, 2
assert_equal @maps.uniq.length, 1
@@ -123,15 +129,12 @@ class SpamControllerTest < ActionController::TestCase
end
test 'should not batch-spam already-spammed maps' do
@maps[0].spam
@maps[0].user.ban
custom_setup(true)
assert_equal 0, @maps[0].status
assert_equal 0, @maps[0].user.status
custom_setup
session[:user_id] = 2
patch(:batch_spam_map, params: { ids: @map_ids })
patch(:batch_spam_maps, params: { ids: @map_ids })
assert_equal @maps.length, 1
assert_equal @maps.uniq.length, 1
@@ -141,9 +144,9 @@ class SpamControllerTest < ActionController::TestCase
test 'should batch-spam maps and skip banning of authors already banned' do
@maps << @map
custom_setup
custom_setup(false)
session[:user_id] = 2
patch(:batch_spam_map, params: { ids: @map_ids })
patch(:batch_spam_maps, params: { ids: @map_ids })
assert_equal @maps.length, 2
assert_equal @maps.uniq.length, 2
@@ -152,4 +155,150 @@ class SpamControllerTest < ActionController::TestCase
assert @maps.all? { |map| map.user.status == 0 }
assert_redirected_to root_path
end
test 'should publish a spammed map owned by a banned non-anonymous author and unban the author' do
@map.spam
@map.user.ban
assert_equal 0, @map.status
assert_equal 0, @map.user.status
session[:user_id] = 2
patch(:publish_map, params: { id: @map.id })
@map.reload
assert_equal 'Map published and author unbanned.', flash[:notice]
assert_redirected_to root_path
assert_equal 1, @map.status
assert_equal 1, @map.user.status
end
test 'should publish a spammed map owned by an unbanned non-anonymous author and skip the unbanning process' do
@map.spam
assert_equal 0, @map.status
assert_equal 1, @map.user.status
session[:user_id] = 2
patch(:publish_map, params: { id: @map.id })
@map.reload
assert_equal 'Map published.', flash[:notice]
assert_redirected_to root_path
assert_equal 1, @map.status
assert_equal 1, @map.user.status
end
test 'should publish a spammed map owned by an anonymous author and skip the unbanning process' do
anon_map = maps(:yaya)
assert_equal 1, anon_map.status
anon_map.spam
assert_equal 0, anon_map.status
session[:user_id] = 2
patch(:publish_map, params: { id: anon_map.id })
anon_map.reload
assert_equal 'Map published.', flash[:notice]
assert_redirected_to root_path
assert_equal 1, anon_map.status
end
test 'should not publish an already published map' do
assert_equal 1, @map.status
session[:user_id] = 2
patch(:publish_map, params: { id: @map.id })
assert_equal 'Map already published.', flash[:notice]
assert_redirected_to root_path
end
test 'should batch-publish maps and unban non-anonymous banned authors' do
@maps << maps(:village)
custom_setup(true)
assert @maps.all? { |map| map.status == 0 }
assert @maps.all? { |map| map.user.status == 0 }
session[:user_id] = 2
patch(:batch_publish_maps, params: { ids: @map_ids })
assert_equal @maps.length, 2
assert_equal @maps.uniq.length, 2
assert_equal '2 maps published and 2 authors unbanned.', flash[:notice]
assert @maps.all? { |map| map.reload.status == 1 }
assert @maps.all? { |map| map.user.status == 1 }
assert_redirected_to root_path
end
test 'should batch-publish maps and skip unbanning of anonymous authors' do
@maps << maps(:yaya)
@maps.collect { |map| map.spam }
@maps[0].user.ban
assert @maps.all? { |map| map.status == 0 }
custom_setup(false)
session[:user_id] = 2
patch(:batch_publish_maps, params: { ids: @map_ids })
assert_equal @maps.length, 2
assert_equal @maps.uniq.length, 2
assert_equal '2 maps published and 1 author unbanned.', flash[:notice]
assert_redirected_to root_path
assert @maps.all? { |map| map.reload.status == 1 }
assert @maps.one? { |map| map.user.nil? }
end
test 'should batch-publish maps and skip banning of authors already unbanned' do
@maps << @map
custom_setup(true)
assert @maps.all? { |map| map.status == 0 }
assert @maps.all? { |map| map.user.status == 0 }
session[:user_id] = 2
patch(:batch_publish_maps, params: { ids: @map_ids })
assert_equal @maps.length, 2
assert_equal @maps.uniq.length, 2
assert_equal '2 maps published and 1 author unbanned.', flash[:notice]
assert @maps.all? { |map| map.reload.status == 1 }
assert @maps.all? { |map| map.user.status == 1 }
assert_redirected_to root_path
end
test 'should not batch-publish a duplicate map' do
@maps << maps(:cubbon)
custom_setup(true)
assert @maps.uniq.one? { |map| map.status == 0 }
assert @maps.uniq.one? { |map| map.user.status == 0 }
session[:user_id] = 2
patch(:batch_publish_maps, params: { ids: @map_ids })
assert_equal @maps.length, 2
assert_equal @maps.uniq.length, 1
assert_equal '1 map published and 1 author unbanned.', flash[:notice]
assert @maps.uniq.one? { |map| map.reload.status == 1 }
assert @maps.uniq.one? { |map| map.user.status == 1 }
assert_redirected_to root_path
end
test 'should not batch-publish already-published maps' do
custom_setup(false)
assert_equal 1, @maps[0].status
assert_equal 1, @maps[0].user.status
session[:user_id] = 2
patch(:batch_publish_maps, params: { ids: @map_ids })
assert_equal @maps.length, 1
assert_equal @maps.uniq.length, 1
assert_equal '0 maps published and 0 authors unbanned.', flash[:notice]
assert_redirected_to root_path
end
end

View File

@@ -7,6 +7,14 @@ class RoutesTest < ActionDispatch::IntegrationTest
end
test "test batch-spam-maps route" do
assert_routing({ path: '/moderate/batch_spam_map/1,2', method: :patch }, { controller: 'spam', action: 'batch_spam_map', ids: '1,2' })
assert_routing({ path: '/moderate/batch_spam_maps/1,2', method: :patch }, { controller: 'spam', action: 'batch_spam_maps', ids: '1,2' })
end
test "test single-publish-map route" do
assert_routing({ path: '/moderate/publish_map/2', method: :patch }, { controller: 'spam', action: 'publish_map', id: '2' })
end
test "test batch-publish-maps route" do
assert_routing({ path: '/moderate/batch_publish_maps/1,2', method: :patch }, { controller: 'spam', action: 'batch_publish_maps', ids: '1,2' })
end
end

View File

@@ -98,10 +98,14 @@ class MapTest < ActiveSupport::TestCase
assert maps.collect(&:name).include?('Saugus Landfill Incinerator')
end
test 'should spam map' do
test 'should spam and publish map' do
map = maps(:saugus)
assert_equal Map::Status::NORMAL, map.status
map.spam
assert_equal Map::Status::BANNED, map.status
map.publish
assert_equal Map::Status::NORMAL, map.status
end
end

View File

@@ -52,11 +52,19 @@ class UserTest < ActiveSupport::TestCase
assert_equal map_images.flatten, user.warpables
end
test 'should ban user' do
test 'should ban and unban user' do
user = users(:quentin)
assert_equal User::Status::NORMAL, user.status
assert_nil user.status_updated_at
user.ban
assert_equal User::Status::BANNED, user.status
assert_not_nil user.status_updated_at
old_time = user.status_updated_at
user.unban
assert_equal User::Status::NORMAL, user.status
assert user.status_updated_at.to_f >= old_time.to_f
end
# def test_should_authenticate_user