thredded 0.1.0 → 0.2.2

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 (196) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.mkdn +202 -0
  3. data/MIT-LICENSE +20 -0
  4. data/README.mkdn +391 -0
  5. data/app/assets/images/thredded/breadcrumb-chevron.svg +1 -0
  6. data/app/assets/javascripts/thredded/currently_online.es6 +25 -0
  7. data/app/assets/javascripts/thredded/post_form.es6 +20 -0
  8. data/app/assets/javascripts/thredded/time_stamps.es6 +8 -0
  9. data/app/assets/javascripts/thredded/topic_form.es6 +55 -0
  10. data/app/assets/javascripts/thredded/users_select.es6 +5 -0
  11. data/app/assets/javascripts/thredded.es6 +10 -0
  12. data/app/assets/stylesheets/thredded/_base.scss +11 -0
  13. data/app/assets/stylesheets/thredded/_dependencies.scss +1 -0
  14. data/app/assets/stylesheets/thredded/_thredded.scss +27 -0
  15. data/app/assets/stylesheets/thredded/base/_alerts.scss +40 -0
  16. data/app/assets/stylesheets/thredded/base/_buttons.scss +39 -0
  17. data/app/assets/stylesheets/thredded/base/_forms.scss +79 -0
  18. data/app/assets/stylesheets/thredded/base/_grid.scss +19 -0
  19. data/app/assets/stylesheets/thredded/base/_lists.scss +31 -0
  20. data/app/assets/stylesheets/thredded/base/_tables.scss +25 -0
  21. data/app/assets/stylesheets/thredded/base/_typography.scss +36 -0
  22. data/app/assets/stylesheets/thredded/base/_variables.scss +54 -0
  23. data/app/assets/stylesheets/thredded/components/_base.scss +13 -0
  24. data/app/assets/stylesheets/thredded/components/_currently-online.scss +48 -0
  25. data/app/assets/stylesheets/thredded/components/_empty.scss +11 -0
  26. data/app/assets/stylesheets/thredded/components/_flash-message.scss +19 -0
  27. data/app/assets/stylesheets/thredded/components/_form-list.scss +35 -0
  28. data/app/assets/stylesheets/thredded/components/_main-section.scss +3 -0
  29. data/app/assets/stylesheets/thredded/components/_messageboard.scss +55 -0
  30. data/app/assets/stylesheets/thredded/components/_pagination.scss +26 -0
  31. data/app/assets/stylesheets/thredded/components/_post-form.scss +21 -0
  32. data/app/assets/stylesheets/thredded/components/_post.scss +66 -0
  33. data/app/assets/stylesheets/thredded/components/_preferences.scss +6 -0
  34. data/app/assets/stylesheets/thredded/components/_select2.scss +42 -0
  35. data/app/assets/stylesheets/thredded/components/_topic-delete.scss +9 -0
  36. data/app/assets/stylesheets/thredded/components/_topic-header.scss +20 -0
  37. data/app/assets/stylesheets/thredded/components/_topics.scss +113 -0
  38. data/app/assets/stylesheets/thredded/layout/_main-container.scss +15 -0
  39. data/app/assets/stylesheets/thredded/layout/_main-navigation.scss +45 -0
  40. data/app/assets/stylesheets/thredded/layout/_topic-navigation.scss +53 -0
  41. data/app/assets/stylesheets/thredded/layout/_user-navigation.scss +87 -0
  42. data/app/assets/stylesheets/thredded/utilities/_is-compact.scss +26 -0
  43. data/app/assets/stylesheets/thredded/utilities/_is-expanded.scss +16 -0
  44. data/app/assets/stylesheets/thredded.scss +3 -0
  45. data/app/commands/thredded/at_notification_extractor.rb +15 -0
  46. data/app/commands/thredded/members_marked_notified.rb +18 -0
  47. data/app/commands/thredded/messageboard_destroyer.rb +60 -0
  48. data/app/commands/thredded/notify_mentioned_users.rb +68 -0
  49. data/app/commands/thredded/notify_private_topic_users.rb +51 -0
  50. data/app/commands/thredded/user_reads_private_topic.rb +22 -0
  51. data/app/commands/thredded/user_resets_private_topic_to_unread.rb +23 -0
  52. data/app/controllers/thredded/application_controller.rb +105 -0
  53. data/app/controllers/thredded/messageboards_controller.rb +58 -0
  54. data/app/controllers/thredded/posts_controller.rb +78 -0
  55. data/app/controllers/thredded/preferences_controller.rb +28 -0
  56. data/app/controllers/thredded/private_topics_controller.rb +65 -0
  57. data/app/controllers/thredded/setups_controller.rb +53 -0
  58. data/app/controllers/thredded/theme_previews_controller.rb +59 -0
  59. data/app/controllers/thredded/topics_controller.rb +153 -0
  60. data/app/decorators/thredded/base_topic_decorator.rb +14 -0
  61. data/app/decorators/thredded/base_user_topic_decorator.rb +63 -0
  62. data/app/decorators/thredded/messageboard_decorator.rb +41 -0
  63. data/app/decorators/thredded/post_decorator.rb +40 -0
  64. data/app/decorators/thredded/private_topic_decorator.rb +23 -0
  65. data/app/decorators/thredded/topic_decorator.rb +25 -0
  66. data/app/decorators/thredded/topic_email_decorator.rb +24 -0
  67. data/app/decorators/thredded/user_private_topic_decorator.rb +13 -0
  68. data/app/decorators/thredded/user_topic_decorator.rb +37 -0
  69. data/app/forms/thredded/private_topic_form.rb +126 -0
  70. data/app/forms/thredded/topic_form.rb +94 -0
  71. data/app/helpers/thredded/application_helper.rb +41 -0
  72. data/app/jobs/thredded/activity_updater_job.rb +20 -0
  73. data/app/jobs/thredded/at_notifier_job.rb +11 -0
  74. data/app/jobs/thredded/notify_private_topic_users_job.rb +11 -0
  75. data/app/mailers/thredded/base_mailer.rb +4 -0
  76. data/app/mailers/thredded/post_mailer.rb +15 -0
  77. data/app/mailers/thredded/private_post_mailer.rb +15 -0
  78. data/app/mailers/thredded/private_topic_mailer.rb +15 -0
  79. data/app/models/concerns/thredded/post_common.rb +105 -0
  80. data/app/models/concerns/thredded/topic_common.rb +48 -0
  81. data/app/models/thredded/ability.rb +60 -0
  82. data/app/models/thredded/category.rb +12 -0
  83. data/app/models/thredded/messageboard.rb +50 -0
  84. data/app/models/thredded/messageboard_user.rb +12 -0
  85. data/app/models/thredded/notification_preference.rb +17 -0
  86. data/app/models/thredded/null_preference.rb +11 -0
  87. data/app/models/thredded/null_topic.rb +15 -0
  88. data/app/models/thredded/null_topic_read.rb +19 -0
  89. data/app/models/thredded/null_user.rb +51 -0
  90. data/app/models/thredded/post.rb +37 -0
  91. data/app/models/thredded/post_notification.rb +17 -0
  92. data/app/models/thredded/private_post.rb +25 -0
  93. data/app/models/thredded/private_topic.rb +60 -0
  94. data/app/models/thredded/private_user.rb +6 -0
  95. data/app/models/thredded/stats.rb +37 -0
  96. data/app/models/thredded/topic.rb +104 -0
  97. data/app/models/thredded/topic_category.rb +6 -0
  98. data/app/models/thredded/user_detail.rb +26 -0
  99. data/app/models/thredded/user_extender.rb +33 -0
  100. data/app/models/thredded/user_permissions/admin/if_admin_column_true.rb +12 -0
  101. data/app/models/thredded/user_permissions/admin/none.rb +12 -0
  102. data/app/models/thredded/user_permissions/message/readers_of_writeable_boards.rb +13 -0
  103. data/app/models/thredded/user_permissions/moderate/if_moderator_column_true.rb +25 -0
  104. data/app/models/thredded/user_permissions/moderate/none.rb +25 -0
  105. data/app/models/thredded/user_permissions/read/all.rb +25 -0
  106. data/app/models/thredded/user_permissions/write/all.rb +25 -0
  107. data/app/models/thredded/user_permissions/write/none.rb +25 -0
  108. data/app/models/thredded/user_preference.rb +6 -0
  109. data/app/models/thredded/user_topic_read.rb +10 -0
  110. data/app/views/layouts/thredded/application.html.erb +15 -0
  111. data/app/views/thredded/categories/_category.html.erb +1 -0
  112. data/app/views/thredded/kaminari/_first_page.html.erb +11 -0
  113. data/app/views/thredded/kaminari/_gap.html.erb +8 -0
  114. data/app/views/thredded/kaminari/_last_page.html.erb +11 -0
  115. data/app/views/thredded/kaminari/_next_page.html.erb +11 -0
  116. data/app/views/thredded/kaminari/_page.html.erb +12 -0
  117. data/app/views/thredded/kaminari/_paginator.html.erb +23 -0
  118. data/app/views/thredded/kaminari/_prev_page.html.erb +11 -0
  119. data/app/views/thredded/messageboards/_messageboard.html.erb +15 -0
  120. data/app/views/thredded/messageboards/index.html.erb +20 -0
  121. data/app/views/thredded/messageboards/new.html.erb +18 -0
  122. data/app/views/thredded/post_mailer/at_notification.html.erb +14 -0
  123. data/app/views/thredded/post_mailer/at_notification.text.erb +10 -0
  124. data/app/views/thredded/posts/_content_field.html.erb +4 -0
  125. data/app/views/thredded/posts/_form.html.erb +1 -0
  126. data/app/views/thredded/posts/_post.html.erb +1 -0
  127. data/app/views/thredded/posts/_user.html.erb +3 -0
  128. data/app/views/thredded/posts/edit.html.erb +13 -0
  129. data/app/views/thredded/posts_common/_form.html.erb +10 -0
  130. data/app/views/thredded/posts_common/_post.html.erb +20 -0
  131. data/app/views/thredded/preferences/_form.html.erb +28 -0
  132. data/app/views/thredded/preferences/_header.html.erb +1 -0
  133. data/app/views/thredded/preferences/edit.html.erb +12 -0
  134. data/app/views/thredded/private_post_mailer/at_notification.html.erb +11 -0
  135. data/app/views/thredded/private_posts/_form.html.erb +1 -0
  136. data/app/views/thredded/private_posts/_private_post.html.erb +1 -0
  137. data/app/views/thredded/private_topic_mailer/message_notification.html.erb +17 -0
  138. data/app/views/thredded/private_topic_mailer/message_notification.text.erb +13 -0
  139. data/app/views/thredded/private_topics/_breadcrumbs.html.erb +4 -0
  140. data/app/views/thredded/private_topics/_form.html.erb +24 -0
  141. data/app/views/thredded/private_topics/_no_private_topics.html.erb +6 -0
  142. data/app/views/thredded/private_topics/_private_topic.html.erb +22 -0
  143. data/app/views/thredded/private_topics/index.html.erb +23 -0
  144. data/app/views/thredded/private_topics/new.html.erb +13 -0
  145. data/app/views/thredded/private_topics/show.html.erb +14 -0
  146. data/app/views/thredded/search/_form.html.erb +7 -0
  147. data/app/views/thredded/shared/_currently_online.html.erb +16 -0
  148. data/app/views/thredded/shared/_flash_messages.html.erb +7 -0
  149. data/app/views/thredded/shared/_header.html.erb +4 -0
  150. data/app/views/thredded/shared/_messageboard_topics_breadcrumbs.html.erb +6 -0
  151. data/app/views/thredded/shared/_notification_preferences.html.erb +7 -0
  152. data/app/views/thredded/shared/_page.html.erb +6 -0
  153. data/app/views/thredded/shared/_time_ago.html.erb +7 -0
  154. data/app/views/thredded/shared/_top_nav.html.erb +36 -0
  155. data/app/views/thredded/shared/_topic_nav.html.erb +22 -0
  156. data/app/views/thredded/theme_previews/_section_title.html.erb +3 -0
  157. data/app/views/thredded/theme_previews/show.html.erb +108 -0
  158. data/app/views/thredded/topics/_form.html.erb +23 -0
  159. data/app/views/thredded/topics/_recent_topics_by_user.html.erb +8 -0
  160. data/app/views/thredded/topics/_topic.html.erb +29 -0
  161. data/app/views/thredded/topics/_topic_form_admin_options.html.erb +12 -0
  162. data/app/views/thredded/topics/by_category.html.erb +56 -0
  163. data/app/views/thredded/topics/edit.html.erb +38 -0
  164. data/app/views/thredded/topics/index.html.erb +18 -0
  165. data/app/views/thredded/topics/menu/_new_topic.html.erb +3 -0
  166. data/app/views/thredded/topics/new.html.erb +11 -0
  167. data/app/views/thredded/topics/search.html.erb +9 -0
  168. data/app/views/thredded/topics/show.html.erb +31 -0
  169. data/app/views/thredded/topics_common/_header.html.erb +6 -0
  170. data/app/views/thredded/users/_link.html.erb +9 -0
  171. data/config/routes.rb +28 -0
  172. data/db/migrate/20160329231848_create_thredded.rb +173 -0
  173. data/lib/generators/thredded/install/USAGE +13 -0
  174. data/lib/generators/thredded/install/install_generator.rb +23 -0
  175. data/lib/generators/thredded/install/templates/initializer.rb +51 -0
  176. data/lib/html/pipeline/at_mention_filter.rb +20 -0
  177. data/lib/html/pipeline/bbcode_filter.rb +19 -0
  178. data/lib/tasks/thredded_tasks.rake +18 -0
  179. data/lib/thredded/at_users.rb +19 -0
  180. data/lib/thredded/engine.rb +35 -0
  181. data/lib/thredded/errors.rb +67 -0
  182. data/lib/thredded/main_app_route_delegator.rb +24 -0
  183. data/lib/thredded/messageboard_user_permissions.rb +22 -0
  184. data/lib/thredded/post_sql_builder.rb +12 -0
  185. data/lib/thredded/post_user_permissions.rb +32 -0
  186. data/lib/thredded/private_topic_user_permissions.rb +26 -0
  187. data/lib/thredded/search_parser.rb +45 -0
  188. data/lib/thredded/search_sql_builder.rb +21 -0
  189. data/lib/thredded/seed_database.rb +76 -0
  190. data/lib/thredded/table_sql_builder.rb +41 -0
  191. data/lib/thredded/topic_sql_builder.rb +11 -0
  192. data/lib/thredded/topic_user_permissions.rb +32 -0
  193. data/lib/thredded/version.rb +3 -0
  194. data/lib/thredded.rb +85 -0
  195. data/thredded.gemspec +68 -0
  196. metadata +248 -122
@@ -0,0 +1,51 @@
1
+ module Thredded
2
+ class NotifyPrivateTopicUsers
3
+ DELIVER_METHOD = Rails.version < '4.2.0' ? :deliver : :deliver_later
4
+
5
+ def initialize(private_topic)
6
+ @post = private_topic.posts.first || Post.new
7
+ @private_topic = private_topic
8
+ end
9
+
10
+ def run
11
+ members = private_topic_recipients
12
+
13
+ return unless members.present?
14
+ user_emails = members.map(&:email)
15
+ PrivateTopicMailer
16
+ .message_notification(private_topic.id, user_emails)
17
+ .send(DELIVER_METHOD)
18
+ mark_notified(members)
19
+ end
20
+
21
+ def private_topic_recipients
22
+ members = private_topic.users - [private_topic.user]
23
+ members = exclude_those_opting_out_of_message_notifications(members)
24
+ members = exclude_previously_notified(members)
25
+ members
26
+ end
27
+
28
+ private
29
+
30
+ attr_reader :post, :private_topic
31
+
32
+ def mark_notified(members)
33
+ members.each do |member|
34
+ post.post_notifications.create(email: member.email)
35
+ end
36
+ end
37
+
38
+ def exclude_those_opting_out_of_message_notifications(members)
39
+ # TODO: implement global notification preferences for private topics.
40
+ members
41
+ end
42
+
43
+ def exclude_previously_notified(members)
44
+ emails_notified = post.post_notifications.map(&:email)
45
+
46
+ members.reject do |member|
47
+ emails_notified.include? member.email
48
+ end
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,22 @@
1
+ module Thredded
2
+ class UserReadsPrivateTopic
3
+ def initialize(private_topic, user)
4
+ @private_topic = private_topic
5
+ @user = user
6
+ end
7
+
8
+ def run
9
+ private_user = Thredded::PrivateUser
10
+ .find_by(private_topic_id: private_topic.id, user_id: user.id)
11
+ private_user.update_attributes(read: true)
12
+ end
13
+
14
+ private
15
+
16
+ attr_reader :private_topic, :user
17
+
18
+ def messageboard
19
+ private_topic.messageboard
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,23 @@
1
+ module Thredded
2
+ class UserResetsPrivateTopicToUnread
3
+ def initialize(private_topic, user)
4
+ @private_topic = private_topic
5
+ @user = user
6
+ end
7
+
8
+ def run
9
+ other_users = Thredded::PrivateUser
10
+ .where(private_topic: private_topic)
11
+ .where.not(user_id: user.id)
12
+ other_users.update_all(read: false)
13
+ end
14
+
15
+ private
16
+
17
+ attr_reader :private_topic, :user
18
+
19
+ def messageboard
20
+ private_topic.messageboard
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,105 @@
1
+ module Thredded
2
+ class ApplicationController < ::ApplicationController
3
+ layout Thredded.layout
4
+
5
+ helper Thredded::Engine.helpers
6
+ helper_method \
7
+ :active_users,
8
+ :messageboard,
9
+ :preferences,
10
+ :unread_private_topics_count,
11
+ :signed_in?
12
+
13
+ rescue_from \
14
+ CanCan::AccessDenied,
15
+ Thredded::Errors::MessageboardNotFound,
16
+ Thredded::Errors::MessageboardReadDenied,
17
+ Thredded::Errors::TopicCreateDenied,
18
+ Thredded::Errors::MessageboardCreateDenied,
19
+ Thredded::Errors::PrivateTopicCreateDenied do |exception|
20
+ redirect_to root_path,
21
+ flash: { alert: exception.message }
22
+ end
23
+
24
+ rescue_from \
25
+ Thredded::Errors::EmptySearchResults,
26
+ Thredded::Errors::TopicNotFound do |exception|
27
+ redirect_to messageboard_topics_path(messageboard),
28
+ flash: { error: exception.message }
29
+ end
30
+
31
+ def signed_in?
32
+ !thredded_current_user.thredded_anonymous?
33
+ end
34
+
35
+ private
36
+
37
+ def unread_private_topics_count
38
+ @unread_private_topics_count ||= Thredded::PrivateTopic
39
+ .joins(:private_users)
40
+ .where(
41
+ thredded_private_users: {
42
+ user_id: thredded_current_user.id,
43
+ read: false
44
+ })
45
+ .count
46
+ end
47
+
48
+ def authorize_reading(obj)
49
+ return if current_ability.can? :read, obj
50
+
51
+ class_name = obj.class.to_s
52
+ error = class_name
53
+ .gsub(/Thredded::/, 'Thredded::Errors::') + 'ReadDenied'
54
+
55
+ fail error.constantize
56
+ end
57
+
58
+ def authorize_creating(obj)
59
+ obj = obj.new if obj.class == Class
60
+
61
+ return if current_ability.can? :create, obj
62
+
63
+ class_name = obj.class.to_s
64
+ error = class_name
65
+ .gsub(/Thredded::/, 'Thredded::Errors::') + 'CreateDenied'
66
+
67
+ fail error.constantize
68
+ end
69
+
70
+ def update_user_activity
71
+ return if messageboard.nil?
72
+
73
+ Thredded::ActivityUpdaterJob.perform_later(
74
+ thredded_current_user.id,
75
+ messageboard.id
76
+ )
77
+ end
78
+
79
+ def current_ability
80
+ @current_ability ||= Ability.new(thredded_current_user)
81
+ end
82
+
83
+ def messageboard
84
+ @messageboard ||= params[:messageboard_id].presence && Messageboard.find_by_slug!(params[:messageboard_id])
85
+ end
86
+
87
+ def preferences
88
+ @preferences ||= thredded_current_user.thredded_user_preference
89
+ end
90
+
91
+ def thredded_current_user
92
+ current_user || NullUser.new
93
+ end
94
+
95
+ def active_users
96
+ users = if messageboard
97
+ messageboard.recently_active_users
98
+ else
99
+ Thredded.user_class.joins(:thredded_user_detail).merge(Thredded::UserDetail.recently_active).to_a
100
+ end.to_a
101
+ users.push(thredded_current_user) unless thredded_current_user.is_a?(NullUser)
102
+ users.uniq
103
+ end
104
+ end
105
+ end
@@ -0,0 +1,58 @@
1
+ module Thredded
2
+ class MessageboardsController < Thredded::ApplicationController
3
+ def index
4
+ @messageboards = Messageboard.where(closed: false).decorate
5
+ end
6
+
7
+ def new
8
+ authorize_creating Messageboard
9
+
10
+ @messageboard = Messageboard.new
11
+ end
12
+
13
+ def create
14
+ @messageboard = Messageboard.new(messageboard_params)
15
+
16
+ if signed_in? && @messageboard.save
17
+ @topic = Topic.create!(topic_params)
18
+ @post = Post.create!(post_params)
19
+
20
+ redirect_to root_path
21
+ else
22
+ show_sign_in_error unless signed_in?
23
+ render action: :new
24
+ end
25
+ end
26
+
27
+ private
28
+
29
+ def show_sign_in_error
30
+ flash.now[:error] = 'You are not signed in. Sign in or create an account before creating your messageboard.'
31
+ end
32
+
33
+ def messageboard_params
34
+ params
35
+ .require(:messageboard)
36
+ .permit(:description, :name, :posting_permission, :security)
37
+ end
38
+
39
+ def topic_params
40
+ {
41
+ messageboard: @messageboard,
42
+ user: thredded_current_user,
43
+ last_user: thredded_current_user,
44
+ title: "Welcome to your messageboard's very first thread",
45
+ }
46
+ end
47
+
48
+ def post_params
49
+ {
50
+ messageboard: @messageboard,
51
+ postable: @topic,
52
+ content: "There's not a whole lot here for now.",
53
+ ip: request.ip,
54
+ user: thredded_current_user,
55
+ }
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,78 @@
1
+ module Thredded
2
+ class PostsController < Thredded::ApplicationController
3
+ include ActionView::RecordIdentifier
4
+
5
+ load_and_authorize_resource only: [:index, :show]
6
+ helper_method :messageboard, :topic
7
+ before_filter :update_user_activity
8
+
9
+ def create
10
+ post = parent_topic.posts.create!(post_params)
11
+
12
+ reset_read_status if for_a_private_topic?
13
+ redirect_to post_path(post)
14
+ end
15
+
16
+ def edit
17
+ authorize! :edit, post
18
+ end
19
+
20
+ def update
21
+ post.update_attributes(post_params.except(:user, :ip))
22
+
23
+ redirect_to post_path(post)
24
+ end
25
+
26
+ def topic
27
+ post.postable
28
+ end
29
+
30
+ private
31
+
32
+ def post_path(post)
33
+ if for_a_private_topic?
34
+ private_topic_path(post.postable, anchor: dom_id(post))
35
+ else
36
+ messageboard_topic_path(messageboard, post.postable, anchor: dom_id(post))
37
+ end
38
+ end
39
+
40
+ def reset_read_status
41
+ Thredded::UserResetsPrivateTopicToUnread.new(parent_topic, thredded_current_user).run
42
+ end
43
+
44
+ def post_params
45
+ params
46
+ .require(:post)
47
+ .permit(:content)
48
+ .merge!(user: thredded_current_user, ip: request.remote_ip)
49
+ .tap { |p| p.merge!(messageboard: messageboard) unless for_a_private_topic? }
50
+ end
51
+
52
+ def parent_topic
53
+ if for_a_private_topic?
54
+ PrivateTopic
55
+ .includes(:private_users)
56
+ .friendly
57
+ .find(params[:private_topic_id])
58
+ else
59
+ Topic
60
+ .where(messageboard: messageboard)
61
+ .friendly
62
+ .find(params[:topic_id])
63
+ end
64
+ end
65
+
66
+ def for_a_private_topic?
67
+ params[:private_topic_id].present?
68
+ end
69
+
70
+ def post
71
+ @post ||= (for_a_private_topic? ? Thredded::PrivatePost : Thredded::Post).find(params[:id])
72
+ end
73
+
74
+ def current_page
75
+ params[:page].nil? ? 1 : params[:page].to_i
76
+ end
77
+ end
78
+ end
@@ -0,0 +1,28 @@
1
+ module Thredded
2
+ class PreferencesController < Thredded::ApplicationController
3
+ helper_method :preference
4
+
5
+ def edit
6
+ end
7
+
8
+ def update
9
+ preference.update_attributes(preference_params)
10
+
11
+ redirect_to :back, flash: { notice: 'Your settings have been updated.' }
12
+ end
13
+
14
+ def preference
15
+ @preference ||= NotificationPreference
16
+ .where(messageboard_id: messageboard.id, user_id: thredded_current_user.id)
17
+ .first_or_create!
18
+ end
19
+
20
+ private
21
+
22
+ def preference_params
23
+ params
24
+ .require(:notification_preference)
25
+ .permit(:notify_on_mention, :notify_on_message, :filter)
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,65 @@
1
+ module Thredded
2
+ class PrivateTopicsController < Thredded::ApplicationController
3
+ helper_method :private_topic
4
+
5
+ def index
6
+ @new_private_topic = PrivateTopicForm.new(user: thredded_current_user)
7
+ @private_topics = PrivateTopic
8
+ .uniq
9
+ .for_user(thredded_current_user)
10
+ .order('updated_at DESC')
11
+ .on_page(params[:page])
12
+ .load
13
+ @decorated_private_topics = Thredded::UserPrivateTopicDecorator
14
+ .decorate_all(thredded_current_user, @private_topics)
15
+ end
16
+
17
+ def show
18
+ authorize! :read, private_topic
19
+ UserReadsPrivateTopic.new(private_topic, thredded_current_user).run
20
+
21
+ @posts = private_topic
22
+ .posts
23
+ .includes(:user)
24
+ .order('id ASC')
25
+
26
+ @post = private_topic.posts.build
27
+ end
28
+
29
+ def new
30
+ @private_topic = PrivateTopicForm.new(user: thredded_current_user)
31
+ authorize_creating @private_topic.private_topic
32
+ end
33
+
34
+ def create
35
+ @private_topic = PrivateTopicForm.new(new_private_topic_params)
36
+ if @private_topic.save
37
+ NotifyPrivateTopicUsersJob
38
+ .perform_later(@private_topic.private_topic.id)
39
+
40
+ UserResetsPrivateTopicToUnread
41
+ .new(@private_topic.private_topic, thredded_current_user)
42
+ .run
43
+
44
+ redirect_to @private_topic.private_topic
45
+ else
46
+ render :new
47
+ end
48
+ end
49
+
50
+ private
51
+
52
+ def private_topic
53
+ @private_topic ||= Thredded::PrivateTopic.find_by_slug(params[:id])
54
+ end
55
+
56
+ def new_private_topic_params
57
+ params
58
+ .require(:private_topic)
59
+ .permit(:title, :locked, :sticky, :content, user_ids: [], category_ids: [])
60
+ .merge(
61
+ user: thredded_current_user,
62
+ ip: request.remote_ip)
63
+ end
64
+ end
65
+ end
@@ -0,0 +1,53 @@
1
+ module Thredded
2
+ class SetupsController < Thredded::ApplicationController
3
+ def new
4
+ show_sign_in_error unless signed_in?
5
+ @messageboard = Messageboard.new
6
+ end
7
+
8
+ def create
9
+ @messageboard = Messageboard.new(messageboard_params)
10
+
11
+ if signed_in? && @messageboard.save
12
+ @topic = Topic.create!(topic_params)
13
+ @post = Post.create!(post_params)
14
+
15
+ redirect_to root_path
16
+ else
17
+ show_sign_in_error unless signed_in?
18
+ render action: :new
19
+ end
20
+ end
21
+
22
+ private
23
+
24
+ def show_sign_in_error
25
+ flash.now[:error] = 'You are not signed in. Sign in or create an account before creating your messageboard.'
26
+ end
27
+
28
+ def messageboard_params
29
+ params
30
+ .require(:messageboard)
31
+ .permit(:description, :name, :posting_permission, :security)
32
+ end
33
+
34
+ def topic_params
35
+ {
36
+ messageboard: @messageboard,
37
+ user: thredded_current_user,
38
+ last_user: thredded_current_user,
39
+ title: "Welcome to your messageboard's very first thread",
40
+ }
41
+ end
42
+
43
+ def post_params
44
+ {
45
+ messageboard: @messageboard,
46
+ postable: @topic,
47
+ content: "There's not a whole lot here for now.",
48
+ ip: request.ip,
49
+ user: thredded_current_user,
50
+ }
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,59 @@
1
+ module Thredded
2
+ class ThemePreviewsController < Thredded::ApplicationController
3
+ before_filter :fail_on_empty_database, if: :no_messageboard?
4
+
5
+ def show
6
+ @messageboard = messageboard
7
+ @messageboards = Messageboard.where(closed: false).decorate
8
+ @topics = messageboard.topics.on_page(current_page).limit(3)
9
+ @decorated_topics = UserTopicDecorator.decorate_all(user, @topics)
10
+ @decorated_private_topics = Thredded::UserPrivateTopicDecorator
11
+ .decorate_all(thredded_current_user, user.thredded_private_topics)
12
+ @topic = @topics.find { |t| t.posts.present? } || @topics.first ||
13
+ @messageboard.topics.build(title: 'Hello', slug: 'hello', user: user)
14
+ @user_topic = UserTopicDecorator.new(user, @topic)
15
+ @posts = @topic.posts.first(3)
16
+ @post = @posts.first || @messageboard.posts.build(id: 1337, postable: @topic, content: 'Hello world', user: user)
17
+ @new_post = @messageboard.posts.build(postable: @topic)
18
+ @new_topic = TopicForm.new(messageboard: messageboard, user: Thredded::NullUser.new)
19
+ @new_private_topic = PrivateTopicForm.new(user: user)
20
+ @private_topic = PrivateTopic.all.first || PrivateTopic.new(id: 1337, title: 'Hello', user: user, last_user: user,
21
+ users: [user])
22
+ @private_post = PrivatePost.first ||
23
+ @private_topic.posts.build(id: 1337, postable: @private_topic, content: 'A private hello world', user: user)
24
+ @preference = preference
25
+ end
26
+
27
+ def messageboard
28
+ @messageboard ||= Messageboard.first
29
+ end
30
+
31
+ def preference
32
+ @preference ||= NotificationPreference
33
+ .where(messageboard_id: messageboard.id, user_id: user.id)
34
+ .first_or_create!
35
+ end
36
+
37
+ def current_page
38
+ 1
39
+ end
40
+
41
+ private
42
+
43
+ def fail_on_empty_database
44
+ fail Thredded::Errors::DatabaseEmpty
45
+ end
46
+
47
+ def no_messageboard?
48
+ !Thredded::Messageboard.any?
49
+ end
50
+
51
+ def user
52
+ @user ||= if thredded_current_user.thredded_anonymous?
53
+ Thredded.user_class.first_or_create!(slug: 'joe', name: 'joe', email: 'joe@example.com')
54
+ else
55
+ thredded_current_user
56
+ end
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,153 @@
1
+ module Thredded
2
+ class TopicsController < Thredded::ApplicationController
3
+ helper_method :current_page, :topic, :user_topic
4
+ before_filter :update_user_activity
5
+
6
+ def index
7
+ authorize_reading messageboard
8
+
9
+ @topics = topics
10
+ @decorated_topics = Thredded::UserTopicDecorator
11
+ .decorate_all(thredded_current_user, @topics)
12
+ initialize_new_topic.tap do |new_topic|
13
+ @new_topic = new_topic if current_ability.can?(:create, new_topic.topic)
14
+ end
15
+ end
16
+
17
+ def show
18
+ authorize! :read, topic
19
+
20
+ @posts = topic.posts
21
+ .includes(:user, :messageboard, :postable)
22
+ .order('id ASC')
23
+ .page(current_page)
24
+
25
+ @new_post = messageboard.posts.build(
26
+ postable: topic,
27
+ filter: messageboard.filter
28
+ )
29
+
30
+ update_read_status
31
+ end
32
+
33
+ def search
34
+ @topics = Topic.search(params[:q], messageboard)
35
+ @decorated_topics = Thredded::UserTopicDecorator
36
+ .decorate_all(thredded_current_user, @topics)
37
+ flash.now[:notice] = "Search Results for '#{params[:q]}'"
38
+ end
39
+
40
+ def new
41
+ @new_topic = TopicForm.new(messageboard: messageboard, user: thredded_current_user)
42
+ authorize_creating @new_topic.topic
43
+ end
44
+
45
+ def category
46
+ @category = messageboard.categories.friendly.find(params[:category_id])
47
+ @topics = @category
48
+ .topics
49
+ .unstuck
50
+ .order_by_updated_time
51
+ .on_page(current_page)
52
+ .load
53
+ @decorated_topics = Thredded::UserTopicDecorator
54
+ .decorate_all(thredded_current_user, @topics)
55
+
56
+ render :index
57
+ end
58
+
59
+ def create
60
+ @new_topic = TopicForm.new(new_topic_params)
61
+ authorize_creating @new_topic.topic
62
+ if @new_topic.save
63
+ redirect_to messageboard_topics_path(messageboard)
64
+ else
65
+ render :new
66
+ end
67
+ end
68
+
69
+ def edit
70
+ authorize! :update, topic
71
+ end
72
+
73
+ def update
74
+ topic.update_attributes!(topic_params.merge(last_user_id: thredded_current_user.id))
75
+ redirect_to messageboard_topic_posts_url(messageboard, topic), flash: { notice: 'Topic updated' }
76
+ end
77
+
78
+ def destroy
79
+ authorize! :destroy, topic
80
+ topic.destroy!
81
+ redirect_to messageboard_topics_path(messageboard), flash: { notice: 'Topic deleted' }
82
+ end
83
+
84
+ private
85
+
86
+ def initialize_new_topic
87
+ TopicForm.new(messageboard: messageboard, user: thredded_current_user)
88
+ end
89
+
90
+ def topic
91
+ @topic ||= messageboard.topics.find_by_slug_with_user_topic_reads!(params[:id])
92
+ end
93
+
94
+ def topics
95
+ messageboard
96
+ .topics
97
+ .includes(:user_topic_reads, :categories, :last_user, :user)
98
+ .order_by_stuck_and_updated_time
99
+ .on_page(current_page)
100
+ .load
101
+ end
102
+
103
+ def topic_params
104
+ params
105
+ .require(:topic)
106
+ .permit(:title, :locked, :sticky, category_ids: [])
107
+ .merge(user: thredded_current_user)
108
+ end
109
+
110
+ def new_topic_params
111
+ params
112
+ .require(:topic)
113
+ .permit(:title, :locked, :sticky, :content, category_ids: [])
114
+ .merge(
115
+ messageboard: messageboard,
116
+ user: thredded_current_user,
117
+ ip: request.remote_ip,
118
+ )
119
+ end
120
+
121
+ def topics_by_category(category_id)
122
+ messageboard.categories.friendly.find(category_id)
123
+ .topics
124
+ .unstuck
125
+ .order_by_updated
126
+ .on_page(current_page)
127
+ .load
128
+ end
129
+
130
+ def current_page
131
+ params[:page] || 1
132
+ end
133
+
134
+ def user_topic
135
+ @user_topic ||= UserTopicDecorator.new(thredded_current_user, topic)
136
+ end
137
+
138
+ def update_read_status
139
+ return if thredded_current_user.thredded_anonymous?
140
+
141
+ read_history = UserTopicRead.where(
142
+ user_id: thredded_current_user,
143
+ topic: topic,
144
+ ).first_or_initialize
145
+
146
+ read_history.update_attributes(
147
+ farthest_post: @posts.last,
148
+ posts_count: topic.posts_count,
149
+ page: current_page,
150
+ )
151
+ end
152
+ end
153
+ end