thredded 0.8.4 → 0.9.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (110) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +48 -9
  3. data/app/assets/images/thredded/breadcrumb-chevron.svg +1 -1
  4. data/app/assets/images/thredded/follow.svg +1 -1
  5. data/app/assets/images/thredded/moderation.svg +1 -4
  6. data/app/assets/images/thredded/private-messages.svg +1 -4
  7. data/app/assets/images/thredded/settings.svg +1 -4
  8. data/app/assets/images/thredded/unfollow.svg +1 -1
  9. data/app/assets/javascripts/thredded/components/time_stamps.es6 +1 -1
  10. data/app/assets/javascripts/thredded/components/user_preferences_form.es6 +26 -49
  11. data/app/assets/javascripts/thredded/dependencies.js +2 -1
  12. data/app/assets/stylesheets/thredded/base/_grid.scss +4 -4
  13. data/app/assets/stylesheets/thredded/base/_typography.scss +18 -0
  14. data/app/assets/stylesheets/thredded/components/_base.scss +4 -0
  15. data/app/assets/stylesheets/thredded/components/_currently-online.scss +4 -0
  16. data/app/assets/stylesheets/thredded/components/_form-list.scss +5 -0
  17. data/app/assets/stylesheets/thredded/components/_messageboard.scss +3 -0
  18. data/app/assets/stylesheets/thredded/components/_post-form.scss +7 -4
  19. data/app/assets/stylesheets/thredded/components/_post.scss +3 -0
  20. data/app/assets/stylesheets/thredded/components/_topic-delete.scss +3 -0
  21. data/app/assets/stylesheets/thredded/components/_topic-header.scss +20 -0
  22. data/app/assets/stylesheets/thredded/components/_topics.scss +13 -1
  23. data/app/assets/stylesheets/thredded/layout/_main-navigation.scss +1 -1
  24. data/app/assets/stylesheets/thredded/layout/_navigation.scss +3 -1
  25. data/app/assets/stylesheets/thredded/layout/_search-navigation.scss +4 -0
  26. data/app/assets/stylesheets/thredded/layout/_user-navigation.scss +3 -0
  27. data/app/commands/thredded/at_notification_extractor.rb +1 -2
  28. data/app/commands/thredded/autofollow_mentioned_users.rb +0 -1
  29. data/app/commands/thredded/notify_following_users.rb +20 -11
  30. data/app/commands/thredded/notify_private_topic_users.rb +12 -28
  31. data/app/controllers/thredded/application_controller.rb +8 -4
  32. data/app/controllers/thredded/messageboards_controller.rb +2 -2
  33. data/app/controllers/thredded/moderation_controller.rb +12 -13
  34. data/app/controllers/thredded/posts_controller.rb +1 -1
  35. data/app/controllers/thredded/preferences_controller.rb +11 -7
  36. data/app/controllers/thredded/private_topics_controller.rb +13 -9
  37. data/app/controllers/thredded/topics_controller.rb +20 -12
  38. data/app/forms/thredded/user_preferences_form.rb +23 -4
  39. data/app/helpers/thredded/application_helper.rb +11 -1
  40. data/app/mailers/thredded/post_mailer.rb +2 -3
  41. data/app/mailers/thredded/private_topic_mailer.rb +2 -3
  42. data/app/models/concerns/thredded/content_moderation_state.rb +1 -1
  43. data/app/models/concerns/thredded/notifier_preference.rb +18 -0
  44. data/app/models/concerns/thredded/post_common.rb +5 -1
  45. data/{lib → app/models/concerns}/thredded/search_parser.rb +0 -0
  46. data/app/models/concerns/thredded/topic_common.rb +3 -3
  47. data/{lib → app/models/concerns}/thredded/topics_search.rb +2 -3
  48. data/app/models/thredded/messageboard.rb +11 -0
  49. data/app/models/thredded/messageboard_notifications_for_followed_topics.rb +29 -0
  50. data/app/models/thredded/notifications_for_followed_topics.rb +22 -0
  51. data/app/models/thredded/notifications_for_private_topics.rb +21 -0
  52. data/app/models/thredded/post.rb +5 -3
  53. data/app/models/thredded/post_moderation_record.rb +1 -1
  54. data/app/models/thredded/private_post.rb +1 -1
  55. data/app/models/thredded/private_topic.rb +8 -4
  56. data/app/models/thredded/stats.rb +1 -1
  57. data/app/models/thredded/topic.rb +10 -7
  58. data/app/models/thredded/user_detail.rb +1 -1
  59. data/app/models/thredded/user_extender.rb +3 -1
  60. data/app/models/thredded/user_preference.rb +16 -5
  61. data/app/models/thredded/user_private_topic_read_state.rb +1 -1
  62. data/app/models/thredded/user_topic_read_state.rb +1 -1
  63. data/app/notifiers/thredded/base_notifier.rb +28 -0
  64. data/app/notifiers/thredded/email_notifier.rb +34 -0
  65. data/app/policies/thredded/post_policy.rb +3 -4
  66. data/app/policies/thredded/private_post_policy.rb +2 -3
  67. data/app/policies/thredded/topic_policy.rb +1 -2
  68. data/app/view_hooks/thredded/all_view_hooks.rb +0 -3
  69. data/app/view_models/thredded/base_topic_view.rb +0 -1
  70. data/app/view_models/thredded/post_view.rb +0 -1
  71. data/app/view_models/thredded/posts_page_view.rb +1 -4
  72. data/app/view_models/thredded/private_topic_view.rb +3 -1
  73. data/app/view_models/thredded/private_topics_page_view.rb +8 -3
  74. data/app/view_models/thredded/topic_posts_page_view.rb +1 -2
  75. data/app/view_models/thredded/topic_view.rb +3 -3
  76. data/app/view_models/thredded/topics_page_view.rb +0 -1
  77. data/app/views/thredded/messageboard_groups/new.html.erb +5 -1
  78. data/app/views/thredded/messageboards/_form.html.erb +7 -2
  79. data/app/views/thredded/posts/_form.html.erb +2 -1
  80. data/app/views/thredded/posts/edit.html.erb +3 -2
  81. data/app/views/thredded/posts_common/_form.html.erb +6 -2
  82. data/app/views/thredded/preferences/_form.html.erb +41 -29
  83. data/app/views/thredded/private_posts/_form.html.erb +1 -0
  84. data/app/views/thredded/private_topics/_form.html.erb +3 -2
  85. data/app/views/thredded/private_topics/_header.html.erb +4 -0
  86. data/app/views/thredded/private_topics/_no_private_topics.html.erb +1 -1
  87. data/app/views/thredded/private_topics/_private_topic.html.erb +5 -2
  88. data/app/views/thredded/private_topics/edit.html.erb +2 -1
  89. data/app/views/thredded/private_topics/header/_participant.html.erb +1 -0
  90. data/app/views/thredded/private_topics/index.html.erb +6 -5
  91. data/app/views/thredded/private_topics/new.html.erb +0 -1
  92. data/app/views/thredded/private_topics/private_topic/_participant.html.erb +1 -0
  93. data/app/views/thredded/private_topics/show.html.erb +1 -3
  94. data/app/views/thredded/topics/_form.html.erb +1 -1
  95. data/app/views/thredded/topics/show.html.erb +4 -5
  96. data/config/locales/en.yml +22 -9
  97. data/config/locales/pt-BR.yml +22 -10
  98. data/config/routes.rb +1 -1
  99. data/db/migrate/20160329231848_create_thredded.rb +23 -3
  100. data/db/upgrade_migrations/20160410111522_upgrade_v0_2_to_v0_3.rb +1 -1
  101. data/db/upgrade_migrations/20161113161801_upgrade_v0_8_to_v0_9.rb +56 -0
  102. data/lib/generators/thredded/install/templates/initializer.rb +28 -0
  103. data/lib/thredded.rb +32 -8
  104. data/lib/thredded/content_formatter.rb +3 -2
  105. data/lib/thredded/database_seeder.rb +11 -8
  106. data/lib/thredded/db_tools.rb +82 -0
  107. data/lib/thredded/html_pipeline/wrap_iframes_filter.rb +12 -0
  108. data/lib/thredded/version.rb +1 -1
  109. metadata +18 -8
  110. data/app/models/thredded/null_preference.rb +0 -16
@@ -9,12 +9,12 @@ module Thredded
9
9
  validate :validate_children
10
10
 
11
11
  delegate :follow_topics_on_mention, :follow_topics_on_mention=,
12
- :notify_on_message, :notify_on_message=,
13
- :followed_topic_emails, :followed_topic_emails=,
12
+ :messageboard_notifications_for_followed_topics_attributes=,
13
+ :notifications_for_followed_topics_attributes=,
14
+ :notifications_for_private_topics_attributes=,
14
15
  to: :user_preference
15
16
 
16
17
  delegate :follow_topics_on_mention, :follow_topics_on_mention=,
17
- :followed_topic_emails, :followed_topic_emails=,
18
18
  to: :user_messageboard_preference,
19
19
  prefix: :messageboard
20
20
 
@@ -36,6 +36,25 @@ module Thredded
36
36
  true
37
37
  end
38
38
 
39
+ def notifications_for_private_topics
40
+ for_every_notifier(user_preference.notifications_for_private_topics)
41
+ end
42
+
43
+ def notifications_for_followed_topics
44
+ for_every_notifier(user_preference.notifications_for_followed_topics)
45
+ end
46
+
47
+ def messageboard_notifications_for_followed_topics
48
+ return nil unless messageboard
49
+ for_every_notifier(user_preference.messageboard_notifications_for_followed_topics.for_messageboard(messageboard))
50
+ end
51
+
52
+ def for_every_notifier(prefs)
53
+ Thredded.notifiers.map do |notifier|
54
+ prefs.find { |n| n.notifier_key == notifier.key } || prefs.build(notifier_key: notifier.key)
55
+ end
56
+ end
57
+
39
58
  private
40
59
 
41
60
  # @return [Thredded::UserPreference]
@@ -52,7 +71,7 @@ module Thredded
52
71
 
53
72
  def validate_children
54
73
  promote_errors(user_preference.errors) if user_preference.invalid?
55
- if messageboard && user_messageboard_preference.invalid?
74
+ if messageboard && user_messageboard_preference.invalid? # rubocop:disable Style/GuardClause
56
75
  promote_errors(user_messageboard_preference.errors, :messageboard)
57
76
  end
58
77
  end
@@ -52,11 +52,21 @@ module Thredded
52
52
  def time_ago(datetime, default: '-')
53
53
  timeago_tag datetime,
54
54
  lang: I18n.locale.to_s.downcase,
55
- format: -> (t, _opts) { t.year == Time.current.year ? :short : :long },
55
+ format: ->(t, _opts) { t.year == Time.current.year ? :short : :long },
56
56
  nojs: true,
57
+ date_only: false,
57
58
  default: default
58
59
  end
59
60
 
61
+ # Override the default timeago_tag_content from rails-timeago
62
+ def timeago_tag_content(time, time_options = {})
63
+ if time_options[:nojs] && (time_options[:limit].nil? || time_options[:limit] < time)
64
+ t 'thredded.time_ago', time: time_ago_in_words(time)
65
+ else
66
+ I18n.l time.to_date, format: time_options[:format]
67
+ end
68
+ end
69
+
60
70
  def paginate(collection, args = {})
61
71
  super(collection, args.reverse_merge(views_prefix: 'thredded'))
62
72
  end
@@ -1,10 +1,9 @@
1
1
  # frozen_string_literal: true
2
- require_dependency 'thredded/topic_email_view'
3
2
  module Thredded
4
3
  class PostMailer < Thredded::BaseMailer
5
4
  def post_notification(post_id, emails)
6
- @post = find_record Post, post_id
7
- email_details = TopicEmailView.new(@post.postable)
5
+ @post = find_record Thredded::Post, post_id
6
+ email_details = Thredded::TopicEmailView.new(@post.postable)
8
7
  headers['X-SMTPAPI'] = email_details.smtp_api_tag('post_notification')
9
8
 
10
9
  mail from: email_details.no_reply,
@@ -1,10 +1,9 @@
1
1
  # frozen_string_literal: true
2
- require_dependency 'thredded/topic_email_view'
3
2
  module Thredded
4
3
  class PrivateTopicMailer < Thredded::BaseMailer
5
4
  def message_notification(private_topic_id, emails)
6
- @topic = find_record PrivateTopic, private_topic_id
7
- email_details = TopicEmailView.new(@topic)
5
+ @topic = find_record Thredded::PrivateTopic, private_topic_id
6
+ email_details = Thredded::TopicEmailView.new(@topic)
8
7
  headers['X-SMTPAPI'] = email_details.smtp_api_tag('private_topic_mailer')
9
8
 
10
9
  mail from: email_details.no_reply,
@@ -5,7 +5,7 @@ module Thredded
5
5
  # @api private
6
6
  module ContentModerationState
7
7
  extend ActiveSupport::Concern
8
- include ModerationState
8
+ include Thredded::ModerationState
9
9
 
10
10
  included do
11
11
  before_validation :set_default_moderation_state, on: :create
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+ module Thredded
3
+ module NotifierPreference
4
+ extend ActiveSupport::Concern
5
+
6
+ included do
7
+ delegate :human_name, to: :notifier, prefix: true
8
+
9
+ def self.detect_or_default(prefs, notifier)
10
+ (prefs && prefs.find { |pref| pref.notifier_key == notifier.key }) || default(notifier)
11
+ end
12
+ end
13
+
14
+ def notifier
15
+ @notifier ||= Thredded.notifiers.find { |notifier| notifier.key == notifier_key }
16
+ end
17
+ end
18
+ end
@@ -28,10 +28,14 @@ module Thredded
28
28
 
29
29
  # @param view_context [Object] the context of the rendering view.
30
30
  # @return [String] formatted and sanitized html-safe post content.
31
- def filtered_content(view_context, users_provider: -> (names) { readers_from_user_names(names) })
31
+ def filtered_content(view_context, users_provider: ->(names) { readers_from_user_names(names) })
32
32
  Thredded::ContentFormatter.new(view_context, users_provider: users_provider).format_content(content)
33
33
  end
34
34
 
35
+ def first_post_in_topic?
36
+ postable.first_post == self
37
+ end
38
+
35
39
  private
36
40
 
37
41
  def ensure_user_detail
@@ -10,7 +10,7 @@ module Thredded
10
10
  foreign_key: 'last_user_id'
11
11
 
12
12
  scope :order_recently_posted_first, -> { order(last_post_at: :desc, id: :desc) }
13
- scope :on_page, -> (page_num) { page(page_num) }
13
+ scope :on_page, ->(page_num) { page(page_num) }
14
14
 
15
15
  validates :hash_id, presence: true, uniqueness: true
16
16
  validates :posts_count, numericality: true
@@ -25,11 +25,11 @@ module Thredded
25
25
  end
26
26
 
27
27
  def user
28
- super || NullUser.new
28
+ super || Thredded::NullUser.new
29
29
  end
30
30
 
31
31
  def last_user
32
- super || NullUser.new
32
+ super || Thredded::NullUser.new
33
33
  end
34
34
 
35
35
  def private?
@@ -1,9 +1,8 @@
1
1
  # frozen_string_literal: true
2
- require 'thredded/search_parser'
3
2
  module Thredded
4
3
  class TopicsSearch
5
4
  def initialize(query, scope)
6
- @terms = SearchParser.new(query).parse
5
+ @terms = Thredded::SearchParser.new(query).parse
7
6
  @scope = scope
8
7
 
9
8
  @search_categories = @search_users = @search_text = nil
@@ -12,7 +11,7 @@ module Thredded
12
11
  # @return [ActiveRecord::Relation<Thredded::Topic>]
13
12
  def search
14
13
  if categories.present?
15
- @scope = @scope.joins(:topic_categories).merge(TopicCategory.where(category_id: categories))
14
+ @scope = @scope.joins(:topic_categories).merge(Thredded::TopicCategory.where(category_id: categories))
16
15
  end
17
16
  if text.present? || users.present?
18
17
  [search_topics, search_posts].compact.reduce(:union)
@@ -81,6 +81,17 @@ module Thredded
81
81
  order(topics_count: :desc)
82
82
  }
83
83
  # rubocop:enable Style/Lambda
84
+
85
+ # Finds the messageboard by its slug or ID, or raises Thredded::Errors::MessageboardNotFound.
86
+ # @param slug_or_id [String]
87
+ # @return [Thredded::Messageboard]
88
+ # @raise [Thredded::Errors::MessageboardNotFound] if the messageboard with the given slug does not exist.
89
+ def self.friendly_find!(slug_or_id)
90
+ friendly.find(slug_or_id)
91
+ rescue ActiveRecord::RecordNotFound
92
+ raise Thredded::Errors::MessageboardNotFound
93
+ end
94
+
84
95
  def last_user
85
96
  last_topic.try(:last_user)
86
97
  end
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+ module Thredded
3
+ class MessageboardNotificationsForFollowedTopics < ActiveRecord::Base
4
+ belongs_to :user_preference,
5
+ primary_key: :user_id,
6
+ foreign_key: :user_id,
7
+ inverse_of: :messageboard_notifications_for_followed_topics
8
+ belongs_to :user,
9
+ class_name: Thredded.user_class,
10
+ inverse_of: :thredded_user_messageboard_preferences
11
+ belongs_to :messageboard
12
+ scope :for_messageboard, ->(messageboard) { where(messageboard_id: messageboard.id) }
13
+
14
+ validates :user_id, presence: true
15
+ validates :messageboard_id, presence: true
16
+
17
+ def self.in(messageboard)
18
+ where(messageboard_id: messageboard.id)
19
+ end
20
+
21
+ include Thredded::NotifierPreference
22
+
23
+ def self.default(_notifier)
24
+ # could be moved to `notifier.defaults(:notifications_for_followed_topics)` or
25
+ # `notifier.defaults(:messageboard_notifications_for_followed_topics)`
26
+ Thredded::BaseNotifier::NotificationsDefault.new(true)
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+ module Thredded
3
+ class NotificationsForFollowedTopics < ActiveRecord::Base
4
+ belongs_to :user,
5
+ class_name: Thredded.user_class,
6
+ inverse_of: :thredded_notifications_for_followed_topics
7
+ belongs_to :messageboard # or is global
8
+ belongs_to :user_preference,
9
+ primary_key: :user_id,
10
+ foreign_key: :user_id,
11
+ inverse_of: :notifications_for_followed_topics
12
+
13
+ validates :user_id, presence: true
14
+
15
+ include Thredded::NotifierPreference
16
+
17
+ def self.default(_notifier)
18
+ # could be moved to `notifier.defaults(:notifications_for_followed_topics)`
19
+ Thredded::BaseNotifier::NotificationsDefault.new(true)
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+ module Thredded
3
+ class NotificationsForPrivateTopics < ActiveRecord::Base
4
+ belongs_to :user,
5
+ class_name: Thredded.user_class,
6
+ inverse_of: :thredded_notifications_for_private_topics
7
+ belongs_to :user_preference,
8
+ primary_key: :user_id,
9
+ foreign_key: :user_id,
10
+ inverse_of: :notifications_for_private_topics
11
+
12
+ validates :user_id, presence: true
13
+
14
+ include Thredded::NotifierPreference
15
+
16
+ def self.default(_notifier)
17
+ # could be moved to `notifier.defaults(:notifications_for_private_topics)`
18
+ Thredded::BaseNotifier::NotificationsDefault.new(true)
19
+ end
20
+ end
21
+ end
@@ -1,8 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
  module Thredded
3
3
  class Post < ActiveRecord::Base
4
- include PostCommon
5
- include ContentModerationState
4
+ include Thredded::PostCommon
5
+ include Thredded::ContentModerationState
6
6
 
7
7
  belongs_to :user,
8
8
  class_name: Thredded.user_class,
@@ -54,7 +54,9 @@ module Thredded
54
54
  def auto_follow_and_notify
55
55
  return unless user
56
56
  # need to do this in-process so that it appears to them immediately
57
- UserTopicFollow.create_unless_exists(user.id, postable_id, :posted)
57
+ if first_post_in_topic? ? Thredded.auto_follow_when_creating_topic : Thredded.auto_follow_when_posting_in_topic
58
+ UserTopicFollow.create_unless_exists(user.id, postable_id, :posted)
59
+ end
58
60
  # everything else can happen later
59
61
  AutoFollowAndNotifyJob.perform_later(id)
60
62
  end
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
  module Thredded
3
3
  class PostModerationRecord < ActiveRecord::Base
4
- include ModerationState
4
+ include Thredded::ModerationState
5
5
  # Rails 4 doesn't support enum _prefix
6
6
  if Rails::VERSION::MAJOR >= 5
7
7
  enum previous_moderation_state: moderation_states, _prefix: :previous
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
  module Thredded
3
3
  class PrivatePost < ActiveRecord::Base
4
- include PostCommon
4
+ include Thredded::PostCommon
5
5
 
6
6
  belongs_to :user,
7
7
  class_name: Thredded.user_class,
@@ -1,9 +1,9 @@
1
1
  # frozen_string_literal: true
2
2
  module Thredded
3
3
  class PrivateTopic < ActiveRecord::Base
4
- include TopicCommon
4
+ include Thredded::TopicCommon
5
5
 
6
- scope :for_user, -> (user) { joins(:private_users).merge(PrivateUser.where(user_id: user.id)) }
6
+ scope :for_user, ->(user) { joins(:private_users).merge(PrivateUser.where(user_id: user.id)) }
7
7
 
8
8
  extend FriendlyId
9
9
  friendly_id :slug_candidates,
@@ -48,8 +48,12 @@ module Thredded
48
48
 
49
49
  before_validation :ensure_user_in_private_users
50
50
 
51
- def self.find_by_slug(slug)
52
- friendly.find(slug)
51
+ # Finds the topic by its slug or ID, or raises Thredded::Errors::PrivateTopicNotFound.
52
+ # @param slug_or_id [String]
53
+ # @return [Thredded::PrivateTopic]
54
+ # @raise [Thredded::Errors::PrivateTopicNotFound] if the topic with the given slug does not exist.
55
+ def self.friendly_find!(slug_or_id)
56
+ friendly.find(slug_or_id)
53
57
  rescue ActiveRecord::RecordNotFound
54
58
  raise Thredded::Errors::PrivateTopicNotFound
55
59
  end
@@ -18,7 +18,7 @@ module Thredded
18
18
  private
19
19
 
20
20
  def messageboards
21
- @messageboards ||= Messageboard.ordered
21
+ @messageboards ||= Thredded::Messageboard.ordered
22
22
  end
23
23
  end
24
24
  end
@@ -1,17 +1,16 @@
1
1
  # frozen_string_literal: true
2
- require 'thredded/topics_search'
3
2
  module Thredded
4
3
  class Topic < ActiveRecord::Base
5
- include TopicCommon
6
- include ContentModerationState
4
+ include Thredded::TopicCommon
5
+ include Thredded::ContentModerationState
7
6
 
8
- scope :for_messageboard, -> (messageboard) { where(messageboard_id: messageboard.id) }
7
+ scope :for_messageboard, ->(messageboard) { where(messageboard_id: messageboard.id) }
9
8
 
10
9
  scope :stuck, -> { where(sticky: true) }
11
10
  scope :unstuck, -> { where(sticky: false) }
12
11
 
13
12
  # Using `search_query` instead of `search` to avoid conflict with Ransack.
14
- scope :search_query, -> (query) { ::Thredded::TopicsSearch.new(query, self).search }
13
+ scope :search_query, ->(query) { ::Thredded::TopicsSearch.new(query, self).search }
15
14
 
16
15
  scope :order_sticky_first, -> { order(sticky: :desc) }
17
16
 
@@ -77,8 +76,12 @@ module Thredded
77
76
  after_commit :update_messageboard_last_topic, on: :update, if: -> { previous_changes.include?('moderation_state') }
78
77
  after_update :update_last_user_and_time_from_last_post!, if: -> { previous_changes.include?('moderation_state') }
79
78
 
80
- def self.find_by_slug!(slug)
81
- friendly.find(slug)
79
+ # Finds the topic by its slug or ID, or raises Thredded::Errors::TopicNotFound.
80
+ # @param slug_or_id [String]
81
+ # @return [Thredded::Topic]
82
+ # @raise [Thredded::Errors::TopicNotFound] if the topic with the given slug does not exist.
83
+ def self.friendly_find!(slug_or_id)
84
+ friendly.find(slug_or_id)
82
85
  rescue ActiveRecord::RecordNotFound
83
86
  raise Thredded::Errors::TopicNotFound
84
87
  end
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
  module Thredded
3
3
  class UserDetail < ActiveRecord::Base
4
- include ModerationState
4
+ include Thredded::ModerationState
5
5
 
6
6
  belongs_to :user, class_name: Thredded.user_class, inverse_of: :thredded_user_detail
7
7
  validates :user_id, presence: true, uniqueness: true
@@ -9,7 +9,7 @@ module Thredded
9
9
  include ::Thredded::UserPermissions::Moderate::IfModeratorColumnTrue
10
10
  include ::Thredded::UserPermissions::Admin::IfAdminColumnTrue
11
11
 
12
- included do
12
+ included do # rubocop:disable Metrics/BlockLength
13
13
  with_options dependent: :nullify, foreign_key: 'user_id', inverse_of: :user do |opt|
14
14
  opt.has_many :thredded_posts, class_name: 'Thredded::Post'
15
15
  opt.has_many :thredded_topics, class_name: 'Thredded::Topic'
@@ -24,6 +24,8 @@ module Thredded
24
24
 
25
25
  with_options dependent: :destroy, foreign_key: 'user_id', inverse_of: :user do |opt|
26
26
  opt.has_many :thredded_user_messageboard_preferences, class_name: 'Thredded::UserMessageboardPreference'
27
+ opt.has_many :thredded_notifications_for_followed_topics, class_name: 'Thredded::NotificationsForFollowedTopics'
28
+ opt.has_many :thredded_notifications_for_private_topics, class_name: 'Thredded::NotificationsForPrivateTopics'
27
29
  opt.has_many :thredded_private_users, class_name: 'Thredded::PrivateUser'
28
30
  opt.has_many :thredded_topic_read_states, class_name: 'Thredded::UserTopicReadState'
29
31
  opt.has_many :thredded_private_topic_read_states, class_name: 'Thredded::UserPrivateTopicReadState'
@@ -2,11 +2,22 @@
2
2
  module Thredded
3
3
  class UserPreference < ActiveRecord::Base
4
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
5
+
6
+ with_options(inverse_of: :user_preference, primary_key: :user_id, foreign_key: :user_id,
7
+ dependent: :destroy) do |opt|
8
+ opt.has_many :messageboard_preferences,
9
+ class_name: 'Thredded::UserMessageboardPreference'
10
+ opt.has_many :messageboard_notifications_for_followed_topics,
11
+ class_name: 'Thredded::MessageboardNotificationsForFollowedTopics'
12
+ opt.has_many :notifications_for_followed_topics,
13
+ class_name: 'Thredded::NotificationsForFollowedTopics'
14
+ opt.has_many :notifications_for_private_topics,
15
+ class_name: 'Thredded::NotificationsForPrivateTopics'
16
+ end
10
17
  validates :user_id, presence: true
18
+
19
+ accepts_nested_attributes_for :notifications_for_followed_topics,
20
+ :notifications_for_private_topics,
21
+ :messageboard_notifications_for_followed_topics
11
22
  end
12
23
  end