mirror of
https://github.com/publiclab/mapknitter.git
synced 2025-12-05 16:00:00 +01:00
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:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -51,3 +51,4 @@ yarn.lock
|
||||
.idea/
|
||||
passenger.3000.pid
|
||||
passenger.3000.pid.lock
|
||||
.vscode
|
||||
|
||||
@@ -46,15 +46,20 @@ class ApplicationController < ActionController::Base
|
||||
end
|
||||
|
||||
def logged_in?
|
||||
user_id = session[:user_id]
|
||||
|
||||
begin
|
||||
user_id && User.find(user_id) ? true : false
|
||||
current_user ? true : false
|
||||
rescue StandardError
|
||||
false
|
||||
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)
|
||||
return unless params[:tags].present?
|
||||
|
||||
|
||||
46
app/controllers/spam_controller.rb
Normal file
46
app/controllers/spam_controller.rb
Normal 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
|
||||
@@ -3,6 +3,14 @@ class Map < ApplicationRecord
|
||||
extend FriendlyId
|
||||
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
|
||||
|
||||
validates_presence_of :name, :slug, :author, :lat, :lon
|
||||
@@ -287,4 +295,8 @@ class Map < ApplicationRecord
|
||||
end
|
||||
User.where(id: user_ids.flatten.uniq).where.not(id: user_id)
|
||||
end
|
||||
|
||||
def spam
|
||||
self.update!(status: Status::BANNED)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,6 +1,14 @@
|
||||
require 'digest/sha1'
|
||||
|
||||
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 :tags
|
||||
has_many :comments
|
||||
@@ -55,4 +63,8 @@ class User < ApplicationRecord
|
||||
def can_edit?(resource)
|
||||
owns?(resource)
|
||||
end
|
||||
|
||||
def ban
|
||||
self.update!(status: Status::BANNED)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -121,7 +121,11 @@ Mapknitter::Application.routes.draw do
|
||||
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'
|
||||
|
||||
get '/warps/:map/:file(.:format)', to: redirect('https://archive.publiclab.org/warps/%{map}/%{file}.%{format}')
|
||||
end
|
||||
|
||||
@@ -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
|
||||
@@ -10,7 +10,7 @@
|
||||
#
|
||||
# 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|
|
||||
t.integer "map_id"
|
||||
@@ -77,7 +77,7 @@ ActiveRecord::Schema.define(version: 2022_06_08_115906) do
|
||||
t.boolean "anon_annotatable", default: false
|
||||
t.string "slug"
|
||||
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 ["slug"], name: "index_maps_on_slug", unique: true
|
||||
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.string "remember_token", limit: 40
|
||||
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
|
||||
end
|
||||
|
||||
|
||||
155
test/controllers/spam_controller_test.rb
Normal file
155
test/controllers/spam_controller_test.rb
Normal 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
|
||||
6
test/fixtures/maps.yml
vendored
6
test/fixtures/maps.yml
vendored
@@ -24,7 +24,7 @@ cubbon:
|
||||
created_at: <%= Time.now %>
|
||||
license: publicdomain
|
||||
user_id: 1
|
||||
author: aaron
|
||||
author: quentin
|
||||
|
||||
nairobi:
|
||||
id: 3
|
||||
@@ -49,8 +49,8 @@ village:
|
||||
description: A mall in Nairobi
|
||||
created_at: <%= Time.now %>
|
||||
license: publicdomain
|
||||
user_id: 2
|
||||
author: aaron
|
||||
user_id: 3
|
||||
author: chris
|
||||
|
||||
yaya:
|
||||
id: 5
|
||||
|
||||
2
test/fixtures/tags.yml
vendored
2
test/fixtures/tags.yml
vendored
@@ -9,5 +9,5 @@ nice:
|
||||
featured:
|
||||
name: featured
|
||||
created_at: <%= Time.now %>
|
||||
map_id: 2
|
||||
map_id: 3
|
||||
user_id: 1
|
||||
|
||||
12
test/integration/routes_test.rb
Normal file
12
test/integration/routes_test.rb
Normal 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
|
||||
@@ -89,12 +89,19 @@ class MapTest < ActiveSupport::TestCase
|
||||
end
|
||||
|
||||
test 'filter bbox with tag if present' do
|
||||
maps = Map.bbox(10,60,30,80,'featured')
|
||||
assert maps.collect(&:name).include?('Cubbon Park')
|
||||
maps = Map.bbox(-5,35,0,40,'featured')
|
||||
assert maps.collect(&:name).include?('Nairobi City')
|
||||
end
|
||||
|
||||
test 'bbox without tag returns results' do
|
||||
maps = Map.bbox(40,-80,50,-60)
|
||||
assert maps.collect(&:name).include?('Saugus Landfill Incinerator')
|
||||
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
|
||||
|
||||
@@ -52,6 +52,13 @@ class UserTest < ActiveSupport::TestCase
|
||||
assert_equal map_images.flatten, user.warpables
|
||||
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
|
||||
# assert_equal users(:quentin), User.authenticate('quentin', 'monkey')
|
||||
# end
|
||||
|
||||
Reference in New Issue
Block a user