thredded 0.0.10 → 0.0.12
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.mkdn +25 -0
- data/app/controllers/thredded/application_controller.rb +31 -13
- data/app/controllers/thredded/posts_controller.rb +9 -7
- data/app/controllers/thredded/topics_controller.rb +14 -33
- data/app/decorators/thredded/post_decorator.rb +10 -1
- data/app/decorators/thredded/topic_decorator.rb +2 -2
- data/app/decorators/thredded/user_topic_decorator.rb +2 -3
- data/app/helpers/thredded/application_helper.rb +0 -5
- data/app/models/thredded/ability.rb +10 -2
- data/app/models/thredded/attachment.rb +1 -1
- data/app/models/thredded/messageboard.rb +1 -0
- data/app/models/thredded/post.rb +63 -13
- data/app/models/thredded/topic.rb +10 -8
- data/app/uploaders/thredded/attachment_uploader.rb +1 -1
- data/app/uploaders/thredded/image_uploader.rb +1 -2
- data/app/views/thredded/messageboards/index.html.erb +3 -0
- data/app/views/thredded/post_mailer/at_notification.html.erb +1 -1
- data/app/views/thredded/posts/_post.html.erb +15 -15
- data/app/views/thredded/posts/edit.html.erb +3 -0
- data/app/views/thredded/posts/index.html.erb +3 -0
- data/app/views/thredded/preferences/edit.html.erb +3 -0
- data/app/views/thredded/private_topics/index.html.erb +3 -0
- data/app/views/thredded/private_topics/new.html.erb +3 -0
- data/app/views/thredded/setups/new.html.erb +2 -1
- data/app/views/thredded/topic_mailer/message_notification.html.erb +1 -1
- data/app/views/thredded/topics/_topic_form.html.erb +1 -1
- data/app/views/thredded/topics/by_category.html.erb +3 -0
- data/app/views/thredded/topics/edit.html.erb +3 -0
- data/app/views/thredded/topics/index.html.erb +3 -0
- data/app/views/thredded/topics/new.html.erb +3 -0
- data/app/views/thredded/topics/search.html.erb +3 -0
- data/lib/html/pipeline/at_mention_filter.rb +19 -0
- data/lib/{thredded/filter/attachment.rb → html/pipeline/attached_image_filter.rb} +18 -10
- data/lib/html/pipeline/bbcode_filter.rb +19 -0
- data/lib/thredded.rb +15 -7
- data/lib/thredded/errors.rb +22 -0
- data/lib/thredded/topic_user_permissions.rb +6 -2
- data/lib/thredded/version.rb +1 -1
- metadata +54 -17
- data/lib/thredded/filter.rb +0 -4
- data/lib/thredded/filter/at_notification.rb +0 -11
- data/lib/thredded/filter/base.rb +0 -13
- data/lib/thredded/filter/bbcode.rb +0 -75
- data/lib/thredded/filter/emoji.rb +0 -11
- data/lib/thredded/filter/markdown.rb +0 -25
- data/lib/thredded/filter/syntax.rb +0 -28
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: df7af89356e2a063cd53eb323694e5e5ddce74a4
|
4
|
+
data.tar.gz: fffc4fd4e91f2828b3d45dd7761bf01861dc8802
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 77112aabf3ce777664be0d5c19cad1449d7d6cc67f67c04ac82b23b3c2a134b176a30c5a0a59f6eade60ab860004f4a9f17c183810f3a615247b33f9d716e32f
|
7
|
+
data.tar.gz: 49465daf00d25b2bd94dfe37d0b322049f4d8b32864f3674dfb92b9207a6b24d28cbe1ae2e2e650e7f59b83e13f2226e85d30f2bf3410da8285f2c098914694e
|
data/README.mkdn
CHANGED
@@ -16,6 +16,10 @@ Thredded.email_incoming_host = 'incoming.example.com'
|
|
16
16
|
Thredded.email_from = 'no-reply@example.com'
|
17
17
|
Thredded.email_outgoing_prefix = '[Thredded] '
|
18
18
|
Thredded.user_path = ->(user){ "/path/to/where/you/show/#{user}" }
|
19
|
+
Thredded.file_storage = :file # or :fog
|
20
|
+
Thredded.asset_root = 'http://assets.website.com/assets' # where important things, like emojis, might live
|
21
|
+
Thredded.layout = 'thredded' # looks for `app/views/layouts/thredded.html.erb`
|
22
|
+
Thredded.avatar_default = 'mm' # or other gravatar defaults or URL to an image
|
19
23
|
```
|
20
24
|
|
21
25
|
Copy the migrations over to your parent application and migrate:
|
@@ -44,4 +48,25 @@ class User < ActiveRecord::Base
|
|
44
48
|
end
|
45
49
|
```
|
46
50
|
|
51
|
+
2. Ensure you have a view layout that thredded will wrap around its views.
|
52
|
+
|
53
|
+
A couple of notes with regards to your layout.
|
54
|
+
|
55
|
+
* When using route helpers -- eg: `new_session_path`, et al -- make sure to prepend main_app to the helper: `main_app.new_session_path` as rails engines like thredded will not know about those routes' existence unless explicitly told so.
|
56
|
+
* As noted above, by default thredded will look for a layout file in your application called `thredded.html.erb`. If you would like to use something else, like your main application layout, you may change it to that in your initializer.
|
57
|
+
* The chosen layout has two content_tags available to yield - `:thredded_page_title` and `:thredded_page_id`. The views within thredded pass those up through to your layout if you would like to use them. Example layout:
|
58
|
+
|
59
|
+
```html
|
60
|
+
<html>
|
61
|
+
<head>
|
62
|
+
<title>My Application | <%= yield :thredded_page_title %></title>
|
63
|
+
</head>
|
64
|
+
<body id="<%= yield :thredded_page_id %>">
|
65
|
+
<%= yield %>
|
66
|
+
</body>
|
67
|
+
</html>
|
68
|
+
```
|
69
|
+
|
70
|
+
* * *
|
71
|
+
|
47
72
|
For more information see [the full-fledged rails app](https://github.com/jayroh/thredded_app)
|
@@ -1,24 +1,48 @@
|
|
1
1
|
module Thredded
|
2
2
|
class ApplicationController < ::ApplicationController
|
3
3
|
helper Thredded::Engine.helpers
|
4
|
-
helper_method :messageboard, :
|
4
|
+
helper_method :messageboard, :preferences
|
5
|
+
layout Thredded.layout
|
6
|
+
|
7
|
+
rescue_from CanCan::AccessDenied,
|
8
|
+
Thredded::Errors::MessageboardNotFound,
|
9
|
+
Thredded::Errors::MessageboardReadDenied,
|
10
|
+
Thredded::Errors::TopicCreateDenied do |exception|
|
5
11
|
|
6
|
-
rescue_from CanCan::AccessDenied do |exception|
|
7
12
|
redirect_to thredded.root_path, alert: exception.message
|
8
13
|
end
|
9
14
|
|
10
|
-
rescue_from Thredded::Errors::
|
11
|
-
|
15
|
+
rescue_from Thredded::Errors::EmptySearchResults,
|
16
|
+
Thredded::Errors::TopicNotFound do |exception|
|
17
|
+
|
18
|
+
redirect_to messageboard_topics_path(messageboard),
|
19
|
+
alert: exception.message
|
12
20
|
end
|
13
21
|
|
14
22
|
private
|
15
23
|
|
16
|
-
def
|
17
|
-
if
|
18
|
-
|
24
|
+
def authorize_reading(obj)
|
25
|
+
if cannot? :read, obj
|
26
|
+
class_name = obj.class.to_s
|
27
|
+
error = class_name
|
28
|
+
.gsub(/Thredded::/, 'Thredded::Errors::') + 'ReadDenied'
|
29
|
+
raise error.constantize
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def authorize_creating(obj)
|
34
|
+
if cannot? :create, obj
|
35
|
+
class_name = obj.class.to_s
|
36
|
+
error = class_name
|
37
|
+
.gsub(/Thredded::/, 'Thredded::Errors::') + 'CreateDenied'
|
38
|
+
raise error.constantize
|
19
39
|
end
|
20
40
|
end
|
21
41
|
|
42
|
+
def update_user_activity
|
43
|
+
messageboard.update_activity_for!(current_user)
|
44
|
+
end
|
45
|
+
|
22
46
|
def current_ability
|
23
47
|
@current_ability ||= Ability.new(current_user)
|
24
48
|
end
|
@@ -32,11 +56,5 @@ module Thredded
|
|
32
56
|
@preferences ||= UserPreference.where(user_id: current_user.id).first
|
33
57
|
end
|
34
58
|
end
|
35
|
-
|
36
|
-
def topic
|
37
|
-
if messageboard
|
38
|
-
@topic ||= messageboard.topics.find(params[:topic_id])
|
39
|
-
end
|
40
|
-
end
|
41
59
|
end
|
42
60
|
end
|
@@ -4,14 +4,15 @@ module Thredded
|
|
4
4
|
helper_method :messageboard, :topic, :user_topic
|
5
5
|
before_filter :update_user_activity
|
6
6
|
|
7
|
-
rescue_from Thredded::Errors::TopicNotFound do |exception|
|
8
|
-
redirect_to messageboard_topics_path(messageboard), alert: exception.message
|
9
|
-
end
|
10
|
-
|
11
7
|
def index
|
12
8
|
authorize! :read, topic
|
13
9
|
|
14
|
-
@posts =
|
10
|
+
@posts = Post
|
11
|
+
.where(topic_id: topic.id)
|
12
|
+
.includes(:user, :messageboard, :topic, :attachments)
|
13
|
+
.order('id ASC')
|
14
|
+
.page(current_page)
|
15
|
+
|
15
16
|
@post = messageboard.posts.build(topic: topic)
|
16
17
|
|
17
18
|
update_read_status!
|
@@ -23,11 +24,12 @@ module Thredded
|
|
23
24
|
end
|
24
25
|
|
25
26
|
def edit
|
26
|
-
authorize! :
|
27
|
+
authorize! :edit, post
|
27
28
|
end
|
28
29
|
|
29
30
|
def update
|
30
|
-
post.update_attributes(post_params)
|
31
|
+
post.update_attributes(post_params.except(:user, :ip))
|
32
|
+
|
31
33
|
redirect_to messageboard_topic_posts_url(messageboard, topic)
|
32
34
|
end
|
33
35
|
|
@@ -1,36 +1,23 @@
|
|
1
1
|
module Thredded
|
2
2
|
class TopicsController < Thredded::ApplicationController
|
3
|
-
helper_method :current_page
|
3
|
+
helper_method :current_page, :topic
|
4
4
|
before_filter :update_user_activity
|
5
5
|
|
6
6
|
def index
|
7
|
-
|
8
|
-
error = 'You are not authorized access to this messageboard.'
|
9
|
-
redirect_to default_home, flash: { error: error }
|
10
|
-
end
|
7
|
+
authorize_reading messageboard
|
11
8
|
|
12
9
|
@topics = topics
|
13
10
|
end
|
14
11
|
|
15
12
|
def search
|
16
|
-
@topics =
|
17
|
-
|
18
|
-
if @topics.empty?
|
19
|
-
error = 'No topics found for this search.'
|
20
|
-
redirect_to messageboard_topics_path(messageboard),
|
21
|
-
flash: { error: error }
|
22
|
-
end
|
13
|
+
@topics = Topic.search(params[:q], messageboard)
|
23
14
|
end
|
24
15
|
|
25
16
|
def new
|
26
17
|
@topic = messageboard.topics.build
|
27
18
|
@topic.posts.build
|
28
19
|
|
29
|
-
|
30
|
-
error = 'Sorry, you are not authorized to post on this messageboard.'
|
31
|
-
redirect_to messageboard_topics_url(messageboard),
|
32
|
-
flash: { error: error }
|
33
|
-
end
|
20
|
+
authorize_creating @topic
|
34
21
|
end
|
35
22
|
|
36
23
|
def by_category
|
@@ -55,9 +42,16 @@ module Thredded
|
|
55
42
|
private
|
56
43
|
|
57
44
|
def topic
|
58
|
-
|
59
|
-
|
60
|
-
|
45
|
+
@topic ||= messageboard.topics.find_by_slug(params[:id])
|
46
|
+
end
|
47
|
+
|
48
|
+
def topics
|
49
|
+
Topic
|
50
|
+
.public
|
51
|
+
.for_messageboard(messageboard)
|
52
|
+
.includes(:user_topic_reads, :categories, :messageboard, :last_user, :user)
|
53
|
+
.order_by_stuck_and_updated_time
|
54
|
+
.on_page(current_page)
|
61
55
|
end
|
62
56
|
|
63
57
|
def topic_params
|
@@ -88,10 +82,6 @@ module Thredded
|
|
88
82
|
})
|
89
83
|
end
|
90
84
|
|
91
|
-
def search_results
|
92
|
-
Topic.full_text_search(params[:q], messageboard)
|
93
|
-
end
|
94
|
-
|
95
85
|
def topics_by_category(category_id)
|
96
86
|
topics = Category.find(category_id)
|
97
87
|
.topics
|
@@ -102,15 +92,6 @@ module Thredded
|
|
102
92
|
.on_page(current_page)
|
103
93
|
end
|
104
94
|
|
105
|
-
def topics
|
106
|
-
Topic
|
107
|
-
.public
|
108
|
-
.for_messageboard(messageboard)
|
109
|
-
.includes(:user_topic_reads)
|
110
|
-
.order_by_stuck_and_updated_time
|
111
|
-
.on_page(current_page)
|
112
|
-
end
|
113
|
-
|
114
95
|
def current_page
|
115
96
|
params[:page] || 1
|
116
97
|
end
|
@@ -15,6 +15,15 @@ module Thredded
|
|
15
15
|
end
|
16
16
|
end
|
17
17
|
|
18
|
+
def user_link
|
19
|
+
if post.user
|
20
|
+
user_path = Thredded.user_path(post.user)
|
21
|
+
"<a href='#{user_path}'>#{post.user}</a>".html_safe
|
22
|
+
else
|
23
|
+
'<a href="#">?</a>'.html_safe
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
18
27
|
def original
|
19
28
|
post
|
20
29
|
end
|
@@ -36,7 +45,7 @@ module Thredded
|
|
36
45
|
end
|
37
46
|
|
38
47
|
def gravatar_url
|
39
|
-
super.gsub
|
48
|
+
super.gsub(/http:/, '')
|
40
49
|
end
|
41
50
|
|
42
51
|
private
|
@@ -24,12 +24,12 @@ module Thredded
|
|
24
24
|
end
|
25
25
|
|
26
26
|
def last_user_link
|
27
|
-
if last_user && last_user.
|
27
|
+
if last_user && last_user.to_s != 'Anonymous User'
|
28
28
|
last_user_path = Thredded.user_path(last_user)
|
29
29
|
|
30
30
|
"<a href='#{last_user_path}'>#{last_user}</a>".html_safe
|
31
31
|
else
|
32
|
-
|
32
|
+
last_user.to_s
|
33
33
|
end
|
34
34
|
end
|
35
35
|
|
@@ -55,7 +55,7 @@ module Thredded
|
|
55
55
|
end
|
56
56
|
|
57
57
|
def user_link
|
58
|
-
if topic.user && topic.user.
|
58
|
+
if topic.user && topic.user.to_s != 'Anonymous User'
|
59
59
|
user_path = Thredded.user_path(topic.user)
|
60
60
|
"<a href='#{user_path}'>#{topic.user}</a>".html_safe
|
61
61
|
else
|
@@ -63,7 +63,6 @@ module Thredded
|
|
63
63
|
end
|
64
64
|
end
|
65
65
|
|
66
|
-
|
67
66
|
def decorated_topic
|
68
67
|
@decorated_topic ||= TopicDecorator.new(topic)
|
69
68
|
end
|
@@ -73,7 +72,7 @@ module Thredded
|
|
73
72
|
attr_reader :topic, :user
|
74
73
|
|
75
74
|
def read_status
|
76
|
-
if user.
|
75
|
+
if user.id > 0
|
77
76
|
@read_status ||= topic.user_topic_reads.select do |reads|
|
78
77
|
reads.user_id == user.id
|
79
78
|
end
|
@@ -12,8 +12,16 @@ module Thredded
|
|
12
12
|
Thredded::MessageboardUserPermissions.new(messageboard, user).readable?
|
13
13
|
end
|
14
14
|
|
15
|
-
can :
|
16
|
-
Thredded::TopicUserPermissions.new(topic, user, user_details).
|
15
|
+
can :admin, Thredded::Topic do |topic|
|
16
|
+
Thredded::TopicUserPermissions.new(topic, user, user_details).adminable?
|
17
|
+
end
|
18
|
+
|
19
|
+
can :edit, Thredded::Topic do |topic|
|
20
|
+
Thredded::TopicUserPermissions.new(topic, user, user_details).editable?
|
21
|
+
end
|
22
|
+
|
23
|
+
can :update, Thredded::Topic do |topic|
|
24
|
+
Thredded::TopicUserPermissions.new(topic, user, user_details).editable?
|
17
25
|
end
|
18
26
|
|
19
27
|
can :read, Thredded::Topic do |topic|
|
data/app/models/thredded/post.rb
CHANGED
@@ -4,14 +4,8 @@ require 'gravtastic'
|
|
4
4
|
module Thredded
|
5
5
|
class Post < ActiveRecord::Base
|
6
6
|
include Gravtastic
|
7
|
-
|
8
|
-
|
9
|
-
include Thredded::Filter::Markdown
|
10
|
-
include Thredded::Filter::Attachment
|
11
|
-
include Thredded::Filter::Emoji
|
12
|
-
include Thredded::Filter::AtNotification
|
13
|
-
|
14
|
-
gravtastic :user_email
|
7
|
+
|
8
|
+
gravtastic :user_email, default: Thredded.avatar_default
|
15
9
|
paginates_per 50
|
16
10
|
|
17
11
|
belongs_to :messageboard, counter_cache: true
|
@@ -28,10 +22,6 @@ module Thredded
|
|
28
22
|
after_save :notify_at_users
|
29
23
|
after_create :modify_parent_posts_counts
|
30
24
|
|
31
|
-
def self.default_scope
|
32
|
-
order('id ASC')
|
33
|
-
end
|
34
|
-
|
35
25
|
def created_date
|
36
26
|
created_at.strftime("%b %d, %Y %I:%M:%S %Z") if created_at
|
37
27
|
end
|
@@ -41,15 +31,75 @@ module Thredded
|
|
41
31
|
end
|
42
32
|
|
43
33
|
def gravatar_url
|
44
|
-
super.gsub
|
34
|
+
super.gsub(/http:/, '')
|
45
35
|
end
|
46
36
|
|
47
37
|
def self.filters
|
48
38
|
['bbcode', 'markdown']
|
49
39
|
end
|
50
40
|
|
41
|
+
def filtered_content
|
42
|
+
pipeline = HTML::Pipeline.new [
|
43
|
+
HTML::Pipeline::AttachedImageFilter,
|
44
|
+
html_filter_for_pipeline,
|
45
|
+
HTML::Pipeline::SanitizationFilter,
|
46
|
+
HTML::Pipeline::AtMentionFilter,
|
47
|
+
HTML::Pipeline::EmojiFilter,
|
48
|
+
HTML::Pipeline::AutolinkFilter,
|
49
|
+
], context_options
|
50
|
+
|
51
|
+
result = pipeline.call(content)
|
52
|
+
result[:output].to_s
|
53
|
+
end
|
54
|
+
|
51
55
|
private
|
52
56
|
|
57
|
+
def context_options
|
58
|
+
{
|
59
|
+
asset_root: Thredded.asset_root,
|
60
|
+
post: self,
|
61
|
+
whitelist: sanitize_whitelist
|
62
|
+
}
|
63
|
+
end
|
64
|
+
|
65
|
+
def sanitize_whitelist
|
66
|
+
HTML::Pipeline::SanitizationFilter::WHITELIST.deep_merge({
|
67
|
+
attributes: {
|
68
|
+
'code' => ['class'],
|
69
|
+
'img' => ['src', 'class', 'width', 'height'],
|
70
|
+
'blockquote' => ['class'],
|
71
|
+
},
|
72
|
+
transformers: [
|
73
|
+
lambda do |env|
|
74
|
+
node = env[:node]
|
75
|
+
node_name = env[:node_name]
|
76
|
+
|
77
|
+
return if env[:is_whitelisted] || !node.element?
|
78
|
+
return if node_name != 'iframe'
|
79
|
+
return if (node['src'] =~ /\A(https?:)?\/\/(?:www\.)?youtube(?:-nocookie)?\.com\//).nil?
|
80
|
+
|
81
|
+
Sanitize.clean_node!(node, {
|
82
|
+
elements: %w[iframe],
|
83
|
+
|
84
|
+
attributes: {
|
85
|
+
'iframe' => %w[allowfullscreen frameborder height src width]
|
86
|
+
}
|
87
|
+
})
|
88
|
+
|
89
|
+
{ node_whitelist: [node]}
|
90
|
+
end
|
91
|
+
]
|
92
|
+
})
|
93
|
+
end
|
94
|
+
|
95
|
+
def html_filter_for_pipeline
|
96
|
+
if filter == 'bbcode'
|
97
|
+
HTML::Pipeline::BbcodeFilter
|
98
|
+
else
|
99
|
+
HTML::Pipeline::MarkdownFilter
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
53
103
|
def modify_parent_posts_counts
|
54
104
|
Thredded::UserDetail.increment_counter(:posts_count, user_id)
|
55
105
|
topic.last_user = user
|
@@ -62,15 +62,17 @@ module Thredded
|
|
62
62
|
order('sticky DESC, updated_at DESC')
|
63
63
|
end
|
64
64
|
|
65
|
-
def self.
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
65
|
+
def self.search(query, messageboard)
|
66
|
+
sql_builder = Thredded::SearchSqlBuilder.new(query, messageboard)
|
67
|
+
sql = sql_builder.build
|
68
|
+
sql_params = [sql].concat(sql_builder.binds)
|
69
|
+
results = find_by_sql(sql_params)
|
70
|
+
|
71
|
+
if results.empty?
|
72
|
+
raise Thredded::Errors::EmptySearchResults, query
|
73
73
|
end
|
74
|
+
|
75
|
+
results
|
74
76
|
end
|
75
77
|
|
76
78
|
def self.decorate
|
@@ -3,8 +3,7 @@ require 'carrierwave/processing/mini_magick'
|
|
3
3
|
module Thredded
|
4
4
|
class ImageUploader < CarrierWave::Uploader::Base
|
5
5
|
include CarrierWave::MiniMagick
|
6
|
-
|
7
|
-
storage :file
|
6
|
+
storage Thredded.file_storage
|
8
7
|
|
9
8
|
def store_dir
|
10
9
|
"uploads/#{model.class.to_s.underscore}/#{mounted_as}/#{model.id}"
|
@@ -1,7 +1,7 @@
|
|
1
|
-
<%
|
1
|
+
<% cache [current_user, 'post', post] do %>
|
2
|
+
<% post = Thredded::PostDecorator.new(post) %>
|
2
3
|
|
3
|
-
<%= content_tag_for(:article, post) do %>
|
4
|
-
<% cache post do %>
|
4
|
+
<%= content_tag_for(:article, post) do %>
|
5
5
|
<header>
|
6
6
|
<%= image_tag post.gravatar_url, class: 'avatar' %>
|
7
7
|
<cite>
|
@@ -11,19 +11,19 @@
|
|
11
11
|
</header>
|
12
12
|
|
13
13
|
<div class="content">
|
14
|
-
|
14
|
+
<%== post.filtered_content %>
|
15
15
|
<ul class="attachments"><%= render post.attachments %></ul>
|
16
16
|
</div>
|
17
|
-
<% end %>
|
18
17
|
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
18
|
+
<footer>
|
19
|
+
<% if can? :edit, post.original %>
|
20
|
+
<%= link_to 'edit post', edit_messageboard_topic_post_path(
|
21
|
+
messageboard,
|
22
|
+
topic,
|
23
|
+
post
|
24
|
+
),
|
25
|
+
class: 'edit' %>
|
26
|
+
<% end %>
|
27
|
+
</footer>
|
28
|
+
<% end %>
|
29
29
|
<% end %>
|
@@ -0,0 +1,19 @@
|
|
1
|
+
require 'thredded/at_users'
|
2
|
+
|
3
|
+
module HTML
|
4
|
+
class Pipeline
|
5
|
+
class AtMentionFilter < Filter
|
6
|
+
def initialize(text, context = nil, result = nil)
|
7
|
+
super text, context, result
|
8
|
+
@text = text.to_s.gsub "\r", ''
|
9
|
+
@post = context[:post]
|
10
|
+
end
|
11
|
+
|
12
|
+
def call
|
13
|
+
html = Thredded::AtUsers.render(@text, @post.messageboard)
|
14
|
+
html.rstrip!
|
15
|
+
html
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -1,9 +1,16 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
1
|
+
|
2
|
+
module HTML
|
3
|
+
class Pipeline
|
4
|
+
class AttachedImageFilter < Filter
|
5
|
+
def initialize(text, context = nil, result = nil)
|
6
|
+
super text, context, result
|
7
|
+
@text = text.gsub "\r", ''
|
8
|
+
@post = context[:post]
|
9
|
+
end
|
10
|
+
|
11
|
+
def call
|
12
|
+
html = @text
|
13
|
+
matches = @text.scan(/(?<full>\[t:(?<tag>\w+)=?(?<img_nmb>\d+)? ?(?<attribs>[^\]]+)?\])/)
|
7
14
|
|
8
15
|
matches.each do |match|
|
9
16
|
str_buff = ''
|
@@ -12,7 +19,7 @@ module Thredded
|
|
12
19
|
img_number = match[2] ? match[2].to_i - 1 : 0 # default to first attachment
|
13
20
|
attribs = match[3]
|
14
21
|
|
15
|
-
if(tag != 'img' ||
|
22
|
+
if(tag != 'img' || !@post.attachments[img_number])
|
16
23
|
next
|
17
24
|
end
|
18
25
|
|
@@ -20,7 +27,7 @@ module Thredded
|
|
20
27
|
str_buff += '<img '
|
21
28
|
|
22
29
|
# get attachment object at spot img_number - 1
|
23
|
-
attachment =
|
30
|
+
attachment = @post.attachments[img_number]
|
24
31
|
str_buff += 'src="' + attachment.attachment.to_s + '" '
|
25
32
|
|
26
33
|
# do attribute stuff, left right first
|
@@ -41,11 +48,12 @@ module Thredded
|
|
41
48
|
str_buff += '/>'
|
42
49
|
|
43
50
|
# replace in post content
|
44
|
-
|
51
|
+
html = html.sub(full, str_buff)
|
45
52
|
end
|
46
53
|
|
47
|
-
|
54
|
+
html
|
48
55
|
end
|
49
56
|
end
|
50
57
|
end
|
51
58
|
end
|
59
|
+
|
@@ -0,0 +1,19 @@
|
|
1
|
+
require 'bbcoder'
|
2
|
+
|
3
|
+
module HTML
|
4
|
+
class Pipeline
|
5
|
+
class BbcodeFilter < TextFilter
|
6
|
+
def initialize(text, context = {}, result = nil)
|
7
|
+
super text, context, result
|
8
|
+
@context = context
|
9
|
+
@text = @text.gsub "\r", ''
|
10
|
+
end
|
11
|
+
|
12
|
+
def call
|
13
|
+
html = BBCoder.new(@text).to_html.gsub(/\n|\r\n/, '<br />')
|
14
|
+
html.rstrip!
|
15
|
+
"<p>#{html}</p>"
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
data/lib/thredded.rb
CHANGED
@@ -7,24 +7,32 @@ require 'friendly_id'
|
|
7
7
|
require 'nested_form'
|
8
8
|
require 'thredded/email_processor'
|
9
9
|
require 'thredded/errors'
|
10
|
-
require 'thredded/filter/base'
|
11
10
|
require 'thredded/at_notifier'
|
12
|
-
require '
|
13
|
-
require '
|
14
|
-
require '
|
15
|
-
require '
|
16
|
-
require 'thredded/filter/markdown'
|
11
|
+
require 'html/pipeline'
|
12
|
+
require 'html/pipeline/bbcode_filter'
|
13
|
+
require 'html/pipeline/attached_image_filter'
|
14
|
+
require 'html/pipeline/at_mention_filter'
|
17
15
|
require 'thredded/messageboard_user_permissions'
|
18
16
|
require 'thredded/post_user_permissions'
|
19
17
|
require 'thredded/private_topic_user_permissions'
|
20
18
|
require 'thredded/topic_user_permissions'
|
19
|
+
require 'thredded/search_sql_builder'
|
21
20
|
|
22
21
|
module Thredded
|
23
22
|
mattr_accessor :user_class,
|
24
23
|
:email_incoming_host,
|
25
24
|
:email_from,
|
26
25
|
:email_outgoing_prefix,
|
27
|
-
:user_path
|
26
|
+
:user_path,
|
27
|
+
:file_storage,
|
28
|
+
:asset_root,
|
29
|
+
:layout,
|
30
|
+
:avatar_default
|
31
|
+
|
32
|
+
self.file_storage = :file # or :fog
|
33
|
+
self.asset_root = '' # or fully qualified URI to assets
|
34
|
+
self.layout = 'thredded'
|
35
|
+
self.avatar_default = 'mm'
|
28
36
|
|
29
37
|
def self.user_class
|
30
38
|
if @@user_class.is_a?(Class)
|
data/lib/thredded/errors.rb
CHANGED
@@ -14,5 +14,27 @@ module Thredded
|
|
14
14
|
'This messageboard does not exist.'
|
15
15
|
end
|
16
16
|
end
|
17
|
+
|
18
|
+
class MessageboardReadDenied < Thredded::Error
|
19
|
+
def message
|
20
|
+
'You are not authorized access to this messageboard.'
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
class TopicCreateDenied < Thredded::Error
|
25
|
+
def message
|
26
|
+
'You are not authorized to post in this messageboard.'
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
class EmptySearchResults < Thredded::Error
|
31
|
+
def initialize(query)
|
32
|
+
@query = query
|
33
|
+
end
|
34
|
+
|
35
|
+
def message
|
36
|
+
"There are no results for your search - '#{@query}'"
|
37
|
+
end
|
38
|
+
end
|
17
39
|
end
|
18
40
|
end
|
@@ -13,8 +13,12 @@ module Thredded
|
|
13
13
|
member? || messageboard_restrictions_allow?
|
14
14
|
end
|
15
15
|
|
16
|
-
def
|
17
|
-
superadmin? ||
|
16
|
+
def adminable?
|
17
|
+
superadmin? || administrates_messageboard?
|
18
|
+
end
|
19
|
+
|
20
|
+
def editable?
|
21
|
+
superadmin? || administrates_messageboard? || started_by_user?
|
18
22
|
end
|
19
23
|
|
20
24
|
def readable?
|
data/lib/thredded/version.rb
CHANGED
metadata
CHANGED
@@ -1,17 +1,17 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: thredded
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.12
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Joel Oliveira
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2014-01-19 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
|
-
name:
|
14
|
+
name: bbcoder
|
15
15
|
requirement: !ruby/object:Gem::Requirement
|
16
16
|
requirements:
|
17
17
|
- - '>='
|
@@ -67,7 +67,7 @@ dependencies:
|
|
67
67
|
- !ruby/object:Gem::Version
|
68
68
|
version: '0'
|
69
69
|
- !ruby/object:Gem::Dependency
|
70
|
-
name:
|
70
|
+
name: fog
|
71
71
|
requirement: !ruby/object:Gem::Requirement
|
72
72
|
requirements:
|
73
73
|
- - '>='
|
@@ -81,7 +81,7 @@ dependencies:
|
|
81
81
|
- !ruby/object:Gem::Version
|
82
82
|
version: '0'
|
83
83
|
- !ruby/object:Gem::Dependency
|
84
|
-
name:
|
84
|
+
name: friendly_id
|
85
85
|
requirement: !ruby/object:Gem::Requirement
|
86
86
|
requirements:
|
87
87
|
- - '>='
|
@@ -95,7 +95,21 @@ dependencies:
|
|
95
95
|
- !ruby/object:Gem::Version
|
96
96
|
version: '0'
|
97
97
|
- !ruby/object:Gem::Dependency
|
98
|
-
name:
|
98
|
+
name: gemoji
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - '>='
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: '0'
|
104
|
+
type: :runtime
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - '>='
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: '0'
|
111
|
+
- !ruby/object:Gem::Dependency
|
112
|
+
name: github-markdown
|
99
113
|
requirement: !ruby/object:Gem::Requirement
|
100
114
|
requirements:
|
101
115
|
- - '>='
|
@@ -136,6 +150,20 @@ dependencies:
|
|
136
150
|
- - '>='
|
137
151
|
- !ruby/object:Gem::Version
|
138
152
|
version: '0'
|
153
|
+
- !ruby/object:Gem::Dependency
|
154
|
+
name: html-pipeline
|
155
|
+
requirement: !ruby/object:Gem::Requirement
|
156
|
+
requirements:
|
157
|
+
- - '>='
|
158
|
+
- !ruby/object:Gem::Version
|
159
|
+
version: '0'
|
160
|
+
type: :runtime
|
161
|
+
prerelease: false
|
162
|
+
version_requirements: !ruby/object:Gem::Requirement
|
163
|
+
requirements:
|
164
|
+
- - '>='
|
165
|
+
- !ruby/object:Gem::Version
|
166
|
+
version: '0'
|
139
167
|
- !ruby/object:Gem::Dependency
|
140
168
|
name: htmlentities
|
141
169
|
requirement: !ruby/object:Gem::Requirement
|
@@ -235,7 +263,21 @@ dependencies:
|
|
235
263
|
- !ruby/object:Gem::Version
|
236
264
|
version: 4.0.0
|
237
265
|
- !ruby/object:Gem::Dependency
|
238
|
-
name:
|
266
|
+
name: rinku
|
267
|
+
requirement: !ruby/object:Gem::Requirement
|
268
|
+
requirements:
|
269
|
+
- - '>='
|
270
|
+
- !ruby/object:Gem::Version
|
271
|
+
version: '0'
|
272
|
+
type: :runtime
|
273
|
+
prerelease: false
|
274
|
+
version_requirements: !ruby/object:Gem::Requirement
|
275
|
+
requirements:
|
276
|
+
- - '>='
|
277
|
+
- !ruby/object:Gem::Version
|
278
|
+
version: '0'
|
279
|
+
- !ruby/object:Gem::Dependency
|
280
|
+
name: sanitize
|
239
281
|
requirement: !ruby/object:Gem::Requirement
|
240
282
|
requirements:
|
241
283
|
- - '>='
|
@@ -249,7 +291,7 @@ dependencies:
|
|
249
291
|
- !ruby/object:Gem::Version
|
250
292
|
version: '0'
|
251
293
|
- !ruby/object:Gem::Dependency
|
252
|
-
name:
|
294
|
+
name: unf
|
253
295
|
requirement: !ruby/object:Gem::Requirement
|
254
296
|
requirements:
|
255
297
|
- - '>='
|
@@ -374,7 +416,7 @@ dependencies:
|
|
374
416
|
- - '>='
|
375
417
|
- !ruby/object:Gem::Version
|
376
418
|
version: '0'
|
377
|
-
description:
|
419
|
+
description: A messageboard and/or forum engine for Rails 4.0 apps
|
378
420
|
email:
|
379
421
|
- joel@thredded.com
|
380
422
|
executables: []
|
@@ -465,6 +507,9 @@ files:
|
|
465
507
|
- db/migrate/20131019014258_index_necessary_columns.rb
|
466
508
|
- db/migrate/20131026202621_move_filter_to_messageboards.rb
|
467
509
|
- db/migrate/20131029034507_convert_textile_to_markdown.rb
|
510
|
+
- lib/html/pipeline/at_mention_filter.rb
|
511
|
+
- lib/html/pipeline/attached_image_filter.rb
|
512
|
+
- lib/html/pipeline/bbcode_filter.rb
|
468
513
|
- lib/tasks/thredded_tasks.rake
|
469
514
|
- lib/thredded/at_notification_extractor.rb
|
470
515
|
- lib/thredded/at_notifier.rb
|
@@ -472,14 +517,6 @@ files:
|
|
472
517
|
- lib/thredded/email_processor.rb
|
473
518
|
- lib/thredded/engine.rb
|
474
519
|
- lib/thredded/errors.rb
|
475
|
-
- lib/thredded/filter/at_notification.rb
|
476
|
-
- lib/thredded/filter/attachment.rb
|
477
|
-
- lib/thredded/filter/base.rb
|
478
|
-
- lib/thredded/filter/bbcode.rb
|
479
|
-
- lib/thredded/filter/emoji.rb
|
480
|
-
- lib/thredded/filter/markdown.rb
|
481
|
-
- lib/thredded/filter/syntax.rb
|
482
|
-
- lib/thredded/filter.rb
|
483
520
|
- lib/thredded/messageboard_user_permissions.rb
|
484
521
|
- lib/thredded/post_sql_builder.rb
|
485
522
|
- lib/thredded/post_user_permissions.rb
|
data/lib/thredded/filter.rb
DELETED
data/lib/thredded/filter/base.rb
DELETED
@@ -1,75 +0,0 @@
|
|
1
|
-
module Thredded
|
2
|
-
module Filter
|
3
|
-
module Bbcode
|
4
|
-
require 'bb-ruby'
|
5
|
-
|
6
|
-
BB = {
|
7
|
-
'Spoilers' => [
|
8
|
-
/\[spoiler\](.*?)\[\/spoiler\1?\]/mi,
|
9
|
-
'<blockquote class="spoiler">\1</blockquote>',
|
10
|
-
'Spoiler Text',
|
11
|
-
'[spoiler]Dumbledore dies[/spoiler]',
|
12
|
-
:spoiler],
|
13
|
-
'YouTube' => [
|
14
|
-
/\[youtube\]https?\:\/\/(www\.)?youtube.com\/((watch)?\?vi?=|embed\/)(.*?)\[\/youtube\1?\]/i,
|
15
|
-
'<iframe class="youtube" width="560" height="315" src="//www.youtube.com/embed/\4?&rel=0&theme=light&showinfo=0&hd=1&autohide=1&color=white" frameborder="0" allowfullscreen="allowfullscreen"></iframe>',
|
16
|
-
'Youtube Video',
|
17
|
-
:video],
|
18
|
-
'Link (Legacy)' => [
|
19
|
-
/\[link=(?:")?(.*?)(?:")?\](.*?)\[\/link\]/mi,
|
20
|
-
'<a href="\1">\2</a>',
|
21
|
-
'Hyperlink to somewhere else',
|
22
|
-
'Maybe try looking on [link=http://google.com]Google[/link]?',
|
23
|
-
:link],
|
24
|
-
'Link (Legacy Implied)' => [
|
25
|
-
/\[link\](.*?)\[\/link\]/mi,
|
26
|
-
'<a href="\1">\1</a>',
|
27
|
-
'Hyperlink (legacy implied)',
|
28
|
-
"Maybe try looking on [link]http://google.com[/link]",
|
29
|
-
:link],
|
30
|
-
}
|
31
|
-
|
32
|
-
def self.included(base)
|
33
|
-
base.class_eval do
|
34
|
-
Thredded::Post::Filters << :bbcode
|
35
|
-
end
|
36
|
-
end
|
37
|
-
|
38
|
-
def filtered_content
|
39
|
-
if filter.to_sym == :bbcode
|
40
|
-
content = super
|
41
|
-
content = replace_code_tags(content)
|
42
|
-
content = replace_quote_tags(content)
|
43
|
-
content = content.bbcode_to_html(BB)
|
44
|
-
content = remove_empty_p_tags(content)
|
45
|
-
content = CGI.unescapeHTML(content)
|
46
|
-
|
47
|
-
content.html_safe
|
48
|
-
else
|
49
|
-
super
|
50
|
-
end
|
51
|
-
end
|
52
|
-
|
53
|
-
def replace_code_tags(content)
|
54
|
-
content.gsub!(/\[code\]/, '<pre><code>')
|
55
|
-
content.gsub!(/\[code:(\D+?)\]/, '<pre><code class="language-\1" lang="\1">')
|
56
|
-
content.gsub!(/\[\/code\]/, '</code></pre>')
|
57
|
-
content.html_safe
|
58
|
-
end
|
59
|
-
|
60
|
-
def replace_quote_tags(content)
|
61
|
-
content.gsub!(/\[quote(:.*)?=(?:")?(.*?)(?:")?\]/,
|
62
|
-
'</p><fieldset><legend>\2</legend><blockquote><p>')
|
63
|
-
content.gsub!(/\[quote(:.*?)?\]/,
|
64
|
-
'</p><fieldset><blockquote><p>')
|
65
|
-
content.gsub!(/\[\/quote\]/,
|
66
|
-
'</p></blockquote></fieldset><p>')
|
67
|
-
content.html_safe
|
68
|
-
end
|
69
|
-
|
70
|
-
def remove_empty_p_tags(content)
|
71
|
-
content.gsub(/<p>\s*?<\/p>/, '')
|
72
|
-
end
|
73
|
-
end
|
74
|
-
end
|
75
|
-
end
|
@@ -1,25 +0,0 @@
|
|
1
|
-
require 'redcarpet'
|
2
|
-
|
3
|
-
module Thredded
|
4
|
-
module Filter
|
5
|
-
module Markdown
|
6
|
-
def self.included(base)
|
7
|
-
base.class_eval do
|
8
|
-
Thredded::Post::Filters << :markdown
|
9
|
-
end
|
10
|
-
end
|
11
|
-
|
12
|
-
def filtered_content
|
13
|
-
if filter.to_sym == :markdown
|
14
|
-
renderer = Redcarpet::Render::HTML.new(hard_wrap: true, filter_html: true)
|
15
|
-
markdown = Redcarpet::Markdown.new(renderer, autolink: true,
|
16
|
-
space_after_headers: true, no_intraemphasis: true,
|
17
|
-
fenced_code: true, gh_blockcode: true)
|
18
|
-
@filtered_content = markdown.render(super).html_safe
|
19
|
-
else
|
20
|
-
super
|
21
|
-
end
|
22
|
-
end
|
23
|
-
end
|
24
|
-
end
|
25
|
-
end
|
@@ -1,28 +0,0 @@
|
|
1
|
-
require 'coderay'
|
2
|
-
require 'htmlentities'
|
3
|
-
|
4
|
-
module Thredded
|
5
|
-
module Filter
|
6
|
-
module Syntax
|
7
|
-
def filtered_content
|
8
|
-
content = String.new(super)
|
9
|
-
content = HTMLEntities.new.decode(content)
|
10
|
-
|
11
|
-
content = content.to_s
|
12
|
-
.gsub(/\<pre\>\<code( lang="(.+?)")?\>(.+?)\<\/code\>\<\/pre\>/m) do
|
13
|
-
filter = $2.nil? ? :ruby : $2.to_sym
|
14
|
-
temp_code = $3.gsub(/"/, '"').
|
15
|
-
gsub(/'/,"'").
|
16
|
-
gsub(/&/, "&").
|
17
|
-
gsub(/>/, ">").
|
18
|
-
gsub(/</, "<").
|
19
|
-
gsub(/\<br \/\>/, "")
|
20
|
-
|
21
|
-
::CodeRay.scan(temp_code, filter).div(css: :class)
|
22
|
-
end
|
23
|
-
|
24
|
-
content.html_safe
|
25
|
-
end
|
26
|
-
end
|
27
|
-
end
|
28
|
-
end
|