thredded 0.14.4 → 0.15.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +3 -2
- data/app/assets/javascripts/thredded/components/preview_area.es6 +3 -0
- data/app/assets/javascripts/thredded/components/spoilers.es6 +37 -0
- data/app/assets/stylesheets/thredded/_email.scss +25 -0
- data/app/assets/stylesheets/thredded/_thredded.scss +1 -0
- data/app/assets/stylesheets/thredded/base/_typography.scss +1 -1
- data/app/assets/stylesheets/thredded/base/_variables.scss +2 -3
- data/app/assets/stylesheets/thredded/components/_post.scss +0 -11
- data/app/assets/stylesheets/thredded/components/_spoiler.scss +41 -0
- data/app/commands/thredded/mark_all_read.rb +1 -7
- data/app/controllers/concerns/thredded/new_post_params.rb +1 -1
- data/app/controllers/concerns/thredded/new_private_post_params.rb +1 -1
- data/app/controllers/concerns/thredded/new_private_topic_params.rb +1 -2
- data/app/controllers/concerns/thredded/new_topic_params.rb +0 -1
- data/app/controllers/thredded/application_controller.rb +10 -0
- data/app/controllers/thredded/posts_controller.rb +1 -2
- data/app/controllers/thredded/private_posts_controller.rb +1 -2
- data/app/controllers/thredded/private_topics_controller.rb +10 -12
- data/app/controllers/thredded/topics_controller.rb +14 -17
- data/app/forms/thredded/edit_topic_form.rb +2 -1
- data/app/forms/thredded/post_form.rb +3 -1
- data/app/forms/thredded/private_post_form.rb +3 -1
- data/app/forms/thredded/private_topic_form.rb +1 -0
- data/app/jobs/thredded/activity_updater_job.rb +18 -8
- data/app/jobs/thredded/auto_follow_and_notify_job.rb +2 -1
- data/app/models/concerns/thredded/post_common.rb +3 -4
- data/app/models/concerns/thredded/topic_common.rb +7 -0
- data/app/models/concerns/thredded/user_topic_read_state_common.rb +62 -5
- data/app/models/thredded/null_user_topic_read_state.rb +8 -0
- data/app/policies/thredded/private_post_policy.rb +16 -0
- data/app/view_hooks/thredded/all_view_hooks.rb +3 -0
- data/app/view_models/thredded/base_topic_view.rb +5 -1
- data/app/view_models/thredded/post_view.rb +13 -1
- data/app/view_models/thredded/posts_page_view.rb +1 -1
- data/app/view_models/thredded/topic_posts_page_view.rb +13 -1
- data/app/views/thredded/posts/_post.html.erb +1 -0
- data/app/views/thredded/posts/edit.html.erb +1 -0
- data/app/views/thredded/posts_common/_before_first_unread_post.html.erb +7 -0
- data/app/views/thredded/posts_common/form/_after_content.html.erb +8 -0
- data/app/views/thredded/posts_common/form/_before_content.html.erb +8 -0
- data/app/views/thredded/private_posts/_private_post.html.erb +2 -1
- data/app/views/thredded/private_posts/edit.html.erb +1 -0
- data/app/views/thredded/private_topics/_form.html.erb +1 -0
- data/app/views/thredded/private_topics/edit.html.erb +2 -1
- data/app/views/thredded/shared/_field_errors.html.erb +3 -0
- data/app/views/thredded/shared/_nav.html.erb +1 -1
- data/app/views/thredded/shared/_page.html.erb +1 -1
- data/app/views/thredded/topics/_form.html.erb +1 -0
- data/app/views/thredded/topics/edit.html.erb +2 -1
- data/config/locales/de.yml +2 -0
- data/config/locales/en.yml +2 -0
- data/config/locales/es.yml +2 -0
- data/config/locales/fr.yml +2 -0
- data/config/locales/it.yml +2 -0
- data/config/locales/pl.yml +2 -0
- data/config/locales/pt-BR.yml +2 -0
- data/config/locales/ru.yml +2 -0
- data/config/locales/zh-CN.yml +2 -0
- data/db/migrate/20160329231848_create_thredded.rb +37 -23
- data/db/upgrade_migrations/{20170811090735_upgrade_thredded_v0_13_to_v_014.rb → 20170811090735_upgrade_thredded_v0_13_to_v0_14.rb} +0 -0
- data/db/upgrade_migrations/20180110200009_upgrade_thredded_v0_14_to_v0_15.rb +91 -0
- data/lib/generators/thredded/install/templates/initializer.rb +16 -7
- data/lib/thredded.rb +143 -125
- data/lib/thredded/arel_compat.rb +57 -0
- data/lib/thredded/base_migration.rb +10 -0
- data/lib/thredded/collection_to_strings_with_cache_renderer.rb +35 -9
- data/lib/thredded/content_formatter.rb +27 -18
- data/lib/thredded/database_seeder.rb +218 -64
- data/lib/thredded/email_transformer.rb +5 -2
- data/lib/thredded/email_transformer/spoiler.rb +25 -0
- data/lib/thredded/formatting_demo_content.rb +12 -0
- data/lib/thredded/html_pipeline/onebox_filter.rb +3 -38
- data/lib/thredded/html_pipeline/spoiler_tag_filter.rb +128 -0
- data/lib/thredded/html_pipeline/utils.rb +47 -0
- data/lib/thredded/rails_lt_5_2_arel_case_node.rb +119 -0
- data/lib/thredded/version.rb +1 -1
- metadata +17 -21
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a8f7ee3eae771c92edf97664c74ebbcb42e403521cc09b9c05e7379dc98956f1
|
4
|
+
data.tar.gz: 66ac525459c09f1efd65c947a8df0d41dde083166d857664f7163d0ed0b131b0
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 0516e987e201972234778107cff4be78997dd15580ec0b721f21654510049598216d946966dd6a87d11553a47f7d55f7cf78f41e9fc7d07d1e0ecafd57470370
|
7
|
+
data.tar.gz: 70d7f20e62b6187f1017ceced464e4e0cba2ca2988dd4adc9d1f8e5776f9c0b9b5d75d29ed3d12e7125361e203b549ea44fcfe3fb884a1c9ef25496c03223a5b
|
data/README.md
CHANGED
@@ -4,7 +4,7 @@ _Thredded_ is a Rails 4.2+ forum/messageboard engine. Its goal is to be as simpl
|
|
4
4
|
|
5
5
|
Some of the features currently in Thredded:
|
6
6
|
|
7
|
-
* Markdown (default) or BBCode post formatting.
|
7
|
+
* Markdown (default) and / or BBCode post formatting, with [onebox] and `<spoiler>` / `[spoiler]` tag support.
|
8
8
|
* (Un)read posts tracking.
|
9
9
|
* Email notifications, topic subscriptions, @-mentions, per-messageboard notification settings.
|
10
10
|
* Private group messaging.
|
@@ -28,6 +28,7 @@ If you're looking for variations on a theme - see [Discourse]. However, It is a
|
|
28
28
|
application and not an engine like Thredded.
|
29
29
|
|
30
30
|
[Discourse]: http://www.discourse.org/
|
31
|
+
[onebox]: https://github.com/discourse/onebox
|
31
32
|
|
32
33
|
Table of Contents
|
33
34
|
=================
|
@@ -94,7 +95,7 @@ Then, see the rest of this Readme for more information about using and customizi
|
|
94
95
|
Add the gem to your Gemfile:
|
95
96
|
|
96
97
|
```ruby
|
97
|
-
gem 'thredded', '~> 0.
|
98
|
+
gem 'thredded', '~> 0.15.1'
|
98
99
|
```
|
99
100
|
|
100
101
|
Add the Thredded [initializer] to your parent app by running the install generator.
|
@@ -1,6 +1,8 @@
|
|
1
1
|
//= require thredded/core/serialize_form
|
2
|
+
//= require thredded/components/spoilers
|
2
3
|
|
3
4
|
(() => {
|
5
|
+
const Thredded = window.Thredded;
|
4
6
|
const PREVIEW_AREA_SELECTOR = '[data-thredded-preview-area]';
|
5
7
|
const PREVIEW_AREA_POST_SELECTOR = '[data-thredded-preview-area-post]';
|
6
8
|
|
@@ -49,6 +51,7 @@
|
|
49
51
|
onPreviewResponse(data) {
|
50
52
|
this.preview.style.display = 'block';
|
51
53
|
this.previewPost.innerHTML = data;
|
54
|
+
Thredded.spoilers.init(this.previewPost);
|
52
55
|
}
|
53
56
|
}
|
54
57
|
|
@@ -0,0 +1,37 @@
|
|
1
|
+
//= require thredded/core/on_page_load
|
2
|
+
|
3
|
+
(() => {
|
4
|
+
const Thredded = window.Thredded;
|
5
|
+
const COMPONENT_SELECTOR = '.thredded--post--content--spoiler';
|
6
|
+
const OPEN_CLASS = 'thredded--post--content--spoiler--is-open';
|
7
|
+
|
8
|
+
Thredded.spoilers = {
|
9
|
+
init(root) {
|
10
|
+
Array.prototype.forEach.call(root.querySelectorAll(COMPONENT_SELECTOR), (node) => {
|
11
|
+
node.addEventListener('mousedown', (evt) => {
|
12
|
+
evt.stopPropagation();
|
13
|
+
this.toggle(evt.currentTarget);
|
14
|
+
});
|
15
|
+
node.addEventListener('keypress', (evt) => {
|
16
|
+
if (event.key === ' ' || event.key === 'Enter') {
|
17
|
+
evt.preventDefault();
|
18
|
+
evt.stopPropagation();
|
19
|
+
this.toggle(evt.currentTarget);
|
20
|
+
}
|
21
|
+
});
|
22
|
+
});
|
23
|
+
},
|
24
|
+
|
25
|
+
toggle(node) {
|
26
|
+
const isOpen = node.classList.contains(OPEN_CLASS);
|
27
|
+
node.classList.toggle(OPEN_CLASS);
|
28
|
+
node.setAttribute('aria-expanded', isOpen ? 'false' : 'true');
|
29
|
+
node.firstElementChild.setAttribute('aria-hidden', isOpen ? 'false' : 'true');
|
30
|
+
node.lastElementChild.setAttribute('aria-hidden', isOpen ? 'true' : 'false');
|
31
|
+
}
|
32
|
+
};
|
33
|
+
|
34
|
+
Thredded.onPageLoad(() => {
|
35
|
+
Thredded.spoilers.init(document);
|
36
|
+
});
|
37
|
+
})();
|
@@ -49,3 +49,28 @@
|
|
49
49
|
}
|
50
50
|
}
|
51
51
|
}
|
52
|
+
|
53
|
+
.thredded--post--content--spoiler {
|
54
|
+
background-color: $thredded-spoiler-background-color;
|
55
|
+
padding: 16px;
|
56
|
+
|
57
|
+
&--summary {
|
58
|
+
border-bottom: 1px solid $thredded-dark-gray;
|
59
|
+
}
|
60
|
+
|
61
|
+
&--contents {
|
62
|
+
color: transparent !important;
|
63
|
+
|
64
|
+
* {
|
65
|
+
color: transparent !important;
|
66
|
+
}
|
67
|
+
|
68
|
+
a {
|
69
|
+
text-decoration: underline;
|
70
|
+
}
|
71
|
+
|
72
|
+
> *:last-child {
|
73
|
+
margin-bottom: 0;
|
74
|
+
}
|
75
|
+
}
|
76
|
+
}
|
@@ -22,7 +22,7 @@ $thredded-inline-spacing: 0.4375em !default;
|
|
22
22
|
|
23
23
|
// Named colors
|
24
24
|
$thredded-brand: #4a90e2 !default;
|
25
|
-
$thredded-dark-gray: #
|
25
|
+
$thredded-dark-gray: rgba(#424242, 0.25) !default;
|
26
26
|
$thredded-light-gray: #eee !default;
|
27
27
|
|
28
28
|
// Colors of text, background, actions (links), and navigation
|
@@ -38,8 +38,7 @@ $thredded-nav-current-color: $thredded-action-color !default;
|
|
38
38
|
$thredded-overlay-background-color: opacify($thredded-background-color, 1) !default;
|
39
39
|
$thredded-overlay-box-shadow: 0 2px 2px 0 rgba(0, 0, 0, .14), 0 3px 1px -2px rgba(0, 0, 0, .2), 0 1px 5px 0 rgba(0, 0, 0, .12) !default;
|
40
40
|
$thredded-secondary-nav-color: $thredded-secondary-text-color !default;
|
41
|
-
$thredded-spoiler-
|
42
|
-
$thredded-spoiler-shown-color: black !default;
|
41
|
+
$thredded-spoiler-background-color: rgba(0, 0, 0, .06) !default;
|
43
42
|
$thredded-code-selected-line-background: #f8eec7 !default;
|
44
43
|
|
45
44
|
|
@@ -124,15 +124,4 @@
|
|
124
124
|
pre {
|
125
125
|
overflow-x: auto;
|
126
126
|
}
|
127
|
-
|
128
|
-
&--spoiler {
|
129
|
-
color: $thredded-spoiler-hidden-color;
|
130
|
-
background-color: $thredded-spoiler-hidden-color;
|
131
|
-
cursor: pointer;
|
132
|
-
|
133
|
-
&:hover,
|
134
|
-
&:focus {
|
135
|
-
color: $thredded-spoiler-shown-color;
|
136
|
-
}
|
137
|
-
}
|
138
127
|
}
|
@@ -0,0 +1,41 @@
|
|
1
|
+
.thredded--post--content--spoiler {
|
2
|
+
background-color: $thredded-spoiler-background-color;
|
3
|
+
cursor: pointer;
|
4
|
+
margin: 0 0 $thredded-small-spacing;
|
5
|
+
padding: $thredded-small-spacing;
|
6
|
+
position: relative;
|
7
|
+
|
8
|
+
&--contents {
|
9
|
+
visibility: hidden;
|
10
|
+
|
11
|
+
> *:last-child {
|
12
|
+
margin-bottom: 0;
|
13
|
+
}
|
14
|
+
}
|
15
|
+
|
16
|
+
&--summary {
|
17
|
+
@extend %thredded--link;
|
18
|
+
position: absolute;
|
19
|
+
visibility: visible;
|
20
|
+
}
|
21
|
+
|
22
|
+
.thredded--post--content--spoiler {
|
23
|
+
visibility: visible;
|
24
|
+
}
|
25
|
+
|
26
|
+
table {
|
27
|
+
td, th {
|
28
|
+
border-color: $thredded-dark-gray;
|
29
|
+
}
|
30
|
+
}
|
31
|
+
|
32
|
+
&.thredded--post--content--spoiler--is-open {
|
33
|
+
> .thredded--post--content--spoiler--contents {
|
34
|
+
visibility: visible;
|
35
|
+
}
|
36
|
+
|
37
|
+
> .thredded--post--content--spoiler--summary {
|
38
|
+
display: none;
|
39
|
+
}
|
40
|
+
}
|
41
|
+
}
|
@@ -8,14 +8,8 @@ module Thredded
|
|
8
8
|
|
9
9
|
unread_topics.each do |topic|
|
10
10
|
last_post = topic.posts.order_oldest_first.last
|
11
|
-
total_pages = topic.posts.page(1).total_pages
|
12
11
|
|
13
|
-
Thredded::UserPrivateTopicReadState.touch!(
|
14
|
-
user.id,
|
15
|
-
topic.id,
|
16
|
-
last_post,
|
17
|
-
total_pages
|
18
|
-
)
|
12
|
+
Thredded::UserPrivateTopicReadState.touch!(user.id, topic.id, last_post)
|
19
13
|
end
|
20
14
|
end
|
21
15
|
end
|
@@ -8,7 +8,7 @@ module Thredded
|
|
8
8
|
def new_private_post_params
|
9
9
|
params.fetch(:post, {})
|
10
10
|
.permit(:content, :quote_private_post_id)
|
11
|
-
.
|
11
|
+
.tap do |p|
|
12
12
|
quote_id = p.delete(:quote_private_post_id)
|
13
13
|
if quote_id
|
14
14
|
post = Thredded::PrivatePost.find(quote_id)
|
@@ -7,11 +7,10 @@ module Thredded
|
|
7
7
|
|
8
8
|
def new_private_topic_params
|
9
9
|
params
|
10
|
-
.
|
10
|
+
.fetch(:private_topic, {})
|
11
11
|
.permit(:title, :content, :user_names, user_ids: [])
|
12
12
|
.merge(
|
13
13
|
user: thredded_current_user,
|
14
|
-
ip: request.remote_ip
|
15
14
|
).tap { |p| adapt_user_ids! p }
|
16
15
|
end
|
17
16
|
|
@@ -67,6 +67,16 @@ module Thredded
|
|
67
67
|
given.all? { |k, v| v == params[k] }
|
68
68
|
end
|
69
69
|
|
70
|
+
# Returns true if the current page is beyond the end of the collection
|
71
|
+
def page_beyond_last?(page_scope)
|
72
|
+
page_scope.to_a.empty? && page_scope.current_page != 1
|
73
|
+
end
|
74
|
+
|
75
|
+
# Returns URL parameters for the last page of the given page scope.
|
76
|
+
def last_page_params(page_scope)
|
77
|
+
{ page: page_scope.total_pages }
|
78
|
+
end
|
79
|
+
|
70
80
|
private
|
71
81
|
|
72
82
|
def thredded_layout
|
@@ -55,8 +55,7 @@ module Thredded
|
|
55
55
|
|
56
56
|
def mark_as_unread
|
57
57
|
authorize post, :read?
|
58
|
-
|
59
|
-
post.mark_as_unread(thredded_current_user, page)
|
58
|
+
post.mark_as_unread(thredded_current_user)
|
60
59
|
after_mark_as_unread # customization hook
|
61
60
|
end
|
62
61
|
|
@@ -8,14 +8,13 @@ module Thredded
|
|
8
8
|
before_action :thredded_require_login!
|
9
9
|
|
10
10
|
def index
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
)
|
11
|
+
page_scope = Thredded::PrivateTopic
|
12
|
+
.distinct
|
13
|
+
.for_user(thredded_current_user)
|
14
|
+
.order_recently_posted_first
|
15
|
+
.page(params[:page])
|
16
|
+
return redirect_to(last_page_params(page_scope)) if page_beyond_last?(page_scope)
|
17
|
+
@private_topics = Thredded::PrivateTopicsPageView.new(thredded_current_user, page_scope)
|
19
18
|
|
20
19
|
Thredded::PrivateTopicForm.new(user: thredded_current_user).tap do |form|
|
21
20
|
@new_private_topic = form if policy(form.private_topic).create?
|
@@ -31,12 +30,11 @@ module Thredded
|
|
31
30
|
.includes(:user)
|
32
31
|
.order_oldest_first
|
33
32
|
.page(current_page)
|
33
|
+
return redirect_to(last_page_params(page_scope)) if page_beyond_last?(page_scope)
|
34
34
|
@posts = Thredded::TopicPostsPageView.new(thredded_current_user, private_topic, page_scope)
|
35
35
|
|
36
36
|
if thredded_signed_in?
|
37
|
-
Thredded::UserPrivateTopicReadState.touch!(
|
38
|
-
thredded_current_user.id, private_topic.id, page_scope.last, current_page
|
39
|
-
)
|
37
|
+
Thredded::UserPrivateTopicReadState.touch!(thredded_current_user.id, private_topic.id, page_scope.last)
|
40
38
|
end
|
41
39
|
|
42
40
|
@new_post = Thredded::PrivatePostForm.new(
|
@@ -45,7 +43,7 @@ module Thredded
|
|
45
43
|
end
|
46
44
|
|
47
45
|
def new
|
48
|
-
@private_topic = Thredded::PrivateTopicForm.new(
|
46
|
+
@private_topic = Thredded::PrivateTopicForm.new(new_private_topic_params)
|
49
47
|
authorize_creating @private_topic.private_topic
|
50
48
|
end
|
51
49
|
|
@@ -23,12 +23,11 @@ module Thredded
|
|
23
23
|
return redirect_to(canonical_messageboard_params)
|
24
24
|
end
|
25
25
|
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
)
|
26
|
+
page_scope = policy_scope(messageboard.topics)
|
27
|
+
.order_sticky_first.order_recently_posted_first
|
28
|
+
.page(current_page)
|
29
|
+
return redirect_to(last_page_params(page_scope)) if page_beyond_last?(page_scope)
|
30
|
+
@topics = Thredded::TopicsPageView.new(thredded_current_user, page_scope)
|
32
31
|
Thredded::TopicForm.new(messageboard: messageboard, user: thredded_current_user).tap do |form|
|
33
32
|
@new_topic = form if policy(form.topic).create?
|
34
33
|
end
|
@@ -41,12 +40,11 @@ module Thredded
|
|
41
40
|
.order_oldest_first
|
42
41
|
.includes(:user, :messageboard, :postable)
|
43
42
|
.page(current_page)
|
43
|
+
return redirect_to(last_page_params(page_scope)) if page_beyond_last?(page_scope)
|
44
44
|
@posts = Thredded::TopicPostsPageView.new(thredded_current_user, topic, page_scope)
|
45
45
|
|
46
46
|
if thredded_signed_in?
|
47
|
-
Thredded::UserTopicReadState.touch!(
|
48
|
-
thredded_current_user.id, topic.id, page_scope.last, current_page
|
49
|
-
)
|
47
|
+
Thredded::UserTopicReadState.touch!(thredded_current_user.id, topic.id, page_scope.last)
|
50
48
|
end
|
51
49
|
|
52
50
|
@new_post = Thredded::PostForm.new(user: thredded_current_user, topic: topic, post_params: new_post_params)
|
@@ -69,14 +67,13 @@ module Thredded
|
|
69
67
|
Thredded::Topic.where(messageboard_id: policy_scope(Thredded::Messageboard.all).pluck(:id))
|
70
68
|
end
|
71
69
|
)
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
)
|
70
|
+
page_scope = topics_scope
|
71
|
+
.search_query(@query)
|
72
|
+
.order_recently_posted_first
|
73
|
+
.includes(:categories, :last_user, :user)
|
74
|
+
.page(current_page)
|
75
|
+
return redirect_to(last_page_params(page_scope)) if page_beyond_last?(page_scope)
|
76
|
+
@topics = Thredded::TopicsPageView.new(thredded_current_user, page_scope)
|
80
77
|
end
|
81
78
|
|
82
79
|
def new
|
@@ -4,7 +4,8 @@ module Thredded
|
|
4
4
|
class EditTopicForm
|
5
5
|
include ActiveModel::Model
|
6
6
|
|
7
|
-
delegate :id, :title, :category_ids, :locked, :sticky, :messageboard, :messageboard_id, :valid?,
|
7
|
+
delegate :id, :title, :title_was, :category_ids, :locked, :sticky, :messageboard, :messageboard_id, :valid?,
|
8
|
+
:errors,
|
8
9
|
to: :@topic
|
9
10
|
|
10
11
|
# @param user [Thredded.user_class]
|
@@ -11,7 +11,7 @@ module Thredded
|
|
11
11
|
|
12
12
|
# @param user [Thredded.user_class]
|
13
13
|
# @param topic [Topic]
|
14
|
-
# @param post [
|
14
|
+
# @param post [Post]
|
15
15
|
# @param post_params [Hash]
|
16
16
|
def initialize(user:, topic:, post: nil, post_params: {})
|
17
17
|
@messageboard = topic.messageboard
|
@@ -47,7 +47,9 @@ module Thredded
|
|
47
47
|
|
48
48
|
def save
|
49
49
|
return false unless @post.valid?
|
50
|
+
was_persisted = @post.persisted?
|
50
51
|
@post.save!
|
52
|
+
Thredded::UserTopicReadState.touch!(@post.user.id, @topic.id, @post) unless was_persisted
|
51
53
|
true
|
52
54
|
end
|
53
55
|
end
|