thredded 0.2.2 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
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