thredded 0.2.2 → 0.3.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 (220) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.mkdn +63 -0
  3. data/Procfile +1 -0
  4. data/README.mkdn +42 -20
  5. data/app/assets/images/thredded/private-messages.svg +4 -0
  6. data/app/assets/images/thredded/settings.svg +4 -0
  7. data/app/assets/javascripts/thredded.es6 +2 -10
  8. data/app/assets/javascripts/thredded/{currently_online.es6 → components/currently_online.es6} +0 -0
  9. data/app/assets/javascripts/thredded/{post_form.es6 → components/post_form.es6} +0 -0
  10. data/app/assets/javascripts/thredded/{time_stamps.es6 → components/time_stamps.es6} +0 -0
  11. data/app/assets/javascripts/thredded/{topic_form.es6 → components/topic_form.es6} +1 -1
  12. data/app/assets/javascripts/thredded/components/topics.es6 +37 -0
  13. data/app/assets/javascripts/thredded/components/user_preferences_form.es6 +45 -0
  14. data/app/assets/javascripts/thredded/components/users_select.es6 +56 -0
  15. data/app/assets/javascripts/thredded/dependencies.js +9 -0
  16. data/app/assets/javascripts/thredded/thredded.es6 +1 -0
  17. data/app/assets/stylesheets/thredded/_base.scss +3 -2
  18. data/app/assets/stylesheets/thredded/_thredded.scss +4 -1
  19. data/app/assets/stylesheets/thredded/base/_buttons.scss +2 -1
  20. data/app/assets/stylesheets/thredded/base/_forms.scss +23 -18
  21. data/app/assets/stylesheets/thredded/base/_grid.scss +1 -1
  22. data/app/assets/stylesheets/thredded/base/_nav.scss +21 -0
  23. data/app/assets/stylesheets/thredded/base/_tables.scss +5 -14
  24. data/app/assets/stylesheets/thredded/base/_typography.scss +9 -4
  25. data/app/assets/stylesheets/thredded/base/_variables.scss +28 -9
  26. data/app/assets/stylesheets/thredded/components/_alerts.scss +19 -0
  27. data/app/assets/stylesheets/thredded/components/_currently-online.scss +1 -1
  28. data/app/assets/stylesheets/thredded/components/_form-list.scss +2 -4
  29. data/app/assets/stylesheets/thredded/components/_icons.scss +3 -0
  30. data/app/assets/stylesheets/thredded/components/_messageboard.scss +4 -4
  31. data/app/assets/stylesheets/thredded/components/_pagination.scss +2 -2
  32. data/app/assets/stylesheets/thredded/components/_post-form.scss +3 -0
  33. data/app/assets/stylesheets/thredded/components/_post.scss +14 -4
  34. data/app/assets/stylesheets/thredded/components/_select2.scss +79 -9
  35. data/app/assets/stylesheets/thredded/components/_topic-header.scss +11 -1
  36. data/app/assets/stylesheets/thredded/components/_topics.scss +13 -11
  37. data/app/assets/stylesheets/thredded/layout/_main-container.scss +3 -3
  38. data/app/assets/stylesheets/thredded/layout/_main-navigation.scss +11 -17
  39. data/app/assets/stylesheets/thredded/layout/_navigation.scss +72 -0
  40. data/app/assets/stylesheets/thredded/layout/_search-navigation.scss +66 -0
  41. data/app/assets/stylesheets/thredded/layout/_user-navigation.scss +35 -61
  42. data/app/commands/thredded/at_notification_extractor.rb +1 -0
  43. data/app/commands/thredded/members_marked_notified.rb +1 -0
  44. data/app/commands/thredded/messageboard_destroyer.rb +7 -2
  45. data/app/commands/thredded/notify_mentioned_users.rb +8 -21
  46. data/app/commands/thredded/notify_private_topic_users.rb +3 -5
  47. data/app/controllers/thredded/application_controller.rb +76 -41
  48. data/app/controllers/thredded/autocomplete_users_controller.rb +46 -0
  49. data/app/controllers/thredded/messageboards_controller.rb +8 -5
  50. data/app/controllers/thredded/posts_controller.rb +20 -22
  51. data/app/controllers/thredded/preferences_controller.rb +19 -14
  52. data/app/controllers/thredded/private_topics_controller.rb +58 -23
  53. data/app/controllers/thredded/setups_controller.rb +1 -0
  54. data/app/controllers/thredded/theme_previews_controller.rb +24 -53
  55. data/app/controllers/thredded/topics_controller.rb +48 -77
  56. data/app/forms/thredded/private_topic_form.rb +1 -21
  57. data/app/forms/thredded/topic_form.rb +3 -7
  58. data/app/forms/thredded/user_preferences_form.rb +62 -0
  59. data/app/helpers/thredded/application_helper.rb +11 -12
  60. data/app/helpers/thredded/urls_helper.rb +103 -0
  61. data/app/jobs/thredded/activity_updater_job.rb +4 -3
  62. data/app/jobs/thredded/at_notifier_job.rb +1 -0
  63. data/app/jobs/thredded/notify_private_topic_users_job.rb +1 -0
  64. data/app/mailer_previews/thredded/base_mailer_preview.rb +101 -0
  65. data/app/mailer_previews/thredded/post_mailer_preview.rb +11 -0
  66. data/app/mailer_previews/thredded/private_post_mailer_preview.rb +11 -0
  67. data/app/mailer_previews/thredded/private_topic_mailer_preview.rb +15 -0
  68. data/app/mailers/thredded/base_mailer.rb +13 -0
  69. data/app/mailers/thredded/post_mailer.rb +4 -2
  70. data/app/mailers/thredded/private_post_mailer.rb +4 -2
  71. data/app/mailers/thredded/private_topic_mailer.rb +4 -2
  72. data/app/models/concerns/thredded/friendly_id_reserved_words_and_pagination.rb +16 -0
  73. data/app/models/concerns/thredded/post_common.rb +68 -63
  74. data/app/models/concerns/thredded/topic_common.rb +31 -8
  75. data/app/models/concerns/thredded/user_topic_read_state_common.rb +31 -0
  76. data/app/models/thredded/category.rb +1 -0
  77. data/app/models/thredded/messageboard.rb +24 -25
  78. data/app/models/thredded/messageboard_user.rb +1 -0
  79. data/app/models/thredded/null_preference.rb +1 -0
  80. data/app/models/thredded/null_user.rb +1 -6
  81. data/app/models/thredded/null_user_topic_read_state.rb +12 -0
  82. data/app/models/thredded/post.rb +6 -9
  83. data/app/models/thredded/post_notification.rb +1 -0
  84. data/app/models/thredded/private_post.rb +6 -2
  85. data/app/models/thredded/private_topic.rb +46 -32
  86. data/app/models/thredded/private_user.rb +3 -2
  87. data/app/models/thredded/stats.rb +1 -0
  88. data/app/models/thredded/topic.rb +40 -64
  89. data/app/models/thredded/topic_category.rb +1 -0
  90. data/app/models/thredded/user_detail.rb +2 -15
  91. data/app/models/thredded/user_extender.rb +29 -14
  92. data/app/models/thredded/user_messageboard_preference.rb +20 -0
  93. data/app/models/thredded/user_permissions/admin/if_admin_column_true.rb +1 -0
  94. data/app/models/thredded/user_permissions/admin/none.rb +1 -0
  95. data/app/models/thredded/user_permissions/message/readers_of_writeable_boards.rb +1 -0
  96. data/app/models/thredded/user_permissions/moderate/if_moderator_column_true.rb +1 -0
  97. data/app/models/thredded/user_permissions/moderate/none.rb +1 -0
  98. data/app/models/thredded/user_permissions/read/all.rb +1 -0
  99. data/app/models/thredded/user_permissions/write/all.rb +1 -0
  100. data/app/models/thredded/user_permissions/write/none.rb +1 -0
  101. data/app/models/thredded/user_preference.rb +7 -1
  102. data/app/models/thredded/user_private_topic_read_state.rb +12 -0
  103. data/app/models/thredded/user_topic_read_state.rb +12 -0
  104. data/app/policies/thredded/messageboard_policy.rb +27 -0
  105. data/app/policies/thredded/post_policy.rb +33 -0
  106. data/app/policies/thredded/private_post_policy.rb +29 -0
  107. data/app/policies/thredded/private_topic_policy.rb +23 -0
  108. data/app/policies/thredded/topic_policy.rb +32 -0
  109. data/app/view_models/thredded/base_topic_view.rb +56 -0
  110. data/app/view_models/thredded/post_view.rb +44 -0
  111. data/app/view_models/thredded/posts_page_view.rb +27 -0
  112. data/app/view_models/thredded/private_topic_view.rb +9 -0
  113. data/app/{decorators/thredded/topic_email_decorator.rb → view_models/thredded/topic_email_view.rb} +2 -1
  114. data/app/view_models/thredded/topic_view.rb +23 -0
  115. data/app/view_models/thredded/topics_page_view.rb +26 -0
  116. data/app/views/thredded/error_pages/forbidden.html.erb +6 -0
  117. data/app/views/thredded/error_pages/not_found.html.erb +6 -0
  118. data/app/views/thredded/messageboards/_messageboard.html.erb +13 -6
  119. data/app/views/thredded/messageboards/index.html.erb +2 -8
  120. data/app/views/thredded/messageboards/new.html.erb +8 -2
  121. data/app/views/thredded/post_mailer/at_notification.html.erb +3 -3
  122. data/app/views/thredded/post_mailer/at_notification.text.erb +1 -1
  123. data/app/views/thredded/posts/_content_field.html.erb +1 -1
  124. data/app/views/thredded/posts/_form.html.erb +5 -1
  125. data/app/views/thredded/posts/_post.html.erb +3 -1
  126. data/app/views/thredded/posts/edit.html.erb +7 -3
  127. data/app/views/thredded/posts_common/_form.html.erb +1 -1
  128. data/app/views/thredded/posts_common/_post.html.erb +14 -8
  129. data/app/views/thredded/preferences/_form.html.erb +37 -15
  130. data/app/views/thredded/preferences/_header.html.erb +1 -1
  131. data/app/views/thredded/preferences/edit.html.erb +4 -6
  132. data/app/views/thredded/private_post_mailer/at_notification.html.erb +6 -4
  133. data/app/views/thredded/private_posts/_form.html.erb +5 -1
  134. data/app/views/thredded/private_posts/_private_post.html.erb +3 -1
  135. data/app/views/thredded/private_topic_mailer/message_notification.html.erb +3 -7
  136. data/app/views/thredded/private_topic_mailer/message_notification.text.erb +1 -3
  137. data/app/views/thredded/private_topics/_breadcrumbs.html.erb +2 -2
  138. data/app/views/thredded/private_topics/_form.html.erb +15 -10
  139. data/app/views/thredded/private_topics/_header.html.erb +12 -0
  140. data/app/views/thredded/private_topics/_no_private_topics.html.erb +2 -2
  141. data/app/views/thredded/private_topics/_private_topic.html.erb +4 -6
  142. data/app/views/thredded/private_topics/edit.html.erb +32 -0
  143. data/app/views/thredded/private_topics/index.html.erb +5 -5
  144. data/app/views/thredded/private_topics/new.html.erb +1 -2
  145. data/app/views/thredded/private_topics/show.html.erb +12 -7
  146. data/app/views/thredded/search/_form.html.erb +9 -6
  147. data/app/views/thredded/shared/{_messageboard_topics_breadcrumbs.html.erb → _breadcrumbs.html.erb} +2 -2
  148. data/app/views/thredded/shared/_header.html.erb +2 -3
  149. data/app/views/thredded/shared/_nav.html.erb +20 -0
  150. data/app/views/thredded/shared/nav/_notification_preferences.html.erb +6 -0
  151. data/app/views/thredded/shared/nav/_private_topics.html.erb +11 -0
  152. data/app/views/thredded/shared/nav/_standalone.html.erb +12 -0
  153. data/app/views/thredded/theme_previews/_section_title.html.erb +2 -2
  154. data/app/views/thredded/theme_previews/show.html.erb +13 -17
  155. data/app/views/thredded/topics/_form.html.erb +8 -6
  156. data/app/views/thredded/topics/_header.html.erb +12 -0
  157. data/app/views/thredded/topics/_topic.html.erb +4 -8
  158. data/app/views/thredded/topics/_topic_form_admin_options.html.erb +1 -1
  159. data/app/views/thredded/topics/edit.html.erb +22 -18
  160. data/app/views/thredded/topics/index.html.erb +3 -3
  161. data/app/views/thredded/topics/new.html.erb +1 -1
  162. data/app/views/thredded/topics/search.html.erb +17 -5
  163. data/app/views/thredded/topics/show.html.erb +14 -11
  164. data/bin/rails +5 -0
  165. data/config.ru +3 -0
  166. data/config/i18n-tasks.yml +16 -0
  167. data/config/locales/en.yml +90 -0
  168. data/config/routes.rb +29 -15
  169. data/db/migrate/20160329231848_create_thredded.rb +29 -33
  170. data/db/seeds.rb +115 -0
  171. data/db/upgrade_migrations/20160410111522_upgrade_v0_2_to_v0_3.rb +59 -0
  172. data/heroku.gemfile +26 -0
  173. data/heroku.gemfile.lock +282 -0
  174. data/lib/generators/thredded/install/install_generator.rb +1 -0
  175. data/lib/generators/thredded/install/templates/initializer.rb +17 -0
  176. data/lib/html/pipeline/at_mention_filter.rb +2 -1
  177. data/lib/html/pipeline/bbcode_filter.rb +13 -4
  178. data/lib/tasks/thredded_tasks.rake +1 -0
  179. data/lib/thredded.rb +19 -17
  180. data/lib/thredded/at_users.rb +1 -0
  181. data/lib/thredded/engine.rb +14 -5
  182. data/lib/thredded/errors.rb +11 -11
  183. data/lib/thredded/main_app_route_delegator.rb +1 -0
  184. data/lib/thredded/search_parser.rb +2 -1
  185. data/lib/thredded/topics_search.rb +67 -0
  186. data/lib/thredded/version.rb +2 -1
  187. data/thredded.gemspec +12 -8
  188. metadata +146 -82
  189. data/app/assets/javascripts/thredded/users_select.es6 +0 -5
  190. data/app/assets/stylesheets/thredded/layout/_topic-navigation.scss +0 -53
  191. data/app/commands/thredded/user_reads_private_topic.rb +0 -22
  192. data/app/commands/thredded/user_resets_private_topic_to_unread.rb +0 -23
  193. data/app/decorators/thredded/base_topic_decorator.rb +0 -14
  194. data/app/decorators/thredded/base_user_topic_decorator.rb +0 -63
  195. data/app/decorators/thredded/messageboard_decorator.rb +0 -41
  196. data/app/decorators/thredded/post_decorator.rb +0 -40
  197. data/app/decorators/thredded/private_topic_decorator.rb +0 -23
  198. data/app/decorators/thredded/topic_decorator.rb +0 -25
  199. data/app/decorators/thredded/user_private_topic_decorator.rb +0 -13
  200. data/app/decorators/thredded/user_topic_decorator.rb +0 -37
  201. data/app/models/thredded/ability.rb +0 -60
  202. data/app/models/thredded/notification_preference.rb +0 -17
  203. data/app/models/thredded/null_topic.rb +0 -15
  204. data/app/models/thredded/null_topic_read.rb +0 -19
  205. data/app/models/thredded/user_topic_read.rb +0 -10
  206. data/app/views/thredded/shared/_notification_preferences.html.erb +0 -7
  207. data/app/views/thredded/shared/_top_nav.html.erb +0 -36
  208. data/app/views/thredded/shared/_topic_nav.html.erb +0 -22
  209. data/app/views/thredded/topics/_recent_topics_by_user.html.erb +0 -8
  210. data/app/views/thredded/topics/by_category.html.erb +0 -56
  211. data/app/views/thredded/topics_common/_header.html.erb +0 -6
  212. data/lib/thredded/messageboard_user_permissions.rb +0 -22
  213. data/lib/thredded/post_sql_builder.rb +0 -12
  214. data/lib/thredded/post_user_permissions.rb +0 -32
  215. data/lib/thredded/private_topic_user_permissions.rb +0 -26
  216. data/lib/thredded/search_sql_builder.rb +0 -21
  217. data/lib/thredded/seed_database.rb +0 -76
  218. data/lib/thredded/table_sql_builder.rb +0 -41
  219. data/lib/thredded/topic_sql_builder.rb +0 -11
  220. data/lib/thredded/topic_user_permissions.rb +0 -32
@@ -1,16 +1,48 @@
1
+ # frozen_string_literal: true
1
2
  module Thredded
2
3
  class PrivateTopic < ActiveRecord::Base
3
4
  include TopicCommon
5
+
6
+ scope :for_user, -> (user) { joins(:private_users).merge(PrivateUser.where(user_id: user.id)) }
7
+
4
8
  extend FriendlyId
5
- friendly_id :title, use: :history
9
+ friendly_id :slug_candidates,
10
+ use: [:history, :reserved],
11
+ # Avoid route conflicts
12
+ reserved_words: ::Thredded::FriendlyIdReservedWordsAndPagination.new(%w(new))
13
+
14
+ belongs_to :user,
15
+ class_name: Thredded.user_class,
16
+ inverse_of: :thredded_private_topics
6
17
 
7
18
  has_many :posts,
8
19
  class_name: 'Thredded::PrivatePost',
9
20
  foreign_key: :postable_id,
10
21
  inverse_of: :postable,
11
22
  dependent: :destroy
12
- has_many :private_users
23
+ has_one :first_post, -> { order_oldest_first },
24
+ class_name: 'Thredded::PrivatePost',
25
+ foreign_key: :postable_id
26
+ has_many :private_users, inverse_of: :private_topic
13
27
  has_many :users, through: :private_users
28
+ has_many :user_read_states,
29
+ class_name: 'Thredded::UserPrivateTopicReadState',
30
+ foreign_key: :postable_id,
31
+ inverse_of: :postable,
32
+ dependent: :destroy
33
+
34
+ validates_each :users do |model, _attr, users|
35
+ # Must include the creator + at least one other user
36
+ if users.length < 2
37
+ model.errors.add(:user_ids, I18n.t('thredded.private_topics.errors.user_ids_length'))
38
+ end
39
+ unless users.include?(model.user)
40
+ # This never happens in the UI, so we don't need to i18n the message.
41
+ model.errors.add(:user_ids, 'must include in user_ids')
42
+ end
43
+ end
44
+
45
+ before_validation :ensure_user_in_private_users
14
46
 
15
47
  def self.find_by_slug(slug)
16
48
  friendly.find(slug)
@@ -18,43 +50,25 @@ module Thredded
18
50
  raise Thredded::Errors::PrivateTopicNotFound
19
51
  end
20
52
 
21
- def decorate
22
- TopicDecorator.new(self)
23
- end
24
-
25
- def user_topic_reads
26
- []
27
- end
28
-
29
- def categories
30
- []
31
- end
32
-
33
- def self.for_user(user)
34
- joins(:private_users)
35
- .where(thredded_private_users: { user_id: user.id })
36
- end
37
-
38
- def add_user(user)
39
- if String == user.class
40
- user = Thredded.user_class.find_by_name(user)
41
- end
42
-
43
- users << user
44
- end
45
-
46
53
  def public?
47
54
  false
48
55
  end
49
56
 
50
- def user_id=(ids)
51
- return unless ids.size > 0
57
+ def should_generate_new_friendly_id?
58
+ slug.blank? || title_changed? || super
59
+ end
60
+
61
+ private
52
62
 
53
- self.users = Thredded.user_class.where(id: ids.uniq)
63
+ def slug_candidates
64
+ [
65
+ :title,
66
+ [:title, '-topic']
67
+ ]
54
68
  end
55
69
 
56
- def users_to_sentence
57
- users.map { |user| user.to_s.capitalize }.to_sentence
70
+ def ensure_user_in_private_users
71
+ users << user if user.present? && !users.include?(user)
58
72
  end
59
73
  end
60
74
  end
@@ -1,6 +1,7 @@
1
+ # frozen_string_literal: true
1
2
  module Thredded
2
3
  class PrivateUser < ActiveRecord::Base
3
- belongs_to :private_topic
4
- belongs_to :user, class_name: Thredded.user_class
4
+ belongs_to :private_topic, inverse_of: :private_users
5
+ belongs_to :user, class_name: Thredded.user_class, inverse_of: :thredded_private_users
5
6
  end
6
7
  end
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  module Thredded
2
3
  class Stats
3
4
  include ActionView::Helpers::NumberHelper
@@ -1,17 +1,34 @@
1
- require 'thredded/search_sql_builder'
2
-
1
+ # frozen_string_literal: true
2
+ require_dependency 'thredded/topics_search'
3
3
  module Thredded
4
4
  class Topic < ActiveRecord::Base
5
5
  include TopicCommon
6
6
 
7
7
  scope :for_messageboard, -> messageboard { where(messageboard_id: messageboard.id) }
8
8
 
9
+ scope :stuck, -> { where(sticky: true) }
10
+ scope :unstuck, -> { where(sticky: false) }
11
+
12
+ # Using `search_query` instead of `search` to avoid conflict with Ransack.
13
+ scope :search_query, -> query { ::Thredded::TopicsSearch.new(query, self).search }
14
+
15
+ scope :order_sticky_first, -> { order(sticky: :desc) }
16
+
9
17
  extend FriendlyId
10
- friendly_id :title, use: [:history, :scoped], scope: :messageboard
18
+ friendly_id :slug_candidates,
19
+ use: [:history, :reserved, :scoped],
20
+ scope: :messageboard,
21
+ # Avoid route conflicts
22
+ reserved_words: ::Thredded::FriendlyIdReservedWordsAndPagination.new(%w(topics))
23
+
24
+ belongs_to :user,
25
+ class_name: Thredded.user_class,
26
+ inverse_of: :thredded_topics
11
27
 
12
28
  belongs_to :messageboard,
13
29
  counter_cache: true,
14
- touch: true
30
+ touch: true,
31
+ inverse_of: :topics
15
32
  validates_presence_of :messageboard_id
16
33
 
17
34
  belongs_to :user_detail,
@@ -25,80 +42,39 @@ module Thredded
25
42
  foreign_key: :postable_id,
26
43
  inverse_of: :postable,
27
44
  dependent: :destroy
45
+ has_one :first_post, -> { order_oldest_first },
46
+ class_name: 'Thredded::Post',
47
+ foreign_key: :postable_id
48
+
28
49
  has_many :topic_categories, dependent: :destroy
29
50
  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
51
+ has_many :user_read_states,
52
+ class_name: 'Thredded::UserTopicReadState',
53
+ foreign_key: :postable_id,
54
+ inverse_of: :postable,
55
+ dependent: :destroy
53
56
 
54
- def self.find_by_slug_with_user_topic_reads!(slug)
55
- includes(:user_topic_reads).friendly.find(slug)
57
+ def self.find_by_slug!(slug)
58
+ friendly.find(slug)
56
59
  rescue ActiveRecord::RecordNotFound
57
60
  raise Thredded::Errors::TopicNotFound
58
61
  end
59
62
 
60
- def decorate
61
- TopicDecorator.new(self)
62
- end
63
-
64
63
  def public?
65
64
  true
66
65
  end
67
66
 
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?
67
+ def should_generate_new_friendly_id?
68
+ slug.blank? || title_changed? || super
94
69
  end
95
70
 
96
- def users_to_sentence
97
- []
98
- end
71
+ private
99
72
 
100
- def should_generate_new_friendly_id?
101
- title_changed?
73
+ def slug_candidates
74
+ [
75
+ :title,
76
+ [:title, '-topic'],
77
+ ]
102
78
  end
103
79
  end
104
80
  end
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  module Thredded
2
3
  class TopicCategory < ActiveRecord::Base
3
4
  belongs_to :category
@@ -1,6 +1,7 @@
1
+ # frozen_string_literal: true
1
2
  module Thredded
2
3
  class UserDetail < ActiveRecord::Base
3
- belongs_to :user, class_name: Thredded.user_class
4
+ belongs_to :user, class_name: Thredded.user_class, inverse_of: :thredded_user_detail
4
5
  validates :user_id, presence: true
5
6
 
6
7
  has_many :topics, class_name: 'Thredded::Topic', foreign_key: :user_id, primary_key: :user_id
@@ -8,19 +9,5 @@ module Thredded
8
9
  has_many :private_posts, class_name: 'Thredded::PrivatePost', foreign_key: :user_id, primary_key: :user_id
9
10
 
10
11
  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
12
  end
26
13
  end
@@ -1,9 +1,4 @@
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
-
1
+ # frozen_string_literal: true
7
2
  module Thredded
8
3
  module UserExtender
9
4
  extend ActiveSupport::Concern
@@ -15,15 +10,35 @@ module Thredded
15
10
  include ::Thredded::UserPermissions::Admin::IfAdminColumnTrue
16
11
 
17
12
  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'
13
+ with_options dependent: :nullify, foreign_key: 'user_id', inverse_of: :user do |opt|
14
+ opt.has_many :thredded_posts, class_name: 'Thredded::Post'
15
+ opt.has_many :thredded_topics, class_name: 'Thredded::Topic'
16
+ opt.has_many :thredded_private_posts, class_name: 'Thredded::PrivatePost'
17
+ opt.has_many :thredded_private_topics, class_name: 'Thredded::PrivateTopic'
18
+ end
19
+
20
+ with_options dependent: :nullify, foreign_key: 'last_user_id', inverse_of: :last_user do |opt|
21
+ opt.has_many :thredded_last_user_topics, class_name: 'Thredded::Topic'
22
+ opt.has_many :thredded_last_user_private_topics, class_name: 'Thredded::PrivateTopic'
23
+ end
24
+
25
+ with_options dependent: :destroy, foreign_key: 'user_id', inverse_of: :user do |opt|
26
+ opt.has_many :thredded_user_messageboard_preferences, class_name: 'Thredded::UserMessageboardPreference'
27
+ opt.has_many :thredded_private_users, class_name: 'Thredded::PrivateUser'
28
+ opt.has_many :thredded_topic_read_states, class_name: 'Thredded::UserTopicReadState'
29
+ opt.has_many :thredded_private_topic_read_states, class_name: 'Thredded::UserPrivateTopicReadState'
30
+ opt.has_one :thredded_user_detail, class_name: 'Thredded::UserDetail'
31
+ opt.has_one :thredded_user_preference, class_name: 'Thredded::UserPreference'
32
+ end
33
+
34
+ has_many :thredded_private_topics,
35
+ through: :thredded_private_users,
36
+ class_name: 'Thredded::PrivateTopic',
37
+ source: :private_topic
38
+ end
24
39
 
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'
40
+ def thredded_user_preference
41
+ super || build_thredded_user_preference
27
42
  end
28
43
 
29
44
  def thredded_anonymous?
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+ module Thredded
3
+ class UserMessageboardPreference < ActiveRecord::Base
4
+ belongs_to :user_preference,
5
+ primary_key: :user_id,
6
+ foreign_key: :user_id,
7
+ inverse_of: :messageboard_preferences
8
+ belongs_to :user,
9
+ class_name: Thredded.user_class,
10
+ inverse_of: :thredded_user_messageboard_preferences
11
+ belongs_to :messageboard
12
+
13
+ validates :user_id, presence: true
14
+ validates :messageboard_id, presence: true
15
+
16
+ def self.in(messageboard)
17
+ where(messageboard_id: messageboard.id).first_or_initialize
18
+ end
19
+ end
20
+ end
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  module Thredded
2
3
  module UserPermissions
3
4
  module Admin
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  module Thredded
2
3
  module UserPermissions
3
4
  module Admin
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  module Thredded
2
3
  module UserPermissions
3
4
  module Message
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  module Thredded
2
3
  module UserPermissions
3
4
  module Moderate
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  module Thredded
2
3
  module UserPermissions
3
4
  module Moderate
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  module Thredded
2
3
  module UserPermissions
3
4
  module Read
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  module Thredded
2
3
  module UserPermissions
3
4
  module Write
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  module Thredded
2
3
  module UserPermissions
3
4
  module Write
@@ -1,6 +1,12 @@
1
+ # frozen_string_literal: true
1
2
  module Thredded
2
3
  class UserPreference < ActiveRecord::Base
3
- belongs_to :user, class_name: Thredded.user_class
4
+ belongs_to :user, class_name: Thredded.user_class, inverse_of: :thredded_user_preference
5
+ has_many :messageboard_preferences,
6
+ class_name: 'Thredded::UserMessageboardPreference',
7
+ primary_key: :user_id,
8
+ foreign_key: :user_id,
9
+ inverse_of: :user_preference
4
10
  validates :user_id, presence: true
5
11
  end
6
12
  end
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+ module Thredded
3
+ class UserPrivateTopicReadState < ActiveRecord::Base
4
+ include UserTopicReadStateCommon
5
+ belongs_to :user,
6
+ class_name: Thredded.user_class,
7
+ inverse_of: :thredded_private_topic_read_states
8
+ belongs_to :postable,
9
+ class_name: 'Thredded::PrivateTopic',
10
+ inverse_of: :user_read_states
11
+ end
12
+ end