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

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

* fix: 🐛 fixed failing system tests and refactored spam functions
This commit is contained in:
PeculiarE
2022-06-23 09:43:22 +01:00
committed by GitHub
parent 65ae4a3088
commit 9a4ea111e2
14 changed files with 280 additions and 13 deletions

1
.gitignore vendored
View File

@@ -51,3 +51,4 @@ yarn.lock
.idea/ .idea/
passenger.3000.pid passenger.3000.pid
passenger.3000.pid.lock passenger.3000.pid.lock
.vscode

View File

@@ -46,15 +46,20 @@ class ApplicationController < ActionController::Base
end end
def logged_in? def logged_in?
user_id = session[:user_id]
begin begin
user_id && User.find(user_id) ? true : false current_user ? true : false
rescue StandardError rescue StandardError
false false
end end
end end
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}."
redirect_to('/' + '?_=' + Time.now.to_i.to_s)
end
end
def save_tags(map) def save_tags(map)
return unless params[:tags].present? return unless params[:tags].present?

View File

@@ -0,0 +1,46 @@
class SpamController < ApplicationController
module ModerationGuards
def spam_check(map)
#check and spam only unspammed maps
map.spam unless map.status == Map::Status::BANNED
end
def ban_check(map)
#check and ban only unbanned non-anonymous authors
map.user.ban 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)
notice_text = 'Map marked as spam.'
notice_text.chop! << ' and author banned.' if ban_check(@map)
else
notice_text = 'Map already marked as spam.'
end
flash[:notice] = notice_text
redirect_back(fallback_location: root_path)
end
def batch_spam_map
spammed_maps = 0
banned_authors = Set.new
params[:ids].split(',').uniq.each do |id|
map = Map.find(id)
if spam_check(map)
spammed_maps += 1
banned_authors << map.user.id if ban_check(map)
end
end
flash[:notice] = helpers.pluralize(spammed_maps, 'map') + ' spammed and ' + helpers.pluralize(banned_authors.size, 'author') + ' banned.'
redirect_back(fallback_location: root_path)
end
end

View File

@@ -3,6 +3,14 @@ class Map < ApplicationRecord
extend FriendlyId extend FriendlyId
friendly_id :name, use: %i(slugged static) friendly_id :name, use: %i(slugged static)
module Status
VALUES = [
BANNED = 0, # Usage: Status::BANNED
NORMAL = 1, # Usage: Status::NORMAL
MODERATED = 4 # Usage: Status::MODERATED
].freeze
end
attr_accessor :image_urls attr_accessor :image_urls
validates_presence_of :name, :slug, :author, :lat, :lon validates_presence_of :name, :slug, :author, :lat, :lon
@@ -287,4 +295,8 @@ class Map < ApplicationRecord
end end
User.where(id: user_ids.flatten.uniq).where.not(id: user_id) User.where(id: user_ids.flatten.uniq).where.not(id: user_id)
end end
def spam
self.update!(status: Status::BANNED)
end
end end

View File

@@ -1,6 +1,14 @@
require 'digest/sha1' require 'digest/sha1'
class User < ApplicationRecord class User < ApplicationRecord
module Status
VALUES = [
BANNED = 0, # Usage: Status::BANNED
NORMAL = 1, # Usage: Status::NORMAL
MODERATED = 5 # Usage: Status::MODERATED
].freeze
end
has_many :maps has_many :maps
has_many :tags has_many :tags
has_many :comments has_many :comments
@@ -55,4 +63,8 @@ class User < ApplicationRecord
def can_edit?(resource) def can_edit?(resource)
owns?(resource) owns?(resource)
end end
def ban
self.update!(status: Status::BANNED)
end
end end

View File

@@ -121,7 +121,11 @@ Mapknitter::Application.routes.draw do
end end
end end
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'
# See how all your routes lay out with 'rails routes' # See how all your routes lay out with 'rails routes'
get '/warps/:map/:file(.:format)', to: redirect('https://archive.publiclab.org/warps/%{map}/%{file}.%{format}')
end end

View File

@@ -0,0 +1,6 @@
class MakeStatusColumnsNotNullable < ActiveRecord::Migration[5.2]
def change
change_column_null :users, :status, false
change_column_null :maps, :status, false
end
end

View File

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

View File

@@ -0,0 +1,155 @@
require 'test_helper'
class SpamControllerTest < ActionController::TestCase
def setup #called before every single test
@map = maps(:saugus)
@maps = [maps(:cubbon)]
end
def custom_setup
@map_ids = @maps.collect { |map| map['id'] }.join(',')
end
test 'should not moderate a map if user not logged in' do
patch(:spam_map, params: { id: @map.id })
@map.reload
assert_equal 'You must be logged in to access this section', flash[:warning]
assert_redirected_to "/login?back_to=/moderate/spam_map/#{@map.id}"
assert_equal 1, @map.status
end
test 'should not moderate maps if user is not an admin or a moderator' do
custom_setup
session[:user_id] = 1
patch(:batch_spam_map, 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)
end
test 'should spam a map owned by an anonymous author and not ban the author' do
anon_map = maps(:yaya)
session[:user_id] = 2
patch(:spam_map, params: { id: anon_map.id })
anon_map.reload
assert_equal 'Map marked as spam.', flash[:notice]
assert_redirected_to root_path
assert_equal 0, anon_map.status
end
test 'should spam a map owned by a non-anonymous author and ban the author' do
session[:user_id] = 2
patch(:spam_map, params: { id: @map.id })
@map.reload
assert_equal 'Map marked as spam and author banned.', flash[:notice]
assert_redirected_to root_path
assert_equal 0, @map.status
assert_equal 0, @map.user.status
end
test 'should spam a map owned by a banned author and not ban the author again' do
session[:user_id] = 2
@map.spam
@map.user.ban
second_map = maps(:cubbon)
patch(:spam_map, params: { id: second_map.id })
second_map.reload
assert_equal 'Map marked as spam.', flash[:notice]
assert_redirected_to root_path
assert_equal 0, @map.status
assert_equal 0, @map.user.status
assert_equal 0, second_map.status
assert_equal 0, second_map.user.status
end
test 'should not spam an already spammed map' do
session[:user_id] = 2
@map.spam
@map.user.ban
patch(:spam_map, params: { id: @map.id })
assert_equal 'Map already marked as spam.', flash[:notice]
assert_redirected_to root_path
assert_equal 0, @map.status
assert_equal 0, @map.user.status
end
test 'should batch-spam maps and ban non-anonymous authors' do
@maps << maps(:village)
custom_setup
session[:user_id] = 2
patch(:batch_spam_map, params: { ids: @map_ids })
assert_equal @maps.length, 2
assert_equal @maps.uniq.length, 2
assert_equal '2 maps spammed and 2 authors banned.', flash[:notice]
assert @maps.all? { |map| map.reload.status == 0 }
assert @maps.all? { |map| map.user.status == 0 }
assert_redirected_to root_path
end
test 'should batch-spam maps and not ban anonymous authors' do
@maps << maps(:yaya)
custom_setup
session[:user_id] = 2
patch(:batch_spam_map, params: { ids: @map_ids })
assert_equal @maps.length, 2
assert_equal @maps.uniq.length, 2
assert_equal '2 maps spammed and 1 author banned.', flash[:notice]
assert_redirected_to root_path
assert @maps.all? { |map| map.reload.status == 0 }
assert @maps.one? { |map| map.user.nil? }
end
test 'should not batch-spam a duplicate map' do
@maps << maps(:cubbon)
custom_setup
session[:user_id] = 2
patch(:batch_spam_map, params: { ids: @map_ids })
assert_equal @maps.length, 2
assert_equal @maps.uniq.length, 1
assert_equal '1 map spammed and 1 author banned.', flash[:notice]
assert @maps.uniq.one? { |map| map.reload.status == 0 }
assert @maps.uniq.one? { |map| map.user.status == 0 }
assert_redirected_to root_path
end
test 'should not batch-spam already-spammed maps' do
@maps[0].spam
@maps[0].user.ban
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 })
assert_equal @maps.length, 1
assert_equal @maps.uniq.length, 1
assert_equal '0 maps spammed and 0 authors banned.', flash[:notice]
assert_redirected_to root_path
end
test 'should batch-spam maps and skip banning of authors already banned' do
@maps << @map
custom_setup
session[:user_id] = 2
patch(:batch_spam_map, params: { ids: @map_ids })
assert_equal @maps.length, 2
assert_equal @maps.uniq.length, 2
assert_equal '2 maps spammed and 1 author banned.', flash[:notice]
assert @maps.all? { |map| map.reload.status == 0 }
assert @maps.all? { |map| map.user.status == 0 }
assert_redirected_to root_path
end
end

View File

@@ -24,7 +24,7 @@ cubbon:
created_at: <%= Time.now %> created_at: <%= Time.now %>
license: publicdomain license: publicdomain
user_id: 1 user_id: 1
author: aaron author: quentin
nairobi: nairobi:
id: 3 id: 3
@@ -49,8 +49,8 @@ village:
description: A mall in Nairobi description: A mall in Nairobi
created_at: <%= Time.now %> created_at: <%= Time.now %>
license: publicdomain license: publicdomain
user_id: 2 user_id: 3
author: aaron author: chris
yaya: yaya:
id: 5 id: 5

View File

@@ -9,5 +9,5 @@ nice:
featured: featured:
name: featured name: featured
created_at: <%= Time.now %> created_at: <%= Time.now %>
map_id: 2 map_id: 3
user_id: 1 user_id: 1

View File

@@ -0,0 +1,12 @@
require 'test_helper'
class RoutesTest < ActionDispatch::IntegrationTest
test "test single-spam-map route" do
assert_routing({ path: '/moderate/spam_map/1', method: :patch }, { controller: 'spam', action: 'spam_map', id: '1' })
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' })
end
end

View File

@@ -89,12 +89,19 @@ class MapTest < ActiveSupport::TestCase
end end
test 'filter bbox with tag if present' do test 'filter bbox with tag if present' do
maps = Map.bbox(10,60,30,80,'featured') maps = Map.bbox(-5,35,0,40,'featured')
assert maps.collect(&:name).include?('Cubbon Park') assert maps.collect(&:name).include?('Nairobi City')
end end
test 'bbox without tag returns results' do test 'bbox without tag returns results' do
maps = Map.bbox(40,-80,50,-60) maps = Map.bbox(40,-80,50,-60)
assert maps.collect(&:name).include?('Saugus Landfill Incinerator') assert maps.collect(&:name).include?('Saugus Landfill Incinerator')
end end
test 'should spam map' do
map = maps(:saugus)
assert_equal Map::Status::NORMAL, map.status
map.spam
assert_equal Map::Status::BANNED, map.status
end
end end

View File

@@ -52,6 +52,13 @@ class UserTest < ActiveSupport::TestCase
assert_equal map_images.flatten, user.warpables assert_equal map_images.flatten, user.warpables
end end
test 'should ban user' do
user = users(:quentin)
assert_equal User::Status::NORMAL, user.status
user.ban
assert_equal User::Status::BANNED, user.status
end
# def test_should_authenticate_user # def test_should_authenticate_user
# assert_equal users(:quentin), User.authenticate('quentin', 'monkey') # assert_equal users(:quentin), User.authenticate('quentin', 'monkey')
# end # end