thredded 0.0.10 → 0.0.12
Sign up to get free protection for your applications and to get access to all the features.
- 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
|