thredded 0.9.4 → 0.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|