Skip to content
Open
Show file tree
Hide file tree
Changes from 8 commits
Commits
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
19 changes: 8 additions & 11 deletions app/controllers/related_works_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,19 +6,16 @@ class RelatedWorksController < ApplicationController

def index
@page_subtitle = t(".page_title", login: @user.login)
@translations_of_user = @user.related_works.posted.where(translation: true)
@remixes_of_user = @user.related_works.posted.where(translation: false)
@translations_by_user = @user.parent_work_relationships.posted.where(translation: true)
@remixes_by_user = @user.parent_work_relationships.posted.where(translation: false)

return if @user == current_user
related_works = @user.related_works.visible_on_user_page(@user).visible_works
parent_work_relationships = @user.parent_work_relationships.visible_on_user_page(@user)
local_parent_work_relationships = parent_work_relationships.of_visible_local_works
external_parent_work_relationships = parent_work_relationships.of_visible_external_works

# Extra constraints on what we display if someone else is viewing @user's
# related works page:
@translations_of_user = @translations_of_user.merge(Work.revealed.non_anon).where(reciprocal: true)
@remixes_of_user = @remixes_of_user.merge(Work.revealed.non_anon).where(reciprocal: true)
@translations_by_user = @translations_by_user.merge(Work.revealed.non_anon).where(reciprocal: true)
@remixes_by_user = @remixes_by_user.merge(Work.revealed.non_anon).where(reciprocal: true)
@translations_of_user = related_works.translations
@remixes_of_user = related_works.remixes
@translations_by_user = (local_parent_work_relationships.translations + external_parent_work_relationships.translations).sort
@remixes_by_user = (local_parent_work_relationships.remixes + external_parent_work_relationships.remixes).sort
end

# GET /related_works/1
Expand Down
10 changes: 10 additions & 0 deletions app/helpers/related_works_helper.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
module RelatedWorksHelper
def related_works_count(user)
related_works = user.related_works.visible_on_user_page(user).visible_works
parent_work_relationships = user.parent_work_relationships.visible_on_user_page(user)
local_parent_work_relationships = parent_work_relationships.of_visible_local_works
external_parent_work_relationships = parent_work_relationships.of_visible_external_works

return related_works.count + local_parent_work_relationships.count + external_parent_work_relationships.count

Check warning on line 8 in app/helpers/related_works_helper.rb

View workflow job for this annotation

GitHub Actions / Rubocop

[rubocop] reported by reviewdog 🐶 Redundant `return` detected. Raw Output: app/helpers/related_works_helper.rb:8:5: C: Style/RedundantReturn: Redundant `return` detected.
end
end
65 changes: 62 additions & 3 deletions app/models/related_work.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,68 @@
attribute :author, :string
attribute :language_id, :integer

scope :posted, -> {
joins("INNER JOIN `works` `child_works` ON `child_works`.`id` = `related_works`.`work_id`").
where("child_works.posted = 1")
scope :translations, -> { where(translation: true) }
scope :remixes, -> { where(translation: false) }
scope :reciprocal, -> { where(reciprocal: true) }

scope :posted, lambda {
joins("INNER JOIN works child_works ON child_works.id = related_works.work_id")
.where("child_works.posted = 1")
}

def self.visible_on_user_page(user)
if User.current_user.is_a?(Admin) || user == User.current_user

Check warning on line 20 in app/models/related_work.rb

View workflow job for this annotation

GitHub Actions / Rubocop

[rubocop] reported by reviewdog 🐶 Convert `if-elsif` to `case-when`. Raw Output: app/models/related_work.rb:20:5: C: Style/CaseLikeIf: Convert `if-elsif` to `case-when`.
posted.merge(Work.unhidden)
elsif User.current_user.is_a?(User)
posted.reciprocal.merge(Work.revealed.non_anon.unhidden)
else
posted.reciprocal.merge(Work.revealed.non_anon.unhidden.unrestricted)
end
end

scope :unhidden, lambda {
joins("INNER JOIN works child_works ON child_works.id = related_works.work_id")
.where("child_works.hidden_by_admin = false")
}

scope :unrestricted, lambda {
joins("INNER JOIN works child_works ON child_works.id = related_works.work_id")
.where("child_works.restricted = false")
}

scope :visible_works, -> {

Check warning on line 39 in app/models/related_work.rb

View workflow job for this annotation

GitHub Actions / Rubocop

[rubocop] reported by reviewdog 🐶 Use the `lambda` method for multiline lambdas. Raw Output: app/models/related_work.rb:39:25: C: Style/Lambda: Use the `lambda` method for multiline lambdas.
if User.current_user.present?
unhidden
else
unhidden.unrestricted
end
}

# Separate scopes for local and external parent works to make tests work
# (the join in of_unhidden_local_works gets both local and external works
# in web environments, but only local ones in automated tests)
Comment on lines +48 to +50
Copy link
Copy Markdown
Contributor Author

@slavalamp slavalamp May 15, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

any code style/structure improvement suggestions are extremely welcome in this pr, and if anyone knows why this happens and if tests could be made work without splitting regular and external works into different scopes, that'd be great


scope :of_local_works, -> { where(parent_type: Work) }
scope :of_external_works, -> { where(parent_type: ExternalWork) }

scope :of_unhidden_local_works, lambda {
of_local_works
.joins("INNER JOIN works parent_works ON parent_works.id = related_works.parent_id")
.where("parent_works.hidden_by_admin = false")
}

scope :of_visible_local_works, lambda {
if User.current_user.present?
of_unhidden_local_works
else
of_unhidden_local_works.where("parent_works.restricted = false")
end
}

scope :of_visible_external_works, lambda {
of_external_works
.joins("INNER JOIN external_works parent_works ON parent_works.id = related_works.parent_id")
.where("parent_works.hidden_by_admin = false")
}

before_validation :set_parent, if: :new_record?
Expand Down
2 changes: 1 addition & 1 deletion app/views/users/_sidebar.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@
<li><%= span_if_current t(".switch.sign_ups", signup_number: @user.challenge_signups.count), user_signups_path(@user) %></li>
<li><%= span_if_current t(".switch.assignments", assignment_number: @user.assignments.unposted.undefaulted.count), user_assignments_path(@user) %></li>
<li><%= span_if_current t(".switch.claims", claim_number: @user.request_claims.unposted.count), user_claims_path(@user) %></li>
<li><%= span_if_current t(".switch.related_works", related_works_number: (@user.related_works.posted.count + @user.parent_work_relationships.count)), user_related_works_path(@user) %></li>
<li><%= span_if_current t(".switch.related_works", related_works_number: related_works_count(@user)), user_related_works_path(@user) %></li>
<% end %>
<li><%= gifts_link(@user) %></li>
</ul>
Expand Down
122 changes: 122 additions & 0 deletions features/works/work_related.feature
Original file line number Diff line number Diff line change
Expand Up @@ -349,6 +349,52 @@ Scenario: Restricted works listed as Inspiration show up [Restricted] for guests
And I view the work "Followup"
Then I should see "Inspired by [Restricted Work] by inspiration"

Scenario: Restricted inspired and inspiring works should not be listed on related work pages for guests
Given I have related works setup
And a related work has been posted and approved
When I am logged in as "remixer"
And I lock the work "Followup"
When I am logged out
And I go to inspiration's related works page
Then I should not see "Followup"
And I should not see "Worldbuilding"
When I go to remixer's related works page
Then I should not see "Followup"
And I should not see "Worldbuilding"

When I am logged in as "remixer"
And I unlock the work "Followup"
When I am logged in as "inspiration"
And I lock the work "Worldbuilding"
When I am logged out
And I go to inspiration's related works page
Then I should not see "Followup"
And I should not see "Worldbuilding"
When I go to remixer's related works page
Then I should not see "Followup"
And I should not see "Worldbuilding"

Scenario: Restricted inspired and inspiring works should not be listed on related work pages for guests
Given I have related works setup
And a translation has been posted and approved
When I am logged in as "translator"
And I lock the work "Worldbuilding Translated"
When I am logged out
And I go to inspiration's related works page
Then I should not see "Worldbuilding"
When I go to translator's related works page
Then I should not see "Worldbuilding"

When I am logged in as "translator"
And I unlock the work "Worldbuilding Translated"
When I am logged in as "inspiration"
And I lock the work "Worldbuilding"
When I am logged out
And I go to inspiration's related works page
Then I should not see "Worldbuilding"
When I go to translator's related works page
Then I should not see "Worldbuilding"

Scenario: Anonymous works listed as inspiration should have links to the authors,
but only for the authors themselves and admins
Given I have related works setup
Expand All @@ -373,6 +419,82 @@ Scenario: Anonymous works listed as inspiration should have links to the authors
Then I should see "Works inspired by this one: Followup by Anonymous"
And I should not see "remixer" within ".afterword .children"

Scenario: Hidden inspired and inspiring works should not be listed on related work pages
Given I have related works setup
And a related work has been posted and approved
When I am logged in as a "policy_and_abuse" admin
And I hide the work "Followup"
And I go to inspiration's related works page
Then I should not see "Followup"
When I go to remixer's related works page
Then I should not see "Followup"
When I am logged in as "remixer"
And I view my related works
Then I should see "Related Works (0)"
And I should not see "Followup"
When I go to inspiration's related works page
Then I should not see "Followup"
When I am logged in as "inspiration"
And I view my related works
Then I should see "Related Works (0)"
And I should not see "Followup"

When I am logged in as a "policy_and_abuse" admin
And I unhide the work "Followup"
And I hide the work "Worldbuilding"
And I go to inspiration's related works page
Then I should not see "Worldbuilding"
When I go to remixer's related works page
Then I should not see "Worldbuilding"
When I am logged in as "inspiration"
And I view my related works
Then I should see "Related Works (0)"
And I should not see "Worldbuilding"
When I go to remixer's related works page
Then I should not see "Worldbuilding"
When I am logged in as "remixer"
And I view my related works
Then I should see "Related Works (0)"
And I should not see "Worldbuilding"

Scenario: Hidden translations and translated works should not be listed on related work pages
Given I have related works setup
And a translation has been posted and approved
When I am logged in as a "policy_and_abuse" admin
And I hide the work "Worldbuilding Translated"
And I go to inspiration's related works page
Then I should not see "Worldbuilding Translated"
When I go to translator's related works page
Then I should not see "Worldbuilding Translated"
When I am logged in as "translator"
And I view my related works
Then I should see "Related Works (0)"
And I should not see "Worldbuilding Translated"
When I go to inspiration's related works page
Then I should not see "Worldbuilding Translated"
When I am logged in as "inspiration"
And I view my related works
Then I should see "Related Works (0)"
And I should not see "Worldbuilding Translated"

When I am logged in as a "policy_and_abuse" admin
And I unhide the work "Worldbuilding Translated"
And I hide the work "Worldbuilding"
And I go to inspiration's related works page
Then I should not see "Worldbuilding"
When I go to translator's related works page
Then I should not see "Worldbuilding"
When I am logged in as "inspiration"
And I view my related works
Then I should see "Related Works (0)"
And I should not see "Worldbuilding"
When I go to translator's related works page
Then I should not see "Worldbuilding"
When I am logged in as "translator"
And I view my related works
Then I should see "Related Works (0)"
And I should not see "Worldbuilding"

Scenario: When a user is notified that a co-authored work has been inspired by a work they posted,
the e-mail should link to each author's URL instead of showing escaped HTML
Given I have related works setup
Expand Down
Loading