thredded 0.5.1 → 0.6.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.mkdn +21 -0
- data/README.mkdn +17 -1
- data/app/assets/stylesheets/thredded/_thredded.scss +1 -0
- data/app/assets/stylesheets/thredded/base/_grid.scss +6 -0
- data/app/assets/stylesheets/thredded/base/_nav.scss +51 -0
- data/app/assets/stylesheets/thredded/base/_variables.scss +1 -0
- data/app/assets/stylesheets/thredded/components/_base.scss +4 -0
- data/app/assets/stylesheets/thredded/components/_post.scss +19 -6
- data/app/assets/stylesheets/thredded/components/_topics.scss +6 -0
- data/app/assets/stylesheets/thredded/layout/_moderation.scss +53 -8
- data/app/assets/stylesheets/thredded/layout/_navigation.scss +1 -1
- data/app/assets/stylesheets/thredded/layout/_user-navigation.scss +7 -47
- data/app/assets/stylesheets/thredded/layout/_user.scss +10 -0
- data/app/commands/thredded/moderate_post.rb +1 -1
- data/app/controllers/thredded/messageboards_controller.rb +1 -1
- data/app/controllers/thredded/moderation_controller.rb +39 -5
- data/app/helpers/thredded/application_helper.rb +4 -2
- data/app/models/concerns/thredded/content_moderation_state.rb +15 -8
- data/app/models/concerns/thredded/post_common.rb +1 -0
- data/app/models/concerns/thredded/topic_common.rb +0 -1
- data/app/models/thredded/messageboard.rb +4 -4
- data/app/models/thredded/post.rb +2 -0
- data/app/models/thredded/post_moderation_record.rb +3 -1
- data/app/models/thredded/topic.rb +17 -0
- data/app/models/thredded/user_extender.rb +7 -0
- data/app/policies/thredded/post_policy.rb +2 -0
- data/app/policies/thredded/topic_policy.rb +6 -6
- data/app/view_models/thredded/post_view.rb +19 -1
- data/app/view_models/thredded/posts_page_view.rb +1 -0
- data/app/view_models/thredded/topic_view.rb +5 -1
- data/app/views/thredded/messageboards/_messageboard.html.erb +3 -3
- data/app/views/thredded/moderation/_nav.html.erb +16 -0
- data/app/views/thredded/moderation/_post.html.erb +9 -2
- data/app/views/thredded/moderation/_post_moderation_record.html.erb +22 -19
- data/app/views/thredded/moderation/_user_moderation_state.html.erb +3 -0
- data/app/views/thredded/moderation/_user_post.html.erb +7 -0
- data/app/views/thredded/moderation/_users_search_form.html.erb +10 -0
- data/app/views/thredded/moderation/history.html.erb +2 -8
- data/app/views/thredded/moderation/pending.html.erb +3 -10
- data/app/views/thredded/moderation/user.html.erb +42 -0
- data/app/views/thredded/moderation/users.html.erb +38 -0
- data/app/views/thredded/posts/_post.html.erb +4 -0
- data/app/views/thredded/posts_common/_header.html.erb +1 -0
- data/app/views/thredded/posts_common/_header_with_topic.html.erb +15 -0
- data/app/views/thredded/posts_common/_header_with_user_and_topic.html.erb +18 -0
- data/app/views/thredded/search/_form.html.erb +3 -1
- data/app/views/thredded/shared/_content_moderation_blocked_state.html.erb +8 -0
- data/app/views/thredded/shared/_nav.html.erb +8 -4
- data/app/views/thredded/topics/_topic.html.erb +6 -0
- data/app/views/thredded/users/_post.html.erb +6 -0
- data/app/views/thredded/users/_posts.html.erb +7 -0
- data/config/locales/en.yml +29 -3
- data/config/locales/pt-BR.yml +34 -9
- data/config/routes.rb +7 -2
- data/db/migrate/20160329231848_create_thredded.rb +22 -21
- data/db/upgrade_migrations/20160611094616_upgrade_v0_5_to_v0_6.rb +25 -0
- data/heroku.gemfile.lock +2 -2
- data/lib/thredded.rb +17 -5
- data/lib/thredded/version.rb +1 -1
- metadata +15 -2
@@ -18,12 +18,14 @@ module Thredded
|
|
18
18
|
end
|
19
19
|
|
20
20
|
# @param datetime [DateTime]
|
21
|
+
# @param default [String] a string to return if time is nil.
|
21
22
|
# @return [String] html_safe datetime presentation
|
22
|
-
def time_ago(datetime)
|
23
|
+
def time_ago(datetime, default: '-')
|
23
24
|
timeago_tag datetime,
|
24
25
|
lang: I18n.locale.to_s.downcase,
|
25
26
|
format: -> (t, _opts) { t.year == Time.current.year ? :short : :long },
|
26
|
-
nojs: true
|
27
|
+
nojs: true,
|
28
|
+
default: default
|
27
29
|
end
|
28
30
|
|
29
31
|
def paginate(collection, args = {})
|
@@ -14,19 +14,24 @@ module Thredded
|
|
14
14
|
# @type [Arel::Table]
|
15
15
|
table = arel_table
|
16
16
|
# @type [Arel::Nodes::Node]
|
17
|
-
|
17
|
+
visible =
|
18
18
|
if Thredded.content_visible_while_pending_moderation
|
19
|
+
# All non-blocked content
|
19
20
|
table[:moderation_state].not_eq(moderation_states[:blocked])
|
20
21
|
else
|
22
|
+
# Only approved content
|
21
23
|
table[:moderation_state].eq(moderation_states[:approved])
|
22
24
|
end
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
25
|
+
if user && !user.thredded_anonymous?
|
26
|
+
# Own content
|
27
|
+
visible = visible.or(table[:user_id].eq(user.id))
|
28
|
+
# Content that one can moderate
|
29
|
+
moderatable_messageboard_ids = user.thredded_can_moderate_messageboards.map(&:id)
|
30
|
+
if moderatable_messageboard_ids.present?
|
31
|
+
visible = visible.or(table[:messageboard_id].in(moderatable_messageboard_ids))
|
28
32
|
end
|
29
|
-
|
33
|
+
end
|
34
|
+
where(visible)
|
30
35
|
end)
|
31
36
|
end
|
32
37
|
|
@@ -41,7 +46,9 @@ module Thredded
|
|
41
46
|
|
42
47
|
# Whether this is visible to the given user based on the moderation state.
|
43
48
|
def moderation_state_visible_to_user?(user)
|
44
|
-
moderation_state_visible_to_all? ||
|
49
|
+
moderation_state_visible_to_all? ||
|
50
|
+
(!user.thredded_anonymous? &&
|
51
|
+
(user_id == user.id || user.thredded_can_moderate_messageboards.map(&:id).include?(messageboard_id)))
|
45
52
|
end
|
46
53
|
|
47
54
|
private
|
@@ -25,8 +25,8 @@ module Thredded
|
|
25
25
|
has_many :user_messageboard_preferences, dependent: :destroy
|
26
26
|
has_many :posts, dependent: :destroy
|
27
27
|
has_many :topics, dependent: :destroy, inverse_of: :messageboard
|
28
|
-
|
29
|
-
|
28
|
+
|
29
|
+
belongs_to :last_topic, class_name: 'Thredded::Topic'
|
30
30
|
|
31
31
|
has_many :user_details, through: :posts
|
32
32
|
has_many :messageboard_users,
|
@@ -54,8 +54,8 @@ module Thredded
|
|
54
54
|
scope :top_level_messageboards, -> { where(group: nil) }
|
55
55
|
scope :by_messageboard_group, ->(group) { where(group: group.id) }
|
56
56
|
|
57
|
-
def
|
58
|
-
|
57
|
+
def last_user
|
58
|
+
last_topic.try(:last_user)
|
59
59
|
end
|
60
60
|
|
61
61
|
def slug_candidates
|
data/app/models/thredded/post.rb
CHANGED
@@ -21,6 +21,8 @@ module Thredded
|
|
21
21
|
has_many :moderation_records,
|
22
22
|
class_name: 'Thredded::PostModerationRecord',
|
23
23
|
dependent: :nullify
|
24
|
+
has_one :last_moderation_record, -> { order_newest_first },
|
25
|
+
class_name: 'Thredded::PostModerationRecord'
|
24
26
|
|
25
27
|
validates :messageboard_id, presence: true
|
26
28
|
|
@@ -8,6 +8,8 @@ module Thredded
|
|
8
8
|
end
|
9
9
|
validates :previous_moderation_state, presence: true
|
10
10
|
|
11
|
+
scope :order_newest_first, -> { order(created_at: :desc, id: :desc) }
|
12
|
+
|
11
13
|
belongs_to :messageboard, inverse_of: :post_moderation_records
|
12
14
|
validates :messageboard_id, presence: true
|
13
15
|
belongs_to :post, inverse_of: :moderation_records
|
@@ -37,7 +39,7 @@ module Thredded
|
|
37
39
|
post: post,
|
38
40
|
post_content: post.content,
|
39
41
|
post_user: post.user,
|
40
|
-
post_user_name: post.user.send
|
42
|
+
post_user_name: post.user.try(:send, Thredded.user_name_column),
|
41
43
|
messageboard_id: post.messageboard_id,
|
42
44
|
)
|
43
45
|
end
|
@@ -63,6 +63,8 @@ module Thredded
|
|
63
63
|
source: :user,
|
64
64
|
through: :user_follows
|
65
65
|
|
66
|
+
after_commit :update_messageboard_last_topic, on: [:create, :destroy]
|
67
|
+
|
66
68
|
def self.find_by_slug!(slug)
|
67
69
|
friendly.find(slug)
|
68
70
|
rescue ActiveRecord::RecordNotFound
|
@@ -107,6 +109,11 @@ module Thredded
|
|
107
109
|
title_changed?
|
108
110
|
end
|
109
111
|
|
112
|
+
# @return [Thredded::PostModerationRecord, nil]
|
113
|
+
def last_moderation_record
|
114
|
+
first_post.try(:last_moderation_record)
|
115
|
+
end
|
116
|
+
|
110
117
|
private
|
111
118
|
|
112
119
|
def slug_candidates
|
@@ -115,5 +122,15 @@ module Thredded
|
|
115
122
|
[:title, '-topic'],
|
116
123
|
]
|
117
124
|
end
|
125
|
+
|
126
|
+
def update_messageboard_last_topic
|
127
|
+
return if messageboard.destroyed?
|
128
|
+
last_topic = if destroyed?
|
129
|
+
messageboard.topics.order_recently_updated_first.select(:id).first
|
130
|
+
else
|
131
|
+
self
|
132
|
+
end
|
133
|
+
messageboard.update!(last_topic_id: last_topic.try(:id))
|
134
|
+
end
|
118
135
|
end
|
119
136
|
end
|
@@ -41,6 +41,13 @@ module Thredded
|
|
41
41
|
opt.has_many :thredded_post_moderation_records, foreign_key: 'post_user_id', inverse_of: :post_user
|
42
42
|
opt.has_many :thredded_post_moderated_records, foreign_key: 'moderator_id', inverse_of: :moderator
|
43
43
|
end
|
44
|
+
|
45
|
+
scope :left_join_thredded_user_details, (lambda do
|
46
|
+
users = arel_table
|
47
|
+
user_details = Thredded::UserDetail.arel_table
|
48
|
+
joins(users.join(user_details, Arel::Nodes::OuterJoin)
|
49
|
+
.on(users[:id].eq(user_details[:user_id])).join_sources)
|
50
|
+
end)
|
44
51
|
end
|
45
52
|
|
46
53
|
def thredded_user_preference
|
@@ -21,17 +21,17 @@ module Thredded
|
|
21
21
|
# @param user [Thredded.user_class]
|
22
22
|
# @param topic [Thredded::Topic]
|
23
23
|
def initialize(user, topic)
|
24
|
-
@user
|
25
|
-
@topic
|
26
|
-
@
|
24
|
+
@user = user
|
25
|
+
@topic = topic
|
26
|
+
@messageboard_policy = MessageboardPolicy.new(user, topic.messageboard)
|
27
27
|
end
|
28
28
|
|
29
29
|
def create?
|
30
|
-
@
|
30
|
+
@messageboard_policy.post?
|
31
31
|
end
|
32
32
|
|
33
33
|
def read?
|
34
|
-
@
|
34
|
+
@messageboard_policy.read? && @topic.moderation_state_visible_to_user?(@user)
|
35
35
|
end
|
36
36
|
|
37
37
|
def update?
|
@@ -43,7 +43,7 @@ module Thredded
|
|
43
43
|
end
|
44
44
|
|
45
45
|
def moderate?
|
46
|
-
@
|
46
|
+
@messageboard_policy.moderate?
|
47
47
|
end
|
48
48
|
end
|
49
49
|
end
|
@@ -11,6 +11,7 @@ module Thredded
|
|
11
11
|
:pending_moderation?,
|
12
12
|
:approved?,
|
13
13
|
:blocked?,
|
14
|
+
:last_moderation_record,
|
14
15
|
to: :@post
|
15
16
|
|
16
17
|
# @param post [Thredded::PostCommon]
|
@@ -28,6 +29,10 @@ module Thredded
|
|
28
29
|
@can_destroy ||= @policy.destroy?
|
29
30
|
end
|
30
31
|
|
32
|
+
def can_moderate?
|
33
|
+
@can_moderate ||= @policy.moderate?
|
34
|
+
end
|
35
|
+
|
31
36
|
def edit_path
|
32
37
|
Thredded::UrlsHelper.edit_post_path(@post)
|
33
38
|
end
|
@@ -36,17 +41,30 @@ module Thredded
|
|
36
41
|
Thredded::UrlsHelper.delete_post_path(@post)
|
37
42
|
end
|
38
43
|
|
44
|
+
def permalink_path
|
45
|
+
Thredded::UrlsHelper.post_permalink_path(@post.id)
|
46
|
+
end
|
47
|
+
|
48
|
+
# rubocop:disable Metrics/CyclomaticComplexity
|
39
49
|
def cache_key
|
50
|
+
moderation_state = unless @post.private_topic_post?
|
51
|
+
if @post.pending_moderation? && !Thredded.content_visible_while_pending_moderation
|
52
|
+
'p'
|
53
|
+
elsif @post.blocked?
|
54
|
+
'-'
|
55
|
+
end
|
56
|
+
end
|
40
57
|
[
|
41
58
|
I18n.locale,
|
42
59
|
@post,
|
43
60
|
@post.user,
|
61
|
+
moderation_state || '+',
|
44
62
|
[
|
45
|
-
!@post.private_topic_post? && @post.pending_moderation? && !Thredded.content_visible_while_pending_moderation,
|
46
63
|
can_update?,
|
47
64
|
can_destroy?
|
48
65
|
].map { |p| p ? '+' : '-' } * ''
|
49
66
|
]
|
50
67
|
end
|
68
|
+
# rubocop:enable Metrics/CyclomaticComplexity
|
51
69
|
end
|
52
70
|
end
|
@@ -2,7 +2,7 @@
|
|
2
2
|
module Thredded
|
3
3
|
# A view model for Topic.
|
4
4
|
class TopicView < BaseTopicView
|
5
|
-
delegate :categories, :id,
|
5
|
+
delegate :categories, :id, :blocked?, :last_moderation_record,
|
6
6
|
to: :@topic
|
7
7
|
|
8
8
|
# @param topic [TopicCommon]
|
@@ -30,6 +30,10 @@ module Thredded
|
|
30
30
|
].compact
|
31
31
|
end
|
32
32
|
|
33
|
+
def can_moderate?
|
34
|
+
@policy.moderate?
|
35
|
+
end
|
36
|
+
|
33
37
|
def edit_path
|
34
38
|
Thredded::UrlsHelper.edit_messageboard_topic_path(@topic.messageboard, @topic)
|
35
39
|
end
|
@@ -11,11 +11,11 @@
|
|
11
11
|
|
12
12
|
<p class="thredded--messageboard--description"><%= messageboard.description %></p>
|
13
13
|
|
14
|
-
<% if messageboard.
|
14
|
+
<% if messageboard.last_topic %>
|
15
15
|
<p class="thredded--messageboard--byline">
|
16
16
|
<%= t 'thredded.messageboard.last_updated_by_html',
|
17
|
-
time_ago: time_ago(messageboard.
|
18
|
-
user: messageboard.
|
17
|
+
time_ago: time_ago(messageboard.last_topic.updated_at),
|
18
|
+
user: messageboard.last_user %>
|
19
19
|
</p>
|
20
20
|
<% end %>
|
21
21
|
<% end %>
|
@@ -0,0 +1,16 @@
|
|
1
|
+
<% content_for :thredded_main_navigation do %>
|
2
|
+
<nav class="thredded--moderation-navigation">
|
3
|
+
<ul class="thredded--moderation-navigation--items">
|
4
|
+
<li class="thredded--moderation-navigation--item thredded--moderation-navigation--pending">
|
5
|
+
<%= link_to t('thredded.nav.moderation_pending'), pending_moderation_path %>
|
6
|
+
</li>
|
7
|
+
<li class="thredded--moderation-navigation--item thredded--moderation-navigation--history">
|
8
|
+
<%= link_to t('thredded.nav.moderation_history'), moderation_history_path %>
|
9
|
+
</li>
|
10
|
+
<li class="thredded--moderation-navigation--item thredded--moderation-navigation--users">
|
11
|
+
<%= link_to t('thredded.nav.moderation_users'), users_moderation_path %>
|
12
|
+
</li>
|
13
|
+
</ul>
|
14
|
+
<%= render 'users_search_form' %>
|
15
|
+
</nav>
|
16
|
+
<% end %>
|
@@ -1,6 +1,13 @@
|
|
1
1
|
<%# @param post [Thredded::PostView] %>
|
2
|
-
<%= content_tag :article, id: dom_id(post), class: 'thredded--post' do %>
|
3
|
-
<%= render 'thredded/posts_common/
|
2
|
+
<%= content_tag :article, id: dom_id(post), class: 'thredded--post thredded--post-moderation' do %>
|
3
|
+
<%= render 'thredded/posts_common/header_with_user_and_topic',
|
4
|
+
post: post,
|
5
|
+
post_user_link: if post.user
|
6
|
+
link_to(post.user, user_moderation_path(post.user.id))
|
7
|
+
else
|
8
|
+
content_tag :em, t('thredded.null_user_name')
|
9
|
+
end
|
10
|
+
%>
|
4
11
|
<%= render 'thredded/posts_common/content', post: post %>
|
5
12
|
<%= render 'thredded/posts_common/actions', post: post %>
|
6
13
|
<%= render 'post_moderation_actions', post: post %>
|
@@ -1,20 +1,24 @@
|
|
1
1
|
<%
|
2
|
-
|
2
|
+
record = post_moderation_record
|
3
|
+
post = record.post
|
4
|
+
if post
|
5
|
+
post_view = Thredded::PostView.new(post, policy(post))
|
6
|
+
end
|
3
7
|
moderation_state_notice_args = {
|
4
|
-
moderator: user_link(
|
5
|
-
time_ago: time_ago(
|
8
|
+
moderator: user_link(record.moderator),
|
9
|
+
time_ago: time_ago(record.created_at)
|
6
10
|
}
|
7
11
|
%>
|
8
|
-
<article class="thredded--post-moderation-record thredded--post-moderation-record-<%=
|
12
|
+
<article class="thredded--post-moderation-record thredded--post-moderation-record-<%= record.moderation_state %>">
|
9
13
|
<header class="thredded--post-moderation-record--header">
|
10
14
|
<p class="thredded--post-moderation-record--moderation-state-notice">
|
11
|
-
<% if
|
15
|
+
<% if record.approved? %>
|
12
16
|
<%= t('thredded.moderation.post_approved_html', moderation_state_notice_args) %>
|
13
|
-
<% elsif
|
17
|
+
<% elsif record.blocked? %>
|
14
18
|
<%= t('thredded.moderation.post_blocked_html', moderation_state_notice_args) %>
|
15
19
|
<% end %>
|
16
20
|
</p>
|
17
|
-
<% if post && post.content !=
|
21
|
+
<% if post && post.content != record.post_content %>
|
18
22
|
<p class="thredded--post-moderation-record--content-changed-notice">
|
19
23
|
<%= t('thredded.moderation.posts_content_changed_since_moderation_html', post_url: post_permalink_path(post)) %>
|
20
24
|
</p>
|
@@ -22,21 +26,20 @@
|
|
22
26
|
<%= t 'thredded.moderation.post_deleted_notice' unless post %>
|
23
27
|
</header>
|
24
28
|
<article class="thredded--post">
|
25
|
-
|
26
|
-
<% if
|
27
|
-
<%=
|
28
|
-
<h2 class="thredded--post--user"><%= user_link post_moderation_record.post_user %></h2>
|
29
|
+
<% post_user_link = capture do %>
|
30
|
+
<% if post.user %>
|
31
|
+
<%= link_to post.user, user_moderation_path(post.user.id) %>
|
29
32
|
<% else %>
|
30
|
-
|
31
|
-
<%= post_moderation_record.post_user_name %>, <em><%= t 'thredded.null_user_name' %></em>
|
32
|
-
</h2>
|
33
|
+
<%= safe_join [record.post_user_name, content_tag(:em, t('thredded.null_user_name'))].compact, ', ' %>
|
33
34
|
<% end %>
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
35
|
+
<% end %>
|
36
|
+
<% if post %>
|
37
|
+
<%= render 'thredded/posts_common/header_with_user_and_topic', post: post_view, post_user_link: post_user_link %>
|
38
|
+
<% else %>
|
39
|
+
<header><h2 class="thredded--post--user"><%= post_user_link %></h2></header>
|
40
|
+
<% end %>
|
38
41
|
<div class="thredded--post--content">
|
39
|
-
<%= Thredded::ContentFormatter.new(self).format_content(
|
42
|
+
<%= Thredded::ContentFormatter.new(self).format_content(record.post_content) %>
|
40
43
|
</div>
|
41
44
|
</article>
|
42
45
|
<%= render 'post_moderation_actions', post: post if post %>
|
@@ -0,0 +1,7 @@
|
|
1
|
+
<%# @param post [Thredded::PostView] %>
|
2
|
+
<%= content_tag :article, id: dom_id(post), class: 'thredded--post thredded--post-moderation' do %>
|
3
|
+
<%= render 'thredded/posts_common/header_with_topic', post: post %>
|
4
|
+
<%= render 'thredded/posts_common/content', post: post %>
|
5
|
+
<%= render 'thredded/posts_common/actions', post: post %>
|
6
|
+
<%= render 'post_moderation_actions', post: post %>
|
7
|
+
<% end %>
|
@@ -0,0 +1,10 @@
|
|
1
|
+
<%= form_tag users_moderation_path, class: 'thredded--form thredded--navigation--search', method: 'get' do %>
|
2
|
+
<%= label_tag :q, t('thredded.moderation.search_users.form_label') %>
|
3
|
+
<%= text_field_tag :q, @query,
|
4
|
+
type: 'search',
|
5
|
+
required: true,
|
6
|
+
# If there are no results the user will likely want to change the query, so auto-focus.
|
7
|
+
autofocus: @query.presence && !@users.presence,
|
8
|
+
placeholder: t('thredded.moderation.search_users.form_placeholder') %>
|
9
|
+
<button type="submit"><%= t 'thredded.search.form.btn_submit' %></button>
|
10
|
+
<% end %>
|
@@ -1,13 +1,7 @@
|
|
1
1
|
<% content_for :thredded_page_title,
|
2
2
|
safe_join([t('thredded.nav.moderation'), t('thredded.nav.moderation_history')], ': ') %>
|
3
|
-
<% content_for :thredded_page_id, 'thredded--
|
4
|
-
|
5
|
-
<ul class="thredded--navigation-breadcrumbs">
|
6
|
-
<li><%= link_to t('thredded.nav.all_messageboards'), messageboards_path %></li>
|
7
|
-
<li><%= link_to t('thredded.nav.moderation'), pending_moderation_path %></li>
|
8
|
-
<li><%= link_to t('thredded.nav.moderation_history'), moderation_history_path %></li>
|
9
|
-
</ul>
|
10
|
-
<% end %>
|
3
|
+
<% content_for :thredded_page_id, 'thredded--moderation-history' %>
|
4
|
+
<%= render 'nav' %>
|
11
5
|
|
12
6
|
<%= thredded_page do %>
|
13
7
|
<%= content_tag :section, class: 'thredded--main-section' do %>
|