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.
Files changed (37) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +1 -0
  3. data/README.md +52 -8
  4. data/app/assets/stylesheets/simple_discussion.scss +63 -0
  5. data/app/controllers/simple_discussion/application_controller.rb +25 -0
  6. data/app/controllers/simple_discussion/forum_categories_controller.rb +17 -0
  7. data/app/controllers/simple_discussion/forum_posts_controller.rb +66 -0
  8. data/app/controllers/simple_discussion/forum_threads_controller.rb +72 -0
  9. data/app/controllers/simple_discussion/notifications_controller.rb +19 -0
  10. data/app/helpers/simple_discussion/forum_posts_helper.rb +29 -0
  11. data/app/helpers/simple_discussion/forum_threads_helper.rb +28 -0
  12. data/app/models/forum_category.rb +13 -0
  13. data/app/models/forum_post.rb +15 -0
  14. data/app/models/forum_subscription.rb +19 -0
  15. data/app/models/forum_thread.rb +75 -0
  16. data/app/views/layouts/simple_discussion.html.erb +59 -0
  17. data/app/views/shared/_spacer.html.erb +1 -0
  18. data/app/views/simple_discussion/forum_posts/_form.html.erb +32 -0
  19. data/app/views/simple_discussion/forum_posts/_forum_post.html.erb +58 -0
  20. data/app/views/simple_discussion/forum_posts/edit.html.erb +24 -0
  21. data/app/views/simple_discussion/forum_threads/_form.html.erb +44 -0
  22. data/app/views/simple_discussion/forum_threads/_forum_thread.html.erb +37 -0
  23. data/app/views/simple_discussion/forum_threads/edit.html.erb +7 -0
  24. data/app/views/simple_discussion/forum_threads/index.html.erb +11 -0
  25. data/app/views/simple_discussion/forum_threads/new.html.erb +5 -0
  26. data/app/views/simple_discussion/forum_threads/show.html.erb +19 -0
  27. data/config/routes.rb +24 -0
  28. data/db/migrate/20170417012930_create_forum_categories.rb +23 -0
  29. data/db/migrate/20170417012931_create_forum_threads.rb +15 -0
  30. data/db/migrate/20170417012932_create_forum_posts.rb +12 -0
  31. data/db/migrate/20170417012933_create_forum_subscriptions.rb +11 -0
  32. data/lib/simple_discussion/engine.rb +5 -0
  33. data/lib/simple_discussion/forum_user.rb +11 -0
  34. data/lib/simple_discussion/version.rb +1 -1
  35. data/lib/simple_discussion.rb +57 -1
  36. data/simple_discussion.gemspec +5 -3
  37. metadata +76 -18
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 3ccd77ab6f6bada74b16f90eee01c740e0ebee5c
4
- data.tar.gz: 4addcafed220fc33c5f07992b5b91243976c419b
3
+ metadata.gz: ea112341c29ae6d453a260175e1fab4fe8742243
4
+ data.tar.gz: '08670316d53d01df1dfc1f0101d59f43e46be643'
5
5
  SHA512:
6
- metadata.gz: 936726f1d48dcf6ae2bef9bdd12b07b67e5e182d4ef5b6dabe21d4aea3369cb46d0a1c0fda4e726ecef5189e92fc93727ddb3c31f72e9423751399de124b01c1
7
- data.tar.gz: 13f97674b2989cb98ec77c4771c59b8df9c153825a0910642ca4cf7d20bc22589ea77aef3b268f349470b152542ac90711f7fbe568d17b19b03dd236c84b48d1
6
+ metadata.gz: 87914991332ab15ab141c77baf1924fc8c89b4f4e01c1fc9121eff52309daa4a23c1310833db545750d25dc82dd9c8805647ff834147d7fca3ed1da96d336390
7
+ data.tar.gz: fde66ad800d05ea86cf1251950f490f2d221d10b8e8807d67863c41f022a54a713391e0e10148a7ea9ede736276dcba5d03a92babd0f1c66fa1b45f084332846
data/.gitignore CHANGED
@@ -7,3 +7,4 @@
7
7
  /pkg/
8
8
  /spec/reports/
9
9
  /tmp/
10
+ .DS_Store
data/README.md CHANGED
@@ -1,11 +1,13 @@
1
1
  # SimpleDiscussion
2
2
 
3
- Welcome to your new gem! In this directory, you'll find the files you need to be able to package up your Ruby library into a gem. Put your Ruby code in the file `lib/simple_discussion`. To experiment with that code, run `bin/console` for an interactive prompt.
4
-
5
- TODO: Delete this and the text above, and describe your gem
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
- $ bundle
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
- Or install it yourself as:
51
+ ```ruby
52
+ mount SimpleDiscussion::Engine => "/forum"
53
+ ```
20
54
 
21
- $ gem install simple_discussion
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
- TODO: Write usage instructions here
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/[USERNAME]/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.
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,7 @@
1
+ <%= content_for :title, "Edit Thread" %>
2
+
3
+ <h1>Edit Thread</h1>
4
+
5
+ <div class="forum_post">
6
+ <%= render 'form', posts: false %>
7
+ </div>
@@ -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,5 @@
1
+ <h1>Start A Discussion</h1>
2
+
3
+ <div class="forum_post">
4
+ <%= render 'form' %>
5
+ </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
@@ -0,0 +1,5 @@
1
+ module SimpleDiscussion
2
+ class Engine < ::Rails::Engine
3
+ engine_name 'simple_discussion'
4
+ end
5
+ end
@@ -0,0 +1,11 @@
1
+ module SimpleDiscussion
2
+ module ForumUser
3
+ extend ActiveSupport::Concern
4
+
5
+ included do
6
+ has_many :forum_threads
7
+ has_many :forum_posts
8
+ has_many :forum_subscriptions
9
+ end
10
+ end
11
+ end
@@ -1,3 +1,3 @@
1
1
  module SimpleDiscussion
2
- VERSION = "0.1.0"
2
+ VERSION = "0.9.0"
3
3
  end
@@ -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
- # Your code goes here...
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('&hellip;'.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
@@ -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.add_development_dependency "bundler", "~> 1.15"
25
- spec.add_development_dependency "rake", "~> 10.0"
26
- spec.add_development_dependency "minitest", "~> 5.0"
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.1.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-07-28 00:00:00.000000000 Z
11
+ date: 2017-08-08 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
- name: bundler
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: '1.15'
20
- type: :development
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: '1.15'
26
+ version: 4.7.0
27
27
  - !ruby/object:Gem::Dependency
28
- name: rake
28
+ name: friendly_id
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
- - - "~>"
31
+ - - ">="
32
32
  - !ruby/object:Gem::Version
33
- version: '10.0'
34
- type: :development
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: '10.0'
40
+ version: 5.2.0
41
41
  - !ruby/object:Gem::Dependency
42
- name: minitest
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: '5.0'
48
- type: :development
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: '5.0'
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