Skip to content

Commit b9c1daf

Browse files
authored
Merge pull request #1959 from codidact/0valt/1250/pinned-links-time-filter
Allow pinned links (the tool) to be filtered by period
2 parents 8102177 + b770ad8 commit b9c1daf

6 files changed

Lines changed: 274 additions & 15 deletions

File tree

app/controllers/pinned_links_controller.rb

Lines changed: 25 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -4,21 +4,36 @@ class PinnedLinksController < ApplicationController
44
before_action :verify_mod_on_current_community, only: [:edit, :update]
55

66
def index
7-
links = if current_user.at_least_global_moderator? && params[:global] == '2'
8-
PinnedLink.unscoped
9-
elsif current_user.at_least_global_moderator? && params[:global] == '1'
10-
PinnedLink.where(community: nil)
11-
else
12-
PinnedLink.where(community: @community)
13-
end
7+
@period = params[:period].presence || 'current'
8+
9+
@links = if current_user.at_least_global_moderator? && params[:global] == '2'
10+
PinnedLink.unscoped
11+
elsif current_user.at_least_global_moderator? && params[:global] == '1'
12+
PinnedLink.where(community: nil)
13+
else
14+
PinnedLink.where(community: @community)
15+
end
16+
17+
@links = case @period
18+
when 'past'
19+
@links.past
20+
when 'current'
21+
@links.current
22+
when 'future'
23+
@links.future
24+
else
25+
@links
26+
end
27+
1428
@links = case params[:filter]
1529
when 'all'
16-
links.all
30+
@links.all
1731
when 'inactive'
18-
links.where(active: false).all
32+
@links.where(active: false).all
1933
else
20-
links.where(active: true).all
34+
@links.where(active: true).all
2135
end
36+
2237
render layout: 'without_sidebar'
2338
end
2439

app/models/pinned_link.rb

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,14 +7,66 @@ class PinnedLink < ApplicationRecord
77
includes(:post, post: [:community])
88
}
99

10+
scope :past, lambda {
11+
timed.where('shown_before < ?', DateTime.now)
12+
}
13+
14+
scope :current, lambda {
15+
where(shown_after: nil, shown_before: nil).or(
16+
timed.where('shown_after is null or shown_after <= ?', DateTime.now)
17+
.where.not('shown_before < ?', DateTime.now)
18+
)
19+
}
20+
21+
scope :future, lambda {
22+
timed.where('shown_after > ?', DateTime.now)
23+
.where('shown_before is null or shown_before >= ?', DateTime.now)
24+
}
25+
26+
scope :timed, lambda {
27+
after = where.not(shown_after: nil)
28+
before = where.not(shown_before: nil)
29+
before.or(after)
30+
}
31+
32+
validate :check_period
1033
validate :check_post_or_url
1134

35+
# Is the link not timed or started in the past & hasn't ended yet?
36+
# @param now [DateTime, nil] timestamp to compare to
37+
# @return [Boolean] check result
38+
def current?(now = DateTime.now)
39+
!timed? || !(future?(now) || past?(now))
40+
end
41+
42+
# Does the link start in the future?
43+
# @param now [DateTime, nil] timestamp to compare to
44+
# @return [Boolean] check result
45+
def future?(now = DateTime.now)
46+
shown_after.present? && shown_after > now && (shown_before.nil? || shown_before >= now)
47+
end
48+
49+
# Has the link ended in the past?
50+
# @param now [DateTime, nil] timestamp to compare to
51+
# @return [Boolean] check result
52+
def past?(now = DateTime.now)
53+
shown_before.present? && shown_before < now
54+
end
55+
1256
# Is the link time-constrained?
1357
# @return [Boolean] check result
1458
def timed?
1559
shown_before.present? || shown_after.present?
1660
end
1761

62+
def check_period
63+
return unless shown_before.present? && shown_after.present?
64+
65+
if shown_before < shown_after
66+
errors.add(:base, 'end date cannot be earlier than the start date')
67+
end
68+
end
69+
1870
def check_post_or_url
1971
unless post_id.present? || link.present?
2072
errors.add(:base, 'either a post or a URL must be set')

app/views/pinned_links/index.html.erb

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,22 @@
3535
class: "button is-muted is-outlined #{(params[:filter] == 'all') ? 'is-active' : ''}" %>
3636
</div>
3737
</div>
38+
<div class="grid--cell">
39+
<div class="button-list is-gutterless">
40+
<%= link_to 'past',
41+
query_url(period: 'past'),
42+
class: "button is-muted is-outlined #{'is-active' if @period == 'past'}" %>
43+
<%= link_to 'current',
44+
query_url(period: 'current'),
45+
class: "button is-muted is-outlined #{'is-active' if @period == 'current'}" %>
46+
<%= link_to 'future',
47+
query_url(period: 'future'),
48+
class: "button is-muted is-outlined #{'is-active' if @period == 'future'}" %>
49+
<%= link_to 'any',
50+
query_url(period: 'any'),
51+
class: "button is-muted is-outlined #{'is-active' if @period == 'any'}" %>
52+
</div>
53+
</div>
3854
<div class="grid--cell is-flexible">
3955
</div>
4056
<div class="grid--cell">
@@ -61,7 +77,7 @@
6177
<% pl_link = pl.post.nil? ? pl.link : ('Post #' + pl.post.id.to_s) %>
6278
<% pl_label = pl.post.nil? ? pl.label : (pl.post.parent.nil? ? pl.post.title : pl.post.parent.title) %>
6379
<tr>
64-
<td>
80+
<td class="nowrap">
6581
<% if pl.shown_before.nil? %>
6682
<% if pl.post.nil? %>
6783
link

test/controllers/pinned_links_controller_test.rb

Lines changed: 98 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,103 @@
33
class PinnedLinksControllerTest < ActionController::TestCase
44
include Devise::Test::ControllerHelpers
55

6+
test ':index should correctly filter by community' do
7+
sign_in users(:global_moderator)
8+
9+
get :index, params: { global: '1' }
10+
@links = assigns(:links)
11+
12+
assert_response(:success)
13+
assert @links.any?
14+
assert @links.none?(&:community_id?)
15+
16+
get :index, params: { global: '2' }
17+
@links = assigns(:links)
18+
19+
assert_response(:success)
20+
assert @links.any?
21+
22+
links_ids = pinned_links.map(&:id)
23+
24+
assert(@links.all? { |link| links_ids.include?(link.id) })
25+
end
26+
27+
test ':index should correctly filter by activity status' do
28+
sign_in users(:moderator)
29+
30+
get :index, params: { filter: 'all' }
31+
assert_response(:success)
32+
assert assigns(:links).any?
33+
34+
get :index, params: { filter: 'inactive' }
35+
@links = assigns(:links)
36+
37+
assert_response(:success)
38+
assert @links.any?
39+
assert @links.none?(&:active?)
40+
end
41+
42+
test ':index should correctly filter by period' do
43+
sign_in users(:moderator)
44+
45+
now = DateTime.now
46+
47+
get :index
48+
@links = assigns(:links)
49+
assert_response(:success)
50+
assert @links.any?
51+
52+
links_ids = pinned_links.map(&:id)
53+
assert(@links.all? { |link| links_ids.include?(link.id) })
54+
55+
get :index, params: { period: 'current' }
56+
@links = assigns(:links)
57+
58+
assert_response(:success)
59+
assert @links.any?
60+
@links.each do |link|
61+
assert link.current?(now)
62+
end
63+
64+
get :index, params: { period: 'past' }
65+
@links = assigns(:links)
66+
67+
assert_response(:success)
68+
assert @links.any?
69+
@links.each do |link|
70+
assert link.past?(now)
71+
end
72+
73+
get :index, params: { period: 'future' }
74+
@links = assigns(:links)
75+
76+
assert_response(:success)
77+
assert @links.any?
78+
@links.each do |link|
79+
assert link.future?(now)
80+
end
81+
end
82+
83+
test ':index should treat invalid period filter as no filter' do
84+
sign_in users(:moderator)
85+
86+
get :index, params: { period: 'invalid' }
87+
@links = assigns(:links)
88+
assert_response(:success)
89+
90+
links_ids = pinned_links.map(&:id)
91+
assert(@links.all? { |link| links_ids.include?(link.id) })
92+
end
93+
94+
test 'only mods or higher should be able to see pinned links' do
95+
users.each do |user|
96+
sign_in user
97+
get :index
98+
99+
assert_response(user.at_least_moderator? ? :success : :not_found)
100+
end
101+
end
102+
6103
test 'only mods or higher should be able to create pinned links' do
7104
post = posts(:question_one)
8105

@@ -61,7 +158,7 @@ class PinnedLinksControllerTest < ActionController::TestCase
61158
assert_equal 'updated label', assigns(:link).label
62159
end
63160

64-
test 'update should correctly handle invlid pinned links' do
161+
test 'update should correctly handle invalid pinned links' do
65162
sign_in users(:moderator)
66163
try_update_pinned_link(pinned_links(:active_with_label), link: nil)
67164
assert_response(:bad_request)

test/fixtures/pinned_links.yml

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,3 +14,32 @@ inactive:
1414
label: test link
1515
link: https://example.com
1616
active: false
17+
18+
timed_past:
19+
community: sample
20+
label: link in the past
21+
link: https://example.com
22+
active: true
23+
shown_before: 2019-01-01T00:00:00.000000Z
24+
25+
timed_present:
26+
community: sample
27+
label: link in the present
28+
link: https://example.com
29+
active: true
30+
shown_after: 2019-01-01T00:00:00.000000Z
31+
shown_before: <%= DateTime.now + 1 %>
32+
33+
timed_future:
34+
community: sample
35+
label: link in the future
36+
link: https://example.com
37+
active: true
38+
shown_after: <%= DateTime.now + 1 %>
39+
shown_before: <%= DateTime.now + 1 %>
40+
41+
global_active_with_label:
42+
community: ~
43+
label: test global link
44+
link: https://example.com
45+
active: true

test/models/pinned_link_test.rb

Lines changed: 53 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,57 @@
11
require 'test_helper'
22

33
class PinnedLinkTest < ActiveSupport::TestCase
4-
# test "the truth" do
5-
# assert true
6-
# end
4+
test ':timed scope should only return time-constrained links' do
5+
timed = PinnedLink.timed
6+
7+
assert timed.any?
8+
timed.each do |link|
9+
assert link.timed?, "link \"#{link.label}\" is not timed"
10+
end
11+
end
12+
13+
test ':current scope should return non-timed or current links' do
14+
now = DateTime.now
15+
current = PinnedLink.current
16+
17+
assert current.any?
18+
current.each do |link|
19+
assert link.current?(now)
20+
end
21+
end
22+
23+
test ':past scope should only return past links' do
24+
now = DateTime.now
25+
past = PinnedLink.past
26+
27+
assert past.any?
28+
past.each do |link|
29+
assert link.past?(now)
30+
end
31+
end
32+
33+
test ':future scope should only return future links' do
34+
now = DateTime.now
35+
future = PinnedLink.future
36+
37+
assert future.any?
38+
future.each do |link|
39+
assert link.future?(now)
40+
end
41+
end
42+
43+
test 'pinned links should be correctly validated' do
44+
valid_with_link = PinnedLink.new(link: 'https://example.com')
45+
valid_with_post = PinnedLink.new(post: posts(:question_one))
46+
period_mismatch = PinnedLink.new(shown_before: DateTime.now - 1, shown_after: DateTime.now)
47+
48+
[
49+
[valid_with_link, true],
50+
[valid_with_post, true],
51+
[period_mismatch, false]
52+
].each do |test|
53+
link, status = test
54+
assert_equal status, link.valid?
55+
end
56+
end
757
end

0 commit comments

Comments
 (0)