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,104 @@
1
+ require 'thredded/search_sql_builder'
2
+
3
+ module Thredded
4
+ class Topic < ActiveRecord::Base
5
+ include TopicCommon
6
+
7
+ scope :for_messageboard, -> messageboard { where(messageboard_id: messageboard.id) }
8
+
9
+ extend FriendlyId
10
+ friendly_id :title, use: [:history, :scoped], scope: :messageboard
11
+
12
+ belongs_to :messageboard,
13
+ counter_cache: true,
14
+ touch: true
15
+ validates_presence_of :messageboard_id
16
+
17
+ belongs_to :user_detail,
18
+ primary_key: :user_id,
19
+ foreign_key: :user_id,
20
+ inverse_of: :topics,
21
+ counter_cache: :topics_count
22
+
23
+ has_many :posts,
24
+ class_name: 'Thredded::Post',
25
+ foreign_key: :postable_id,
26
+ inverse_of: :postable,
27
+ dependent: :destroy
28
+ has_many :topic_categories, dependent: :destroy
29
+ has_many :categories, through: :topic_categories
30
+ has_many :user_topic_reads, dependent: :destroy
31
+
32
+ def self.stuck
33
+ where(sticky: true)
34
+ end
35
+
36
+ def self.unstuck
37
+ where(sticky: false)
38
+ end
39
+
40
+ def self.order_by_updated_time
41
+ order('thredded_topics.updated_at DESC')
42
+ end
43
+
44
+ def self.order_by_stuck_and_updated_time
45
+ order('thredded_topics.sticky DESC, thredded_topics.updated_at DESC')
46
+ end
47
+
48
+ def self.search(query, messageboard)
49
+ results = find_by_sql(SearchSqlBuilder.new(query, messageboard).build)
50
+ fail(Thredded::Errors::EmptySearchResults, query) if results.empty?
51
+ results
52
+ end
53
+
54
+ def self.find_by_slug_with_user_topic_reads!(slug)
55
+ includes(:user_topic_reads).friendly.find(slug)
56
+ rescue ActiveRecord::RecordNotFound
57
+ raise Thredded::Errors::TopicNotFound
58
+ end
59
+
60
+ def decorate
61
+ TopicDecorator.new(self)
62
+ end
63
+
64
+ def public?
65
+ true
66
+ end
67
+
68
+ # rubocop:disable all
69
+ def self.inherited(child)
70
+ child.instance_eval do
71
+ def model_name
72
+ Topic.model_name
73
+ end
74
+ end
75
+
76
+ super
77
+ end
78
+ # rubocop:enable all
79
+
80
+ def self.select_options
81
+ subclasses.map(&:to_s).sort
82
+ end
83
+
84
+ def self.recent
85
+ limit(10)
86
+ end
87
+
88
+ def updating?
89
+ id.present?
90
+ end
91
+
92
+ def categories_to_sentence
93
+ categories.map(&:name).to_sentence if categories.any?
94
+ end
95
+
96
+ def users_to_sentence
97
+ []
98
+ end
99
+
100
+ def should_generate_new_friendly_id?
101
+ title_changed?
102
+ end
103
+ end
104
+ end
@@ -0,0 +1,6 @@
1
+ module Thredded
2
+ class TopicCategory < ActiveRecord::Base
3
+ belongs_to :category
4
+ belongs_to :topic
5
+ end
6
+ end
@@ -0,0 +1,26 @@
1
+ module Thredded
2
+ class UserDetail < ActiveRecord::Base
3
+ belongs_to :user, class_name: Thredded.user_class
4
+ validates :user_id, presence: true
5
+
6
+ has_many :topics, class_name: 'Thredded::Topic', foreign_key: :user_id, primary_key: :user_id
7
+ has_many :posts, class_name: 'Thredded::Post', foreign_key: :user_id, primary_key: :user_id
8
+ has_many :private_posts, class_name: 'Thredded::PrivatePost', foreign_key: :user_id, primary_key: :user_id
9
+
10
+ scope :recently_active, -> { where(arel_table[:last_seen_at].gt(Thredded.active_user_threshold.ago)) }
11
+
12
+ # Find or create and return a {UserDetail} for a given user.
13
+ # @param user [Thredded.user_class]
14
+ # @return [Thredded::UserDetail] a persisted UserDetail.
15
+ def self.for_user(user)
16
+ for_user_id(user.id)
17
+ end
18
+
19
+ # Find or create and return a {UserDetail} for a given user ID.
20
+ # @param user_id [Fixnum, String]
21
+ # @return [Thredded::UserDetail] a persisted UserDetail.
22
+ def self.for_user_id(user_id)
23
+ where(user_id: user_id).first_or_create!
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,33 @@
1
+ require_relative './user_permissions/read/all'
2
+ require_relative './user_permissions/write/all'
3
+ require_relative './user_permissions/message/readers_of_writeable_boards'
4
+ require_relative './user_permissions/moderate/if_moderator_column_true'
5
+ require_relative './user_permissions/admin/if_admin_column_true'
6
+
7
+ module Thredded
8
+ module UserExtender
9
+ extend ActiveSupport::Concern
10
+
11
+ include ::Thredded::UserPermissions::Read::All
12
+ include ::Thredded::UserPermissions::Write::All
13
+ include ::Thredded::UserPermissions::Message::ReadersOfWriteableBoards
14
+ include ::Thredded::UserPermissions::Moderate::IfModeratorColumnTrue
15
+ include ::Thredded::UserPermissions::Admin::IfAdminColumnTrue
16
+
17
+ included do
18
+ has_many :thredded_notification_preferences, class_name: 'Thredded::NotificationPreference', foreign_key: 'user_id'
19
+ has_many :thredded_posts, class_name: 'Thredded::Post', foreign_key: 'user_id'
20
+ has_many :thredded_private_topics, through: :thredded_private_users, class_name: 'Thredded::PrivateTopic', source: :private_topic
21
+ has_many :thredded_private_users, class_name: 'Thredded::PrivateUser', foreign_key: 'user_id'
22
+ has_many :thredded_topics, class_name: 'Thredded::Topic', foreign_key: 'user_id'
23
+ has_many :thredded_read_topics, class_name: 'Thredded::UserTopicRead', foreign_key: 'user_id'
24
+
25
+ has_one :thredded_user_detail, class_name: 'Thredded::UserDetail', foreign_key: 'user_id'
26
+ has_one :thredded_user_preference, class_name: 'Thredded::UserPreference', foreign_key: 'user_id'
27
+ end
28
+
29
+ def thredded_anonymous?
30
+ false
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,12 @@
1
+ module Thredded
2
+ module UserPermissions
3
+ module Admin
4
+ module IfAdminColumnTrue
5
+ # @return [boolean] Whether this user has full admin rights on Thredded.
6
+ def thredded_admin?
7
+ send(Thredded.admin_column)
8
+ end
9
+ end
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,12 @@
1
+ module Thredded
2
+ module UserPermissions
3
+ module Admin
4
+ module None
5
+ # @return [boolean] Whether this user has full admin rights on Thredded.
6
+ def thredded_admin?
7
+ false
8
+ end
9
+ end
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,13 @@
1
+ module Thredded
2
+ module UserPermissions
3
+ module Message
4
+ module ReadersOfWriteableBoards
5
+ # @return [ActiveRecord::Relation<Thredded.user_class>] the users this user can include in a private topic
6
+ def thredded_can_message_users
7
+ # By default, return everyone who can read the messageboards this user can post in
8
+ Thredded.user_class.thredded_messageboards_readers(thredded_can_write_messageboards)
9
+ end
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,25 @@
1
+ module Thredded
2
+ module UserPermissions
3
+ module Moderate
4
+ module IfModeratorColumnTrue
5
+ extend ActiveSupport::Concern
6
+ included { extend ClassMethods }
7
+
8
+ # @return [ActiveRecord::Relation<Thredded::Messageboard>] messageboards that the user can moderate
9
+ def thredded_can_moderate_messageboards
10
+ send(Thredded.moderator_column) ? Thredded::Messageboard.all : Thredded::Messageboard.none
11
+ end
12
+
13
+ module ClassMethods
14
+ # Users that can moderate the given messageboards.
15
+ #
16
+ # @param _messageboards [Array<Thredded::Messageboard>]
17
+ # @return [ActiveRecord::Relation<Thredded.user_class>] users that can moderate the given messageboards
18
+ def thredded_messageboards_moderators(_messageboards)
19
+ where(Thredded.moderator_column => true)
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,25 @@
1
+ module Thredded
2
+ module UserPermissions
3
+ module Moderate
4
+ module None
5
+ extend ActiveSupport::Concern
6
+ included { extend ClassMethods }
7
+
8
+ # @return [ActiveRecord::Relation<Thredded::Messageboard>] messageboards that the user can moderate
9
+ def thredded_can_moderate_messageboards
10
+ Thredded::Messageboard.none
11
+ end
12
+
13
+ module ClassMethods
14
+ # Users that can moderate the given messageboards.
15
+ #
16
+ # @param _messageboards [Array<Thredded::Messageboard>]
17
+ # @return [ActiveRecord::Relation<Thredded.user_class>] users that can moderate the given messageboards
18
+ def thredded_messageboards_moderators(_messageboards)
19
+ none
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,25 @@
1
+ module Thredded
2
+ module UserPermissions
3
+ module Read
4
+ module All
5
+ extend ActiveSupport::Concern
6
+ included { extend ClassMethods }
7
+
8
+ # @return [ActiveRecord::Relation<Thredded::Messageboard>] messageboards that the user can read
9
+ def thredded_can_read_messageboards
10
+ Thredded::Messageboard.all
11
+ end
12
+
13
+ module ClassMethods
14
+ # Users that can read the given messageboards.
15
+ #
16
+ # @param _messageboards [Array<Thredded::Messageboard>]
17
+ # @return [ActiveRecord::Relation<Thredded.user_class>] users that can read the given messageboards
18
+ def thredded_messageboards_readers(_messageboards)
19
+ all
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,25 @@
1
+ module Thredded
2
+ module UserPermissions
3
+ module Write
4
+ module All
5
+ extend ActiveSupport::Concern
6
+ included { extend ClassMethods }
7
+
8
+ # @return [ActiveRecord::Relation<Thredded::Messageboard>] messageboards that the user can post in
9
+ def thredded_can_write_messageboards
10
+ Thredded::Messageboard.all
11
+ end
12
+
13
+ module ClassMethods
14
+ # Users that can post to the given messageboards.
15
+ #
16
+ # @param _messageboards [Array<Thredded::Messageboard>]
17
+ # @return [ActiveRecord::Relation<Thredded.user_class>] users that can post to the given messageboards
18
+ def thredded_messageboards_writers(_messageboards)
19
+ all
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,25 @@
1
+ module Thredded
2
+ module UserPermissions
3
+ module Write
4
+ module None
5
+ extend ActiveSupport::Concern
6
+ included { extend ClassMethods }
7
+
8
+ # @return [ActiveRecord::Relation<Thredded::Messageboard>] messageboards that the user can post in
9
+ def thredded_can_write_messageboards
10
+ Thredded::Messageboard.none
11
+ end
12
+
13
+ module ClassMethods
14
+ # Users that can post to the given messageboards.
15
+ #
16
+ # @param _messageboards [Array<Thredded::Messageboard>]
17
+ # @return [ActiveRecord::Relation<Thredded.user_class>] users that can post to the given messageboards
18
+ def thredded_messageboards_writers(_messageboards)
19
+ none
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,6 @@
1
+ module Thredded
2
+ class UserPreference < ActiveRecord::Base
3
+ belongs_to :user, class_name: Thredded.user_class
4
+ validates :user_id, presence: true
5
+ end
6
+ end
@@ -0,0 +1,10 @@
1
+ module Thredded
2
+ class UserTopicRead < ActiveRecord::Base
3
+ belongs_to :topic
4
+ belongs_to :user, class_name: Thredded.user_class
5
+ belongs_to :farthest_post,
6
+ class_name: 'Thredded::Post', foreign_key: 'post_id'
7
+ validates :user_id, uniqueness: { scope: :topic }
8
+ belongs_to :post
9
+ end
10
+ end
@@ -0,0 +1,15 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <title>Thredded | <%= yield :thredded_page_title %></title>
5
+ <meta content='width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0' name='viewport' />
6
+ <%= stylesheet_link_tag 'thredded' %>
7
+ <%= csrf_meta_tag %>
8
+ <%= javascript_include_tag 'thredded' %>
9
+ <%== Gravatar.prefetch_dns %>
10
+ <meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=no">
11
+ </head>
12
+ <body>
13
+ <%= yield %>
14
+ </body>
15
+ </html>
@@ -0,0 +1 @@
1
+ <li class="<%= category.name.underscore %>"><%= category.name %></li>
@@ -0,0 +1,11 @@
1
+ <%# Link to the "First" page
2
+ - available local variables
3
+ url: url to the first page
4
+ current_page: a page object for the currently displayed page
5
+ total_pages: total number of pages
6
+ per_page: number of items to fetch per page
7
+ remote: data-remote
8
+ -%>
9
+ <span class="first">
10
+ <%= link_to_unless current_page.first?, t('views.pagination.first').html_safe, url, :remote => remote %>
11
+ </span>
@@ -0,0 +1,8 @@
1
+ <%# Non-link tag that stands for skipped pages...
2
+ - available local variables
3
+ current_page: a page object for the currently displayed page
4
+ total_pages: total number of pages
5
+ per_page: number of items to fetch per page
6
+ remote: data-remote
7
+ -%>
8
+ <span class="page gap"><%= t('views.pagination.truncate').html_safe %></span>
@@ -0,0 +1,11 @@
1
+ <%# Link to the "Last" page
2
+ - available local variables
3
+ url: url to the last page
4
+ current_page: a page object for the currently displayed page
5
+ total_pages: total number of pages
6
+ per_page: number of items to fetch per page
7
+ remote: data-remote
8
+ -%>
9
+ <span class="last">
10
+ <%= link_to_unless current_page.last?, t('views.pagination.last').html_safe, url, :remote => remote %>
11
+ </span>
@@ -0,0 +1,11 @@
1
+ <%# Link to the "Next" page
2
+ - available local variables
3
+ url: url to the next page
4
+ current_page: a page object for the currently displayed page
5
+ total_pages: total number of pages
6
+ per_page: number of items to fetch per page
7
+ remote: data-remote
8
+ -%>
9
+ <span class="next">
10
+ <%= link_to_unless current_page.last?, t('views.pagination.next').html_safe, url, :rel => 'next', :remote => remote %>
11
+ </span>
@@ -0,0 +1,12 @@
1
+ <%# Link showing page number
2
+ - available local variables
3
+ page: a page object for "this" page
4
+ url: url to this page
5
+ current_page: a page object for the currently displayed page
6
+ total_pages: total number of pages
7
+ per_page: number of items to fetch per page
8
+ remote: data-remote
9
+ -%>
10
+ <span class="page<%= ' current' if page.current? %>">
11
+ <%= link_to_unless page.current?, page, url, {:remote => remote, :rel => page.next? ? 'next' : page.prev? ? 'prev' : nil} %>
12
+ </span>
@@ -0,0 +1,23 @@
1
+ <%# The container tag
2
+ - available local variables
3
+ current_page: a page object for the currently displayed page
4
+ total_pages: total number of pages
5
+ per_page: number of items to fetch per page
6
+ remote: data-remote
7
+ paginator: the paginator that renders the pagination tags inside
8
+ -%>
9
+ <%= paginator.render do -%>
10
+ <nav class="pagination thredded--pagination">
11
+ <%= first_page_tag unless current_page.first? %>
12
+ <%= prev_page_tag unless current_page.first? %>
13
+ <% each_page do |page| -%>
14
+ <% if page.left_outer? || page.right_outer? || page.inside_window? -%>
15
+ <%= page_tag page %>
16
+ <% elsif !page.was_truncated? -%>
17
+ <%= gap_tag %>
18
+ <% end -%>
19
+ <% end -%>
20
+ <%= next_page_tag unless current_page.last? %>
21
+ <%= last_page_tag unless current_page.last? %>
22
+ </nav>
23
+ <% end -%>
@@ -0,0 +1,11 @@
1
+ <%# Link to the "Previous" page
2
+ - available local variables
3
+ url: url to the previous page
4
+ current_page: a page object for the currently displayed page
5
+ total_pages: total number of pages
6
+ per_page: number of items to fetch per page
7
+ remote: data-remote
8
+ -%>
9
+ <span class="prev">
10
+ <%= link_to_unless current_page.first?, t('views.pagination.previous').html_safe, url, :rel => 'prev', :remote => remote %>
11
+ </span>
@@ -0,0 +1,15 @@
1
+ <% if current_ability.can? :read, messageboard.original %>
2
+ <%= link_to messageboard_topics_path(messageboard), class: 'thredded--messageboard' do %>
3
+ <header>
4
+ <h2 class="thredded--messageboard--title"><%= messageboard.name %></h2>
5
+ <h3 class="thredded--messageboard--meta"><%= messageboard.meta %></h3>
6
+ </header>
7
+
8
+ <p class="thredded--messageboard--description"><%= messageboard.description %></p>
9
+
10
+ <p class="thredded--messageboard--byline">
11
+ Updated <%= time_ago messageboard.latest_topic.updated_at %>
12
+ <cite>by <%= messageboard.latest_user %></cite>
13
+ </p>
14
+ <% end %>
15
+ <% end %>
@@ -0,0 +1,20 @@
1
+ <% content_for :thredded_page_title, 'Messageboards' %>
2
+ <% content_for :thredded_page_id, 'thredded--messageboards-index' %>
3
+ <% content_for :thredded_breadcrumbs do %>
4
+ <ul class="thredded--navigation-breadcrumbs">
5
+ <li>
6
+ <%= link_to 'All Message Boards', messageboards_path %>
7
+ </li>
8
+ </ul>
9
+ <% end %>
10
+ <%= thredded_page do %>
11
+ <section class="thredded--main-section thredded--messageboards">
12
+ <%= render @messageboards %>
13
+ </section>
14
+
15
+ <% if current_ability.can? :create, Thredded::Messageboard %>
16
+ <div class="thredded--messageboard--create">
17
+ <a class="thredded--button" href="<%= new_messageboard_path %>">Create a New Messageboard</a>
18
+ </div>
19
+ <% end %>
20
+ <% end %>
@@ -0,0 +1,18 @@
1
+ <% content_for :thredded_page_title, 'Create a New Messageboard' %>
2
+ <% content_for :thredded_page_id, 'thredded--messageboards-new' %>
3
+ <%= thredded_page do %>
4
+ <%= form_for @messageboard, class: 'thredded--form' do |f| %>
5
+ <ul class="thredded--form-list">
6
+ <li>
7
+ <%= f.label :name %>
8
+ <%= f.text_field :name %>
9
+ </li>
10
+ <li>
11
+ <%= f.label :description %>
12
+ <%= f.text_field :description %>
13
+ </li>
14
+
15
+ <li><%= f.submit 'Create New Messageboard', class: 'thredded--form--submit' %></li>
16
+ </ul>
17
+ <% end %>
18
+ <% end %>
@@ -0,0 +1,14 @@
1
+ <%= @post.filtered_content(self) %>
2
+
3
+ <hr />
4
+
5
+ <p>
6
+ This email was sent to you because <%= @post.user %> mentioned you in
7
+ "<%= link_to @post.postable.title, messageboard_topic_posts_url(@post.messageboard, @post.postable, anchor: "post_#{@post.id}") %>".
8
+ <%= link_to 'View the conversation here.', messageboard_topic_posts_url(@post.messageboard, @post.postable) %>
9
+ </p>
10
+
11
+ <p>
12
+ To unsubscribe from these emails, update your
13
+ <%= link_to 'preferences', edit_messageboard_preferences_url(@post.messageboard) %>.
14
+ </p>
@@ -0,0 +1,10 @@
1
+ <%= @post.content %>
2
+
3
+ ---
4
+
5
+ This email was sent to you because <%= @post.user %> mentioned you in
6
+ "<%= @post.postable.title %>". Go here to view the conversation:
7
+ <%= messageboard_topic_posts_url @post.messageboard, @post.postable, anchor: "post_#{@post.id}" %>
8
+
9
+ To unsubscribe from these emails, update your preferences here:
10
+ <%= edit_messageboard_preferences_url @post.messageboard %>
@@ -0,0 +1,4 @@
1
+ <li>
2
+ <%= form.label :content %>
3
+ <%= form.text_area :content, { rows: 5, required: true } %>
4
+ </li>
@@ -0,0 +1 @@
1
+ <%= render 'thredded/posts_common/form', topic: topic, post: post, button_text: button_text %>
@@ -0,0 +1 @@
1
+ <%= render 'thredded/posts_common/post', post: post %>
@@ -0,0 +1,3 @@
1
+ <span class="posts_count">
2
+ <%= post.user_posts_count %> posts
3
+ </span>
@@ -0,0 +1,13 @@
1
+ <% content_for :thredded_page_title, 'Edit Post' %>
2
+ <% content_for :thredded_page_id, 'thredded--edit-post' %>
3
+ <%= thredded_page do %>
4
+ <section class="thredded--main-section">
5
+ <h3 class="thredded--post-form--title">Edit Post</h3>
6
+
7
+ <%= render 'thredded/posts/form',
8
+ messageboard: messageboard,
9
+ topic: topic,
10
+ post: @post,
11
+ button_text: 'Update Post' %>
12
+ </section>
13
+ <% end %>
@@ -0,0 +1,10 @@
1
+ <%= form_for (post.private_topic_post? ? [topic, post] : [messageboard, topic, post]), as: :post,
2
+ html: { class: 'thredded--form thredded--post-form', 'data-thredded-post-form' => true } do |form| %>
3
+ <ul class="thredded--form-list">
4
+ <%= render 'thredded/posts/content_field', form: form %>
5
+
6
+ <li>
7
+ <%= form.submit button_text, class: 'thredded--form--submit' %>
8
+ </li>
9
+ </ul>
10
+ <% end %>
@@ -0,0 +1,20 @@
1
+ <% post = Thredded::PostDecorator.new(post) %>
2
+
3
+ <%= content_tag_for :article, post, class: 'thredded--post' do %>
4
+ <header>
5
+ <%= image_tag post.avatar_url, class: 'thredded--post--avatar' unless post.user_anonymous? %>
6
+ <h2 class="thredded--post--user"><%= user_link post.user %></h2>
7
+ <p class="thredded--post--created-at"><%= time_ago post.created_at %></p>
8
+
9
+ </header>
10
+
11
+ <div class="thredded--post--content">
12
+ <% cache [post, 'content'] do %>
13
+ <%= post.filtered_content(self) %>
14
+ <% end %>
15
+ </div>
16
+
17
+ <% if current_ability.can? :edit, post.original %>
18
+ <%= link_to 'Edit Post', edit_post_path(post.original), class: 'thredded--post--edit' %>
19
+ <% end %>
20
+ <% end %>