thredded 0.14.4 → 0.15.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (78) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +3 -2
  3. data/app/assets/javascripts/thredded/components/preview_area.es6 +3 -0
  4. data/app/assets/javascripts/thredded/components/spoilers.es6 +37 -0
  5. data/app/assets/stylesheets/thredded/_email.scss +25 -0
  6. data/app/assets/stylesheets/thredded/_thredded.scss +1 -0
  7. data/app/assets/stylesheets/thredded/base/_typography.scss +1 -1
  8. data/app/assets/stylesheets/thredded/base/_variables.scss +2 -3
  9. data/app/assets/stylesheets/thredded/components/_post.scss +0 -11
  10. data/app/assets/stylesheets/thredded/components/_spoiler.scss +41 -0
  11. data/app/commands/thredded/mark_all_read.rb +1 -7
  12. data/app/controllers/concerns/thredded/new_post_params.rb +1 -1
  13. data/app/controllers/concerns/thredded/new_private_post_params.rb +1 -1
  14. data/app/controllers/concerns/thredded/new_private_topic_params.rb +1 -2
  15. data/app/controllers/concerns/thredded/new_topic_params.rb +0 -1
  16. data/app/controllers/thredded/application_controller.rb +10 -0
  17. data/app/controllers/thredded/posts_controller.rb +1 -2
  18. data/app/controllers/thredded/private_posts_controller.rb +1 -2
  19. data/app/controllers/thredded/private_topics_controller.rb +10 -12
  20. data/app/controllers/thredded/topics_controller.rb +14 -17
  21. data/app/forms/thredded/edit_topic_form.rb +2 -1
  22. data/app/forms/thredded/post_form.rb +3 -1
  23. data/app/forms/thredded/private_post_form.rb +3 -1
  24. data/app/forms/thredded/private_topic_form.rb +1 -0
  25. data/app/jobs/thredded/activity_updater_job.rb +18 -8
  26. data/app/jobs/thredded/auto_follow_and_notify_job.rb +2 -1
  27. data/app/models/concerns/thredded/post_common.rb +3 -4
  28. data/app/models/concerns/thredded/topic_common.rb +7 -0
  29. data/app/models/concerns/thredded/user_topic_read_state_common.rb +62 -5
  30. data/app/models/thredded/null_user_topic_read_state.rb +8 -0
  31. data/app/policies/thredded/private_post_policy.rb +16 -0
  32. data/app/view_hooks/thredded/all_view_hooks.rb +3 -0
  33. data/app/view_models/thredded/base_topic_view.rb +5 -1
  34. data/app/view_models/thredded/post_view.rb +13 -1
  35. data/app/view_models/thredded/posts_page_view.rb +1 -1
  36. data/app/view_models/thredded/topic_posts_page_view.rb +13 -1
  37. data/app/views/thredded/posts/_post.html.erb +1 -0
  38. data/app/views/thredded/posts/edit.html.erb +1 -0
  39. data/app/views/thredded/posts_common/_before_first_unread_post.html.erb +7 -0
  40. data/app/views/thredded/posts_common/form/_after_content.html.erb +8 -0
  41. data/app/views/thredded/posts_common/form/_before_content.html.erb +8 -0
  42. data/app/views/thredded/private_posts/_private_post.html.erb +2 -1
  43. data/app/views/thredded/private_posts/edit.html.erb +1 -0
  44. data/app/views/thredded/private_topics/_form.html.erb +1 -0
  45. data/app/views/thredded/private_topics/edit.html.erb +2 -1
  46. data/app/views/thredded/shared/_field_errors.html.erb +3 -0
  47. data/app/views/thredded/shared/_nav.html.erb +1 -1
  48. data/app/views/thredded/shared/_page.html.erb +1 -1
  49. data/app/views/thredded/topics/_form.html.erb +1 -0
  50. data/app/views/thredded/topics/edit.html.erb +2 -1
  51. data/config/locales/de.yml +2 -0
  52. data/config/locales/en.yml +2 -0
  53. data/config/locales/es.yml +2 -0
  54. data/config/locales/fr.yml +2 -0
  55. data/config/locales/it.yml +2 -0
  56. data/config/locales/pl.yml +2 -0
  57. data/config/locales/pt-BR.yml +2 -0
  58. data/config/locales/ru.yml +2 -0
  59. data/config/locales/zh-CN.yml +2 -0
  60. data/db/migrate/20160329231848_create_thredded.rb +37 -23
  61. data/db/upgrade_migrations/{20170811090735_upgrade_thredded_v0_13_to_v_014.rb → 20170811090735_upgrade_thredded_v0_13_to_v0_14.rb} +0 -0
  62. data/db/upgrade_migrations/20180110200009_upgrade_thredded_v0_14_to_v0_15.rb +91 -0
  63. data/lib/generators/thredded/install/templates/initializer.rb +16 -7
  64. data/lib/thredded.rb +143 -125
  65. data/lib/thredded/arel_compat.rb +57 -0
  66. data/lib/thredded/base_migration.rb +10 -0
  67. data/lib/thredded/collection_to_strings_with_cache_renderer.rb +35 -9
  68. data/lib/thredded/content_formatter.rb +27 -18
  69. data/lib/thredded/database_seeder.rb +218 -64
  70. data/lib/thredded/email_transformer.rb +5 -2
  71. data/lib/thredded/email_transformer/spoiler.rb +25 -0
  72. data/lib/thredded/formatting_demo_content.rb +12 -0
  73. data/lib/thredded/html_pipeline/onebox_filter.rb +3 -38
  74. data/lib/thredded/html_pipeline/spoiler_tag_filter.rb +128 -0
  75. data/lib/thredded/html_pipeline/utils.rb +47 -0
  76. data/lib/thredded/rails_lt_5_2_arel_case_node.rb +119 -0
  77. data/lib/thredded/version.rb +1 -1
  78. metadata +17 -21
@@ -2,7 +2,7 @@
2
2
  <%= render 'thredded/shared/header' %>
3
3
  <%= render 'thredded/shared/flash_messages' %>
4
4
  <%= yield :thredded_page_content %>
5
- <%= render 'thredded/shared/currently_online' %>
5
+ <%= render 'thredded/shared/currently_online' if Thredded.currently_online_enabled %>
6
6
  <%- end %>
7
7
 
8
8
  <%# If thredded JS is loaded via an [async] script, the JS may
@@ -10,6 +10,7 @@
10
10
  <li class="title">
11
11
  <%= form.label :title, t('thredded.topics.form.title_label') %>
12
12
  <%= form.text_field :title, placeholder: placeholder, required: true %>
13
+ <%= render 'thredded/shared/field_errors', messages: form.object.errors[:title] if form.object.errors.include?(:title) %>
13
14
  </li>
14
15
 
15
16
  <% if form.object.category_options.any? %>
@@ -4,7 +4,7 @@
4
4
  <ul class="thredded--navigation-breadcrumbs">
5
5
  <li><%= link_to t('thredded.nav.all_messageboards'), messageboards_path %></li>
6
6
  <li><%= link_to messageboard.name, @edit_topic.messageboard_path %></li>
7
- <li><%= link_to @edit_topic.title, @edit_topic.path %></li>
7
+ <li><%= link_to @edit_topic.title_was || @edit_topic.title, @edit_topic.path %></li>
8
8
  <li><%= link_to t('thredded.nav.edit_topic'), @edit_topic.edit_path %></li>
9
9
  </ul>
10
10
  <% end %>
@@ -22,6 +22,7 @@
22
22
  placeholder: t('thredded.topics.form.title_placeholder'),
23
23
  autofocus: true,
24
24
  required: true %>
25
+ <%= render 'thredded/shared/field_errors', messages: form.object.errors[:title] if form.object.errors.include?(:title) %>
25
26
  </li>
26
27
 
27
28
  <% if form.object.category_options.any? %>
@@ -132,6 +132,8 @@ de:
132
132
  update_btn_submitting: :thredded.form.update_btn_submitting
133
133
  pending_moderation_notice: Dein Beitrag wird veröffentlicht, sobald ein Moderator ihn angeschaut hat.
134
134
  quote_btn: Zitat
135
+ spoiler_summary: Spoiler - klicken um zu zeigen.
136
+ spoiler_summary_for_email: 'Spoiler - Wählen Sie den Inhalt unten, um zu sehen:'
135
137
  preferences:
136
138
  edit:
137
139
  page_title: :thredded.nav.settings
@@ -131,6 +131,8 @@ en:
131
131
  update_btn_submitting: :thredded.form.update_btn_submitting
132
132
  pending_moderation_notice: Your post will be published when it has been reviewed by a moderator.
133
133
  quote_btn: Quote
134
+ spoiler_summary: Spoiler - click to show.
135
+ spoiler_summary_for_email: 'Spoiler - select the contents below to see:'
134
136
  preferences:
135
137
  edit:
136
138
  page_title: :thredded.nav.settings
@@ -133,6 +133,8 @@ es:
133
133
  update_btn_submitting: :thredded.form.update_btn_submitting
134
134
  pending_moderation_notice: Tu post será publicado cuando haya sido revisado por un moderador.
135
135
  quote_btn: Citar
136
+ spoiler_summary: 'Spoiler: haz clic para mostrar.'
137
+ spoiler_summary_for_email: 'Spoiler: selecciona el contenido a continuación para ver:'
136
138
  preferences:
137
139
  edit:
138
140
  page_title: :thredded.nav.settings
@@ -131,6 +131,8 @@ fr:
131
131
  update_btn_submitting: :thredded.form.update_btn_submitting
132
132
  pending_moderation_notice: Votre commentaire sera publié dès qu'il aura été validé par un modérateur.
133
133
  quote_btn: Citer
134
+ spoiler_summary: Spoiler - cliquez pour afficher.
135
+ spoiler_summary_for_email: 'Spoiler - sélectionnez le contenu ci-dessous pour voir:'
134
136
  preferences:
135
137
  edit:
136
138
  page_title: :thredded.nav.settings
@@ -131,6 +131,8 @@ it:
131
131
  update_btn_submitting: :thredded.form.update_btn_submitting
132
132
  pending_moderation_notice: Il tuo messaggio sarà pubblicato dopo essere stato controllato da un moderatore.
133
133
  quote_btn: Citazione
134
+ spoiler_summary: Spoiler - clicca per mostrare.
135
+ spoiler_summary_for_email: 'Spoiler: seleziona i contenuti sotto per vedere:'
134
136
  preferences:
135
137
  edit:
136
138
  page_title: :thredded.nav.settings
@@ -131,6 +131,8 @@ pl:
131
131
  update_btn_submitting: :thredded.form.update_btn_submitting
132
132
  pending_moderation_notice: Twój post zostanie zamieszczony po zweryfikowaniu go przez moderatora.
133
133
  quote_btn: Zacytować
134
+ spoiler_summary: Spoiler - kliknij, aby pokazać.
135
+ spoiler_summary_for_email: 'Spoiler - wybierz zawartość poniżej, aby zobaczyć:'
134
136
  preferences:
135
137
  edit:
136
138
  page_title: :thredded.nav.settings
@@ -133,6 +133,8 @@ pt-BR:
133
133
  update_btn_submitting: :thredded.form.update_btn_submitting
134
134
  pending_moderation_notice: O envio da mensagem será publicada quando foi revisado por um moderador.
135
135
  quote_btn: Citar
136
+ spoiler_summary: Spoiler - clique para mostrar.
137
+ spoiler_summary_for_email: 'Spoiler - selecione o conteúdo abaixo para ver:'
136
138
  preferences:
137
139
  edit:
138
140
  page_title: :thredded.nav.settings
@@ -129,6 +129,8 @@ ru:
129
129
  update_btn_submitting: :thredded.form.update_btn_submitting
130
130
  pending_moderation_notice: Ваш пост будет опубликован, когда будет одобрен модератором.
131
131
  quote_btn: Цитировать
132
+ spoiler_summary: Спойлер - нажмите, чтобы показать.
133
+ spoiler_summary_for_email: 'Спойлер - выделите содержимое ниже, чтобы увидеть:'
132
134
  preferences:
133
135
  edit:
134
136
  page_title: :thredded.nav.settings
@@ -126,6 +126,8 @@ zh-CN:
126
126
  update_btn_submitting: :thredded.form.update_btn_submitting
127
127
  pending_moderation_notice: 你的回复将在审核之后公开发表
128
128
  quote_btn: 引用
129
+ spoiler_summary: 剧透 - 点击显示。
130
+ spoiler_summary_for_email: 扰流板 - 选择下面的内容来查看:
129
131
  preferences:
130
132
  edit:
131
133
  page_title: :thredded.nav.settings
@@ -25,18 +25,23 @@ class CreateThredded < Thredded::BaseMigration
25
25
 
26
26
  create_table :thredded_categories do |t|
27
27
  t.references :messageboard, null: false, index: false
28
- t.string :name, limit: 191, null: false
29
- t.string :description, limit: 255
28
+ t.text :name, null: false
29
+ t.text :description
30
30
  t.timestamps null: false
31
- t.string :slug, limit: 191, null: false
32
- t.index %i[messageboard_id slug], name: :index_thredded_categories_on_messageboard_id_and_slug, unique: true
31
+ t.text :slug, null: false
32
+ t.index %i[messageboard_id slug],
33
+ name: :index_thredded_categories_on_messageboard_id_and_slug,
34
+ unique: true,
35
+ length: { slug: max_key_length }
33
36
  t.index [:messageboard_id], name: :index_thredded_categories_on_messageboard_id
34
37
  end
35
- DbTextSearch::CaseInsensitive.add_index connection, :thredded_categories, :name, name: :thredded_categories_name_ci
38
+ DbTextSearch::CaseInsensitive.add_index connection, :thredded_categories, :name,
39
+ name: :thredded_categories_name_ci,
40
+ **(max_key_length ? { length: max_key_length } : {})
36
41
 
37
42
  create_table :thredded_messageboards do |t|
38
- t.string :name, limit: 191, null: false
39
- t.string :slug, limit: 191
43
+ t.text :name, null: false
44
+ t.text :slug
40
45
  t.text :description
41
46
  t.integer :topics_count, default: 0
42
47
  t.integer :posts_count, default: 0
@@ -46,14 +51,16 @@ class CreateThredded < Thredded::BaseMigration
46
51
  t.timestamps null: false
47
52
  t.boolean :locked, null: false, default: false
48
53
  t.index [:messageboard_group_id], name: :index_thredded_messageboards_on_messageboard_group_id
49
- t.index [:slug], name: :index_thredded_messageboards_on_slug
54
+ t.index [:slug],
55
+ name: :index_thredded_messageboards_on_slug,
56
+ unique: true,
57
+ length: { slug: max_key_length }
50
58
  end
51
59
 
52
60
  create_table :thredded_posts do |t|
53
61
  t.references :user, type: user_id_type, index: false
54
62
  t.text :content, limit: 65_535
55
- t.string :ip, limit: 255
56
- t.string :source, limit: 255, default: 'web'
63
+ t.string :source, limit: 191, default: 'web'
57
64
  t.references :postable, null: false, index: false
58
65
  t.references :messageboard, null: false, index: false
59
66
  t.integer :moderation_state, null: false
@@ -63,7 +70,7 @@ class CreateThredded < Thredded::BaseMigration
63
70
  name: :index_thredded_posts_for_display
64
71
  t.index [:messageboard_id], name: :index_thredded_posts_on_messageboard_id
65
72
  t.index [:postable_id], name: :index_thredded_posts_on_postable_id
66
- t.index [:postable_id], name: :index_thredded_posts_on_postable_id_and_postable_type
73
+ t.index %i[postable_id created_at], name: :index_thredded_posts_on_postable_id_and_created_at
67
74
  t.index [:user_id], name: :index_thredded_posts_on_user_id
68
75
  end
69
76
  DbTextSearch::FullText.add_index connection, :thredded_posts, :content, name: :thredded_posts_content_fts
@@ -72,21 +79,25 @@ class CreateThredded < Thredded::BaseMigration
72
79
  t.references :user, type: user_id_type, index: false
73
80
  t.text :content, limit: 65_535
74
81
  t.references :postable, null: false, index: false
75
- t.string :ip, limit: 255
76
82
  t.timestamps null: false
83
+ t.index %i[postable_id created_at], name: :index_thredded_private_posts_on_postable_id_and_created_at
77
84
  end
78
85
 
79
86
  create_table :thredded_private_topics do |t|
80
87
  t.references :user, type: user_id_type, index: false
81
88
  t.references :last_user, index: false
82
- t.string :title, limit: 255, null: false
83
- t.string :slug, limit: 191, null: false
89
+ t.text :title, null: false
90
+ t.text :slug, null: false
84
91
  t.integer :posts_count, default: 0
85
- t.string :hash_id, limit: 191, null: false
92
+ t.string :hash_id, limit: 20, null: false
86
93
  t.datetime :last_post_at
87
94
  t.timestamps null: false
95
+ t.index [:last_post_at], name: :index_thredded_private_topics_on_last_post_at
88
96
  t.index [:hash_id], name: :index_thredded_private_topics_on_hash_id
89
- t.index [:slug], name: :index_thredded_private_topics_on_slug
97
+ t.index [:slug],
98
+ name: :index_thredded_private_topics_on_slug,
99
+ unique: true,
100
+ length: { slug: max_key_length }
90
101
  end
91
102
 
92
103
  create_table :thredded_private_users do |t|
@@ -107,22 +118,25 @@ class CreateThredded < Thredded::BaseMigration
107
118
  create_table :thredded_topics do |t|
108
119
  t.references :user, type: user_id_type, index: false
109
120
  t.references :last_user, index: false
110
- t.string :title, limit: 255, null: false
111
- t.string :slug, limit: 191, null: false
121
+ t.text :title, null: false
122
+ t.text :slug, null: false
112
123
  t.references :messageboard, null: false, index: false
113
124
  t.integer :posts_count, default: 0, null: false
114
125
  t.boolean :sticky, default: false, null: false
115
126
  t.boolean :locked, default: false, null: false
116
- t.string :hash_id, limit: 191, null: false
117
- t.string :type, limit: 191
127
+ t.string :hash_id, limit: 20, null: false
118
128
  t.integer :moderation_state, null: false
119
129
  t.datetime :last_post_at
120
130
  t.timestamps null: false
121
131
  t.index %i[moderation_state sticky updated_at],
122
132
  order: { sticky: :desc, updated_at: :desc },
123
133
  name: :index_thredded_topics_for_display
134
+ t.index [:last_post_at], name: :index_thredded_topics_on_last_post_at
124
135
  t.index [:hash_id], name: :index_thredded_topics_on_hash_id
125
- t.index [:slug], name: :index_thredded_topics_on_slug, unique: true
136
+ t.index [:slug],
137
+ name: :index_thredded_topics_on_slug,
138
+ unique: true,
139
+ length: { slug: max_key_length }
126
140
  t.index [:messageboard_id], name: :index_thredded_topics_on_messageboard_id
127
141
  t.index [:user_id], name: :index_thredded_topics_on_user_id
128
142
  end
@@ -149,7 +163,8 @@ class CreateThredded < Thredded::BaseMigration
149
163
  t.references :thredded_messageboard, null: false, index: false
150
164
  t.datetime :last_seen_at, null: false
151
165
  t.index %i[thredded_messageboard_id thredded_user_detail_id],
152
- name: :index_thredded_messageboard_users_primary
166
+ name: :index_thredded_messageboard_users_primary,
167
+ unique: true
153
168
  t.index %i[thredded_messageboard_id last_seen_at],
154
169
  name: :index_thredded_messageboard_users_for_recently_active
155
170
  end
@@ -182,7 +197,6 @@ class CreateThredded < Thredded::BaseMigration
182
197
  create_table table_name do |t|
183
198
  t.references :user, type: user_id_type, null: false, index: false
184
199
  t.references :postable, null: false, index: false
185
- t.integer :page, default: 1, null: false
186
200
  t.timestamp :read_at, null: false
187
201
  t.index %i[user_id postable_id], name: :"#{table_name}_user_postable", unique: true
188
202
  end
@@ -0,0 +1,91 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'thredded/base_migration'
4
+
5
+ class UpgradeThreddedV014ToV015 < Thredded::BaseMigration
6
+ def change # rubocop:disable Metrics/MethodLength
7
+ # Work around race condition on last_seen_at update
8
+ # https://github.com/thredded/thredded/pull/674
9
+ remove_index :thredded_messageboard_users,
10
+ name: :index_thredded_messageboard_users_primary
11
+ add_index :thredded_messageboard_users,
12
+ %i[thredded_messageboard_id thredded_user_detail_id],
13
+ name: :index_thredded_messageboard_users_primary,
14
+ unique: true
15
+
16
+ # Remove database string length limits.
17
+ # https://github.com/thredded/thredded/pull/703
18
+ remove_index :thredded_categories, name: :thredded_categories_name_ci
19
+ remove_string_limit :thredded_categories, :name
20
+ DbTextSearch::CaseInsensitive.add_index connection, :thredded_categories, :name,
21
+ name: :thredded_categories_name_ci,
22
+ **(max_key_length ? { length: max_key_length } : {})
23
+ remove_string_limit :thredded_categories, :description
24
+ remove_string_limit :thredded_categories, :slug,
25
+ indices: [
26
+ [%i[messageboard_id slug],
27
+ name: :index_thredded_categories_on_messageboard_id_and_slug,
28
+ unique: true,
29
+ length: { slug: max_key_length }]
30
+ ]
31
+
32
+ remove_string_limit :thredded_messageboards, :name
33
+ remove_string_limit :thredded_messageboards, :slug,
34
+ indices: [
35
+ [%i[slug],
36
+ name: :index_thredded_messageboards_on_slug,
37
+ unique: true,
38
+ length: { slug: max_key_length }]
39
+ ]
40
+
41
+ change_column :thredded_posts, :ip, :string, limit: 45
42
+ change_column :thredded_posts, :source, :string, limit: 191
43
+
44
+ remove_string_limit :thredded_private_topics, :title
45
+ remove_string_limit :thredded_private_topics, :slug,
46
+ indices: [
47
+ [%i[slug],
48
+ name: :index_thredded_private_topics_on_slug,
49
+ unique: true,
50
+ length: { slug: max_key_length }]
51
+ ]
52
+ change_column :thredded_private_topics, :hash_id, :string, limit: 20
53
+
54
+ remove_string_limit :thredded_topics, :title
55
+ remove_string_limit :thredded_topics, :slug,
56
+ indices: [
57
+ [%i[slug],
58
+ name: :index_thredded_topics_on_slug,
59
+ unique: true,
60
+ length: { slug: max_key_length }]
61
+ ]
62
+ change_column :thredded_topics, :hash_id, :string, limit: 20
63
+ remove_column :thredded_topics, :type
64
+
65
+ # Remove IP tracking column from posts
66
+ # https://github.com/thredded/thredded/pull/705
67
+ remove_column :thredded_posts, :ip
68
+ remove_column :thredded_private_posts, :ip
69
+
70
+ # Jump to first unread post
71
+ # https://github.com/thredded/thredded/pull/695
72
+ remove_column :thredded_user_topic_read_states, :page
73
+ remove_column :thredded_user_private_topic_read_states, :page
74
+ add_index :thredded_topics, [:last_post_at], name: :index_thredded_topics_on_last_post_at
75
+ add_index :thredded_private_topics, [:last_post_at], name: :index_thredded_private_topics_on_last_post_at
76
+ add_index :thredded_posts, %i[postable_id created_at], name: :index_thredded_posts_on_postable_id_and_created_at
77
+ add_index :thredded_private_posts, %i[postable_id created_at],
78
+ name: :index_thredded_private_posts_on_postable_id_and_created_at
79
+
80
+ # Cleanup
81
+ remove_index :thredded_posts, name: :index_thredded_posts_on_postable_id_and_created_at
82
+ end
83
+
84
+ private
85
+
86
+ def remove_string_limit(table, column, type: :text, indices: [])
87
+ indices.each { |(_, options)| remove_index table, name: options[:name] }
88
+ change_column table, column, type, limit: nil
89
+ indices.each { |args| add_index table, *args }
90
+ end
91
+ end
@@ -51,13 +51,10 @@ Thredded.admin_column = :admin
51
51
  # Whether posts and topics pending moderation are visible to regular users.
52
52
  Thredded.content_visible_while_pending_moderation = true
53
53
 
54
- # Whether users that are following a topic are listed on topic page.
55
- Thredded.show_topic_followers = false
56
-
57
54
  # This model can be customized further by overriding a handful of methods on the User model.
58
55
  # For more information, see app/models/thredded/user_extender.rb.
59
56
 
60
- # ==> Ordering configuration
57
+ # ==> UI configuration
61
58
 
62
59
  # How to calculate the position of messageboards in a list:
63
60
  # :position (default) set the position manually (new messageboards go to the bottom, by creation timestamp)
@@ -65,6 +62,18 @@ Thredded.show_topic_followers = false
65
62
  # :topics_count_desc most topics first
66
63
  Thredded.messageboards_order = :position
67
64
 
65
+ # Whether users that are following a topic are listed on the topic page.
66
+ Thredded.show_topic_followers = false
67
+
68
+ # Whether the list of users who are currently online is displayed.
69
+ Thredded.currently_online_enabled = true
70
+
71
+ # Whether private messaging functionality is enabled.
72
+ Thredded.private_messaging_enabled = true
73
+
74
+ # The layout for rendering Thredded views.
75
+ Thredded.layout = 'thredded/application'
76
+
68
77
  # ==> Email Configuration
69
78
  # Email "From:" field will use the following
70
79
  # Thredded.email_from = 'no-reply@example.com'
@@ -75,9 +84,9 @@ Thredded.messageboards_order = :position
75
84
  # The parent mailer for all Thredded mailers
76
85
  # Thredded.parent_mailer = 'ActionMailer::Base'
77
86
 
78
- # ==> View Configuration
79
- # Set the layout for rendering the thredded views.
80
- Thredded.layout = 'thredded/application'
87
+ # ==> Model configuration
88
+ # The range of valid topic title lengths. Default:
89
+ # Thredded.topic_title_length_range = (1..200)
81
90
 
82
91
  # ==> URLs
83
92
  # How Thredded generates URL slugs from text.
data/lib/thredded.rb CHANGED
@@ -16,11 +16,13 @@ require 'inline_svg'
16
16
  # Require these explictly to make sure they are not reloaded.
17
17
  # This allows for configuring them by accessing class methods in the initializer.
18
18
  require 'thredded/formatting_demo_content'
19
+ require 'thredded/html_pipeline/utils'
19
20
  require 'thredded/html_pipeline/at_mention_filter'
20
21
  require 'thredded/html_pipeline/autolink_filter'
21
22
  require 'thredded/html_pipeline/kramdown_filter'
22
23
  require 'thredded/html_pipeline/onebox_filter'
23
24
  require 'thredded/html_pipeline/wrap_iframes_filter'
25
+ require 'thredded/html_pipeline/spoiler_tag_filter'
24
26
 
25
27
  # Asset compilation
26
28
  require 'autoprefixer-rails'
@@ -39,152 +41,168 @@ require 'thredded/content_formatter'
39
41
  require 'thredded/email_transformer'
40
42
  require 'thredded/base_notifier'
41
43
 
44
+ require 'thredded/arel_compat'
42
45
  require 'thredded/collection_to_strings_with_cache_renderer'
43
46
 
44
47
  module Thredded
45
- mattr_accessor \
46
- :autocomplete_min_length,
47
- :active_user_threshold,
48
- :avatar_url,
49
- :email_from,
50
- :email_outgoing_prefix,
51
- :layout,
52
- :messageboards_order,
53
- :routes_id_constraint,
54
- :user_display_name_method,
55
- :user_name_column,
56
- :user_path
57
-
58
- # @return [Symbol] The name of the method used by Thredded controllers and views to fetch the currently signed-in user
59
- mattr_accessor :current_user_method
60
-
61
- # @return [Symbol] The name of the moderator flag column on the users table for the default permissions model
62
- mattr_accessor :moderator_column
63
-
64
- # @return [Symbol] The name of the admin flag column on the users table for the default permissions model
65
- mattr_accessor :admin_column
66
-
67
- # @return [Boolean] Whether posts that are pending moderation are visible to regular users.
68
- mattr_accessor :content_visible_while_pending_moderation
69
-
70
- # @return [Array] The notifiers, by default just the EmailNotifier
71
- mattr_accessor :notifiers
72
-
73
- # @return [Boolean] Whether users that are following a topic are listed on topic page.
74
- mattr_accessor :show_topic_followers
75
-
76
- # @return [Symbol] The name of the method used by Thredded to display users
77
- mattr_accessor :user_display_name_method
78
-
79
- # @return [String] The name of the parent mailer class for Thredded mailers.
80
- mattr_accessor :parent_mailer
81
- self.parent_mailer = 'ActionMailer::Base'
48
+ class << self
49
+ attr_accessor \
50
+ :autocomplete_min_length,
51
+ :active_user_threshold,
52
+ :avatar_url,
53
+ :email_from,
54
+ :email_outgoing_prefix,
55
+ :layout,
56
+ :messageboards_order,
57
+ :routes_id_constraint,
58
+ :user_name_column
82
59
 
83
- # @return [Proc] The proc that Thredded uses to generate URL slugs from text.
84
- mattr_accessor :slugifier
85
- self.slugifier = ->(input) { input.parameterize }
60
+ # A lambda that generates a URL path to the user page for the given user.
61
+ attr_writer :user_path
86
62
 
87
- # @return [Boolean] Whether the user should get subscribed to a new topic they've created.
88
- mattr_accessor :auto_follow_when_creating_topic
89
- self.auto_follow_when_creating_topic = true
63
+ # @return [Symbol] The name of the method used by Thredded controllers and views to get the currently signed-in user
64
+ attr_accessor :current_user_method
90
65
 
91
- # @return [Boolean] Whether the user should get subscribed to a topic after posting in it.
92
- mattr_accessor :auto_follow_when_posting_in_topic
93
- self.auto_follow_when_posting_in_topic = true
66
+ # @return [Symbol] The name of the moderator flag column on the users table for the default permissions model
67
+ attr_accessor :moderator_column
94
68
 
95
- # @return [String] The name of the user class
96
- mattr_reader :user_class_name
69
+ # @return [Symbol] The name of the admin flag column on the users table for the default permissions model
70
+ attr_accessor :admin_column
97
71
 
98
- self.active_user_threshold = 5.minutes
99
- self.admin_column = :admin
100
- self.avatar_url = ->(user) { Gravatar.src(user.email, 156, 'mm') }
101
- self.layout = 'thredded/application'
102
- self.moderator_column = :admin
103
- self.user_name_column = :name
104
- self.content_visible_while_pending_moderation = true
105
- self.show_topic_followers = false
106
- self.messageboards_order = :position
107
- self.autocomplete_min_length = 2
108
- self.routes_id_constraint = /[1-9]\d*/
72
+ # @return [Boolean] Whether posts that are pending moderation are visible to regular users.
73
+ attr_accessor :content_visible_while_pending_moderation
109
74
 
110
- # @return [Thredded::AllViewHooks] View hooks configuration.
111
- def self.view_hooks
112
- instance = Thredded::AllViewHooks.instance
113
- unless instance
114
- fail '`Thredded.view_hooks` must be configured in a `Rails.application.config.to_prepare` block!'
75
+ # @return [Boolean] Whether users that are following a topic are listed on the topic page.
76
+ attr_accessor :show_topic_followers
77
+
78
+ # @return [Symbol] The name of the method used by Thredded to display users
79
+ attr_writer :user_display_name_method
80
+
81
+ # @return [String] The name of the parent mailer class for Thredded mailers.
82
+ attr_accessor :parent_mailer
83
+
84
+ # @return [Proc] The proc that Thredded uses to generate URL slugs from text.
85
+ attr_accessor :slugifier
86
+
87
+ # @return [Boolean] Whether the user should get subscribed to a new topic they've created.
88
+ attr_accessor :auto_follow_when_creating_topic
89
+
90
+ # @return [Boolean] Whether the user should get subscribed to a topic after posting in it.
91
+ attr_accessor :auto_follow_when_posting_in_topic
92
+
93
+ # @return [String] The name of the user class
94
+ attr_reader :user_class_name
95
+
96
+ # @return [Range<Integer>] The range of valid topic title lengths.
97
+ attr_accessor :topic_title_length_range
98
+
99
+ # @return [Boolean] Whether the list of users who are currently online is displayed.
100
+ attr_accessor :currently_online_enabled
101
+
102
+ # @return [Boolean] Whether the private messaging functionality is enabled.
103
+ attr_accessor :private_messaging_enabled
104
+
105
+ # @return [Thredded::AllViewHooks] View hooks configuration.
106
+ def view_hooks
107
+ instance = Thredded::AllViewHooks.instance
108
+ unless instance
109
+ fail '`Thredded.view_hooks` must be configured in a `Rails.application.config.to_prepare` block!'
110
+ end
111
+ instance
115
112
  end
116
- instance
117
- end
118
113
 
119
- def self.notifiers
120
- @@notifiers ||= [Thredded::EmailNotifier.new] # rubocop:disable Style/ClassVars
121
- end
114
+ # @return [Array] The notifiers, by default just the EmailNotifier
115
+ def notifiers
116
+ @notifiers ||= [Thredded::EmailNotifier.new]
117
+ end
122
118
 
123
- def self.notifiers=(notifiers)
124
- notifiers.each { |notifier| BaseNotifier.validate_notifier(notifier) }
125
- @@notifiers = notifiers # rubocop:disable Style/ClassVars
126
- end
119
+ # @param [Array] notifiers
120
+ def notifiers=(notifiers)
121
+ notifiers.each { |notifier| Thredded::BaseNotifier.validate_notifier(notifier) }
122
+ @notifiers = notifiers
123
+ end
127
124
 
128
- def self.user_display_name_method
129
- @@user_display_name_method || user_name_column
130
- end
125
+ # @return [Symbol] The name of the method used by Thredded to display users
126
+ def user_display_name_method
127
+ @user_display_name_method || user_name_column
128
+ end
131
129
 
132
- # @param value [:position, :topics_count_desc, :last_post_at_desc]
133
- def self.messageboards_order=(value)
134
- case value
135
- when :position, :topics_count_desc, :last_post_at_desc
136
- @@messageboards_order = value # rubocop:disable Style/ClassVars
137
- else
138
- fail ArgumentError, "Unexpected value for Thredded.messageboards_order: #{value}"
130
+ # @param value [:position, :topics_count_desc, :last_post_at_desc]
131
+ def messageboards_order=(value)
132
+ case value
133
+ when :position, :topics_count_desc, :last_post_at_desc
134
+ @messageboards_order = value
135
+ else
136
+ fail ArgumentError, "Unexpected value for Thredded.messageboards_order: #{value}"
137
+ end
139
138
  end
140
- end
141
139
 
142
- # @param user_class_name [String]
143
- def self.user_class=(user_class_name)
144
- unless user_class_name.is_a?(String)
145
- fail "Thredded.user_class must be set to a String, got #{user_class_name.inspect}"
140
+ # @param user_class_name [String]
141
+ def user_class=(user_class_name)
142
+ unless user_class_name.is_a?(String)
143
+ fail "Thredded.user_class must be set to a String, got #{user_class_name.inspect}"
144
+ end
145
+ @user_class_name = user_class_name
146
146
  end
147
- @@user_class_name = user_class_name # rubocop:disable Style/ClassVars
148
- end
149
147
 
150
- # @return [Class<Thredded::UserExtender>] the user class from the host application.
151
- def self.user_class
152
- # This is nil before the initializer is installed.
153
- return nil if @@user_class_name.nil?
154
- @@user_class_name.constantize
155
- end
148
+ # @return [Class<Thredded::UserExtender>] the user class from the host application.
149
+ def user_class
150
+ # This is nil before the initializer is installed.
151
+ return nil if @user_class_name.nil?
152
+ @user_class_name.constantize
153
+ end
156
154
 
157
- # @param view_context [Object] context to execute the lambda in.
158
- # @param user [Thredded.user_class]
159
- # @return [String] path to the user evaluated in the specified context.
160
- def self.user_path(view_context, user)
161
- view_context.instance_exec(user, &@@user_path)
162
- end
155
+ # @param view_context [Object] context to execute the lambda in.
156
+ # @param user [Thredded.user_class]
157
+ # @return [String] path to the user evaluated in the specified context.
158
+ def user_path(view_context, user)
159
+ view_context.instance_exec(user, &@user_path)
160
+ end
163
161
 
164
- # Whether the layout is a thredded layout as opposed to the application layout.
165
- def self.standalone_layout?
166
- layout.is_a?(String) && layout.start_with?('thredded/')
167
- end
162
+ # Whether the layout is a thredded layout as opposed to the application layout.
163
+ def standalone_layout?
164
+ layout.is_a?(String) && layout.start_with?('thredded/')
165
+ end
168
166
 
169
- # Returns a view for the given posts' scope, applying read permission filters to the scope.
170
- # Can be used in main_app, e.g. for showing the recent user posts on the profile page.
171
- #
172
- # @param scope [ActiveRecord::Relation<Thredded::Post>] the posts scope for which to return the view.
173
- # @param current_user [Thredded.user_class, nil] the user viewing the posts.
174
- # @return [PostsPageView]
175
- def self.posts_page_view(scope:, current_user:)
176
- current_user ||= Thredded::NullUser.new
177
- PostsPageView.new(
178
- current_user,
179
- Pundit.policy_scope!(current_user, scope)
180
- .where(messageboard_id: Pundit.policy_scope!(current_user, Thredded::Messageboard.all).pluck(:id))
181
- .includes(:postable)
182
- )
183
- end
167
+ # Returns a view for the given posts' scope, applying read permission filters to the scope.
168
+ # Can be used in main_app, e.g. for showing the recent user posts on the profile page.
169
+ #
170
+ # @param scope [ActiveRecord::Relation<Thredded::Post>] the posts scope for which to return the view.
171
+ # @param current_user [Thredded.user_class, nil] the user viewing the posts.
172
+ # @return [PostsPageView]
173
+ def posts_page_view(scope:, current_user:)
174
+ current_user ||= Thredded::NullUser.new
175
+ Thredded::PostsPageView.new(
176
+ current_user,
177
+ Pundit.policy_scope!(current_user, scope)
178
+ .where(messageboard_id: Pundit.policy_scope!(current_user, Thredded::Messageboard.all).pluck(:id))
179
+ .includes(:postable)
180
+ )
181
+ end
184
182
 
185
- # @api private
186
- def self.rails_gte_51?
187
- @rails_gte_51 = (Rails.gem_version >= Gem::Version.new('5.1.0')) if @rails_gte_51.nil?
188
- @rails_gte_51
183
+ # @api private
184
+ def rails_gte_51?
185
+ @rails_gte_51 = (Rails.gem_version >= Gem::Version.new('5.1.0')) if @rails_gte_51.nil?
186
+ @rails_gte_51
187
+ end
189
188
  end
189
+
190
+ self.active_user_threshold = 5.minutes
191
+ self.admin_column = :admin
192
+ self.auto_follow_when_creating_topic = true
193
+ self.auto_follow_when_posting_in_topic = true
194
+ self.autocomplete_min_length = 2
195
+ self.avatar_url = ->(user) { Gravatar.src(user.email, 156, 'mm') }
196
+ self.content_visible_while_pending_moderation = true
197
+ self.layout = 'thredded/application'
198
+ self.messageboards_order = :position
199
+ self.moderator_column = :admin
200
+ self.parent_mailer = 'ActionMailer::Base'
201
+ self.routes_id_constraint = /[1-9]\d*/
202
+ self.show_topic_followers = false
203
+ self.slugifier = ->(input) { input.parameterize }
204
+ self.topic_title_length_range = (1..200)
205
+ self.user_name_column = :name
206
+ self.private_messaging_enabled = true
207
+ self.currently_online_enabled = true
190
208
  end