From 470e204b3b40cfb156a4979d9ef0cf491d5ca51a Mon Sep 17 00:00:00 2001
From: Vaishnav Deore <86405648+Waishnav@users.noreply.github.com>
Date: Sun, 18 Aug 2024 21:29:52 +0530
Subject: [PATCH] feat(gsoc'24): report as spam and moderation review page
(#25)
---
Gemfile | 6 +-
README.md | 4 +
.../application_controller.rb | 6 ++
.../forum_posts_controller.rb | 29 ++++++-
.../forum_threads_controller.rb | 6 ++
app/models/forum_post.rb | 1 +
app/models/spam_report.rb | 15 ++++
app/views/layouts/simple_discussion.html.erb | 51 +++++++++++++
.../forum_posts/_forum_post.html.erb | 76 +++++++++----------
.../_report_post_modal_form.html.erb | 68 +++++++++++++++++
.../forum_posts/_spam_report.html.erb | 57 ++++++++++++++
.../forum_posts/edit.html.erb | 4 +-
.../forum_threads/_form.html.erb | 1 -
.../forum_threads/show.html.erb | 26 +++++--
.../forum_threads/spam_reports.html.erb | 16 ++++
config/locales/en.yml | 3 +
config/locales/es.yml | 3 +
config/locales/fr.yml | 3 +
config/routes.rb | 2 +
.../20240624124709_create_spam_reports.rb | 12 +++
simple_discussion.gemspec | 1 +
test/dummy/app/models/user.rb | 4 +
.../20240813072347_add_moderator_to_users.rb | 5 ++
test/dummy/db/schema.rb | 50 ++++++++----
test/fixtures/users.yml | 5 ++
test/integration/forum_test.rb | 71 ++++++++++++++++-
26 files changed, 453 insertions(+), 72 deletions(-)
create mode 100644 app/models/spam_report.rb
create mode 100644 app/views/simple_discussion/forum_posts/_report_post_modal_form.html.erb
create mode 100644 app/views/simple_discussion/forum_posts/_spam_report.html.erb
create mode 100644 app/views/simple_discussion/forum_threads/spam_reports.html.erb
create mode 100644 db/migrate/20240624124709_create_spam_reports.rb
create mode 100644 test/dummy/db/migrate/20240813072347_add_moderator_to_users.rb
diff --git a/Gemfile b/Gemfile
index ae8dea2..5797428 100644
--- a/Gemfile
+++ b/Gemfile
@@ -8,12 +8,10 @@ gemspec
# used for dummy rails app integration
gem "devise"
gem "puma"
-gem "sprockets-rails"
-
-# testing against sqlite3 db
-gem "sqlite3", "~> 1.7"
# testing
gem "appraisal"
gem "standardrb"
gem "font-awesome-sass", "~> 5.13.1"
+gem "sqlite3"
+gem "sprockets-rails"
diff --git a/README.md b/README.md
index 0433edc..e07c58b 100644
--- a/README.md
+++ b/README.md
@@ -132,6 +132,10 @@ By default, SimpleDiscussion will attempt to send email and slack notifications
SimpleDiscussion.setup do |config|
config.send_email_notifications = false # Default: true
config.send_slack_notifications = false # Default: true
+
+ config.markdown_circuit_embed = false # Default: false
+ config.markdown_user_tagging = false # Default: false
+ config.markdown_video_embed = false # Default false
end
```
diff --git a/app/controllers/simple_discussion/application_controller.rb b/app/controllers/simple_discussion/application_controller.rb
index e464f26..4d1a8cc 100644
--- a/app/controllers/simple_discussion/application_controller.rb
+++ b/app/controllers/simple_discussion/application_controller.rb
@@ -34,6 +34,12 @@ def require_mod_or_author_for_thread!
end
end
+ def require_mod!
+ unless is_moderator?
+ redirect_to_root
+ end
+ end
+
private
def redirect_to_root
diff --git a/app/controllers/simple_discussion/forum_posts_controller.rb b/app/controllers/simple_discussion/forum_posts_controller.rb
index 23e59e9..5cc6103 100644
--- a/app/controllers/simple_discussion/forum_posts_controller.rb
+++ b/app/controllers/simple_discussion/forum_posts_controller.rb
@@ -29,8 +29,22 @@ def update
end
def destroy
- @forum_post.destroy!
- redirect_to simple_discussion.forum_thread_path(@forum_thread)
+ # if @forum_post is first post of forum_thread then we need to destroy forum_thread
+ if @forum_thread.forum_posts.first == @forum_post
+ @forum_thread.destroy!
+ if params[:from] == "moderators_page"
+ redirect_to simple_discussion.spam_reports_forum_threads_path
+ else
+ redirect_to simple_discussion.root_path
+ end
+ else
+ @forum_post.destroy!
+ if params[:from] == "moderators_page"
+ redirect_to simple_discussion.spam_reports_forum_threads_path
+ else
+ redirect_to simple_discussion.forum_thread_path(@forum_thread)
+ end
+ end
end
def solved
@@ -52,6 +66,17 @@ def unsolved
redirect_to simple_discussion.forum_thread_path(@forum_thread, anchor: ActionView::RecordIdentifier.dom_id(@forum_post))
end
+ def report_post
+ @forum_post = @forum_thread.forum_posts.find(params[:id])
+ @spam_report = SpamReport.new(forum_post: @forum_post, user: current_user, reason: params[:reason], details: params[:details])
+
+ if @spam_report.save
+ redirect_to simple_discussion.forum_thread_path(@forum_thread, anchor: ActionView::RecordIdentifier.dom_id(@forum_post))
+ else
+ render template: "simple_discussion/forum_threads/show"
+ end
+ end
+
private
def set_forum_thread
diff --git a/app/controllers/simple_discussion/forum_threads_controller.rb b/app/controllers/simple_discussion/forum_threads_controller.rb
index 570597a..048879c 100644
--- a/app/controllers/simple_discussion/forum_threads_controller.rb
+++ b/app/controllers/simple_discussion/forum_threads_controller.rb
@@ -2,6 +2,7 @@ class SimpleDiscussion::ForumThreadsController < SimpleDiscussion::ApplicationCo
before_action :authenticate_user!, only: [:mine, :participating, :new, :create]
before_action :set_forum_thread, only: [:show, :edit, :update, :destroy]
before_action :require_mod_or_author_for_thread!, only: [:edit, :update, :destroy]
+ before_action :require_mod!, only: [:spam_reports]
def index
@forum_threads = ForumThread.pinned_first.sorted.includes(:user, :forum_category).paginate(page: page_number)
@@ -27,6 +28,11 @@ def participating
render action: :index
end
+ def spam_reports
+ @spam_reports = SpamReport.includes(:forum_post).paginate(page: page_number)
+ render action: :spam_reports
+ end
+
def show
@forum_post = ForumPost.new
@forum_post.user = current_user
diff --git a/app/models/forum_post.rb b/app/models/forum_post.rb
index dfd9202..80ccced 100644
--- a/app/models/forum_post.rb
+++ b/app/models/forum_post.rb
@@ -2,6 +2,7 @@
class ForumPost < ApplicationRecord
belongs_to :forum_thread, counter_cache: true, touch: true
belongs_to :user
+ has_many :spam_reports, dependent: :destroy
validates :user_id, :body, presence: true
validate :clean_body, if: -> { SimpleDiscussion.profanity_filter }
diff --git a/app/models/spam_report.rb b/app/models/spam_report.rb
new file mode 100644
index 0000000..bc9b113
--- /dev/null
+++ b/app/models/spam_report.rb
@@ -0,0 +1,15 @@
+class SpamReport < ApplicationRecord
+ belongs_to :forum_post
+ belongs_to :user
+
+ validates :forum_post_id, :user_id, :reason, presence: true
+ validates :details, presence: true, if: -> { reason == "others" }
+
+ enum reason: {
+ sexual_content: 0,
+ violent_content: 1,
+ irrelevant_content: 2,
+ misleading_content: 3,
+ others: 4
+ }
+end
diff --git a/app/views/layouts/simple_discussion.html.erb b/app/views/layouts/simple_discussion.html.erb
index 80f1e73..c4f1dd5 100644
--- a/app/views/layouts/simple_discussion.html.erb
+++ b/app/views/layouts/simple_discussion.html.erb
@@ -41,6 +41,14 @@
<%= t('.unanswered') %>
<% end %>
+ <% if is_moderator? %>
+ <%= forum_link_to simple_discussion.spam_reports_forum_threads_path do %>
+
+ <%= icon "fa-fw fa", "exclamation-triangle" %>
+ <%= t('.spam_posts') %>
+
+ <% end %>
+ <% end %>
<% if @forum_thread.present? && @forum_thread.persisted? %>
@@ -67,3 +75,46 @@
<% parent_layout("application") %>
+
+
diff --git a/app/views/simple_discussion/forum_posts/_forum_post.html.erb b/app/views/simple_discussion/forum_posts/_forum_post.html.erb
index 9f2964f..1e8f496 100644
--- a/app/views/simple_discussion/forum_posts/_forum_post.html.erb
+++ b/app/views/simple_discussion/forum_posts/_forum_post.html.erb
@@ -1,20 +1,40 @@
-<%# We don't currently cache the forum posts because they have permissions to deal with %>
-
-<%= content_tag :div, id: dom_id(forum_post), class: "forum-post" do %>
-
@@ -25,5 +38,6 @@
<%= render partial: "simple_discussion/forum_posts/forum_post", collection: @forum_thread.forum_posts.includes(:user).sorted %>
<%= render partial: "simple_discussion/forum_posts/form" if user_signed_in? %>
+<%= render partial: "simple_discussion/forum_posts/report_post_modal_form", locals: { forum_thread: @forum_thread } if user_signed_in? %>
<%= render partial: "login_bar" if !user_signed_in? %>
diff --git a/app/views/simple_discussion/forum_threads/spam_reports.html.erb b/app/views/simple_discussion/forum_threads/spam_reports.html.erb
new file mode 100644
index 0000000..e5ffbdd
--- /dev/null
+++ b/app/views/simple_discussion/forum_threads/spam_reports.html.erb
@@ -0,0 +1,16 @@
+
+
+
+ Forum Post |
+ Reason |
+ Reported by |
+ View in Thread |
+ Delete |
+
+
+
+ <%= render partial: "simple_discussion/forum_posts/spam_report", collection: @spam_reports%>
+
+
+
+ <%= will_paginate @spam_reports, url_builder: simple_discussion, renderer: SimpleDiscussion::BootstrapLinkRenderer %>
diff --git a/config/locales/en.yml b/config/locales/en.yml
index de519dd..a6943e4 100644
--- a/config/locales/en.yml
+++ b/config/locales/en.yml
@@ -57,3 +57,6 @@ en:
login: "Log in"
commented: "commented:"
inappropriate_language_error_message: "contains inappropriate language: %{words}"
+ unmark_as_solution: "Unmark as Solution"
+ mark_as_solution: "Mark as Solution"
+ report_post: "Report Post"
diff --git a/config/locales/es.yml b/config/locales/es.yml
index 851831c..804b5e7 100644
--- a/config/locales/es.yml
+++ b/config/locales/es.yml
@@ -56,3 +56,6 @@ es:
login: "Iniciar sesión"
commented: "comentó:"
inappropriate_language_error_message: "contiene lenguaje inapropiado: %{words}"
+ unmark_as_solution: "Desmarcar como solución"
+ mark_as_solution: "Marcar como solución"
+ report_post: "Reportar publicación"
diff --git a/config/locales/fr.yml b/config/locales/fr.yml
index c137054..31b362c 100644
--- a/config/locales/fr.yml
+++ b/config/locales/fr.yml
@@ -58,3 +58,6 @@ fr:
login: "Se connecter"
commented: "a commenté:"
inappropriate_language_error_message: "contient un langage inapproprié : %{words}"
+ unmark_as_solution: "Démarquer comme solution"
+ mark_as_solution: "Marquer comme solution"
+ report_post: "Signaler le post"
diff --git a/config/routes.rb b/config/routes.rb
index b490da3..2345640 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -6,6 +6,7 @@
get :unanswered
get :mine
get :participating
+ get :spam_reports
get "category/:id", to: "forum_categories#index", as: :forum_category
end
@@ -13,6 +14,7 @@
member do
put :solved
put :unsolved
+ post :report_post
end
end
diff --git a/db/migrate/20240624124709_create_spam_reports.rb b/db/migrate/20240624124709_create_spam_reports.rb
new file mode 100644
index 0000000..a744dfc
--- /dev/null
+++ b/db/migrate/20240624124709_create_spam_reports.rb
@@ -0,0 +1,12 @@
+class CreateSpamReports < ActiveRecord::Migration[7.0]
+ def change
+ create_table :spam_reports do |t|
+ t.references :forum_post, null: false, foreign_key: true
+ t.references :user, null: false, foreign_key: true # The user who reported the post
+ t.integer :reason, null: false # Enum for reason
+ t.text :details # optional column if the reason is 'other'
+
+ t.timestamps
+ end
+ end
+end
diff --git a/simple_discussion.gemspec b/simple_discussion.gemspec
index d7da08a..3bcc7c4 100644
--- a/simple_discussion.gemspec
+++ b/simple_discussion.gemspec
@@ -27,5 +27,6 @@ Gem::Specification.new do |spec|
spec.add_dependency "rails", ">= 4.2"
spec.add_dependency "will_paginate", ">= 3.1.0"
spec.add_dependency "language_filter", ">= 0.3.01"
+
spec.metadata["rubygems_mfa_required"] = "true"
end
diff --git a/test/dummy/app/models/user.rb b/test/dummy/app/models/user.rb
index 5286d1f..a420c59 100644
--- a/test/dummy/app/models/user.rb
+++ b/test/dummy/app/models/user.rb
@@ -5,4 +5,8 @@ class User < ApplicationRecord
# :confirmable, :lockable, :timeoutable, :trackable and :omniauthable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :validatable
+
+ def moderator?
+ moderator
+ end
end
diff --git a/test/dummy/db/migrate/20240813072347_add_moderator_to_users.rb b/test/dummy/db/migrate/20240813072347_add_moderator_to_users.rb
new file mode 100644
index 0000000..a3cfacd
--- /dev/null
+++ b/test/dummy/db/migrate/20240813072347_add_moderator_to_users.rb
@@ -0,0 +1,5 @@
+class AddModeratorToUsers < ActiveRecord::Migration[7.0]
+ def change
+ add_column :users, :moderator, :boolean, default: false
+ end
+end
diff --git a/test/dummy/db/schema.rb b/test/dummy/db/schema.rb
index 6eba6dd..9b5ad67 100644
--- a/test/dummy/db/schema.rb
+++ b/test/dummy/db/schema.rb
@@ -10,13 +10,22 @@
#
# It's strongly recommended that you check this file into your version control system.
-ActiveRecord::Schema.define(version: 2021_03_31_160746) do
+ActiveRecord::Schema.define(version: 2024_08_13_072347) do
create_table "forum_categories", force: :cascade do |t|
t.string "name", null: false
t.string "slug", null: false
t.string "color", default: "000000"
- t.datetime "created_at"
- t.datetime "updated_at"
+ t.datetime "created_at", precision: nil
+ t.datetime "updated_at", precision: nil
+ end
+
+ create_table "forum_leaderboards", force: :cascade do |t|
+ t.integer "user_id", null: false
+ t.integer "points", default: 0, null: false
+ t.datetime "created_at", precision: nil, null: false
+ t.datetime "updated_at", precision: nil, null: false
+ t.index ["points"], name: "index_forum_leaderboards_on_points"
+ t.index ["user_id"], name: "index_forum_leaderboards_on_user_id", unique: true
end
create_table "forum_posts", force: :cascade do |t|
@@ -24,16 +33,16 @@
t.integer "user_id"
t.text "body"
t.boolean "solved", default: false
- t.datetime "created_at"
- t.datetime "updated_at"
+ t.datetime "created_at", precision: nil
+ t.datetime "updated_at", precision: nil
end
create_table "forum_subscriptions", force: :cascade do |t|
t.integer "forum_thread_id"
t.integer "user_id"
t.string "subscription_type"
- t.datetime "created_at"
- t.datetime "updated_at"
+ t.datetime "created_at", precision: nil
+ t.datetime "updated_at", precision: nil
end
create_table "forum_threads", force: :cascade do |t|
@@ -44,27 +53,42 @@
t.integer "forum_posts_count", default: 0
t.boolean "pinned", default: false
t.boolean "solved", default: false
- t.datetime "created_at"
- t.datetime "updated_at"
+ t.datetime "created_at", precision: nil
+ t.datetime "updated_at", precision: nil
+ end
+
+ create_table "spam_reports", force: :cascade do |t|
+ t.integer "forum_post_id", null: false
+ t.integer "user_id", null: false
+ t.integer "reason", null: false
+ t.text "details"
+ t.datetime "created_at", precision: nil, null: false
+ t.datetime "updated_at", precision: nil, null: false
+ t.index ["forum_post_id"], name: "index_spam_reports_on_forum_post_id"
+ t.index ["user_id"], name: "index_spam_reports_on_user_id"
end
create_table "users", force: :cascade do |t|
t.string "email", default: "", null: false
t.string "encrypted_password", default: "", null: false
t.string "reset_password_token"
- t.datetime "reset_password_sent_at"
- t.datetime "remember_created_at"
- t.datetime "created_at", precision: 6, null: false
- t.datetime "updated_at", precision: 6, null: false
+ t.datetime "reset_password_sent_at", precision: nil
+ t.datetime "remember_created_at", precision: nil
+ t.datetime "created_at", precision: nil, null: false
+ t.datetime "updated_at", precision: nil, null: false
t.string "name"
+ t.boolean "moderator", default: false
t.index ["email"], name: "index_users_on_email", unique: true
t.index ["reset_password_token"], name: "index_users_on_reset_password_token", unique: true
end
+ add_foreign_key "forum_leaderboards", "users"
add_foreign_key "forum_posts", "forum_threads"
add_foreign_key "forum_posts", "users"
add_foreign_key "forum_subscriptions", "forum_threads"
add_foreign_key "forum_subscriptions", "users"
add_foreign_key "forum_threads", "forum_categories"
add_foreign_key "forum_threads", "users"
+ add_foreign_key "spam_reports", "forum_posts"
+ add_foreign_key "spam_reports", "users"
end
diff --git a/test/fixtures/users.yml b/test/fixtures/users.yml
index 297acdf..4323c34 100644
--- a/test/fixtures/users.yml
+++ b/test/fixtures/users.yml
@@ -5,3 +5,8 @@ one:
two:
name: "Second User"
email: "second@example.com"
+
+moderator:
+ name: "Moderator"
+ email: "moderator@moderator.com"
+ moderator: true
diff --git a/test/integration/forum_test.rb b/test/integration/forum_test.rb
index 882dabf..7ccf989 100644
--- a/test/integration/forum_test.rb
+++ b/test/integration/forum_test.rb
@@ -6,27 +6,34 @@ class ForumTest < ActionDispatch::IntegrationTest
include SimpleDiscussion::Engine.routes.url_helpers
setup do
- sign_in users(:one)
+ @regular_user = users(:one)
+ @moderator_user = users(:moderator)
+ @forum_thread = forum_threads(:hello)
+ @forum_post = forum_posts(:hello)
@filter = LanguageFilter::Filter.new
end
test "threads index" do
+ sign_in @regular_user
get "/"
assert_response :success
assert_match "Community", response.body
end
test "categories" do
+ sign_in @regular_user
get forum_category_forum_threads_path(forum_categories(:general))
assert_response :success
end
test "show forum thread" do
- get forum_thread_path(forum_threads(:hello))
+ sign_in @regular_user
+ get forum_thread_path(@forum_thread)
assert_response :success
end
test "create a forum thread" do
+ sign_in @regular_user
assert_difference "ForumThread.count" do
assert_difference "ForumPost.count" do
post forum_threads_path, params: {
@@ -45,8 +52,9 @@ class ForumTest < ActionDispatch::IntegrationTest
end
test "reply to a forum thread" do
+ sign_in @regular_user
assert_difference "ForumPost.count" do
- post forum_thread_forum_posts_path(forum_threads(:hello)), params: {
+ post forum_thread_forum_posts_path(@forum_thread), params: {
forum_post: {
body: "Reply"
}
@@ -57,6 +65,7 @@ class ForumTest < ActionDispatch::IntegrationTest
end
test "cannot create a forum thread with inappropriate language in title" do
+ sign_in @regular_user
inappropriate_word = @filter.matchlist.to_a.sample
assert_no_difference "ForumThread.count" do
assert_no_difference "ForumPost.count" do
@@ -77,6 +86,8 @@ class ForumTest < ActionDispatch::IntegrationTest
end
test "cannot create a forum thread with inappropriate language in body" do
+ sign_in @regular_user
+
inappropriate_word = @filter.matchlist.to_a.sample
assert_no_difference "ForumThread.count" do
assert_no_difference "ForumPost.count" do
@@ -97,9 +108,11 @@ class ForumTest < ActionDispatch::IntegrationTest
end
test "cannot reply to a forum thread with inappropriate language" do
+ sign_in @regular_user
+
inappropriate_word = @filter.matchlist.to_a.sample
assert_no_difference "ForumPost.count" do
- post forum_thread_forum_posts_path(forum_threads(:hello)), params: {
+ post forum_thread_forum_posts_path(@forum_thread), params: {
forum_post: {
body: "contains inappropriate language: #{inappropriate_word}"
}
@@ -111,6 +124,8 @@ class ForumTest < ActionDispatch::IntegrationTest
end
test "can create a forum thread with appropriate language in title and body" do
+ sign_in @regular_user
+
assert_difference "ForumThread.count" do
assert_difference "ForumPost.count" do
post forum_threads_path, params: {
@@ -127,4 +142,52 @@ class ForumTest < ActionDispatch::IntegrationTest
assert_redirected_to forum_thread_path(ForumThread.last)
end
+
+ test "can report a post" do
+ sign_in @regular_user
+
+ assert_difference "SpamReport.count" do
+ post report_post_forum_thread_forum_post_path(@forum_thread, @forum_post), params: {
+ reason: "irrelevant_content"
+ }
+ end
+ assert_redirected_to forum_thread_path(@forum_thread, anchor: dom_id(@forum_post))
+
+ spam_report = SpamReport.last
+ assert_equal @forum_post, spam_report.forum_post
+ assert_equal @regular_user, spam_report.user
+ assert_equal "irrelevant_content", spam_report.reason
+ end
+
+ test "can report a post with 'other' reason and details" do
+ sign_in @regular_user
+
+ assert_difference "SpamReport.count" do
+ post report_post_forum_thread_forum_post_path(@forum_thread, @forum_post), params: {
+ reason: "others",
+ details: "This post contains copyrighted material."
+ }
+ end
+
+ assert_redirected_to forum_thread_path(@forum_thread, anchor: dom_id(@forum_post))
+
+ spam_report = SpamReport.last
+ assert_equal "others", spam_report.reason
+ assert_equal "This post contains copyrighted material.", spam_report.details
+ end
+
+ test "modeartor can view spam reports page" do
+ sign_in @moderator_user
+
+ get spam_reports_forum_threads_path
+ assert_response :success
+ end
+
+ test "regular user can't view spam reports page" do
+ sign_in @regular_user
+
+ get spam_reports_forum_threads_path
+ assert_response :redirect
+ assert_redirected_to root_path
+ end
end