Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
41 commits
Select commit Hold shift + click to select a range
734e908
added the profanity, hate, violance, sex filter using `language_filte…
Waishnav Jun 17, 2024
08b32d0
fix: moderator can delete thread
Waishnav Jun 23, 2024
1d002a5
added js for dropdown, and for markdown editor in future
Waishnav Jun 23, 2024
4d48d4d
ui: dropdown for delete, edit, mark as solution actions
Waishnav Jun 23, 2024
24b22b1
feat: user can now report the forum post as spam
Waishnav Jun 25, 2024
155f8d4
feat: moderator can review and delete reported posts
Waishnav Jun 25, 2024
0391c97
fix standardrb formatting issue
Waishnav Jul 25, 2024
187c67f
fix: renaming report_spam button to report_post
Waishnav Jul 27, 2024
59f4315
fix: remove js bundeling
Waishnav Jul 28, 2024
b926dd4
fix: ci fail due to old schema in test dummy
Waishnav Jul 28, 2024
cb339c5
fix: ci fail due to migration version
Waishnav Jul 28, 2024
ecf53bb
fix: ci fail due to moderator? undefined
Waishnav Jul 28, 2024
a8ed0cb
removing stimulus controller
Waishnav Jul 28, 2024
1a85df0
added the profanity, hate, violance, sex filter using `language_filte…
Waishnav Jun 17, 2024
d32eb92
added js for dropdown, and for markdown editor in future
Waishnav Jun 23, 2024
b27d1e9
feat: user can now report the forum post as spam
Waishnav Jun 25, 2024
2f669a9
feat: adding markdown editor using simplemde
Waishnav Jul 7, 2024
94dba46
fix: simplemde initialization for forum thread form
Waishnav Jul 22, 2024
b306e8f
added js for dropdown, and for markdown editor in future
Waishnav Jun 23, 2024
0d0dab9
feat: user can now report the forum post as spam
Waishnav Jun 25, 2024
982b823
feat: adding markdown editor using simplemde
Waishnav Jul 7, 2024
66cf83f
feat: embedding circuit in forum posts
Waishnav Jul 21, 2024
ffed01d
feat: server and client side md parsing with feature flags
Waishnav Jul 22, 2024
5434a1b
add: build files should be pushed
Waishnav Jul 22, 2024
42d55c6
fix: empty tag dropdown for no error from stimulus controller
Waishnav Jul 22, 2024
e2ce1df
fix: modal missing
Waishnav Jul 22, 2024
d9c8170
removing build files
Waishnav Jul 28, 2024
0118bf7
fix: test fail standardrb
Waishnav Jul 28, 2024
ff24eb5
fix: redcarpet missing issue ci fail
Waishnav Jul 28, 2024
3658dd5
Merge branch 'master' into markdown-syntax-extensions
Waishnav Aug 14, 2024
324e97e
fix: ci fail due to user_path helper method doesn't exist
Waishnav Aug 14, 2024
6881aa5
fix: typo
Waishnav Aug 14, 2024
c9014cc
feat: leaderboard page with point distribution
Waishnav Aug 9, 2024
b30898a
fix: responsiveness of leaderboard page
Waishnav Aug 9, 2024
f88d112
fix: ci fail standardrb fix
Waishnav Aug 14, 2024
87266fd
added updated schema
Waishnav Aug 14, 2024
db4cbba
fix: migration version
Waishnav Aug 14, 2024
e42391c
fix ci
Waishnav Aug 14, 2024
fd98ff8
add: new tests coverage
Waishnav Aug 15, 2024
495b9ef
add: test for forum thread addition and deletion
Waishnav Aug 18, 2024
d100113
removed puts statement
Waishnav Aug 18, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
43 changes: 43 additions & 0 deletions app/assets/stylesheets/simple_discussion.scss
Original file line number Diff line number Diff line change
Expand Up @@ -248,8 +248,13 @@
border: 1px solid #80808029;

.card-body {
overflow-x: auto;
margin-top: 16px;
}
.card-body iframe {
border-radius: $post-body-border-radius;
border: 2px solid #80808029;
}
}

// Formatting the listtile for user details
Expand Down Expand Up @@ -296,3 +301,41 @@
.thread-page-container {
padding: 24px;
}

.preview::before {
content: "Preview";
width: 80px;
}

p {
font-size: 18px;
}

blockquote {
border-left: 5px solid #e9ecef;
padding: 10px 20px;
margin: 10px 0;
font-size: 18px;
font-weight: 500;
color: #6c757d;
}

.CodeMirror-line span {
font-size: 18px;
}

.simple_discussion .leaderboard .user-avatar {
border-radius: 50%;
width: 56px;
height: 56px;
overflow: hidden;
img {
width: 100%;
height: 100%;
object-fit: cover;
}
}

.simple_discussion .leaderboard {
margin-top: 24px;
}
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
101 changes: 86 additions & 15 deletions app/controllers/simple_discussion/forum_posts_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,26 @@ class SimpleDiscussion::ForumPostsController < SimpleDiscussion::ApplicationCont
before_action :require_mod_or_author_for_post!, only: [:edit, :update, :destroy]
before_action :require_mod_or_author_for_thread!, only: [:solved, :unsolved]

POINTS = {
create_post: 10, # on forum post creation
delete_post: -10, # on forum post deletion
marked_as_solution: 100, # if forum thread author/moderator marked the post as solved
unmarked_as_solution: -100, # undoing the marked as solution
delete_reported_post: -100 # if moderator deletes the post hence it is spam post
}

def create
@forum_post = @forum_thread.forum_posts.new(forum_post_params)
@forum_post.user_id = current_user.id

if @forum_post.save
SimpleDiscussion::ForumPostNotificationJob.perform_later(@forum_post)
redirect_to simple_discussion.forum_thread_path(@forum_thread, anchor: "forum_post_#{@forum_post.id}")
else
render template: "simple_discussion/forum_threads/show", status: :unprocessable_entity
ActiveRecord::Base.transaction do
if @forum_post.save
update_leaderboard(current_user, POINTS[:create_post])
SimpleDiscussion::ForumPostNotificationJob.perform_later(@forum_post)
redirect_to simple_discussion.forum_thread_path(@forum_thread, anchor: "forum_post_#{@forum_post.id}")
else
render template: "simple_discussion/forum_threads/show", status: :unprocessable_entity
end
end
end

Expand All @@ -29,29 +40,73 @@ 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
is_first_post = @forum_thread.forum_posts.first == @forum_post

ActiveRecord::Base.transaction do
if is_first_post
@forum_thread.destroy!
else
@forum_post.destroy!
end

# leaderboard points distribution
if is_moderator? && (@forum_post.user != current_user)
update_leaderboard(@forum_post.user, POINTS[:delete_reported_post])
# further we can distribute points if needed to the user who reported the post

# spam_report = SpamReport.find_by(forum_post: @forum_post)
# update_leaderboard(spam_report.user, POINTS[:report_spam]) if spam_report
else
update_leaderboard(@forum_post.user, POINTS[:delete_post])
end
end

redirect_to redirect_after_delete_path(is_first_post)
end

def solved
@forum_post = @forum_thread.forum_posts.find(params[:id])
ActiveRecord::Base.transaction do
@forum_post = @forum_thread.forum_posts.find(params[:id])

# update the previously solved post's author's leaderboard points
previously_solved_posts = @forum_thread.forum_posts.where(solved: true)
previously_solved_posts.each do |post|
update_user_leaderboard(post.user, POINTS[:unmarked_as_solution])
end

@forum_thread.forum_posts.update_all(solved: false)
@forum_post.update_column(:solved, true)
@forum_thread.update_column(:solved, true)
# update the current post's author leaderboard points
update_leaderboard(@forum_post.user, POINTS[:marked_as_solution])
@forum_thread.forum_posts.update_all(solved: false)
@forum_post.update_column(:solved, true)
@forum_thread.update_column(:solved, true)
end

redirect_to simple_discussion.forum_thread_path(@forum_thread, anchor: ActionView::RecordIdentifier.dom_id(@forum_post))
end

def unsolved
@forum_post = @forum_thread.forum_posts.find(params[:id])

@forum_thread.forum_posts.update_all(solved: false)
@forum_thread.update_column(:solved, false)
ActiveRecord::Base.transaction do
@forum_post = @forum_thread.forum_posts.find(params[:id])
update_leaderboard(@forum_post.user, POINTS[:unmarked_as_solution])
@forum_thread.forum_posts.update_all(solved: false)
@forum_thread.update_column(:solved, false)
end

redirect_to simple_discussion.forum_thread_path(@forum_thread, anchor: ActionView::RecordIdentifier.dom_id(@forum_post))
end

def report_spam
@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 All @@ -69,4 +124,20 @@ def set_forum_post
def forum_post_params
params.require(:forum_post).permit(:body)
end

def update_leaderboard(user, points)
leaderboard = user.forum_leaderboard || user.build_forum_leaderboard
leaderboard.points += points
leaderboard.save!
end

def redirect_after_delete_path(is_first_post)
if params[:from] == "moderators_page"
simple_discussion.spam_reports_forum_threads_path
elsif is_first_post
simple_discussion.root_path
else
simple_discussion.forum_thread_path(@forum_thread)
end
end
end
45 changes: 39 additions & 6 deletions app/controllers/simple_discussion/forum_threads_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,13 @@ 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]

POINTS = {
create_thread: 20, # on forum thread creation
delete_thread: -20, # on forum thread deletion
delete_reported_thread_by_moderator: -100 # if moderator deletes the thread hence it is spam post
}

def index
@forum_threads = ForumThread.pinned_first.sorted.includes(:user, :forum_category).paginate(page: page_number)
Expand All @@ -27,6 +34,15 @@ 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 leaderboard
@ranked_users = ForumLeaderboard.order(points: :desc).paginate(page: page_number)
end

def show
@forum_post = ForumPost.new
@forum_post.user = current_user
Expand All @@ -41,11 +57,14 @@ def create
@forum_thread = current_user.forum_threads.new(forum_thread_params)
@forum_thread.forum_posts.each { |post| post.user_id = current_user.id }

if @forum_thread.save
SimpleDiscussion::ForumThreadNotificationJob.perform_later(@forum_thread)
redirect_to simple_discussion.forum_thread_path(@forum_thread)
else
render action: :new, status: :unprocessable_entity
ActiveRecord::Base.transaction do
if @forum_thread.save
update_leaderboard(current_user, POINTS[:create_thread])
SimpleDiscussion::ForumThreadNotificationJob.perform_later(@forum_thread)
redirect_to simple_discussion.forum_thread_path(@forum_thread)
else
render action: :new, status: :unprocessable_entity
end
end
end

Expand All @@ -61,7 +80,15 @@ def update
end

def destroy
@forum_thread.destroy!
ActiveRecord::Base.transaction do
@forum_thread.destroy!
if is_moderator? && (@forum_thread.user != current_user)
update_leaderboard(@forum_thread.user, POINTS[:delete_reported_thread_by_moderator])
else
update_leaderboard(@forum_thread.user, POINTS[:delete_thread])
end
end

redirect_to simple_discussion.forum_threads_path
end

Expand All @@ -74,4 +101,10 @@ def set_forum_thread
def forum_thread_params
params.require(:forum_thread).permit(:title, :forum_category_id, forum_posts_attributes: [:body])
end

def update_leaderboard(user, points)
leaderboard = user.forum_leaderboard || user.build_forum_leaderboard
leaderboard.points += points
leaderboard.save!
end
end
58 changes: 57 additions & 1 deletion app/helpers/simple_discussion/forum_posts_helper.rb
Original file line number Diff line number Diff line change
@@ -1,3 +1,46 @@
require "redcarpet"
class CustomRenderer < Redcarpet::Render::HTML
def initialize(circuit_embed: false, video_embed: false, user_tagging: false)
@circuit_embed = circuit_embed
@video_embed = video_embed
@user_tagging = user_tagging
super()
end

def image(url, title, alt_text)
case alt_text
when "Circuit"
if @circuit_embed
"<iframe width=\"540\" height=\"300\" src=\"#{url}\" frameborder=\"0\"></iframe><br>"
else
"<img src=\"#{url}\" alt=\"#{alt_text}\" title=\"#{title}\"><br>"
end
when "Video"
if @video_embed
video_id = url.split("v=")[1].split("&")[0]
"<iframe width=\"540\" height=\"300\" src=\"https://www.youtube.com/embed/#{video_id}\" frameborder=\"0\" allowfullscreen></iframe><br>"
else
"<img src=\"#{url}\" alt=\"#{alt_text}\" title=\"#{title}\"><br>"
end
else
# default image rendering
"<img src=\"#{url}\" alt=\"#{alt_text}\" title=\"#{title}\"><br>"
end
end

def link(link, _title, content)
if @user_tagging && link.start_with?("/users/")
uri = URI.parse(link)
uri.path =~ %r{^/users/\d+/?$}
# remove the brackets from the content
content = content.gsub(/[()]/, "")
"<a class='tag-user' target='_blank' href=\"#{link}\">#{content}</a>"
else
"<a href=\"#{link}\">#{content}</a>"
end
end
end

module SimpleDiscussion::ForumPostsHelper
def category_link(category)
link_to category.name, simple_discussion.forum_category_forum_threads_path(category),
Expand All @@ -6,7 +49,20 @@ def category_link(category)

# Override this method to provide your own content formatting like Markdown
def formatted_content(text)
simple_format(text)
options = {
hard_wrap: true,
filter_html: true,
autolink: true,
tables: true
}

renderer = CustomRenderer.new(
circuit_embed: SimpleDiscussion.markdown_circuit_embed,
video_embed: SimpleDiscussion.markdown_video_embed,
user_tagging: SimpleDiscussion.markdown_user_tagging
)
markdown = Redcarpet::Markdown.new(renderer, options)
markdown.render(text).html_safe
end

def forum_post_classes(forum_post)
Expand Down
3 changes: 3 additions & 0 deletions app/models/forum_leaderboard.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
class ForumLeaderboard < ApplicationRecord
belongs_to :user
end
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
Loading