simple_discussion 0.1.0 → 0.9.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +1 -0
- data/README.md +52 -8
- data/app/assets/stylesheets/simple_discussion.scss +63 -0
- data/app/controllers/simple_discussion/application_controller.rb +25 -0
- data/app/controllers/simple_discussion/forum_categories_controller.rb +17 -0
- data/app/controllers/simple_discussion/forum_posts_controller.rb +66 -0
- data/app/controllers/simple_discussion/forum_threads_controller.rb +72 -0
- data/app/controllers/simple_discussion/notifications_controller.rb +19 -0
- data/app/helpers/simple_discussion/forum_posts_helper.rb +29 -0
- data/app/helpers/simple_discussion/forum_threads_helper.rb +28 -0
- data/app/models/forum_category.rb +13 -0
- data/app/models/forum_post.rb +15 -0
- data/app/models/forum_subscription.rb +19 -0
- data/app/models/forum_thread.rb +75 -0
- data/app/views/layouts/simple_discussion.html.erb +59 -0
- data/app/views/shared/_spacer.html.erb +1 -0
- data/app/views/simple_discussion/forum_posts/_form.html.erb +32 -0
- data/app/views/simple_discussion/forum_posts/_forum_post.html.erb +58 -0
- data/app/views/simple_discussion/forum_posts/edit.html.erb +24 -0
- data/app/views/simple_discussion/forum_threads/_form.html.erb +44 -0
- data/app/views/simple_discussion/forum_threads/_forum_thread.html.erb +37 -0
- data/app/views/simple_discussion/forum_threads/edit.html.erb +7 -0
- data/app/views/simple_discussion/forum_threads/index.html.erb +11 -0
- data/app/views/simple_discussion/forum_threads/new.html.erb +5 -0
- data/app/views/simple_discussion/forum_threads/show.html.erb +19 -0
- data/config/routes.rb +24 -0
- data/db/migrate/20170417012930_create_forum_categories.rb +23 -0
- data/db/migrate/20170417012931_create_forum_threads.rb +15 -0
- data/db/migrate/20170417012932_create_forum_posts.rb +12 -0
- data/db/migrate/20170417012933_create_forum_subscriptions.rb +11 -0
- data/lib/simple_discussion/engine.rb +5 -0
- data/lib/simple_discussion/forum_user.rb +11 -0
- data/lib/simple_discussion/version.rb +1 -1
- data/lib/simple_discussion.rb +57 -1
- data/simple_discussion.gemspec +5 -3
- metadata +76 -18
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: ea112341c29ae6d453a260175e1fab4fe8742243
|
4
|
+
data.tar.gz: '08670316d53d01df1dfc1f0101d59f43e46be643'
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 87914991332ab15ab141c77baf1924fc8c89b4f4e01c1fc9121eff52309daa4a23c1310833db545750d25dc82dd9c8805647ff834147d7fca3ed1da96d336390
|
7
|
+
data.tar.gz: fde66ad800d05ea86cf1251950f490f2d221d10b8e8807d67863c41f022a54a713391e0e10148a7ea9ede736276dcba5d03a92babd0f1c66fa1b45f084332846
|
data/.gitignore
CHANGED
data/README.md
CHANGED
@@ -1,11 +1,13 @@
|
|
1
1
|
# SimpleDiscussion
|
2
2
|
|
3
|
-
|
4
|
-
|
5
|
-
|
3
|
+
SimpleDiscussion is a Rails forum gem extracting the forum from
|
4
|
+
[GoRails' forum](https://gorails.com/forum). It includes categories,
|
5
|
+
simple moderation, the ability to mark threads as solved, and more.
|
6
6
|
|
7
7
|
## Installation
|
8
8
|
|
9
|
+
Before you get started, SimpleDiscussion requires a `User` model in your application (for now).
|
10
|
+
|
9
11
|
Add this line to your application's Gemfile:
|
10
12
|
|
11
13
|
```ruby
|
@@ -14,15 +16,57 @@ gem 'simple_discussion'
|
|
14
16
|
|
15
17
|
And then execute:
|
16
18
|
|
17
|
-
|
19
|
+
```bash
|
20
|
+
bundle
|
21
|
+
```
|
22
|
+
|
23
|
+
Install the migrations and migrate:
|
24
|
+
|
25
|
+
```bash
|
26
|
+
rails simple_discussion:install:migrations
|
27
|
+
rails db:migrate
|
28
|
+
```
|
29
|
+
|
30
|
+
Add SimpleDiscussion to your `User` model. The model **must** have `name` method which will be used to display the user's name on the forum. Currently only a model named `User` will work, but this will be fixed shortly.
|
31
|
+
|
32
|
+
```ruby
|
33
|
+
class User < ActiveRecord::Base
|
34
|
+
include SimpleDiscussion::ForumUser
|
35
|
+
|
36
|
+
def name
|
37
|
+
"#{first_name} #{last_name}"
|
38
|
+
end
|
39
|
+
end
|
40
|
+
```
|
41
|
+
|
42
|
+
Optionally, you can add a `moderator` flag to the `User` model to allow users to edit threads and posts they didn't write.
|
43
|
+
|
44
|
+
```bash
|
45
|
+
rails g migration AddModeratorToUsers moderator:boolean
|
46
|
+
rails db:migrate
|
47
|
+
```
|
48
|
+
|
49
|
+
Add the following line to your `config/routes.rb` file:
|
18
50
|
|
19
|
-
|
51
|
+
```ruby
|
52
|
+
mount SimpleDiscussion::Engine => "/forum"
|
53
|
+
```
|
20
54
|
|
21
|
-
|
55
|
+
You can also add the CSS to your `application.css` to load some helpful default styles.
|
56
|
+
|
57
|
+
```javascript
|
58
|
+
//= require simple_discussion
|
59
|
+
```
|
22
60
|
|
23
61
|
## Usage
|
24
62
|
|
25
|
-
|
63
|
+
To get all the basic functionality, the only thing you need to do is add a link to SimpleDiscussion in your navbar.
|
64
|
+
|
65
|
+
```erb
|
66
|
+
<%= link_to "Forum", simple_discussion_path %>
|
67
|
+
```
|
68
|
+
|
69
|
+
This will take the user to the views inside the Rails engine.
|
26
70
|
|
27
71
|
## Development
|
28
72
|
|
@@ -32,7 +76,7 @@ To install this gem onto your local machine, run `bundle exec rake install`. To
|
|
32
76
|
|
33
77
|
## Contributing
|
34
78
|
|
35
|
-
Bug reports and pull requests are welcome on GitHub at https://github.com/
|
79
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/excid3/simple_discussion. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](http://contributor-covenant.org) code of conduct.
|
36
80
|
|
37
81
|
## License
|
38
82
|
|
@@ -0,0 +1,63 @@
|
|
1
|
+
/* Allow pagination to be centered */
|
2
|
+
.forum-threads-nav nav {
|
3
|
+
display: inline-block;
|
4
|
+
}
|
5
|
+
|
6
|
+
/* Highlight the active navigation item in the sidebar */
|
7
|
+
.forum-thread-filters {
|
8
|
+
a {
|
9
|
+
color: #555;
|
10
|
+
}
|
11
|
+
|
12
|
+
.active {
|
13
|
+
color: #222;
|
14
|
+
font-weight: bolder;
|
15
|
+
}
|
16
|
+
}
|
17
|
+
|
18
|
+
/* Formatting for the forum threads */
|
19
|
+
.forum-thread {
|
20
|
+
h4 a {
|
21
|
+
color: #222;
|
22
|
+
}
|
23
|
+
}
|
24
|
+
|
25
|
+
.thread-details {
|
26
|
+
color: #999;
|
27
|
+
font-size: 12px;
|
28
|
+
text-transform: uppercase;
|
29
|
+
}
|
30
|
+
|
31
|
+
/* Display the forum post count above the "posts" text on the index page */
|
32
|
+
.thread-posts-count {
|
33
|
+
color: #222;
|
34
|
+
text-align: center;
|
35
|
+
|
36
|
+
span, small {
|
37
|
+
display: block;
|
38
|
+
}
|
39
|
+
|
40
|
+
.count {
|
41
|
+
font-size: 1.5em;
|
42
|
+
font-weight: 300;
|
43
|
+
line-height: 1em;
|
44
|
+
}
|
45
|
+
}
|
46
|
+
|
47
|
+
/* Formatting for the forum posts themselves */
|
48
|
+
.forum-post {
|
49
|
+
position: relative;
|
50
|
+
|
51
|
+
/* When the URL anchor matches a post id, highlight that item */
|
52
|
+
&:target {
|
53
|
+
background-color: rgb(248, 238, 199);
|
54
|
+
}
|
55
|
+
|
56
|
+
/* Fill this out if you want to highlight the OPs posts */
|
57
|
+
&.original-poster {
|
58
|
+
}
|
59
|
+
|
60
|
+
&.solved {
|
61
|
+
border: 2px solid #5cb85c;
|
62
|
+
}
|
63
|
+
}
|
@@ -0,0 +1,25 @@
|
|
1
|
+
class SimpleDiscussion::ApplicationController < ::ApplicationController
|
2
|
+
layout "simple_discussion"
|
3
|
+
|
4
|
+
def page_number
|
5
|
+
page = params.fetch(:page, '').gsub(/[^0-9]/, '').to_i
|
6
|
+
page = "1" if page.zero?
|
7
|
+
page
|
8
|
+
end
|
9
|
+
|
10
|
+
def is_moderator_or_owner?(object)
|
11
|
+
is_moderator? || object.user == current_user
|
12
|
+
end
|
13
|
+
helper_method :is_moderator_or_owner?
|
14
|
+
|
15
|
+
def is_moderator?
|
16
|
+
current_user.respond_to?(:moderator) && current_user.moderator?
|
17
|
+
end
|
18
|
+
helper_method :is_moderator?
|
19
|
+
|
20
|
+
def require_moderator_or_author!
|
21
|
+
unless is_moderator_or_owner?(@forum_thread)
|
22
|
+
redirect_to simple_discussion.root_path, alert: "You aren't allowed to do that."
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
class SimpleDiscussion::ForumCategoriesController < SimpleDiscussion::ApplicationController
|
2
|
+
before_action :set_category
|
3
|
+
|
4
|
+
def index
|
5
|
+
@forum_threads = ForumThread.where(forum_category: @category) if @category.present?
|
6
|
+
@forum_threads = @forum_threads.pinned_first.sorted.includes(:user, :forum_category).paginate(per_page: 10, page: page_number)
|
7
|
+
render "simple_discussion/forum_threads/index"
|
8
|
+
end
|
9
|
+
|
10
|
+
private
|
11
|
+
|
12
|
+
def set_category
|
13
|
+
@category = ForumCategory.friendly.find(params[:id])
|
14
|
+
rescue ActiveRecord::RecordNotFound
|
15
|
+
redirect_to simple_discussion.forum_threads_path
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,66 @@
|
|
1
|
+
class SimpleDiscussion::ForumPostsController < SimpleDiscussion::ApplicationController
|
2
|
+
before_action :authenticate_user!
|
3
|
+
before_action :set_forum_thread
|
4
|
+
before_action :set_forum_post, only: [:edit, :update]
|
5
|
+
before_action :require_moderator_or_author!, only: [:edit, :update, :solved, :unsolved]
|
6
|
+
|
7
|
+
def create
|
8
|
+
@forum_post = @forum_thread.forum_posts.new(forum_post_params)
|
9
|
+
@forum_post.user_id = current_user.id
|
10
|
+
|
11
|
+
if @forum_post.save
|
12
|
+
#ForumNotificationJob.perform_later(@forum_post.id)
|
13
|
+
redirect_to simple_discussion.forum_thread_path(@forum_thread, anchor: "forum_post_#{@forum_post.id}")
|
14
|
+
else
|
15
|
+
render template: "simple_discussion/forum_threads/show"
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def edit
|
20
|
+
end
|
21
|
+
|
22
|
+
def update
|
23
|
+
if @forum_post.update(forum_post_params)
|
24
|
+
redirect_to simple_discussion.forum_thread_path(@forum_thread)
|
25
|
+
else
|
26
|
+
render action: :edit
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def solved
|
31
|
+
@forum_post = @forum_thread.forum_posts.find(params[:id])
|
32
|
+
|
33
|
+
@forum_thread.forum_posts.update_all(solved: false)
|
34
|
+
@forum_post.update_column(:solved, true)
|
35
|
+
@forum_thread.update_column(:solved, true)
|
36
|
+
|
37
|
+
redirect_to simple_discussion.forum_thread_path(@forum_thread, anchor: ActionView::RecordIdentifier.dom_id(@forum_post))
|
38
|
+
end
|
39
|
+
|
40
|
+
def unsolved
|
41
|
+
@forum_post = @forum_thread.forum_posts.find(params[:id])
|
42
|
+
|
43
|
+
@forum_thread.forum_posts.update_all(solved: false)
|
44
|
+
@forum_thread.update_column(:solved, false)
|
45
|
+
|
46
|
+
redirect_to simple_discussion.forum_thread_path(@forum_thread, anchor: ActionView::RecordIdentifier.dom_id(@forum_post))
|
47
|
+
end
|
48
|
+
|
49
|
+
private
|
50
|
+
|
51
|
+
def set_forum_thread
|
52
|
+
@forum_thread = ForumThread.friendly.find(params[:forum_thread_id])
|
53
|
+
end
|
54
|
+
|
55
|
+
def set_forum_post
|
56
|
+
if current_user.moderator?
|
57
|
+
@forum_post = @forum_thread.forum_posts.find(params[:id])
|
58
|
+
else
|
59
|
+
@forum_post = current_user.forum_posts.find(params[:id])
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
def forum_post_params
|
64
|
+
params.require(:forum_post).permit(:body)
|
65
|
+
end
|
66
|
+
end
|
@@ -0,0 +1,72 @@
|
|
1
|
+
class SimpleDiscussion::ForumThreadsController < SimpleDiscussion::ApplicationController
|
2
|
+
before_action :authenticate_user!, only: [:mine, :participating, :new, :create]
|
3
|
+
before_action :set_forum_thread, only: [:show, :edit, :update]
|
4
|
+
before_action :require_moderator_or_author!, only: [:edit, :update]
|
5
|
+
|
6
|
+
def index
|
7
|
+
@forum_threads = ForumThread.pinned_first.sorted.includes(:user, :forum_category).paginate(page: page_number)
|
8
|
+
end
|
9
|
+
|
10
|
+
def answered
|
11
|
+
@forum_threads = ForumThread.solved.sorted.includes(:user, :forum_category).paginate(page: page_number)
|
12
|
+
render action: :index
|
13
|
+
end
|
14
|
+
|
15
|
+
def unanswered
|
16
|
+
@forum_threads = ForumThread.unsolved.sorted.includes(:user, :forum_category).paginate(page: page_number)
|
17
|
+
render action: :index
|
18
|
+
end
|
19
|
+
|
20
|
+
def mine
|
21
|
+
@forum_threads = ForumThread.where(user: current_user).sorted.includes(:user, :forum_category).paginate(page: page_number)
|
22
|
+
render action: :index
|
23
|
+
end
|
24
|
+
|
25
|
+
def participating
|
26
|
+
@forum_threads = ForumThread.includes(:user, :forum_category).joins(:forum_posts).where(forum_posts: { user_id: current_user.id }).distinct(forum_posts: :id).sorted.paginate(page: page_number)
|
27
|
+
render action: :index
|
28
|
+
end
|
29
|
+
|
30
|
+
def show
|
31
|
+
@forum_post = ForumPost.new
|
32
|
+
@forum_post.user = current_user
|
33
|
+
end
|
34
|
+
|
35
|
+
def new
|
36
|
+
@forum_thread = ForumThread.new
|
37
|
+
@forum_thread.forum_posts.new
|
38
|
+
end
|
39
|
+
|
40
|
+
def create
|
41
|
+
@forum_thread = current_user.forum_threads.new(forum_thread_params)
|
42
|
+
@forum_thread.forum_posts.each{ |post| post.user_id = current_user.id }
|
43
|
+
|
44
|
+
if @forum_thread.save
|
45
|
+
#ForumThreadNotificationJob.perform_later(@forum_thread.forum_posts.first.id)
|
46
|
+
redirect_to simple_discussion.forum_thread_path(@forum_thread)
|
47
|
+
else
|
48
|
+
render action: :new
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
def edit
|
53
|
+
end
|
54
|
+
|
55
|
+
def update
|
56
|
+
if @forum_thread.update(forum_thread_params)
|
57
|
+
redirect_to simple_discussion.forum_thread_path(@forum_thread), notice: "Your changes were saved."
|
58
|
+
else
|
59
|
+
render action: :edit
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
private
|
64
|
+
|
65
|
+
def set_forum_thread
|
66
|
+
@forum_thread = ForumThread.friendly.find(params[:id])
|
67
|
+
end
|
68
|
+
|
69
|
+
def forum_thread_params
|
70
|
+
params.require(:forum_thread).permit(:title, :forum_category_id, forum_posts_attributes: [:body])
|
71
|
+
end
|
72
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
class SimpleDiscussion::NotificationsController < SimpleDiscussion::ApplicationController
|
2
|
+
before_action :authenticate_user!
|
3
|
+
before_action :set_forum_thread
|
4
|
+
|
5
|
+
def create
|
6
|
+
@forum_thread.toggle_subscription(current_user)
|
7
|
+
redirect_to simple_discussion.forum_thread_path(@forum_thread)
|
8
|
+
end
|
9
|
+
|
10
|
+
def show
|
11
|
+
redirect_to simple_discussion.forum_thread_path(@forum_thread)
|
12
|
+
end
|
13
|
+
|
14
|
+
private
|
15
|
+
|
16
|
+
def set_forum_thread
|
17
|
+
@forum_thread = ForumThread.friendly.find(params[:forum_thread_id])
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
module SimpleDiscussion::ForumPostsHelper
|
2
|
+
# Override this to use avatars from other places than Gravatar
|
3
|
+
def avatar_tag(email)
|
4
|
+
gravatar_image_tag(email, gravatar: { size: 40 }, class: "rounded avatar")
|
5
|
+
end
|
6
|
+
|
7
|
+
def category_link(category)
|
8
|
+
link_to category.name, simple_discussion.forum_category_forum_threads_path(category),
|
9
|
+
style: "color: #{category.color}"
|
10
|
+
end
|
11
|
+
|
12
|
+
# Override this method to provide your own content formatting like Markdown
|
13
|
+
def formatted_content(text)
|
14
|
+
simple_format(text)
|
15
|
+
end
|
16
|
+
|
17
|
+
def forum_post_classes(forum_post)
|
18
|
+
klasses = ["forum-post", "card", "mb-3"]
|
19
|
+
klasses << "solved" if forum_post.solved?
|
20
|
+
klasses << "original-poster" if forum_post.user == @forum_thread.user
|
21
|
+
klasses
|
22
|
+
end
|
23
|
+
|
24
|
+
def forum_user_badge(user)
|
25
|
+
if user.respond_to?(:moderator) && user.moderator?
|
26
|
+
content_tag :span, "Mod", class: "badge badge-default"
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module SimpleDiscussion::ForumThreadsHelper
|
2
|
+
# Used for flagging links in the navbar as active
|
3
|
+
def forum_link_to(path, opts={}, &block)
|
4
|
+
link_to path, class: forum_link_class(path, opts), &block
|
5
|
+
end
|
6
|
+
|
7
|
+
def forum_link_class(matches, opts={})
|
8
|
+
case matches
|
9
|
+
when Array
|
10
|
+
"active" if matches.any?{ |m| request.path.starts_with?(m) }
|
11
|
+
when String
|
12
|
+
"active" if opts.fetch(:exact, false) ? request.path == matches : request.path.starts_with?(matches)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
# A nice hack to manipulate the layout so we can have sub-layouts
|
17
|
+
# without any changes in the user's application.
|
18
|
+
#
|
19
|
+
# We use this for rendering the sidebar layout for all the forum pages
|
20
|
+
#
|
21
|
+
# https://mattbrictson.com/easier-nested-layouts-in-rails
|
22
|
+
#
|
23
|
+
def parent_layout(layout)
|
24
|
+
@view_flow.set(:layout, output_buffer)
|
25
|
+
output = render(file: "layouts/#{layout}")
|
26
|
+
self.output_buffer = ActionView::OutputBuffer.new(output)
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
class ForumCategory < ApplicationRecord
|
2
|
+
extend FriendlyId
|
3
|
+
friendly_id :name, use: :slugged
|
4
|
+
|
5
|
+
scope :sorted, ->{ order(name: :asc) }
|
6
|
+
|
7
|
+
validates :name, :slug, :color, presence: true
|
8
|
+
|
9
|
+
def color
|
10
|
+
colour = super
|
11
|
+
colour.start_with?("#") ? colour : "##{colour}"
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
class ForumPost < ApplicationRecord
|
2
|
+
belongs_to :forum_thread, counter_cache: true, touch: true
|
3
|
+
belongs_to :user
|
4
|
+
has_many :reactions, as: :reactable
|
5
|
+
|
6
|
+
validates :user_id, :body, presence: true
|
7
|
+
|
8
|
+
scope :sorted, ->{ order(:created_at) }
|
9
|
+
|
10
|
+
after_update :solve_forum_thread, if: :solved?
|
11
|
+
|
12
|
+
def solve_forum_thread
|
13
|
+
forum_thread.update(solved: true)
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
class ForumSubscription < ApplicationRecord
|
2
|
+
belongs_to :forum_thread
|
3
|
+
belongs_to :user
|
4
|
+
|
5
|
+
scope :optin, ->{ where(subscription_type: :optin) }
|
6
|
+
scope :optout, ->{ where(subscription_type: :optout) }
|
7
|
+
|
8
|
+
validates :subscription_type, presence: true, inclusion: { in: %w{ optin optout } }
|
9
|
+
validates :user_id, uniqueness: { scope: :forum_thread_id }
|
10
|
+
|
11
|
+
def toggle!
|
12
|
+
case subscription_type
|
13
|
+
when "optin"
|
14
|
+
update(subscription_type: "optout")
|
15
|
+
when "optout"
|
16
|
+
update(subscription_type: "optin")
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,75 @@
|
|
1
|
+
class ForumThread < ApplicationRecord
|
2
|
+
extend FriendlyId
|
3
|
+
friendly_id :title, use: :slugged
|
4
|
+
|
5
|
+
belongs_to :forum_category
|
6
|
+
belongs_to :user
|
7
|
+
has_many :forum_posts
|
8
|
+
has_many :forum_subscriptions
|
9
|
+
has_many :optin_subscribers, ->{ where(forum_subscriptions: { subscription_type: :optin }) }, through: :forum_subscriptions, source: :user
|
10
|
+
has_many :optout_subscribers, ->{ where(forum_subscriptions: { subscription_type: :optout }) }, through: :forum_subscriptions, source: :user
|
11
|
+
has_many :users, through: :forum_posts
|
12
|
+
|
13
|
+
accepts_nested_attributes_for :forum_posts
|
14
|
+
|
15
|
+
validates :forum_category, presence: true
|
16
|
+
validates :user_id, :title, presence: true
|
17
|
+
validates_associated :forum_posts
|
18
|
+
|
19
|
+
scope :pinned_first, ->{ order(pinned: :desc) }
|
20
|
+
scope :solved, ->{ where(solved: true) }
|
21
|
+
scope :sorted, ->{ order(updated_at: :desc) }
|
22
|
+
scope :unpinned, ->{ where.not(pinned: true) }
|
23
|
+
scope :unsolved, ->{ where.not(solved: true) }
|
24
|
+
|
25
|
+
def subscribed_users
|
26
|
+
(users + optin_subscribers).uniq - optout_subscribers
|
27
|
+
end
|
28
|
+
|
29
|
+
def subscription_for(user)
|
30
|
+
return nil if user.nil?
|
31
|
+
forum_subscriptions.find_by(user_id: user.id)
|
32
|
+
end
|
33
|
+
|
34
|
+
def subscribed?(user)
|
35
|
+
return false if user.nil?
|
36
|
+
|
37
|
+
subscription = subscription_for(user)
|
38
|
+
|
39
|
+
if subscription.present?
|
40
|
+
subscription.subscription_type == "optin"
|
41
|
+
else
|
42
|
+
forum_posts.where(user_id: user.id).any?
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def toggle_subscription(user)
|
47
|
+
subscription = subscription_for(user)
|
48
|
+
|
49
|
+
if subscription.present?
|
50
|
+
subscription.toggle!
|
51
|
+
elsif forum_posts.where(user_id: user.id).any?
|
52
|
+
forum_subscriptions.create(user: user, subscription_type: "optout")
|
53
|
+
else
|
54
|
+
forum_subscriptions.create(user: user, subscription_type: "optin")
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
def subscribed_reason(user)
|
59
|
+
return "You’re not receiving notifications from this thread." if user.nil?
|
60
|
+
|
61
|
+
subscription = subscription_for(user)
|
62
|
+
|
63
|
+
if subscription.present?
|
64
|
+
if subscription.subscription_type == "optout"
|
65
|
+
"You're ignoring this thread."
|
66
|
+
elsif subscription.subscription_type == "optin"
|
67
|
+
"You're receiving notifications because you've subscribed to this thread."
|
68
|
+
end
|
69
|
+
elsif forum_posts.where(user_id: user.id).any?
|
70
|
+
"You're receiving notifications because you've posted in this thread."
|
71
|
+
else
|
72
|
+
"You're not receiving notifications from this thread."
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
<div class="row">
|
2
|
+
<div class="col-sm-3">
|
3
|
+
|
4
|
+
<div class="card card-block">
|
5
|
+
<%= link_to 'Ask A Question', simple_discussion.new_forum_thread_path, class: "btn btn-outline-primary btn-block" %>
|
6
|
+
<hr />
|
7
|
+
|
8
|
+
<div class="forum-thread-filters">
|
9
|
+
<h5><strong>Filters</strong></h5>
|
10
|
+
<div><%= forum_link_to simple_discussion.forum_threads_path, exact: true do %><%= icon "bars" %> All Threads <% end %></div>
|
11
|
+
<% if user_signed_in? %>
|
12
|
+
<div><%= forum_link_to simple_discussion.mine_forum_threads_path do %><%= icon "user-circle-o" %> My Questions<% end %></div>
|
13
|
+
<div><%= forum_link_to simple_discussion.participating_forum_threads_path do %><%= icon "comments-o" %> Participating<% end %></div>
|
14
|
+
<% end %>
|
15
|
+
<div><%= forum_link_to simple_discussion.answered_forum_threads_path do %><%= icon "check" %> Answered<% end %></div>
|
16
|
+
<div><%= forum_link_to simple_discussion.unanswered_forum_threads_path do %><%= icon "question" %> Unanswered<% end %></div>
|
17
|
+
</div>
|
18
|
+
|
19
|
+
<hr />
|
20
|
+
|
21
|
+
<div class="forum-thread-filters">
|
22
|
+
<p><strong>By Category</strong></p>
|
23
|
+
<div><%= forum_link_to simple_discussion.forum_threads_path, exact: true do %><%= icon "circle" %> All<% end %></div>
|
24
|
+
<% ForumCategory.sorted.each do |category| %>
|
25
|
+
<div>
|
26
|
+
<%= forum_link_to simple_discussion.forum_category_forum_threads_path(category) do %>
|
27
|
+
<%= icon "circle", style: "color: #{category.color}" %>
|
28
|
+
<%= category.name %>
|
29
|
+
<% end %>
|
30
|
+
</div>
|
31
|
+
<% end %>
|
32
|
+
</div>
|
33
|
+
|
34
|
+
<% if @forum_thread.present? && @forum_thread.persisted? %>
|
35
|
+
<hr />
|
36
|
+
|
37
|
+
<%# User has not posted in the thread or subscribed %>
|
38
|
+
<h5>Notifications</h5>
|
39
|
+
|
40
|
+
<%= link_to simple_discussion.forum_thread_notifications_path(@forum_thread), method: :post, class: "btn btn-secondary btn-sm btn-block mb-2" do %>
|
41
|
+
<% if @forum_thread.subscribed? current_user %>
|
42
|
+
<%= icon "bell-slash" %> Unsubscribe
|
43
|
+
<% else %>
|
44
|
+
<%= icon "bell" %> Subscribe
|
45
|
+
<% end %>
|
46
|
+
<% end %>
|
47
|
+
|
48
|
+
<small><%= @forum_thread.subscribed_reason(current_user) %></small>
|
49
|
+
<% end %>
|
50
|
+
</div>
|
51
|
+
|
52
|
+
</div>
|
53
|
+
|
54
|
+
<div class="col">
|
55
|
+
<%= yield %>
|
56
|
+
</div>
|
57
|
+
</div>
|
58
|
+
|
59
|
+
<% parent_layout("application") %>
|
@@ -0,0 +1 @@
|
|
1
|
+
<hr/>
|
@@ -0,0 +1,32 @@
|
|
1
|
+
<%= form_for [@forum_thread, @forum_post],
|
2
|
+
url: (@forum_post.persisted? ? simple_discussion.forum_thread_forum_post_path(@forum_thread, @forum_post) : simple_discussion.forum_thread_forum_posts_path(@forum_thread)),
|
3
|
+
html: { data: { behavior: "comment-form" } } do |f| %>
|
4
|
+
|
5
|
+
<% if @forum_post.errors.any? %>
|
6
|
+
<div id="error_explanation">
|
7
|
+
<h2><%= pluralize(@forum_post.errors.count, "error") %> prohibited this forum_post from being saved:</h2>
|
8
|
+
|
9
|
+
<ul>
|
10
|
+
<% @forum_post.errors.full_messages.each do |message| %>
|
11
|
+
<li><%= message %></li>
|
12
|
+
<% end %>
|
13
|
+
</ul>
|
14
|
+
</div>
|
15
|
+
<% end %>
|
16
|
+
|
17
|
+
<div class="form-group">
|
18
|
+
<%= f.text_area :body, placeholder: "Add a comment", rows: 8, class: "form-control simplemde", data: { behavior: "comment-body" } %>
|
19
|
+
</div>
|
20
|
+
|
21
|
+
<div class="text-right">
|
22
|
+
<div class="pull-left">
|
23
|
+
<small>
|
24
|
+
<%# Describe text formatting options here with a link %>
|
25
|
+
<%#= link_to "Parsed with Markdown", "https://guides.github.com/features/mastering-markdown/", target: "_blank" %>
|
26
|
+
</small>
|
27
|
+
</div>
|
28
|
+
|
29
|
+
<%= f.button "#{"Update" unless f.object.new_record?} Comment", class: "btn btn-primary", data: {disable_with: "<i class='fa fa-spinner fa-spin'></i> Saving comment..."} %>
|
30
|
+
</div>
|
31
|
+
|
32
|
+
<% end %>
|
@@ -0,0 +1,58 @@
|
|
1
|
+
<%# We don't currently cache the forum posts because they have permissions to deal with %>
|
2
|
+
|
3
|
+
<%= content_tag :div, id: dom_id(forum_post), class: forum_post_classes(forum_post) do %>
|
4
|
+
<div class="card-header">
|
5
|
+
|
6
|
+
<% if is_moderator_or_owner?(forum_post) %>
|
7
|
+
<div class="pull-right">
|
8
|
+
<%= link_to icon("pencil"), simple_discussion.edit_forum_thread_forum_post_path(@forum_thread, forum_post),
|
9
|
+
class: "text-muted",
|
10
|
+
data: { toggle: "tooltip", placement: "left" },
|
11
|
+
title: "Edit this post"
|
12
|
+
%>
|
13
|
+
</div>
|
14
|
+
<% end %>
|
15
|
+
|
16
|
+
<div>
|
17
|
+
<%= avatar_tag(forum_post.user.email) %>
|
18
|
+
|
19
|
+
<strong class="forum-post-user">
|
20
|
+
<%= forum_post.user.name %> <%= forum_user_badge(forum_post.user) %>
|
21
|
+
</strong>
|
22
|
+
<small>
|
23
|
+
commented on
|
24
|
+
<%= link_to forum_post.created_at.strftime("%b %d, %Y"), simple_discussion.forum_thread_path(@forum_thread, anchor: "forum_post_#{forum_post.id}") %>:
|
25
|
+
</small>
|
26
|
+
</div>
|
27
|
+
</div>
|
28
|
+
|
29
|
+
<div class="card-block">
|
30
|
+
<%= formatted_content forum_post.body %>
|
31
|
+
</div>
|
32
|
+
|
33
|
+
<% if @forum_thread.solved? && forum_post.solved? %>
|
34
|
+
<div class="card-footer">
|
35
|
+
<div class="pull-right">
|
36
|
+
<strong class="text-success"><%= icon("check") %> Solved</strong>
|
37
|
+
|
38
|
+
<% if is_moderator_or_owner?(@forum_thread) %>
|
39
|
+
<small>
|
40
|
+
<%= link_to "Undo", simple_discussion.unsolved_forum_thread_forum_post_path(@forum_thread, forum_post), method: :put %>
|
41
|
+
</small
|
42
|
+
<% end %>
|
43
|
+
</div>
|
44
|
+
</div>
|
45
|
+
|
46
|
+
<% elsif is_moderator_or_owner?(@forum_thread) %>
|
47
|
+
<div class="card-footer">
|
48
|
+
<div class="pull-right">
|
49
|
+
<small>
|
50
|
+
<%= link_to simple_discussion.solved_forum_thread_forum_post_path(@forum_thread, forum_post), method: :put do %>
|
51
|
+
<%= icon("check") %>
|
52
|
+
This solved my question
|
53
|
+
<% end %>
|
54
|
+
</small>
|
55
|
+
</div>
|
56
|
+
</div>
|
57
|
+
<% end %>
|
58
|
+
<% end %>
|
@@ -0,0 +1,24 @@
|
|
1
|
+
<p><small><%= link_to "← Back to the thread", simple_discussion.forum_thread_path(@forum_thread) %></small></p>
|
2
|
+
|
3
|
+
<h2><%= content_tag :span, "Pinned", class: "text-muted" if @forum_thread.pinned? %> <%= @forum_thread.title %></h2>
|
4
|
+
|
5
|
+
<p class="thread-details">
|
6
|
+
<strong><%= category_link(@forum_thread.forum_category) %></strong>
|
7
|
+
• Asked <%= time_ago_in_words @forum_thread.created_at %> by <%= @forum_thread.user.name %>
|
8
|
+
</p>
|
9
|
+
|
10
|
+
<br />
|
11
|
+
|
12
|
+
<%= content_tag :div, id: dom_id(@forum_post), class: forum_post_classes(@forum_post) do %>
|
13
|
+
<div class="card-header">
|
14
|
+
<div>
|
15
|
+
<%= avatar_tag(@forum_post.user.email) %>
|
16
|
+
<strong class="forum-post-user"><%= @forum_post.user.name %></strong>
|
17
|
+
<small> commented on <%= link_to @forum_post.created_at.strftime("%b %d, %Y"), simple_discussion.forum_thread_url(@forum_thread, anchor: "forum_post_#{@forum_post.id}") %>:</small>
|
18
|
+
</div>
|
19
|
+
</div>
|
20
|
+
|
21
|
+
<div class="card-block">
|
22
|
+
<%= render "form" %>
|
23
|
+
</div>
|
24
|
+
<% end %>
|
@@ -0,0 +1,44 @@
|
|
1
|
+
<%= form_for @forum_thread,
|
2
|
+
url: (@forum_thread.persisted? ? simple_discussion.forum_thread_path(@forum_thread) : simple_discussion.forum_threads_path),
|
3
|
+
html: { data: {behavior: "comment-form"} } do |f| %>
|
4
|
+
|
5
|
+
<% if @forum_thread.errors.any? %>
|
6
|
+
<div id="error_explanation">
|
7
|
+
<h2><%= pluralize(@forum_thread.errors.count, "error") %> prohibited this forum_thread from being saved:</h2>
|
8
|
+
|
9
|
+
<ul>
|
10
|
+
<% @forum_thread.errors.full_messages.each do |message| %>
|
11
|
+
<li><%= message %></li>
|
12
|
+
<% end %>
|
13
|
+
</ul>
|
14
|
+
</div>
|
15
|
+
<% end %>
|
16
|
+
|
17
|
+
<div class="form-group">
|
18
|
+
<%= f.label :forum_category_id, "Choose a Category" %>
|
19
|
+
<%= f.collection_select :forum_category_id, ForumCategory.sorted, :id, :name, {include_blank: "Pick a category..."}, {autofocus: true, class: "form-control"} %>
|
20
|
+
</div>
|
21
|
+
|
22
|
+
<div class="form-group">
|
23
|
+
<%= f.label :title %>
|
24
|
+
<%= f.text_field :title, placeholder: "How do I...?", class: "form-control" %>
|
25
|
+
</div>
|
26
|
+
|
27
|
+
<% if local_assigns.fetch(:posts, true) %>
|
28
|
+
<%= f.fields_for :forum_posts do |p| %>
|
29
|
+
<div class="form-group">
|
30
|
+
<%= p.label :body, "What do you need help with?" %>
|
31
|
+
<%= p.text_area :body, placeholder: "Add a comment", rows: 10, class: "form-control simplemde", data: { behavior: "comment-body" } %>
|
32
|
+
</div>
|
33
|
+
<% end %>
|
34
|
+
<% end %>
|
35
|
+
|
36
|
+
<div class="form-group text-right">
|
37
|
+
<% if f.object.new_record? %>
|
38
|
+
<%= f.button "Ask Your Question", class: "btn btn-primary", data: {disable_with: "<i class='fa fa-spinner fa-spin'></i> Saving..."} %>
|
39
|
+
<% else %>
|
40
|
+
<%= f.button "Update Thread", class: "btn btn-primary", data: {disable_with: "<i class='fa fa-spinner fa-spin'></i> Saving..."} %>
|
41
|
+
<% end %>
|
42
|
+
</div>
|
43
|
+
|
44
|
+
<% end %>
|
@@ -0,0 +1,37 @@
|
|
1
|
+
<%= cache forum_thread do %>
|
2
|
+
<div class="forum-thread">
|
3
|
+
<div class="row">
|
4
|
+
|
5
|
+
<div class="col-sm-1 text-center">
|
6
|
+
<%= avatar_tag(forum_thread.user.email) %>
|
7
|
+
</div>
|
8
|
+
|
9
|
+
<div class="col">
|
10
|
+
<h4>
|
11
|
+
<% if forum_thread.solved? %>
|
12
|
+
<span class="text-success"><%= icon "check-circle" %></span>
|
13
|
+
<% end %>
|
14
|
+
|
15
|
+
<%= link_to simple_discussion.forum_thread_path(forum_thread) do %>
|
16
|
+
<%= icon "thumb-tack", class: "text-muted" if forum_thread.pinned? %> <%= forum_thread.title %>
|
17
|
+
<% end %>
|
18
|
+
</h4>
|
19
|
+
|
20
|
+
<div class="thread-details">
|
21
|
+
<strong><%= category_link(forum_thread.forum_category) %></strong>
|
22
|
+
• Asked <%= time_ago_in_words forum_thread.created_at %> by <%= forum_thread.user.name %>
|
23
|
+
</div>
|
24
|
+
|
25
|
+
<p class="text-muted"><%= truncate(forum_thread.forum_posts.first.body, length: 200) %></p>
|
26
|
+
</div>
|
27
|
+
|
28
|
+
<div class="col-sm-1 text-center">
|
29
|
+
<%= link_to simple_discussion.forum_thread_path(forum_thread), class: "thread-posts-count" do %>
|
30
|
+
<span class="count"><%= forum_thread.forum_posts_count %></span>
|
31
|
+
<small><%= "post".pluralize(forum_thread.forum_posts_count) %></small>
|
32
|
+
<% end %>
|
33
|
+
</div>
|
34
|
+
|
35
|
+
</div>
|
36
|
+
</div>
|
37
|
+
<% end %>
|
@@ -0,0 +1,11 @@
|
|
1
|
+
<div class="card card-block mb-3">
|
2
|
+
<%= render partial: "simple_discussion/forum_threads/forum_thread", collection: @forum_threads, spacer_template: "shared/spacer" %>
|
3
|
+
|
4
|
+
<% if @forum_threads.none? %>
|
5
|
+
<div>No results found for your search. Check out <%= link_to "the latest questions", simple_discussion.forum_threads_path %> instead?</div>
|
6
|
+
<% end %>
|
7
|
+
</div>
|
8
|
+
|
9
|
+
<div class="forum-threads-nav text-center">
|
10
|
+
<%= will_paginate @forum_threads, url_builder: simple_discussion, renderer: SimpleDiscussion::BootstrapLinkRenderer %>
|
11
|
+
</div>
|
@@ -0,0 +1,19 @@
|
|
1
|
+
<h1><%= icon "thumb-tack", class: "text-muted" if @forum_thread.pinned? %> <%= @forum_thread.title %></h1>
|
2
|
+
|
3
|
+
<% if is_moderator_or_owner?(@forum_thread) %>
|
4
|
+
<div class="pull-right">
|
5
|
+
<%= link_to icon("pencil"), simple_discussion.edit_forum_thread_path(@forum_thread),
|
6
|
+
class: "text-muted",
|
7
|
+
data: { toggle: "tooltip", placement: "left" },
|
8
|
+
title: "Edit this thread" %>
|
9
|
+
</div>
|
10
|
+
<% end %>
|
11
|
+
|
12
|
+
<p class="thread-details">
|
13
|
+
<strong><%= category_link(@forum_thread.forum_category) %></strong>
|
14
|
+
• Asked <%= time_ago_in_words @forum_thread.created_at %> by <%= @forum_thread.user.name %>
|
15
|
+
</p>
|
16
|
+
|
17
|
+
<%= render partial: "simple_discussion/forum_posts/forum_post", collection: @forum_thread.forum_posts.includes(:user).sorted %>
|
18
|
+
|
19
|
+
<%= render partial: "simple_discussion/forum_posts/form" if user_signed_in? %>
|
data/config/routes.rb
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
SimpleDiscussion::Engine.routes.draw do
|
2
|
+
scope module: :simple_discussion do
|
3
|
+
resources :forum_threads, path: :threads do
|
4
|
+
collection do
|
5
|
+
get :answered
|
6
|
+
get :unanswered
|
7
|
+
get :mine
|
8
|
+
get :participating
|
9
|
+
get "category/:id", to: "forum_categories#index", as: :forum_category
|
10
|
+
end
|
11
|
+
|
12
|
+
resources :forum_posts, path: :posts do
|
13
|
+
member do
|
14
|
+
put :solved
|
15
|
+
put :unsolved
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
resource :notifications
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
root to: "simple_discussion/forum_threads#index"
|
24
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
class CreateForumCategories < ActiveRecord::Migration[4.2]
|
2
|
+
def change
|
3
|
+
create_table :forum_categories, force: :cascade do |t|
|
4
|
+
t.string :name, null: false
|
5
|
+
t.string :slug, null: false
|
6
|
+
t.string :color, default: "000000"
|
7
|
+
|
8
|
+
t.timestamps
|
9
|
+
end
|
10
|
+
|
11
|
+
ForumCategory.reset_column_information
|
12
|
+
|
13
|
+
ForumCategory.create(
|
14
|
+
name: "General",
|
15
|
+
color: "#4ea1d3",
|
16
|
+
)
|
17
|
+
|
18
|
+
ForumCategory.create(
|
19
|
+
name: "Feedback",
|
20
|
+
color: "#16bc9c",
|
21
|
+
)
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
class CreateForumThreads < ActiveRecord::Migration[4.2]
|
2
|
+
def change
|
3
|
+
create_table :forum_threads, force: :cascade do |t|
|
4
|
+
t.references :forum_category, foreign_key: true
|
5
|
+
t.references :user, foreign_key: true
|
6
|
+
t.string :title, null: false
|
7
|
+
t.string :slug, null: false
|
8
|
+
t.integer :forum_posts_count, default: 0
|
9
|
+
t.boolean :pinned, default: false
|
10
|
+
t.boolean :solved, default: false
|
11
|
+
|
12
|
+
t.timestamps
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
class CreateForumPosts < ActiveRecord::Migration[4.2]
|
2
|
+
def change
|
3
|
+
create_table :forum_posts, force: :cascade do |t|
|
4
|
+
t.references :forum_thread, foreign_key: true
|
5
|
+
t.references :user, foreign_key: true
|
6
|
+
t.text :body
|
7
|
+
t.boolean :solved, default: false
|
8
|
+
|
9
|
+
t.timestamps
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
class CreateForumSubscriptions < ActiveRecord::Migration[4.2]
|
2
|
+
def change
|
3
|
+
create_table :forum_subscriptions, force: :cascade do |t|
|
4
|
+
t.references :forum_thread, foreign_key: true
|
5
|
+
t.references :user, foreign_key: true
|
6
|
+
t.string :subscription_type
|
7
|
+
|
8
|
+
t.timestamps
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
data/lib/simple_discussion.rb
CHANGED
@@ -1,5 +1,61 @@
|
|
1
|
+
require 'friendly_id'
|
2
|
+
require 'will_paginate'
|
3
|
+
require 'gravatar_image_tag'
|
4
|
+
|
1
5
|
require "simple_discussion/version"
|
6
|
+
require "simple_discussion/engine"
|
7
|
+
require "simple_discussion/forum_user"
|
8
|
+
|
9
|
+
require 'will_paginate/view_helpers/action_view'
|
2
10
|
|
3
11
|
module SimpleDiscussion
|
4
|
-
#
|
12
|
+
# This code serves two purposes
|
13
|
+
# 1. It patches will_paginate to work with scoped and mounted Rails engines
|
14
|
+
# by adding in the url_builder option
|
15
|
+
# 2. It adds Bootstrap 4 styling to will_paginate
|
16
|
+
|
17
|
+
class BootstrapLinkRenderer < WillPaginate::ActionView::LinkRenderer
|
18
|
+
|
19
|
+
# This method adds the `url_builder` option so we can pass in the
|
20
|
+
# mounted Rails engine's scope for will_paginate
|
21
|
+
def url(page)
|
22
|
+
@base_url_params ||= begin
|
23
|
+
url_params = merge_get_params(default_url_params)
|
24
|
+
merge_optional_params(url_params)
|
25
|
+
end
|
26
|
+
|
27
|
+
url_params = @base_url_params.dup
|
28
|
+
add_current_page_param(url_params, page)
|
29
|
+
|
30
|
+
# Add optional url_builder support
|
31
|
+
(@options[:url_builder] || @template).url_for(url_params)
|
32
|
+
end
|
33
|
+
|
34
|
+
protected
|
35
|
+
def html_container(html)
|
36
|
+
tag :nav, tag(:ul, html, class: ul_class)
|
37
|
+
end
|
38
|
+
|
39
|
+
def page_number(page)
|
40
|
+
item_class = if(page == current_page)
|
41
|
+
'active page-item'
|
42
|
+
else
|
43
|
+
'page-item'
|
44
|
+
end
|
45
|
+
|
46
|
+
tag :li, link(page, page, :rel => rel_value(page), :class => 'page-link'), :class => item_class
|
47
|
+
end
|
48
|
+
|
49
|
+
def gap
|
50
|
+
tag :li, link('…'.html_safe, '#', :class => 'page-link'), :class => 'page-item disabled'
|
51
|
+
end
|
52
|
+
|
53
|
+
def previous_or_next_page(page, text, classname)
|
54
|
+
tag :li, link(text, page || '#', :class => 'page-link'), :class => [(classname[0..3] if @options[:page_links]), (classname if @options[:page_links]), ('disabled' unless page), 'page-item'].join(' ')
|
55
|
+
end
|
56
|
+
|
57
|
+
def ul_class
|
58
|
+
["pagination", container_attributes[:class]].compact.join(" ")
|
59
|
+
end
|
60
|
+
end
|
5
61
|
end
|
data/simple_discussion.gemspec
CHANGED
@@ -21,7 +21,9 @@ Gem::Specification.new do |spec|
|
|
21
21
|
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
22
22
|
spec.require_paths = ["lib"]
|
23
23
|
|
24
|
-
spec.
|
25
|
-
spec.
|
26
|
-
spec.
|
24
|
+
spec.add_dependency 'font-awesome-sass', '~> 4.7.0'
|
25
|
+
spec.add_dependency 'friendly_id', '>= 5.2.0'
|
26
|
+
spec.add_dependency 'gravatar_image_tag'
|
27
|
+
spec.add_dependency 'rails', '>= 4.2'
|
28
|
+
spec.add_dependency 'will_paginate', '>= 3.1.0'
|
27
29
|
end
|
metadata
CHANGED
@@ -1,57 +1,85 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: simple_discussion
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.9.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Chris Oliver
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2017-
|
11
|
+
date: 2017-08-08 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
|
-
name:
|
14
|
+
name: font-awesome-sass
|
15
15
|
requirement: !ruby/object:Gem::Requirement
|
16
16
|
requirements:
|
17
17
|
- - "~>"
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version:
|
20
|
-
type: :
|
19
|
+
version: 4.7.0
|
20
|
+
type: :runtime
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
24
|
- - "~>"
|
25
25
|
- !ruby/object:Gem::Version
|
26
|
-
version:
|
26
|
+
version: 4.7.0
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
|
-
name:
|
28
|
+
name: friendly_id
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
30
30
|
requirements:
|
31
|
-
- - "
|
31
|
+
- - ">="
|
32
32
|
- !ruby/object:Gem::Version
|
33
|
-
version:
|
34
|
-
type: :
|
33
|
+
version: 5.2.0
|
34
|
+
type: :runtime
|
35
35
|
prerelease: false
|
36
36
|
version_requirements: !ruby/object:Gem::Requirement
|
37
37
|
requirements:
|
38
|
-
- - "
|
38
|
+
- - ">="
|
39
39
|
- !ruby/object:Gem::Version
|
40
|
-
version:
|
40
|
+
version: 5.2.0
|
41
41
|
- !ruby/object:Gem::Dependency
|
42
|
-
name:
|
42
|
+
name: gravatar_image_tag
|
43
43
|
requirement: !ruby/object:Gem::Requirement
|
44
44
|
requirements:
|
45
|
-
- - "
|
45
|
+
- - ">="
|
46
46
|
- !ruby/object:Gem::Version
|
47
|
-
version: '
|
48
|
-
type: :
|
47
|
+
version: '0'
|
48
|
+
type: :runtime
|
49
49
|
prerelease: false
|
50
50
|
version_requirements: !ruby/object:Gem::Requirement
|
51
51
|
requirements:
|
52
|
-
- - "
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: rails
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '4.2'
|
62
|
+
type: :runtime
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ">="
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '4.2'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: will_paginate
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - ">="
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: 3.1.0
|
76
|
+
type: :runtime
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - ">="
|
53
81
|
- !ruby/object:Gem::Version
|
54
|
-
version:
|
82
|
+
version: 3.1.0
|
55
83
|
description: A simple, extensible Rails forum
|
56
84
|
email:
|
57
85
|
- excid3@gmail.com
|
@@ -66,9 +94,39 @@ files:
|
|
66
94
|
- LICENSE.txt
|
67
95
|
- README.md
|
68
96
|
- Rakefile
|
97
|
+
- app/assets/stylesheets/simple_discussion.scss
|
98
|
+
- app/controllers/simple_discussion/application_controller.rb
|
99
|
+
- app/controllers/simple_discussion/forum_categories_controller.rb
|
100
|
+
- app/controllers/simple_discussion/forum_posts_controller.rb
|
101
|
+
- app/controllers/simple_discussion/forum_threads_controller.rb
|
102
|
+
- app/controllers/simple_discussion/notifications_controller.rb
|
103
|
+
- app/helpers/simple_discussion/forum_posts_helper.rb
|
104
|
+
- app/helpers/simple_discussion/forum_threads_helper.rb
|
105
|
+
- app/models/forum_category.rb
|
106
|
+
- app/models/forum_post.rb
|
107
|
+
- app/models/forum_subscription.rb
|
108
|
+
- app/models/forum_thread.rb
|
109
|
+
- app/views/layouts/simple_discussion.html.erb
|
110
|
+
- app/views/shared/_spacer.html.erb
|
111
|
+
- app/views/simple_discussion/forum_posts/_form.html.erb
|
112
|
+
- app/views/simple_discussion/forum_posts/_forum_post.html.erb
|
113
|
+
- app/views/simple_discussion/forum_posts/edit.html.erb
|
114
|
+
- app/views/simple_discussion/forum_threads/_form.html.erb
|
115
|
+
- app/views/simple_discussion/forum_threads/_forum_thread.html.erb
|
116
|
+
- app/views/simple_discussion/forum_threads/edit.html.erb
|
117
|
+
- app/views/simple_discussion/forum_threads/index.html.erb
|
118
|
+
- app/views/simple_discussion/forum_threads/new.html.erb
|
119
|
+
- app/views/simple_discussion/forum_threads/show.html.erb
|
69
120
|
- bin/console
|
70
121
|
- bin/setup
|
122
|
+
- config/routes.rb
|
123
|
+
- db/migrate/20170417012930_create_forum_categories.rb
|
124
|
+
- db/migrate/20170417012931_create_forum_threads.rb
|
125
|
+
- db/migrate/20170417012932_create_forum_posts.rb
|
126
|
+
- db/migrate/20170417012933_create_forum_subscriptions.rb
|
71
127
|
- lib/simple_discussion.rb
|
128
|
+
- lib/simple_discussion/engine.rb
|
129
|
+
- lib/simple_discussion/forum_user.rb
|
72
130
|
- lib/simple_discussion/version.rb
|
73
131
|
- simple_discussion.gemspec
|
74
132
|
homepage: https://github.com/excid3/simple_discussion
|