simple_discussion 0.1.0 → 0.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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