thredded 0.14.4 → 0.15.1

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 (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
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'thredded/email_transformer/onebox'
4
+ require 'thredded/email_transformer/spoiler'
4
5
 
5
6
  module Thredded
6
7
  # This transformer should applied to emails so that they render correctly in the email clients.
@@ -11,8 +12,10 @@ module Thredded
11
12
  # Rails.application.config.roadie.before_transformation = Thredded::EmailTransformer
12
13
  #
13
14
  module EmailTransformer
14
- mattr_accessor :transformers
15
- self.transformers = [Onebox]
15
+ class << self
16
+ attr_accessor :transformers
17
+ end
18
+ @transformers = [Onebox, Spoiler]
16
19
 
17
20
  # @param doc [Nokogiri::HTML::Document]
18
21
  def self.call(doc)
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'thredded/email_transformer/base'
4
+ module Thredded
5
+ module EmailTransformer
6
+ # Changes the spoiler tags to work in emails.
7
+ class Spoiler < Base
8
+ def call
9
+ doc.css('.thredded--post--content--spoiler--summary').each do |node|
10
+ node.content = I18n.t('thredded.posts.spoiler_summary_for_email')
11
+ end
12
+ doc.css('.thredded--post--content--spoiler--contents img').each do |img|
13
+ text = "#{img['src']} #{" (#{img['alt']})" if img['alt'].present?}"
14
+ img.swap(
15
+ if img.parent.name == 'a' && img.parent.children.size == 1
16
+ doc.document.create_text_node(text)
17
+ else
18
+ doc.document.create_element('a', text, href: img['src'], target: '_blank')
19
+ end
20
+ )
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
@@ -8,6 +8,18 @@ module Thredded
8
8
  end
9
9
  self.parts = [
10
10
  <<-'MARKDOWN',
11
+ #### Spoilers
12
+
13
+ Use `<spoiler></spoiler>` tags to create spoiler boxes like this one:
14
+
15
+ <spoiler>
16
+ Harry Potter books are better than the movies.
17
+
18
+ ![nyancat](https://storage.googleapis.com/glebm-stuff/nyancat.gif)
19
+
20
+ https://www.youtube.com/watch?v=5lBBUPVuusM
21
+ </spoiler>
22
+
11
23
  #### Oneboxes
12
24
 
13
25
  URLs of supported resources are replaced with boxes like these:
@@ -5,6 +5,8 @@ require 'onebox'
5
5
  module Thredded
6
6
  module HtmlPipeline
7
7
  class OneboxFilter < ::HTML::Pipeline::Filter
8
+ include ::Thredded::HtmlPipeline::Utils
9
+
8
10
  SANITIZE_CONFIG = Sanitize::Config.merge(
9
11
  Sanitize::Config::ONEBOX,
10
12
  attributes: {
@@ -51,7 +53,7 @@ module Thredded
51
53
  next unless url.present? && url == element.content && on_its_own_line?(element)
52
54
  onebox_html = render_onebox_with_cache(url)
53
55
  next if onebox_html.empty?
54
- fixup_paragraph! element
56
+ extract_block_from_paragraph! element
55
57
  element.swap onebox_html
56
58
  end
57
59
  doc
@@ -101,43 +103,6 @@ module Thredded
101
103
  nodes.take_while { |node| !node_name?(node, 'br') }
102
104
  .all? { |node| node.text? && node.blank? }
103
105
  end
104
-
105
- def fixup_paragraph!(element)
106
- # Can't have a div inside a paragraph, so split the paragraph
107
- p = element.parent
108
- return unless node_name?(p, 'p')
109
- children_after = p.children[p.children.index(element) + 1..-1]
110
- remove_leading_blanks! children_after
111
- # Move the onebox out of and after the paragraph
112
- p.add_next_sibling element
113
- # Move all the elements after the onebox to a new paragraph
114
- unless children_after.empty?
115
- new_p = Nokogiri::XML::Node.new 'p', doc
116
- element.add_next_sibling new_p
117
- children_after.each { |child| new_p.add_child child }
118
- end
119
- # The original paragraph might have been split just after a <br> or whitespace, remove them if so:
120
- remove_leading_blanks! p.children.reverse
121
- p.remove if p.children.empty?
122
- end
123
-
124
- # @param children [Nokogiri::XML::NodeSet]
125
- def remove_leading_blanks!(children)
126
- to_remove = children.take_while do |c|
127
- if node_name?(c, 'br') || c.text? && c.content.blank?
128
- c.remove
129
- true
130
- else
131
- c.content = c.content.lstrip
132
- false
133
- end
134
- end
135
- to_remove.each { |c| children.delete(c) }
136
- end
137
-
138
- def node_name?(node, node_name)
139
- node && node.node_name && node.node_name.casecmp(node_name).zero?
140
- end
141
106
  end
142
107
  end
143
108
  end
@@ -0,0 +1,128 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Thredded
4
+ module HtmlPipeline
5
+ module SpoilerTagFilter
6
+ PLACEHOLDER_START_CLASS = 'thredded-spoiler-placeholder-start-24uob7bajv5'
7
+ PLACEHOLDER_END_CLASS = 'thredded-spoiler-placeholder-end-24uob7bajv5'
8
+ PLACEHOLDER_START_BLOCK = "<p class='#{PLACEHOLDER_START_CLASS}' title='%s'></p>"
9
+ PLACEHOLDER_END_BLOCK = "<p class='#{PLACEHOLDER_END_CLASS}' title='%s'></p>"
10
+ PLACEHOLDER_START_SPAN = "<i class='#{PLACEHOLDER_START_CLASS}' title='%s'></i>"
11
+ PLACEHOLDER_END_SPAN = "<i class='#{PLACEHOLDER_END_CLASS}' title='%s'></i>"
12
+
13
+ class << self
14
+ # @return [[[String, String]]] Pairs of opening and closing spoiler tags.
15
+ attr_reader :spoiler_tags
16
+
17
+ # @api private
18
+ attr_reader :spoiler_tags_replacements
19
+
20
+ # @api private
21
+ attr_reader :spoiler_tags_inverse_replacements
22
+
23
+ # @param [[[String, String]]] spoiler_tags
24
+ def spoiler_tags=(spoiler_tags)
25
+ @spoiler_tags = spoiler_tags.freeze
26
+ @spoiler_tags_replacements = spoiler_tags.flat_map.with_index do |(open, close), i|
27
+ [[/((?:\A|\r?\n|>)[ ]*)#{Regexp.escape(open)}(?=[ \t]*\r?(?:\n|\z))/, "\\1#{PLACEHOLDER_START_BLOCK % i}"],
28
+ [/((?:\A|\r?\n|>)[ ]*)#{Regexp.escape(close)}(?=[ \t]*\r?(?:\n|\z))/, "\\1#{PLACEHOLDER_END_BLOCK % i}"],
29
+ [open, PLACEHOLDER_START_SPAN % i],
30
+ [close, PLACEHOLDER_END_SPAN % i]]
31
+ end
32
+ @spoiler_tags_inverse_replacements = spoiler_tags.flat_map.with_index do |(open, close), i|
33
+ [[PLACEHOLDER_START_BLOCK % i, open],
34
+ [PLACEHOLDER_END_BLOCK % i, close],
35
+ [PLACEHOLDER_START_SPAN % i, open],
36
+ [PLACEHOLDER_END_SPAN % i, close]]
37
+ end
38
+ end
39
+ end
40
+
41
+ self.spoiler_tags = [
42
+ %w[<spoiler> </spoiler>],
43
+ ].freeze
44
+
45
+ class BeforeMarkup < ::HTML::Pipeline::Filter
46
+ def call
47
+ @html = +html # Unfreeze
48
+ SpoilerTagFilter.spoiler_tags_replacements.each do |(pattern, replacement)|
49
+ html.gsub! pattern, replacement
50
+ end
51
+ html
52
+ end
53
+ end
54
+
55
+ class AfterMarkup < ::HTML::Pipeline::Filter
56
+ include ::Thredded::HtmlPipeline::Utils
57
+
58
+ SPOILER_START = [PLACEHOLDER_START_BLOCK, PLACEHOLDER_START_SPAN].freeze
59
+ SPOILER_END = [PLACEHOLDER_END_BLOCK, PLACEHOLDER_END_SPAN].freeze
60
+
61
+ def call
62
+ doc.css(".#{PLACEHOLDER_START_CLASS}").each do |placeholder_start|
63
+ process(placeholder_start)
64
+ end
65
+ doc.search('.//text()').each do |node|
66
+ content = +node.content # Unfreeze
67
+ SpoilerTagFilter.spoiler_tags_inverse_replacements.each do |(pattern, replacement)|
68
+ content.gsub! pattern, replacement
69
+ end
70
+ node.content = content
71
+ end
72
+ doc
73
+ end
74
+
75
+ protected
76
+
77
+ # @param [Nokogiri::XML::Document] document
78
+ # @param [Array<Nokogiri::XML::Node>] children
79
+ def build_spoiler_tag(document, children)
80
+ document.create_element(
81
+ 'div',
82
+ class: 'thredded--post--content--spoiler',
83
+ tabindex: 0,
84
+ role: 'figure',
85
+ 'aria-expanded' => 'false',
86
+ ) << document.create_element(
87
+ 'div',
88
+ I18n.t('thredded.posts.spoiler_summary'),
89
+ class: 'thredded--post--content--spoiler--summary',
90
+ 'aria-hidden' => 'false',
91
+ ) << document.create_element(
92
+ 'div',
93
+ class: 'thredded--post--content--spoiler--contents',
94
+ 'aria-hidden' => 'true',
95
+ ) { |contents_tag| children.each { |child| contents_tag.add_child child } }
96
+ end
97
+
98
+ private
99
+
100
+ def process(placeholder_start)
101
+ placeholder_end, children = find_placeholder_end(placeholder_start)
102
+ placeholder_start.remove
103
+ return if !placeholder_end || children.empty?
104
+ spoiler_tag = placeholder_end.replace(build_spoiler_tag(doc.document, children))
105
+ extract_block_from_paragraph! spoiler_tag
106
+ spoiler_tag
107
+ end
108
+
109
+ def find_placeholder_end(sibling)
110
+ children = []
111
+ placeholder_end = nil
112
+ loop do
113
+ sibling = sibling.next_sibling
114
+ break if sibling.nil?
115
+ if sibling[:class] == PLACEHOLDER_START_CLASS
116
+ sibling = process(sibling) || sibling
117
+ elsif sibling[:class] == PLACEHOLDER_END_CLASS
118
+ placeholder_end = sibling
119
+ break
120
+ end
121
+ children << sibling
122
+ end
123
+ [placeholder_end, children]
124
+ end
125
+ end
126
+ end
127
+ end
128
+ end
@@ -0,0 +1,47 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Thredded
4
+ module HtmlPipeline
5
+ module Utils
6
+ private
7
+
8
+ # If the given block element is contained in a paragraph, splits the paragraph and removes unnecessary whitespace.
9
+ # @param [Nokogiri::HTML::Node] element the block element that may be inside a paragraph.
10
+ def extract_block_from_paragraph!(element)
11
+ p = element.parent
12
+ return unless node_name?(p, 'p')
13
+ children_after = p.children[p.children.index(element) + 1..-1]
14
+ remove_leading_blanks! children_after
15
+ # Move the element out of and after the paragraph
16
+ p.add_next_sibling element
17
+ # Move all the elements after the onebox to a new paragraph
18
+ unless children_after.empty?
19
+ new_p = Nokogiri::XML::Node.new 'p', doc
20
+ element.add_next_sibling new_p
21
+ children_after.each { |child| new_p.add_child child }
22
+ end
23
+ # The original paragraph might have been split just after a <br> or whitespace, remove them if so:
24
+ remove_leading_blanks! p.children.reverse
25
+ p.remove if p.children.empty?
26
+ end
27
+
28
+ # @param children [Nokogiri::XML::NodeSet]
29
+ def remove_leading_blanks!(children)
30
+ to_remove = children.take_while do |c|
31
+ if node_name?(c, 'br') || c.text? && c.content.blank?
32
+ c.remove
33
+ true
34
+ else
35
+ c.content = c.content.lstrip
36
+ false
37
+ end
38
+ end
39
+ to_remove.each { |c| children.delete(c) }
40
+ end
41
+
42
+ def node_name?(node, node_name)
43
+ node && node.node_name && node.node_name.casecmp(node_name).zero?
44
+ end
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,119 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Arel
4
+ module Nodes
5
+ class Case < Arel::Nodes::Node
6
+ include Arel::OrderPredications
7
+ include Arel::Predications
8
+ include Arel::AliasPredication
9
+
10
+ attr_accessor :case, :conditions, :default
11
+
12
+ def initialize(expression = nil, default = nil)
13
+ @case = expression
14
+ @conditions = []
15
+ @default = default
16
+ end
17
+
18
+ def when(condition, expression = nil)
19
+ @conditions << When.new(Nodes.build_quoted(condition), expression)
20
+ self
21
+ end
22
+
23
+ def then(expression)
24
+ @conditions.last.right = Nodes.build_quoted(expression)
25
+ self
26
+ end
27
+
28
+ def else(expression)
29
+ @default = Else.new Nodes.build_quoted(expression)
30
+ self
31
+ end
32
+
33
+ def initialize_copy(other)
34
+ super
35
+ @case = @case.clone if @case
36
+ @conditions = @conditions.map(&:clone)
37
+ @default = @default.clone if @default
38
+ end
39
+
40
+ def hash
41
+ [@case, @conditions, @default].hash
42
+ end
43
+
44
+ def eql?(other)
45
+ self.class == other.class &&
46
+ self.case == other.case &&
47
+ conditions == other.conditions &&
48
+ default == other.default
49
+ end
50
+
51
+ alias == eql?
52
+ end
53
+
54
+ class When < Binary
55
+ end
56
+
57
+ class Else < Unary
58
+ end
59
+ end
60
+ end
61
+
62
+ module Arel
63
+ module Visitors
64
+ class DepthFirst < Arel::Visitors::Visitor
65
+ alias visit_Arel_Nodes_Else unary
66
+
67
+ def visit_Arel_Nodes_Case(o) # rubocop:disable Style/MethodName
68
+ visit o.case
69
+ visit o.conditions
70
+ visit o.default
71
+ end
72
+
73
+ alias visit_Arel_Nodes_When binary
74
+ end
75
+ end
76
+ end
77
+
78
+ module Arel
79
+ module Predications
80
+ def when(right)
81
+ Nodes::Case.new(self).when quoted_node(right)
82
+ end
83
+ end
84
+ end
85
+
86
+ module Arel
87
+ module Visitors
88
+ class ToSql < Arel::Visitors::Reduce
89
+ def visit_Arel_Nodes_Case(o, collector) # rubocop:disable Style/MethodName
90
+ collector << 'CASE '
91
+ if o.case
92
+ visit o.case, collector
93
+ collector << ' '
94
+ end
95
+ o.conditions.each do |condition|
96
+ visit condition, collector
97
+ collector << ' '
98
+ end
99
+ if o.default
100
+ visit o.default, collector
101
+ collector << ' '
102
+ end
103
+ collector << 'END'
104
+ end
105
+
106
+ def visit_Arel_Nodes_When(o, collector) # rubocop:disable Style/MethodName
107
+ collector << 'WHEN '
108
+ visit o.left, collector
109
+ collector << ' THEN '
110
+ visit o.right, collector
111
+ end
112
+
113
+ def visit_Arel_Nodes_Else(o, collector) # rubocop:disable Style/MethodName
114
+ collector << 'ELSE '
115
+ visit o.expr, collector
116
+ end
117
+ end
118
+ end
119
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Thredded
4
- VERSION = '0.14.4'
4
+ VERSION = '0.15.1'
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: thredded
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.14.4
4
+ version: 0.15.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Joel Oliveira
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2018-04-29 00:00:00.000000000 Z
12
+ date: 2018-06-08 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: pundit
@@ -157,14 +157,14 @@ dependencies:
157
157
  requirements:
158
158
  - - ">="
159
159
  - !ruby/object:Gem::Version
160
- version: '0'
160
+ version: 1.17.0
161
161
  type: :runtime
162
162
  prerelease: false
163
163
  version_requirements: !ruby/object:Gem::Requirement
164
164
  requirements:
165
165
  - - ">="
166
166
  - !ruby/object:Gem::Version
167
- version: '0'
167
+ version: 1.17.0
168
168
  - !ruby/object:Gem::Dependency
169
169
  name: onebox
170
170
  requirement: !ruby/object:Gem::Requirement
@@ -174,7 +174,7 @@ dependencies:
174
174
  version: '1.8'
175
175
  - - ">="
176
176
  - !ruby/object:Gem::Version
177
- version: 1.8.13
177
+ version: 1.8.48
178
178
  type: :runtime
179
179
  prerelease: false
180
180
  version_requirements: !ruby/object:Gem::Requirement
@@ -184,7 +184,7 @@ dependencies:
184
184
  version: '1.8'
185
185
  - - ">="
186
186
  - !ruby/object:Gem::Version
187
- version: 1.8.13
187
+ version: 1.8.48
188
188
  - !ruby/object:Gem::Dependency
189
189
  name: html-pipeline
190
190
  requirement: !ruby/object:Gem::Requirement
@@ -633,20 +633,6 @@ dependencies:
633
633
  - - ">="
634
634
  - !ruby/object:Gem::Version
635
635
  version: '0'
636
- - !ruby/object:Gem::Dependency
637
- name: coffee-rails
638
- requirement: !ruby/object:Gem::Requirement
639
- requirements:
640
- - - ">="
641
- - !ruby/object:Gem::Version
642
- version: '0'
643
- type: :development
644
- prerelease: false
645
- version_requirements: !ruby/object:Gem::Requirement
646
- requirements:
647
- - - ">="
648
- - !ruby/object:Gem::Version
649
- version: '0'
650
636
  description: |-
651
637
  The best Rails 4.2+ forums engine ever. Its goal is to be as simple and feature rich as possible.
652
638
  Thredded works with SQLite, MySQL (v5.6.4+), and PostgreSQL. See the demo at https://thredded.org/.
@@ -686,6 +672,7 @@ files:
686
672
  - app/assets/javascripts/thredded/components/post_form.es6
687
673
  - app/assets/javascripts/thredded/components/preview_area.es6
688
674
  - app/assets/javascripts/thredded/components/quote_post.es6
675
+ - app/assets/javascripts/thredded/components/spoilers.es6
689
676
  - app/assets/javascripts/thredded/components/time_stamps.es6
690
677
  - app/assets/javascripts/thredded/components/topic_form.es6
691
678
  - app/assets/javascripts/thredded/components/topics.es6
@@ -738,6 +725,7 @@ files:
738
725
  - app/assets/stylesheets/thredded/components/_post.scss
739
726
  - app/assets/stylesheets/thredded/components/_preferences.scss
740
727
  - app/assets/stylesheets/thredded/components/_preview_area.scss
728
+ - app/assets/stylesheets/thredded/components/_spoiler.scss
741
729
  - app/assets/stylesheets/thredded/components/_topic-delete.scss
742
730
  - app/assets/stylesheets/thredded/components/_topic-header.scss
743
731
  - app/assets/stylesheets/thredded/components/_topics.scss
@@ -900,6 +888,7 @@ files:
900
888
  - app/views/thredded/posts/edit.html.erb
901
889
  - app/views/thredded/posts/new.html.erb
902
890
  - app/views/thredded/posts_common/_actions.html.erb
891
+ - app/views/thredded/posts_common/_before_first_unread_post.html.erb
903
892
  - app/views/thredded/posts_common/_content.html.erb
904
893
  - app/views/thredded/posts_common/_form.html.erb
905
894
  - app/views/thredded/posts_common/_header.html.erb
@@ -943,6 +932,7 @@ files:
943
932
  - app/views/thredded/shared/_breadcrumbs.html.erb
944
933
  - app/views/thredded/shared/_content_moderation_blocked_state.html.erb
945
934
  - app/views/thredded/shared/_currently_online.html.erb
935
+ - app/views/thredded/shared/_field_errors.html.erb
946
936
  - app/views/thredded/shared/_flash_messages.html.erb
947
937
  - app/views/thredded/shared/_header.html.erb
948
938
  - app/views/thredded/shared/_nav.html.erb
@@ -995,12 +985,14 @@ files:
995
985
  - db/upgrade_migrations/20170125033319_upgrade_v0_9_to_v0_10.rb
996
986
  - db/upgrade_migrations/20170312131417_upgrade_thredded_v0_10_to_v0_11.rb
997
987
  - db/upgrade_migrations/20170420163138_upgrade_thredded_v0_11_to_v0_12.rb
998
- - db/upgrade_migrations/20170811090735_upgrade_thredded_v0_13_to_v_014.rb
988
+ - db/upgrade_migrations/20170811090735_upgrade_thredded_v0_13_to_v0_14.rb
989
+ - db/upgrade_migrations/20180110200009_upgrade_thredded_v0_14_to_v0_15.rb
999
990
  - lib/generators/thredded/install/USAGE
1000
991
  - lib/generators/thredded/install/install_generator.rb
1001
992
  - lib/generators/thredded/install/templates/initializer.rb
1002
993
  - lib/tasks/thredded_tasks.rake
1003
994
  - lib/thredded.rb
995
+ - lib/thredded/arel_compat.rb
1004
996
  - lib/thredded/base_migration.rb
1005
997
  - lib/thredded/base_notifier.rb
1006
998
  - lib/thredded/collection_to_strings_with_cache_renderer.rb
@@ -1010,6 +1002,7 @@ files:
1010
1002
  - lib/thredded/email_transformer.rb
1011
1003
  - lib/thredded/email_transformer/base.rb
1012
1004
  - lib/thredded/email_transformer/onebox.rb
1005
+ - lib/thredded/email_transformer/spoiler.rb
1013
1006
  - lib/thredded/engine.rb
1014
1007
  - lib/thredded/errors.rb
1015
1008
  - lib/thredded/formatting_demo_content.rb
@@ -1017,7 +1010,10 @@ files:
1017
1010
  - lib/thredded/html_pipeline/autolink_filter.rb
1018
1011
  - lib/thredded/html_pipeline/kramdown_filter.rb
1019
1012
  - lib/thredded/html_pipeline/onebox_filter.rb
1013
+ - lib/thredded/html_pipeline/spoiler_tag_filter.rb
1014
+ - lib/thredded/html_pipeline/utils.rb
1020
1015
  - lib/thredded/html_pipeline/wrap_iframes_filter.rb
1016
+ - lib/thredded/rails_lt_5_2_arel_case_node.rb
1021
1017
  - lib/thredded/version.rb
1022
1018
  - lib/thredded/view_hooks/config.rb
1023
1019
  - lib/thredded/view_hooks/renderer.rb