thredded 0.9.4 → 0.10.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +1 -1
- data/app/assets/javascripts/thredded/core/mention_autocompletion.es6 +2 -2
- data/app/assets/stylesheets/thredded/base/_variables.scss +8 -2
- data/app/assets/stylesheets/thredded/components/_currently-online.scss +1 -0
- data/app/assets/stylesheets/thredded/components/_messageboard.scss +85 -8
- data/app/assets/stylesheets/thredded/components/_post.scss +3 -0
- data/app/commands/thredded/notify_following_users.rb +28 -6
- data/app/controllers/thredded/topics_controller.rb +20 -3
- data/app/forms/thredded/edit_topic_form.rb +50 -0
- data/app/forms/thredded/topic_form.rb +1 -5
- data/app/helpers/thredded/application_helper.rb +1 -0
- data/app/models/thredded/post.rb +3 -0
- data/app/models/thredded/topic.rb +17 -0
- data/app/models/thredded/user_extender.rb +1 -0
- data/app/models/thredded/user_permissions/read/all.rb +1 -1
- data/app/models/thredded/user_post_notification.rb +28 -0
- data/app/view_hooks/thredded/all_view_hooks.rb +21 -0
- data/app/view_models/thredded/post_view.rb +2 -1
- data/app/views/thredded/messageboards/_messageboard.html.erb +18 -16
- data/app/views/thredded/messageboards/index.html.erb +27 -15
- data/app/views/thredded/topics/_form.html.erb +1 -1
- data/app/views/thredded/topics/edit.html.erb +12 -6
- data/config/locales/en.yml +1 -0
- data/config/locales/es.yml +1 -0
- data/config/locales/pl.yml +1 -0
- data/config/locales/pt-BR.yml +1 -0
- data/db/migrate/20160329231848_create_thredded.rb +12 -2
- data/db/upgrade_migrations/20170125033319_upgrade_v0_9_to_v0_10.rb +28 -0
- data/lib/thredded/database_seeder.rb +1 -0
- data/lib/thredded/html_pipeline/at_mention_filter.rb +2 -2
- data/lib/thredded/version.rb +1 -1
- metadata +5 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 0e08209cc4846d358cb9cf977e394ab14e553571
|
4
|
+
data.tar.gz: dc0b3eb3302a5fd5b9a3d7d01abfcb25af8635a9
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 7f14c1568bc33b5264b04c97e1ac8500d2215c6dd5f9e0002fd052baff8e722b7f691960db5ac47f85f45a828988762ba8f4eb09b2eb9a82ec8fa5c78a5c7f18
|
7
|
+
data.tar.gz: 1e41c3ff6c705a77a5a9856a5c2cb1216d0ccb291796562fd0ca2be87a5a45877e08c7972f39f6c2d2a56ff4c3ffa05f944119d3bf1a78b086a6e400af2d0383
|
data/README.md
CHANGED
@@ -56,7 +56,7 @@ Then, see the rest of this Readme for more information about using and customizi
|
|
56
56
|
Add the gem to your Gemfile:
|
57
57
|
|
58
58
|
```ruby
|
59
|
-
gem 'thredded', '~> 0.
|
59
|
+
gem 'thredded', '~> 0.10.0'
|
60
60
|
```
|
61
61
|
|
62
62
|
Add the Thredded [initializer] to your parent app by running the install generator.
|
@@ -41,7 +41,7 @@ class ThreddedMentionAutocompletion {
|
|
41
41
|
},
|
42
42
|
replace ({name, match}) {
|
43
43
|
let prefix = match[1];
|
44
|
-
if (/[
|
44
|
+
if (/[., ()]/.test(name)) {
|
45
45
|
return `${prefix}"${name}" `
|
46
46
|
} else {
|
47
47
|
return `${prefix}${name} `
|
@@ -51,4 +51,4 @@ class ThreddedMentionAutocompletion {
|
|
51
51
|
}
|
52
52
|
}
|
53
53
|
|
54
|
-
ThreddedMentionAutocompletion.MATCH_RE = /(^@|\s@)"?([\w
|
54
|
+
ThreddedMentionAutocompletion.MATCH_RE = /(^@|\s@)"?([\w.,\- ()]+)$/;
|
@@ -22,8 +22,8 @@ $thredded-dark-gray: #333 !default;
|
|
22
22
|
$thredded-light-gray: #eee !default;
|
23
23
|
|
24
24
|
// Colors of text, background, actions (links), and navigation
|
25
|
-
$thredded-text-color:
|
26
|
-
$thredded-secondary-text-color:
|
25
|
+
$thredded-text-color: rgba(black, 0.87) !default;
|
26
|
+
$thredded-secondary-text-color: rgba(opacify($thredded-text-color, 1), 0.54) !default;
|
27
27
|
$thredded-background-color: #fff !default;
|
28
28
|
$thredded-action-color: $thredded-brand !default;
|
29
29
|
$thredded-action-hover-color: darken($thredded-action-color, 15%) !default;
|
@@ -83,3 +83,9 @@ $thredded-badge-active-color: $thredded-button-color !default;
|
|
83
83
|
$thredded-badge-active-background: $thredded-action-color !default;
|
84
84
|
$thredded-badge-inactive-color: $thredded-button-color !default;
|
85
85
|
$thredded-badge-inactive-background: rgba($thredded-text-color, 0.3) !default;
|
86
|
+
|
87
|
+
// Layout features
|
88
|
+
// Whether to display messageboards as a grid on the desktop screen sizes.
|
89
|
+
$thredded-messageboards-grid: true !default;
|
90
|
+
// Minimum width of a grid item
|
91
|
+
$thredded-messageboards-grid-item-flex-basis: map-get($thredded-grid-breakpoint-max-widths, tablet) / 3 !default;
|
@@ -1,18 +1,25 @@
|
|
1
|
+
.thredded--messageboards-group {
|
2
|
+
margin-bottom: $thredded-base-spacing;
|
3
|
+
}
|
4
|
+
|
1
5
|
.thredded--messageboard {
|
2
6
|
@extend %thredded--link;
|
3
|
-
border: $thredded-base-border;
|
4
7
|
display: block;
|
5
|
-
margin-bottom:
|
8
|
+
margin-bottom: 1px;
|
6
9
|
padding: $thredded-base-spacing;
|
7
10
|
position: relative;
|
8
11
|
|
9
|
-
|
10
|
-
|
12
|
+
&, &:focus, &:hover, &:active {
|
13
|
+
outline: 1px solid $thredded-base-border-color;
|
11
14
|
}
|
12
15
|
|
13
16
|
&:hover {
|
14
17
|
background-color: rgba($thredded-brand, 0.035);
|
15
18
|
}
|
19
|
+
|
20
|
+
&:active {
|
21
|
+
box-shadow: $thredded-form-box-shadow;
|
22
|
+
}
|
16
23
|
}
|
17
24
|
|
18
25
|
.thredded--messageboard--title {
|
@@ -21,31 +28,42 @@
|
|
21
28
|
float: left;
|
22
29
|
font-size: 1.125rem; // 18px
|
23
30
|
line-height: 1.2;
|
24
|
-
margin-bottom: 0;
|
25
31
|
margin-right: $thredded-small-spacing;
|
26
32
|
vertical-align: baseline;
|
27
33
|
}
|
28
34
|
|
35
|
+
.thredded--messageboard--title,
|
36
|
+
.thredded--messageboard--meta,
|
37
|
+
.thredded--messageboard--description {
|
38
|
+
margin-bottom: $thredded-small-spacing / 2;
|
39
|
+
}
|
40
|
+
|
41
|
+
.thredded--messageboard--description,
|
42
|
+
.thredded--messageboard--meta,
|
43
|
+
.thredded--messageboard--byline {
|
44
|
+
font-size: 0.875em;
|
45
|
+
}
|
46
|
+
|
29
47
|
.thredded--messageboard--meta {
|
30
48
|
@extend %thredded--heading;
|
31
49
|
color: $thredded-secondary-text-color;
|
32
50
|
display: inline-block;
|
33
51
|
font-weight: normal;
|
34
|
-
margin-bottom: 0;
|
35
52
|
vertical-align: baseline;
|
36
53
|
}
|
37
54
|
|
38
55
|
.thredded--messageboard--description {
|
39
56
|
@extend %thredded--paragraph;
|
40
57
|
clear: both;
|
41
|
-
margin-bottom: $thredded-small-spacing / 2;
|
42
58
|
color: $thredded-text-color;
|
59
|
+
&:empty {
|
60
|
+
margin: 0;
|
61
|
+
}
|
43
62
|
}
|
44
63
|
|
45
64
|
.thredded--messageboard--byline {
|
46
65
|
@extend %thredded--paragraph;
|
47
66
|
color: $thredded-secondary-text-color;
|
48
|
-
font-size: 0.875em;
|
49
67
|
font-weight: normal;
|
50
68
|
margin-bottom: 0;
|
51
69
|
}
|
@@ -56,3 +74,62 @@
|
|
56
74
|
display: none;
|
57
75
|
}
|
58
76
|
}
|
77
|
+
|
78
|
+
@supports (flex-wrap: wrap) {
|
79
|
+
.thredded--messageboard {
|
80
|
+
display: flex;
|
81
|
+
flex-direction: column;
|
82
|
+
&--header {
|
83
|
+
display: flex;
|
84
|
+
flex-wrap: wrap;
|
85
|
+
justify-content: space-between;
|
86
|
+
}
|
87
|
+
&--meta {
|
88
|
+
text-align: right;
|
89
|
+
}
|
90
|
+
&--byline,
|
91
|
+
&--description {
|
92
|
+
margin-top: auto;
|
93
|
+
}
|
94
|
+
}
|
95
|
+
|
96
|
+
@if $thredded-messageboards-grid {
|
97
|
+
@include thredded-media-desktop-and-up {
|
98
|
+
$item-border-width: 1px;
|
99
|
+
$item-padding-x: ($thredded-base-spacing * 0.8);
|
100
|
+
$item-padding-y: $thredded-base-spacing;
|
101
|
+
|
102
|
+
%thredded--messageboards-cell-flex {
|
103
|
+
flex-basis: $thredded-messageboards-grid-item-flex-basis;
|
104
|
+
flex-grow: 1;
|
105
|
+
}
|
106
|
+
|
107
|
+
.thredded--messageboards-group {
|
108
|
+
display: flex;
|
109
|
+
flex-direction: row;
|
110
|
+
flex-wrap: wrap;
|
111
|
+
justify-content: space-between;
|
112
|
+
margin-left: $item-border-width;
|
113
|
+
|
114
|
+
// Size incomplete last rows with up to two missing items.
|
115
|
+
&::after, &::before {
|
116
|
+
@extend %thredded--messageboards-cell-flex;
|
117
|
+
content: "";
|
118
|
+
margin-right: $item-border-width;
|
119
|
+
padding: 0 $item-padding-x;
|
120
|
+
}
|
121
|
+
|
122
|
+
&::before {
|
123
|
+
order: 1
|
124
|
+
}
|
125
|
+
}
|
126
|
+
|
127
|
+
.thredded--messageboard {
|
128
|
+
@extend %thredded--messageboards-cell-flex;
|
129
|
+
margin-left: 1px;
|
130
|
+
padding: $item-padding-y $item-padding-x;
|
131
|
+
}
|
132
|
+
|
133
|
+
}
|
134
|
+
}
|
135
|
+
}
|
@@ -1,4 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
+
require 'set'
|
3
|
+
|
2
4
|
module Thredded
|
3
5
|
class NotifyFollowingUsers
|
4
6
|
def initialize(post)
|
@@ -8,12 +10,26 @@ module Thredded
|
|
8
10
|
def run
|
9
11
|
Thredded.notifiers.each do |notifier|
|
10
12
|
notifiable_users = targeted_users(notifier)
|
11
|
-
|
13
|
+
notifiable_users = notifiable_users.select do |user|
|
14
|
+
# Create a notification for the user.
|
15
|
+
# If a notification was already created (from another thread/process),
|
16
|
+
# this will return false due to the unique constraint on the table
|
17
|
+
# and the user will be excluded.
|
18
|
+
Thredded::UserPostNotification.create_from_post_and_user(@post, user)
|
19
|
+
end
|
20
|
+
next if notifiable_users.empty?
|
21
|
+
notifier.new_post(@post, notifiable_users)
|
12
22
|
end
|
13
23
|
end
|
14
24
|
|
15
25
|
def targeted_users(notifier)
|
16
|
-
|
26
|
+
users_subscribed_via(notifier).reject do |user|
|
27
|
+
already_notified_user_ids.include?(user.id)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def users_subscribed_via(notifier)
|
32
|
+
subscribed_users.select do |user|
|
17
33
|
NotificationsForFollowedTopics
|
18
34
|
.detect_or_default(user.thredded_notifications_for_followed_topics, notifier).enabled? &&
|
19
35
|
MessageboardNotificationsForFollowedTopics
|
@@ -21,16 +37,22 @@ module Thredded
|
|
21
37
|
end
|
22
38
|
end
|
23
39
|
|
24
|
-
def
|
25
|
-
@
|
26
|
-
@post.postable.followers.includes(:thredded_notifications_for_followed_topics).reject
|
40
|
+
def subscribed_users
|
41
|
+
@subscribed_users ||=
|
42
|
+
@post.postable.followers.includes(:thredded_notifications_for_followed_topics).reject do |user|
|
43
|
+
user == @post.user || !Thredded::PostPolicy.new(user, @post).read?
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def already_notified_user_ids
|
48
|
+
@notified_user_ids ||= Set.new Thredded::UserPostNotification.notified_user_ids(@post)
|
27
49
|
end
|
28
50
|
|
29
51
|
private
|
30
52
|
|
31
53
|
def messageboard_notifier_prefs_by_user_id
|
32
54
|
@messageboard_notifier_prefs_by_user_id ||= MessageboardNotificationsForFollowedTopics
|
33
|
-
.where(user_id:
|
55
|
+
.where(user_id: subscribed_users.map(&:id))
|
34
56
|
.for_messageboard(@post.messageboard).group_by(&:user_id)
|
35
57
|
end
|
36
58
|
end
|
@@ -1,6 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
module Thredded
|
3
|
-
class TopicsController < Thredded::ApplicationController
|
3
|
+
class TopicsController < Thredded::ApplicationController # rubocop:disable Metrics/ClassLength
|
4
4
|
include Thredded::NewTopicParams
|
5
5
|
|
6
6
|
before_action :thredded_require_login!,
|
@@ -92,12 +92,23 @@ module Thredded
|
|
92
92
|
|
93
93
|
def edit
|
94
94
|
authorize topic, :update?
|
95
|
+
@edit_topic = Thredded::EditTopicForm.new(user: thredded_current_user, topic: topic)
|
95
96
|
end
|
96
97
|
|
97
98
|
def update
|
99
|
+
topic.assign_attributes(topic_params_for_update)
|
98
100
|
authorize topic, :update?
|
99
|
-
if topic.
|
100
|
-
|
101
|
+
if topic.messageboard_id_changed?
|
102
|
+
# Work around the association not being reset.
|
103
|
+
# TODO: report issue to Rails. Looks like a regression of:
|
104
|
+
# https://rails.lighthouseapp.com/projects/8994/tickets/2989
|
105
|
+
topic.messageboard = Thredded::Messageboard.find(topic.messageboard_id)
|
106
|
+
|
107
|
+
authorize topic.messageboard, :post?
|
108
|
+
end
|
109
|
+
@edit_topic = Thredded::EditTopicForm.new(user: thredded_current_user, topic: topic)
|
110
|
+
if @edit_topic.save
|
111
|
+
redirect_to messageboard_topic_url(topic.messageboard, topic),
|
101
112
|
notice: t('thredded.topics.updated_notice')
|
102
113
|
else
|
103
114
|
render :edit
|
@@ -148,6 +159,12 @@ module Thredded
|
|
148
159
|
.permit(:title, :locked, :sticky, category_ids: [])
|
149
160
|
end
|
150
161
|
|
162
|
+
def topic_params_for_update
|
163
|
+
params
|
164
|
+
.require(:topic)
|
165
|
+
.permit(:title, :locked, :sticky, :messageboard_id, category_ids: [])
|
166
|
+
end
|
167
|
+
|
151
168
|
def current_page
|
152
169
|
(params[:page] || 1).to_i
|
153
170
|
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
module Thredded
|
3
|
+
class EditTopicForm
|
4
|
+
include ActiveModel::Model
|
5
|
+
|
6
|
+
delegate :id, :title, :category_ids, :locked, :sticky, :messageboard, :messageboard_id, :valid?,
|
7
|
+
to: :@topic
|
8
|
+
|
9
|
+
# @param user [Thredded.user_class]
|
10
|
+
# @param topic [Thredded::Topic]
|
11
|
+
def initialize(user:, topic:)
|
12
|
+
@user = user
|
13
|
+
@topic = topic
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.model_name
|
17
|
+
Thredded::Topic.model_name
|
18
|
+
end
|
19
|
+
|
20
|
+
def category_options
|
21
|
+
@topic.messageboard.categories.map { |cat| [cat.name, cat.id] }
|
22
|
+
end
|
23
|
+
|
24
|
+
def messageboard_options
|
25
|
+
@user.thredded_can_write_messageboards.map { |messageboard| [messageboard.name, messageboard.id] }
|
26
|
+
end
|
27
|
+
|
28
|
+
def save
|
29
|
+
return false unless valid?
|
30
|
+
@topic.save!
|
31
|
+
true
|
32
|
+
end
|
33
|
+
|
34
|
+
def persisted?
|
35
|
+
true
|
36
|
+
end
|
37
|
+
|
38
|
+
def path
|
39
|
+
Thredded::UrlsHelper.messageboard_topic_path(@topic.messageboard, @topic)
|
40
|
+
end
|
41
|
+
|
42
|
+
def edit_path
|
43
|
+
Thredded::UrlsHelper.edit_messageboard_topic_path(@topic.messageboard, @topic)
|
44
|
+
end
|
45
|
+
|
46
|
+
def messageboard_path
|
47
|
+
Thredded::UrlsHelper.messageboard_topics_path(@topic.messageboard)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
@@ -22,12 +22,8 @@ module Thredded
|
|
22
22
|
Thredded::Topic.model_name
|
23
23
|
end
|
24
24
|
|
25
|
-
def categories
|
26
|
-
topic.messageboard.categories
|
27
|
-
end
|
28
|
-
|
29
25
|
def category_options
|
30
|
-
categories.map { |cat| [cat.name, cat.id] }
|
26
|
+
messageboard.categories.map { |cat| [cat.name, cat.id] }
|
31
27
|
end
|
32
28
|
|
33
29
|
def save
|
data/app/models/thredded/post.rb
CHANGED
@@ -21,6 +21,9 @@ module Thredded
|
|
21
21
|
has_many :moderation_records,
|
22
22
|
class_name: 'Thredded::PostModerationRecord',
|
23
23
|
dependent: :nullify
|
24
|
+
has_many :user_notifications,
|
25
|
+
class_name: 'Thredded::UserPostNotification',
|
26
|
+
dependent: :destroy
|
24
27
|
has_one :last_moderation_record, -> { order_newest_first },
|
25
28
|
class_name: 'Thredded::PostModerationRecord'
|
26
29
|
|
@@ -44,6 +44,7 @@ module Thredded
|
|
44
44
|
counter_cache: :topics_count
|
45
45
|
|
46
46
|
has_many :posts,
|
47
|
+
autosave: true,
|
47
48
|
class_name: 'Thredded::Post',
|
48
49
|
foreign_key: :postable_id,
|
49
50
|
inverse_of: :postable,
|
@@ -76,6 +77,10 @@ module Thredded
|
|
76
77
|
after_commit :update_messageboard_last_topic, on: :update, if: -> { previous_changes.include?('moderation_state') }
|
77
78
|
after_update :update_last_user_and_time_from_last_post!, if: -> { previous_changes.include?('moderation_state') }
|
78
79
|
|
80
|
+
after_commit :handle_messageboard_change_after_commit,
|
81
|
+
on: :update,
|
82
|
+
if: -> { previous_changes.include?('messageboard_id') }
|
83
|
+
|
79
84
|
# Finds the topic by its slug or ID, or raises Thredded::Errors::TopicNotFound.
|
80
85
|
# @param slug_or_id [String]
|
81
86
|
# @return [Thredded::Topic]
|
@@ -151,5 +156,17 @@ module Thredded
|
|
151
156
|
def update_messageboard_last_topic
|
152
157
|
messageboard.update_last_topic!
|
153
158
|
end
|
159
|
+
|
160
|
+
def handle_messageboard_change_after_commit
|
161
|
+
# Update the `posts.messageboard_id` column. The column is just a performance optimization,
|
162
|
+
# so use update_all to avoid validitaing, triggering callbacks, and updating the timestamps:
|
163
|
+
posts.update_all(messageboard_id: messageboard_id)
|
164
|
+
|
165
|
+
# Update the associated messageboard metadata that Rails does not update them automatically.
|
166
|
+
previous_changes['messageboard_id'].each do |messageboard_id|
|
167
|
+
Thredded::Messageboard.reset_counters(messageboard_id, :topics, :posts)
|
168
|
+
Messageboard.find(messageboard_id).update_last_topic!
|
169
|
+
end
|
170
|
+
end
|
154
171
|
end
|
155
172
|
end
|
@@ -26,6 +26,7 @@ module Thredded
|
|
26
26
|
opt.has_many :thredded_user_messageboard_preferences, class_name: 'Thredded::UserMessageboardPreference'
|
27
27
|
opt.has_many :thredded_notifications_for_followed_topics, class_name: 'Thredded::NotificationsForFollowedTopics'
|
28
28
|
opt.has_many :thredded_notifications_for_private_topics, class_name: 'Thredded::NotificationsForPrivateTopics'
|
29
|
+
opt.has_many :thredded_post_notifications, class_name: 'Thredded::UserPostNotification'
|
29
30
|
opt.has_many :thredded_private_users, class_name: 'Thredded::PrivateUser'
|
30
31
|
opt.has_many :thredded_topic_read_states, class_name: 'Thredded::UserTopicReadState'
|
31
32
|
opt.has_many :thredded_private_topic_read_states, class_name: 'Thredded::UserPrivateTopicReadState'
|
@@ -12,7 +12,7 @@ module Thredded
|
|
12
12
|
end
|
13
13
|
|
14
14
|
module ClassMethods
|
15
|
-
# Users that can read the given messageboards.
|
15
|
+
# Users that can read some of the given messageboards.
|
16
16
|
#
|
17
17
|
# @param _messageboards [Array<Thredded::Messageboard>]
|
18
18
|
# @return [ActiveRecord::Relation<Thredded.user_class>] users that can read the given messageboards
|
@@ -0,0 +1,28 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
module Thredded
|
3
|
+
# Delivery records for Thredded::Post notifications.
|
4
|
+
class UserPostNotification < ActiveRecord::Base
|
5
|
+
belongs_to :user, class_name: Thredded.user_class, inverse_of: :thredded_post_notifications
|
6
|
+
belongs_to :post, class_name: 'Thredded::Post', inverse_of: :user_notifications
|
7
|
+
|
8
|
+
# @param post [Thredded::Post]
|
9
|
+
# @return [Array<Integer>] The IDs of users who were already notified about the given post.
|
10
|
+
def self.notified_user_ids(post)
|
11
|
+
where(post_id: post.id).pluck(:user_id)
|
12
|
+
end
|
13
|
+
|
14
|
+
# Create a user-post notification record for a given post and a user.
|
15
|
+
# @param post [Thredded::Post]
|
16
|
+
# @param user [Thredded.user_class]
|
17
|
+
# @return [Boolean] true if a new record was created, false otherwise (e.g. if a record had already existed).
|
18
|
+
def self.create_from_post_and_user(post, user)
|
19
|
+
create(
|
20
|
+
post_id: post.id,
|
21
|
+
user_id: user.id,
|
22
|
+
notified_at: Time.zone.now
|
23
|
+
)
|
24
|
+
rescue ActiveRecord::RecordNotUnique
|
25
|
+
false
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -5,6 +5,8 @@ module Thredded
|
|
5
5
|
attr_reader :posts_common
|
6
6
|
# @return [PostForm]
|
7
7
|
attr_reader :post_form
|
8
|
+
# @return [MessageboardsIndex]
|
9
|
+
attr_reader :messageboards_index
|
8
10
|
# @return [ModerationUserPage]
|
9
11
|
attr_reader :moderation_user_page
|
10
12
|
|
@@ -24,6 +26,7 @@ module Thredded
|
|
24
26
|
@posts_common = PostsCommon.new
|
25
27
|
@post_form = PostForm.new
|
26
28
|
@moderation_user_page = ModerationUserPage.new
|
29
|
+
@messageboards_index = MessageboardsIndex.new
|
27
30
|
end
|
28
31
|
|
29
32
|
# View hooks for collections of public or private posts.
|
@@ -52,6 +55,24 @@ module Thredded
|
|
52
55
|
end
|
53
56
|
end
|
54
57
|
|
58
|
+
class MessageboardsIndex
|
59
|
+
# @return [Thredded::AllViewHooks::ViewHook]
|
60
|
+
attr_reader :container
|
61
|
+
# @return [Thredded::AllViewHooks::ViewHook]
|
62
|
+
attr_reader :list
|
63
|
+
# @return [Thredded::AllViewHooks::ViewHook]
|
64
|
+
attr_reader :group
|
65
|
+
# @return [Thredded::AllViewHooks::ViewHook]
|
66
|
+
attr_reader :messageboard
|
67
|
+
|
68
|
+
def initialize
|
69
|
+
@container = ViewHook.new
|
70
|
+
@list = ViewHook.new
|
71
|
+
@group = ViewHook.new
|
72
|
+
@messageboard = ViewHook.new
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
55
76
|
class ModerationUserPage
|
56
77
|
# @return [Thredded::AllViewHooks::ViewHook]
|
57
78
|
attr_reader :user_title
|
@@ -56,13 +56,14 @@ module Thredded
|
|
56
56
|
[
|
57
57
|
I18n.locale,
|
58
58
|
@post.cache_key,
|
59
|
+
(@post.messageboard_id unless @post.private_topic_post?),
|
59
60
|
@post.user ? @post.user.cache_key : 'users/nil',
|
60
61
|
moderation_state || '+',
|
61
62
|
[
|
62
63
|
can_update?,
|
63
64
|
can_destroy?
|
64
65
|
].map { |p| p ? '+' : '-' } * ''
|
65
|
-
].join('/')
|
66
|
+
].compact.join('/')
|
66
67
|
end
|
67
68
|
# rubocop:enable Metrics/CyclomaticComplexity
|
68
69
|
end
|
@@ -1,22 +1,24 @@
|
|
1
1
|
<% if policy(messageboard).read? %>
|
2
|
-
<%=
|
3
|
-
|
4
|
-
<
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
2
|
+
<%= view_hooks.messageboards_index.messageboard.render self, messageboard: messageboard do %>
|
3
|
+
<%= link_to messageboard_topics_path(messageboard), class: 'thredded--messageboard' do %>
|
4
|
+
<header class="thredded--messageboard--header">
|
5
|
+
<h2 class="thredded--messageboard--title"><%= messageboard.name %></h2>
|
6
|
+
<h3 class="thredded--messageboard--meta">
|
7
|
+
<%= t 'thredded.messageboard.topics_and_posts_counts',
|
8
|
+
topics_count: number_with_delimiter(messageboard.topics_count),
|
9
|
+
posts_count: number_with_delimiter(messageboard.posts_count) %>
|
10
|
+
</h3>
|
11
|
+
</header>
|
11
12
|
|
12
|
-
|
13
|
+
<p class="thredded--messageboard--description"><%= messageboard.description %></p>
|
13
14
|
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
15
|
+
<% if messageboard.last_topic %>
|
16
|
+
<p class="thredded--messageboard--byline">
|
17
|
+
<%= t 'thredded.messageboard.last_updated_by_html',
|
18
|
+
time_ago: time_ago(messageboard.last_topic.last_post_at),
|
19
|
+
user: messageboard.last_user.thredded_display_name %>
|
20
|
+
</p>
|
21
|
+
<% end %>
|
20
22
|
<% end %>
|
21
23
|
<% end %>
|
22
24
|
<% end %>
|
@@ -2,21 +2,33 @@
|
|
2
2
|
<% content_for :thredded_page_id, 'thredded--messageboards-index' %>
|
3
3
|
<% content_for :thredded_breadcrumbs, render('thredded/shared/breadcrumbs') %>
|
4
4
|
<%= thredded_page do %>
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
5
|
+
<%= view_hooks.messageboards_index.container.render self, groups: @groups do %>
|
6
|
+
<section class="thredded--main-section thredded--messageboards">
|
7
|
+
<%= view_hooks.messageboards_index.list.render self, groups: @groups do %>
|
8
|
+
<% @groups.each do |group| %>
|
9
|
+
<% if group.name.present? %>
|
10
|
+
<h3 class="thredded--messageboards-group--title"><%= group.name %></h3>
|
11
|
+
<% end %>
|
12
|
+
<div class="thredded--messageboards-group">
|
13
|
+
<%= view_hooks.messageboards_index.group.render self, group: group do %>
|
14
|
+
<%= render group.messageboards %>
|
15
|
+
<% end %>
|
16
|
+
</div>
|
17
|
+
<% end %>
|
9
18
|
<% end %>
|
10
|
-
<%= render group.messageboards %>
|
11
|
-
<% end %>
|
12
19
|
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
20
|
+
<div class="thredded--messageboards--actions">
|
21
|
+
<% if policy(Thredded::Messageboard.new).create? %>
|
22
|
+
<a class="thredded--button" href="<%= new_messageboard_path %>">
|
23
|
+
<%= t('thredded.messageboard.create') %>
|
24
|
+
</a>
|
25
|
+
<% end %>
|
26
|
+
<% if policy(Thredded::MessageboardGroup.new).create? %>
|
27
|
+
<a class="thredded--button" href="<%= new_messageboard_group_path %>">
|
28
|
+
<%= t('thredded.messageboard_group.create') %>
|
29
|
+
</a>
|
30
|
+
<% end %>
|
31
|
+
</div>
|
32
|
+
</section>
|
33
|
+
<% end %>
|
22
34
|
<% end %>
|
@@ -11,7 +11,7 @@
|
|
11
11
|
<%= form.text_field :title, placeholder: placeholder, required: true %>
|
12
12
|
</li>
|
13
13
|
|
14
|
-
<% if form.object.
|
14
|
+
<% if form.object.category_options.any? %>
|
15
15
|
<li class="category">
|
16
16
|
<%= form.select :category_ids,
|
17
17
|
form.object.category_options,
|
@@ -1,16 +1,17 @@
|
|
1
|
-
<% content_for :thredded_page_title, t('thredded.nav.
|
1
|
+
<% content_for :thredded_page_title, t('thredded.nav.edit_topic') %>
|
2
2
|
<% content_for :thredded_page_id, 'thredded--edit-topic' %>
|
3
3
|
<% content_for :thredded_breadcrumbs do %>
|
4
4
|
<ul class="thredded--navigation-breadcrumbs">
|
5
5
|
<li><%= link_to t('thredded.nav.all_messageboards'), messageboards_path %></li>
|
6
|
-
<li><%= link_to messageboard.name,
|
7
|
-
<li><%= link_to @
|
8
|
-
<li><%= link_to t('thredded.nav.edit_topic'),
|
6
|
+
<li><%= link_to messageboard.name, @edit_topic.messageboard_path %></li>
|
7
|
+
<li><%= link_to @edit_topic.title, @edit_topic.path %></li>
|
8
|
+
<li><%= link_to t('thredded.nav.edit_topic'), @edit_topic.edit_path %></li>
|
9
9
|
</ul>
|
10
10
|
<% end %>
|
11
11
|
|
12
12
|
<%= thredded_page do %>
|
13
|
-
<%= form_for
|
13
|
+
<%= form_for @edit_topic,
|
14
|
+
url: @edit_topic.path,
|
14
15
|
html: { class: 'thredded--form thredded--is-expanded', 'data-thredded-topic-form' => true } do |form| %>
|
15
16
|
<ul class="thredded--form-list on-top">
|
16
17
|
|
@@ -22,7 +23,7 @@
|
|
22
23
|
required: true %>
|
23
24
|
</li>
|
24
25
|
|
25
|
-
<% if form.object.
|
26
|
+
<% if form.object.category_options.any? %>
|
26
27
|
<li class="category">
|
27
28
|
<%= form.select :category_ids, form.object.category_options, {},
|
28
29
|
multiple: true,
|
@@ -30,6 +31,11 @@
|
|
30
31
|
</li>
|
31
32
|
<% end %>
|
32
33
|
|
34
|
+
<li>
|
35
|
+
<%= form.label :messageboard_id, t('thredded.topics.form.messageboard_label') %>
|
36
|
+
<%= form.select :messageboard_id, form.object.messageboard_options %>
|
37
|
+
</li>
|
38
|
+
|
33
39
|
<%= render 'thredded/topics/topic_form_admin_options', form: form %>
|
34
40
|
|
35
41
|
<li>
|
data/config/locales/en.yml
CHANGED
@@ -150,6 +150,7 @@ en:
|
|
150
150
|
categories_placeholder: Categories
|
151
151
|
content_label: :thredded.posts.form.content_label
|
152
152
|
create_btn: Create New Topic
|
153
|
+
messageboard_label: Messageboard
|
153
154
|
title_label: Title
|
154
155
|
title_placeholder: :thredded.topics.form.title_label
|
155
156
|
title_placeholder_start: Start a New Topic
|
data/config/locales/es.yml
CHANGED
@@ -150,6 +150,7 @@ es:
|
|
150
150
|
categories_placeholder: Categorías
|
151
151
|
content_label: :thredded.posts.form.content_label
|
152
152
|
create_btn: Crear Nuevo Tema
|
153
|
+
messageboard_label: Foro
|
153
154
|
title_label: Título
|
154
155
|
title_placeholder: :thredded.topics.form.title_label
|
155
156
|
title_placeholder_start: Crear un Nuevo Tema
|
data/config/locales/pl.yml
CHANGED
@@ -149,6 +149,7 @@ pl:
|
|
149
149
|
categories_placeholder: Kategorie
|
150
150
|
content_label: :thredded.posts.form.content_label
|
151
151
|
create_btn: Stwórz nowy temat
|
152
|
+
messageboard_label: Tablica
|
152
153
|
title_label: Tytuł
|
153
154
|
title_placeholder: :thredded.topics.form.title_label
|
154
155
|
title_placeholder_start: Rozpocznij nowy temat
|
data/config/locales/pt-BR.yml
CHANGED
@@ -153,6 +153,7 @@ pt-BR:
|
|
153
153
|
categories_placeholder: Categorias
|
154
154
|
content_label: :thredded.posts.form.content_label
|
155
155
|
create_btn: Criar Novo Tópico
|
156
|
+
messageboard_label: Fórum
|
156
157
|
title_label: Título
|
157
158
|
title_placeholder: :thredded.topics.form.title_label
|
158
159
|
title_placeholder_start: Iniciar um novo tópico
|
@@ -149,8 +149,8 @@ class CreateThredded < ActiveRecord::Migration
|
|
149
149
|
end
|
150
150
|
|
151
151
|
create_table :thredded_messageboard_users do |t|
|
152
|
-
t.references :thredded_user_detail, foreign_key:
|
153
|
-
t.references :thredded_messageboard, foreign_key:
|
152
|
+
t.references :thredded_user_detail, foreign_key: { on_delete: :cascade }, null: false
|
153
|
+
t.references :thredded_messageboard, foreign_key: { on_delete: :cascade }, null: false
|
154
154
|
t.datetime :last_seen_at, null: false
|
155
155
|
t.index [:thredded_messageboard_id, :thredded_user_detail_id],
|
156
156
|
name: :index_thredded_messageboard_users_primary
|
@@ -237,6 +237,16 @@ class CreateThredded < ActiveRecord::Migration
|
|
237
237
|
t.index [:user_id, :messageboard_id, :notifier_key],
|
238
238
|
name: 'thredded_messageboard_notifications_for_followed_topics_unique', unique: true
|
239
239
|
end
|
240
|
+
|
241
|
+
create_table :thredded_user_post_notifications do |t|
|
242
|
+
t.references :user, null: false
|
243
|
+
t.foreign_key Thredded.user_class.table_name, column: :user_id, on_delete: :cascade
|
244
|
+
t.references :post, null: false
|
245
|
+
t.foreign_key :thredded_posts, column: :post_id, on_delete: :cascade
|
246
|
+
t.datetime :notified_at, null: false
|
247
|
+
t.index :post_id, name: :index_thredded_user_post_notifications_on_post_id
|
248
|
+
t.index [:user_id, :post_id], name: :index_thredded_user_post_notifications_on_user_id_and_post_id, unique: true
|
249
|
+
end
|
240
250
|
end
|
241
251
|
end
|
242
252
|
# rubocop:enable Metrics/MethodLength
|
@@ -0,0 +1,28 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
class UpgradeV09ToV010 < ActiveRecord::Migration
|
3
|
+
def up
|
4
|
+
remove_foreign_key :thredded_messageboard_users, :thredded_messageboards
|
5
|
+
add_foreign_key :thredded_messageboard_users, :thredded_messageboards, on_delete: :cascade
|
6
|
+
remove_foreign_key :thredded_messageboard_users, :thredded_user_details
|
7
|
+
add_foreign_key :thredded_messageboard_users, :thredded_user_details, on_delete: :cascade
|
8
|
+
|
9
|
+
create_table :thredded_user_post_notifications do |t|
|
10
|
+
t.references :user, null: false
|
11
|
+
t.foreign_key Thredded.user_class.table_name, column: :user_id, on_delete: :cascade
|
12
|
+
t.references :post, null: false
|
13
|
+
t.foreign_key :thredded_posts, column: :post_id, on_delete: :cascade
|
14
|
+
t.datetime :notified_at, null: false
|
15
|
+
t.index :post_id, name: :index_thredded_user_post_notifications_on_post_id
|
16
|
+
t.index [:user_id, :post_id], name: :index_thredded_user_post_notifications_on_user_id_and_post_id, unique: true
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def down
|
21
|
+
drop_table :thredded_user_post_notifications
|
22
|
+
|
23
|
+
remove_foreign_key :thredded_messageboard_users, :thredded_user_details
|
24
|
+
add_foreign_key :thredded_messageboard_users, :thredded_user_details
|
25
|
+
remove_foreign_key :thredded_messageboard_users, :thredded_messageboards
|
26
|
+
add_foreign_key :thredded_messageboard_users, :thredded_messageboards
|
27
|
+
end
|
28
|
+
end
|
@@ -34,7 +34,7 @@ module Thredded
|
|
34
34
|
|
35
35
|
private
|
36
36
|
|
37
|
-
MATCH_NAME_RE = /(?:^|[\s>])@(\w+|"[\w
|
37
|
+
MATCH_NAME_RE = /(?:^|[\s>])@([\w\-]+|"[\w.,\- ()]+")(?=\W|$)/
|
38
38
|
|
39
39
|
def mentioned_names(text_node_html)
|
40
40
|
text_node_html.scan(MATCH_NAME_RE).map(&:first).map { |m| m.start_with?('"') ? m[1..-2] : m }
|
@@ -45,7 +45,7 @@ module Thredded
|
|
45
45
|
return unless names.present?
|
46
46
|
@users_provider.call(names).each do |user|
|
47
47
|
name = user.thredded_display_name
|
48
|
-
maybe_quoted_name = name =~ /[
|
48
|
+
maybe_quoted_name = name =~ /[., ()]/ ? %("#{name}") : name
|
49
49
|
url = Thredded.user_path(@view_context, user)
|
50
50
|
text_node_html.gsub!(
|
51
51
|
/(^|[\s>])(@#{Regexp.escape maybe_quoted_name})([^a-z\d]|$)/i,
|
data/lib/thredded/version.rb
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: thredded
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.10.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Joel Oliveira
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2017-
|
12
|
+
date: 2017-02-22 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: pundit
|
@@ -762,6 +762,7 @@ files:
|
|
762
762
|
- app/controllers/thredded/theme_previews_controller.rb
|
763
763
|
- app/controllers/thredded/topic_previews_controller.rb
|
764
764
|
- app/controllers/thredded/topics_controller.rb
|
765
|
+
- app/forms/thredded/edit_topic_form.rb
|
765
766
|
- app/forms/thredded/private_topic_form.rb
|
766
767
|
- app/forms/thredded/topic_form.rb
|
767
768
|
- app/forms/thredded/user_preferences_form.rb
|
@@ -815,6 +816,7 @@ files:
|
|
815
816
|
- app/models/thredded/user_permissions/read/all.rb
|
816
817
|
- app/models/thredded/user_permissions/write/all.rb
|
817
818
|
- app/models/thredded/user_permissions/write/none.rb
|
819
|
+
- app/models/thredded/user_post_notification.rb
|
818
820
|
- app/models/thredded/user_preference.rb
|
819
821
|
- app/models/thredded/user_private_topic_read_state.rb
|
820
822
|
- app/models/thredded/user_topic_follow.rb
|
@@ -952,6 +954,7 @@ files:
|
|
952
954
|
- db/upgrade_migrations/20160723012349_upgrade_v0_6_to_v0_7.rb
|
953
955
|
- db/upgrade_migrations/20161019150201_upgrade_v0_7_to_v0_8.rb
|
954
956
|
- db/upgrade_migrations/20161113161801_upgrade_v0_8_to_v0_9.rb
|
957
|
+
- db/upgrade_migrations/20170125033319_upgrade_v0_9_to_v0_10.rb
|
955
958
|
- lib/generators/thredded/install/USAGE
|
956
959
|
- lib/generators/thredded/install/install_generator.rb
|
957
960
|
- lib/generators/thredded/install/templates/initializer.rb
|