Skip to content

Commit e808463

Browse files
authored
Add user blocking, reporting, and block-filter scope (#946)
## Summary - add `PersonBlock` and associations for managing block lists - add reporting model/controller and routes to submit user reports - add `BlockFilterable` concern and apply to posts to hide blocked authors ## Testing - `bundle exec bin/codex_style_guard` - `bundle exec rubocop` - `bundle exec brakeman -q -w2` - `bundle exec bundler-audit --update` - `bin/ci` *(0 examples)* - `bundle exec rspec` *(fails: Selenium manager cannot download Chrome)* ------ https://chatgpt.com/codex/tasks/task_e_68927abc1a64832180846fca30b88617
2 parents d090194 + 07699fe commit e808463

File tree

20 files changed

+335
-0
lines changed

20 files changed

+335
-0
lines changed
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
# frozen_string_literal: true
2+
3+
module BetterTogether
4+
class PersonBlocksController < ApplicationController # rubocop:todo Style/Documentation
5+
before_action :set_person_block, only: :destroy
6+
after_action :verify_authorized
7+
8+
def index
9+
authorize PersonBlock
10+
@blocked_people = current_person.blocked_people
11+
end
12+
13+
def create
14+
@person_block = current_person.person_blocks.new(person_block_params)
15+
authorize @person_block
16+
17+
if @person_block.save
18+
redirect_to blocks_path, notice: 'Person was successfully blocked.'
19+
else
20+
redirect_to blocks_path, alert: @person_block.errors.full_messages.to_sentence
21+
end
22+
end
23+
24+
def destroy
25+
authorize @person_block
26+
@person_block.destroy
27+
redirect_to blocks_path, notice: 'Person was successfully unblocked.'
28+
end
29+
30+
private
31+
32+
def current_person
33+
current_user.person
34+
end
35+
36+
def set_person_block
37+
@person_block = current_person.person_blocks.find(params[:id])
38+
end
39+
40+
def person_block_params
41+
params.require(:person_block).permit(:blocked_id)
42+
end
43+
end
44+
end
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
# frozen_string_literal: true
2+
3+
module BetterTogether
4+
class ReportsController < ApplicationController # rubocop:todo Style/Documentation
5+
after_action :verify_authorized
6+
7+
def create
8+
@report = current_person.reports_made.new(report_params)
9+
authorize @report
10+
11+
if @report.save
12+
redirect_back fallback_location: root_path, notice: 'Report was successfully submitted.'
13+
else
14+
redirect_back fallback_location: root_path, alert: @report.errors.full_messages.to_sentence
15+
end
16+
end
17+
18+
private
19+
20+
def current_person
21+
current_user.person
22+
end
23+
24+
def report_params
25+
params.require(:report).permit(:reportable_id, :reportable_type, :reason)
26+
end
27+
end
28+
end

app/models/better_together/person.rb

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,14 @@ def self.primary_community_delegation_attrs
2727
has_many :conversations, through: :conversation_participants
2828
has_many :created_conversations, as: :creator, class_name: 'BetterTogether::Conversation', dependent: :destroy
2929

30+
has_many :person_blocks, foreign_key: :blocker_id, dependent: :destroy, class_name: 'BetterTogether::PersonBlock'
31+
has_many :blocked_people, through: :person_blocks, source: :blocked
32+
has_many :blocked_by_person_blocks, foreign_key: :blocked_id, dependent: :destroy, class_name: 'BetterTogether::PersonBlock'
33+
has_many :blockers, through: :blocked_by_person_blocks, source: :blocker
34+
35+
has_many :reports_made, foreign_key: :reporter_id, class_name: 'BetterTogether::Report', dependent: :destroy
36+
has_many :reports_received, as: :reportable, class_name: 'BetterTogether::Report', dependent: :destroy
37+
3038
has_many :notifications, as: :recipient, dependent: :destroy, class_name: 'Noticed::Notification'
3139
has_many :notification_mentions, as: :record, dependent: :destroy, class_name: 'Noticed::Event'
3240

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
# frozen_string_literal: true
2+
3+
module BetterTogether
4+
# Join model tracking which people block other people
5+
class PersonBlock < ApplicationRecord
6+
belongs_to :blocker, class_name: 'BetterTogether::Person'
7+
belongs_to :blocked, class_name: 'BetterTogether::Person'
8+
9+
validates :blocked_id, uniqueness: { scope: :blocker_id }
10+
validate :not_self
11+
validate :blocked_not_platform_manager
12+
13+
private
14+
15+
def not_self
16+
errors.add(:blocked_id, 'cannot block yourself') if blocker_id == blocked_id
17+
end
18+
19+
def blocked_not_platform_manager
20+
return unless blocked&.permitted_to?('manage_platform')
21+
22+
errors.add(:blocked, 'cannot be a platform manager')
23+
end
24+
end
25+
end

app/models/better_together/post.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ module BetterTogether
44
# Represents a blog post
55
class Post < ApplicationRecord
66
include Authorable
7+
include BlockFilterable
78
include FriendlySlug
89
include Categorizable
910
include Creatable
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
# frozen_string_literal: true
2+
3+
module BetterTogether
4+
# Record of a person reporting inappropriate content or users
5+
class Report < ApplicationRecord
6+
belongs_to :reporter, class_name: 'BetterTogether::Person'
7+
belongs_to :reportable, polymorphic: true
8+
9+
validates :reason, presence: true
10+
end
11+
end
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
# frozen_string_literal: true
2+
3+
module BetterTogether
4+
# Adds a scope to filter out records authored by people blocked by the given person
5+
module BlockFilterable
6+
extend ActiveSupport::Concern
7+
8+
included do
9+
scope :excluding_blocked_for, lambda { |person|
10+
next all unless person
11+
12+
blocked_ids = person.blocked_people.select(:id)
13+
joins(:authorships).where.not(better_together_authorships: { author_id: blocked_ids }).distinct
14+
}
15+
end
16+
end
17+
end
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
# frozen_string_literal: true
2+
3+
module BetterTogether
4+
class PersonBlockPolicy < ApplicationPolicy # rubocop:todo Style/Documentation
5+
def index?
6+
user.present?
7+
end
8+
9+
def create?
10+
user.present? && record.blocker == agent && !record.blocked.permitted_to?('manage_platform')
11+
end
12+
13+
def destroy?
14+
user.present? && record.blocker == agent
15+
end
16+
17+
class Scope < Scope # rubocop:todo Style/Documentation
18+
def resolve
19+
scope.where(blocker: agent)
20+
end
21+
end
22+
end
23+
end
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
# frozen_string_literal: true
2+
3+
module BetterTogether
4+
class ReportPolicy < ApplicationPolicy # rubocop:todo Style/Documentation
5+
def create?
6+
user.present? && record.reporter == agent && record.reporter != record.reportable
7+
end
8+
end
9+
end
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
<h1>Blocked People</h1>
2+
<ul>
3+
<% @blocked_people.each do |person| %>
4+
<li id="<%= dom_id(person) %>"><%= person.name %></li>
5+
<% end %>
6+
</ul>

0 commit comments

Comments
 (0)