Skip to content

Commit

Permalink
feat(gsoc'24): report as spam and moderation review page (#25)
Browse files Browse the repository at this point in the history
  • Loading branch information
Waishnav authored Aug 18, 2024
1 parent 734f8a4 commit 470e204
Show file tree
Hide file tree
Showing 26 changed files with 453 additions and 72 deletions.
6 changes: 2 additions & 4 deletions Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -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"
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
```

Expand Down
6 changes: 6 additions & 0 deletions app/controllers/simple_discussion/application_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
29 changes: 27 additions & 2 deletions app/controllers/simple_discussion/forum_posts_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand Down
6 changes: 6 additions & 0 deletions app/controllers/simple_discussion/forum_threads_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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
Expand Down
1 change: 1 addition & 0 deletions app/models/forum_post.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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 }
Expand Down
15 changes: 15 additions & 0 deletions app/models/spam_report.rb
Original file line number Diff line number Diff line change
@@ -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
51 changes: 51 additions & 0 deletions app/views/layouts/simple_discussion.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,14 @@
<%= t('.unanswered') %>
</div>
<% end %>
<% if is_moderator? %>
<%= forum_link_to simple_discussion.spam_reports_forum_threads_path do %>
<div class="btn-link">
<%= icon "fa-fw fa", "exclamation-triangle" %>
<%= t('.spam_posts') %>
</div>
<% end %>
<% end %>
</div>
<% if @forum_thread.present? && @forum_thread.persisted? %>
<div class="mt-3">
Expand All @@ -67,3 +75,46 @@
</div>

<% parent_layout("application") %>

<script type="module">
import { Application, Controller } from "https://unpkg.com/@hotwired/stimulus/dist/stimulus.js"
window.Stimulus = Application.start()

Stimulus.register("dropdown", class extends Controller {
static targets = ["dropdownButton", "dropdownMenu"]

connect() {
this.dropdownButtonTarget.addEventListener("click", this.toggleDropdown.bind(this))
// if click somewhere else in the document, close the dropdown
window.addEventListener("click", (event) => {
if (!this.dropdownButtonTarget.contains(event.target)) {
this.dropdownMenuTarget.classList.remove("show")
}
})
}

// note that we are using bootstrap
toggleDropdown() {
this.dropdownMenuTarget.classList.toggle("show")
}
})


Stimulus.register("report-post", class extends Controller {
static targets = ["reportPostButton"]

connect() {
const reportPostForm = document.getElementById("reportPostForm")
const postId = this.element.dataset.postId
this.reportPostButtonTarget.addEventListener("click", () => {
const formActionArray = reportPostForm.action.split("/")
console.log(formActionArray)
if (formActionArray[formActionArray.length - 2] === "threads") {
reportPostForm.action += `/posts/${postId}/report_post`
} else {
reportPostForm.action = reportPostForm.action.replace(/\/\d+\//, `/${postId}/`)
}
})
}
})
</script>
76 changes: 37 additions & 39 deletions app/views/simple_discussion/forum_posts/_forum_post.html.erb
Original file line number Diff line number Diff line change
@@ -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 %>
<div class="forum-post-header">
<% if is_moderator_or_owner?(forum_post) %>
<div class="float-right">
<%= link_to icon("fas","edit"), simple_discussion.edit_forum_thread_forum_post_path(@forum_thread, forum_post),
class: "text-muted",
data: { toggle: "tooltip", placement: "left" },
title: t('edit_this_post') %> &nbsp;
<%= link_to icon("fas","trash"), simple_discussion.forum_thread_forum_post_path(@forum_thread, forum_post),
class: "text-muted",
method: :delete,
data: { toggle: "tooltip", placement: "left", confirm: "Are you sure you want to delete this post?" },
title: t('delete_this_post') %>
</div>
<% end %>
<%= content_tag :div, id: forum_post.solved ? 'solution' : dom_id(forum_post), class: "forum-post" do %>
<div class="forum-post-header">
<div class="float-right d-flex align-items-center">
<% unless forum_post == @forum_thread.forum_posts.first %>
<% if is_moderator_or_owner?(@forum_thread) %>
<% if @forum_thread.solved? && forum_post.solved? %>
<%= link_to t('unmark_as_solution'), simple_discussion.unsolved_forum_thread_forum_post_path(@forum_thread, forum_post), method: :put, class: "badge badge-info p-2 mr-2" %>
<% else %>
<%= link_to t('mark_as_solution'), simple_discussion.solved_forum_thread_forum_post_path(@forum_thread, forum_post), method: :put, class: "badge badge-info p-2 mr-2" %>
<% end %>
<% end %>
<% end %>
<% if forum_post.solved? && forum_post != @forum_thread.forum_posts.first && !is_moderator_or_owner?(@forum_thread) %>
<span class="badge badge-success p-2 mr-2"><%= t('solution') %></span>
<% end %>
<% if user_signed_in? %>
<div class="dropdown" data-controller="dropdown">
<button class="btn btn-light bg-white" role="button" data-dropdown-target="dropdownButton">
<%= icon("fas","ellipsis-v") %>
</button>
<div class="dropdown-menu dropdown-menu-right" data-dropdown-target="dropdownMenu">
<% if is_moderator_or_owner?(forum_post) %>
<%= link_to t('edit_post'), simple_discussion.edit_forum_thread_forum_post_path(@forum_thread, forum_post),
class: "dropdown-item",
data: { toggle: "tooltip", placement: "left" },
title: t('edit_this_post') %>
<%= link_to t('delete_post'), simple_discussion.forum_thread_forum_post_path(@forum_thread, forum_post),
class: "dropdown-item",
method: :delete,
data: { toggle: "tooltip", placement: "left", confirm: "Are you sure you want to delete this post?" },
title: t('delete_this_post') %>
<% end %>
<%= link_to t('report_post'), "#", class: "dropdown-item", data: { controller: "report-post", report_post_target: "reportPostButton", toggle: "modal", target: "#reportPostModal", post_id: forum_post.id } %>
</div>
</div>
<% end %>
</div>
<div class="user-details d-flex flex-row">
<div class="user-avatar mr-2" ><img src="<%= gravatar_url_for(forum_post.user.email, size: 30) %>" alt="avatar of user" ></div>
<div class="details d-flex flex-column justify-content-between">
Expand All @@ -29,26 +49,4 @@
<div class="card-body p-3">
<%= formatted_content forum_post.body %>
</div>

<% if @forum_thread.solved? && forum_post.solved? %>
<div class="card-footer border-0 bg-transparent pt-0 mb-0">
<div class="pull-right">
<strong class="text-success"><%= icon("fas","check") %> <%= t('solved') %></strong>
<% if is_moderator_or_owner?(@forum_thread) %>
<%= link_to t('undo'), simple_discussion.unsolved_forum_thread_forum_post_path(@forum_thread, forum_post), class: "btn btn-sm btn-secondary text-white mx-2", method: :put %>
<% end %>
</div>
</div>

<% elsif is_moderator_or_owner?(@forum_thread) %>
<div class="card-footer border-0 bg-transparent pt-0 mb-0" style="background-color: #e1fff2; color: #4DDB9B; font-size: 1.05rem; font-weight: 500">
<div class="pull-right">
<%= link_to simple_discussion.solved_forum_thread_forum_post_path(@forum_thread, forum_post), method: :put do %>
<%= icon("fas", "check") %>&nbsp;&nbsp;
<%= t('mark_as_solved') %>
<% end %>
</div>
</div>
<% end %>

<% end %>
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
<div class="modal fade" id="reportPostModal" tabindex="-1" role="dialog" aria-labelledby="reportSpamModalLabel" aria-hidden="true">
<div class="modal-dialog modal-dialog-centered" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="reportSpamModalLabel">Report Post as Spam</h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
</div>
<div class="modal-body">
<%= form_with id:"reportPostForm", url: simple_discussion.forum_thread_path(forum_thread), method: :post, local: true do |f| %>
<% if @spam_report && @spam_report.errors.any? %>
<div class="alert alert-danger" role="alert">
<% @spam_report.errors.full_messages.each do |message| %>
<li><%= message %></li>
<% end %>
</div>
<% end %>
<div class="form-group">
<%= f.collection_radio_buttons :reason, SpamReport.reasons.keys, :to_s, :humanize, include_hidden: false do |b| %>
<div class="form-check">
<%= b.radio_button(class: "form-check-input toggle-reason", required: true) %>
<%= b.label(class: "form-check-label") %>
</div>
<% end %>
</div>
<div class="form-group" id="details-group" style="display: none;">
<%= f.label :details, "Reason in detail (if others)" %>
<%= f.text_area :details, class: "form-control", rows: 2, id: "details-input" %>
<div class="invalid-feedback">Please provide details for 'Other' reason.</div>
</div>
<%= f.submit "Report", class: "btn btn-danger" %>
<% end %>
</div>
</div>
</div>
</div>

<script>
$(document).ready(function() {
const $detailsGroup = $('#details-group');
const $detailsInput = $('#details-input');
const $form = $('#reportPostForm');

$('.toggle-reason').change(function() {
if ($(this).val() === 'others') {
$detailsGroup.show();
$detailsInput.attr('required', true);
} else {
$detailsGroup.hide();
$detailsInput.removeAttr('required');
}
});

$form.on('submit', function(e) {
if ($('input[name="reason"]:checked').val() === 'others' && $detailsInput.val().trim() === '') {
e.preventDefault();
$detailsInput.addClass('is-invalid');
} else {
$detailsInput.removeClass('is-invalid');
}
});

$detailsInput.on('input', function() {
$(this).removeClass('is-invalid');
});
});
</script>
57 changes: 57 additions & 0 deletions app/views/simple_discussion/forum_posts/_spam_report.html.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
<%= content_tag :tr, id: dom_id(spam_report), class: "forum-post" do %>
<td>
<div class="border rounded-lg">
<div class="forum-post-header">
<div class="user-details d-flex flex-row">
<div class="user-avatar mr-2" ><img src="<%= gravatar_url_for(spam_report.forum_post.user.email, size: 30) %>" alt="avatar of user" ></div>
<div class="details d-flex flex-column justify-content-between">
<p class="title">
<%= spam_report.forum_post.user.name %> <%= forum_user_badge(spam_report.forum_post.user) %>
</p>
<p class="subtitle"><%= t('on') %> &nbsp;<%= spam_report.forum_post.created_at.strftime("%b %d, %Y") %></p>
</div>
</div>
</div>

<div class="card-body p-3">
<%= formatted_content spam_report.forum_post.body %>
</div>
</div>
</td>

<td>
<div class="d-flex flex-row justify-content-between align-items-center p-2">
<% if spam_report.reason == "others" %>
Other:<%= spam_report.details %>
<% else %>
<%= spam_report.reason.humanize %>
<% end %>
</div>
</td>

<td>
<div class="d-flex flex-row justify-content-between align-items-center p-2">
<%= link_to spam_report.user.name, user_path(spam_report.user), class: "btn btn-outline-primary", title: t('user_profile') %>
</div>
</td>
<td>
<div class="d-flex flex-row justify-content-between align-items-center p-2">
<%= link_to simple_discussion.forum_thread_path(spam_report.forum_post.forum_thread, anchor: "forum_post_#{spam_report.forum_post.id}"),
class: "btn btn-dark",
data: { toggle: "tooltip", placement: "left" } do %>
<svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-square-arrow-out-up-right"><path d="M21 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h6"/><path d="m21 3-9 9"/><path d="M15 3h6v6"/></svg>
<% end %>
</div>
</td>
<td>
<div class="d-flex flex-row justify-content-between align-items-center p-2">
<%= link_to simple_discussion.forum_thread_forum_post_path(spam_report.forum_post.forum_thread, spam_report.forum_post, from: "moderators_page"),
method: :delete,
data: { toggle: "tooltip", placement: "left", confirm: "Are you sure you want to delete this post?" },
title: t('delete_this_post'),
class: "btn btn-danger" do %>
<i class="fas fa-trash-alt"></i>
<% end %>
</div>
</td>
<% end %>
Loading

0 comments on commit 470e204

Please sign in to comment.