DEV: Correctly tag heredocs (#16061)

This allows text editors to use correct syntax coloring for the heredoc sections.

Heredoc tag names we use:

languages: SQL, JS, RUBY, LUA, HTML, CSS, SCSS, SH, HBS, XML, YAML/YML, MF, ICS
other: MD, TEXT/TXT, RAW, EMAIL
This commit is contained in:
Jarek Radosz
2022-02-28 20:50:55 +01:00
committed by GitHub
parent 7c4be7f649
commit 2fc70c5572
59 changed files with 392 additions and 392 deletions

View File

@@ -45,14 +45,14 @@ class PostsController < ApplicationController
opts[:limit] = MARKDOWN_TOPIC_PAGE_SIZE opts[:limit] = MARKDOWN_TOPIC_PAGE_SIZE
topic_view = TopicView.new(params[:topic_id], current_user, opts) topic_view = TopicView.new(params[:topic_id], current_user, opts)
content = topic_view.posts.map do |p| content = topic_view.posts.map do |p|
<<~HEREDOC <<~MD
#{p.user.username} | #{p.updated_at} | ##{p.post_number} #{p.user.username} | #{p.updated_at} | ##{p.post_number}
#{p.raw} #{p.raw}
------------------------- -------------------------
HEREDOC MD
end end
render plain: content.join render plain: content.join
end end

View File

@@ -247,7 +247,7 @@ class SessionController < ApplicationController
rescue ActiveRecord::RecordInvalid => e rescue ActiveRecord::RecordInvalid => e
if SiteSetting.verbose_discourse_connect_logging if SiteSetting.verbose_discourse_connect_logging
Rails.logger.warn(<<~EOF) Rails.logger.warn(<<~TEXT)
Verbose SSO log: Record was invalid: #{e.record.class.name} #{e.record.id} Verbose SSO log: Record was invalid: #{e.record.class.name} #{e.record.id}
#{e.record.errors.to_h} #{e.record.errors.to_h}
@@ -256,7 +256,7 @@ class SessionController < ApplicationController
SSO Diagnostics: SSO Diagnostics:
#{sso.diagnostics} #{sso.diagnostics}
EOF TEXT
end end
text = nil text = nil

View File

@@ -303,11 +303,11 @@ module Jobs
# Simulate the args being dumped/parsed through JSON # Simulate the args being dumped/parsed through JSON
parsed_opts = JSON.parse(JSON.dump(opts)) parsed_opts = JSON.parse(JSON.dump(opts))
if opts != parsed_opts if opts != parsed_opts
Discourse.deprecate(<<~MSG.squish, since: "2.9", drop_from: "3.0") Discourse.deprecate(<<~TEXT.squish, since: "2.9", drop_from: "3.0")
#{klass.name} was enqueued with argument values which do not cleanly serialize to/from JSON. #{klass.name} was enqueued with argument values which do not cleanly serialize to/from JSON.
This means that the job will be run with slightly different values than the ones supplied to `enqueue`. This means that the job will be run with slightly different values than the ones supplied to `enqueue`.
Argument values should be strings, booleans, numbers, or nil (or arrays/hashes of those value types). Argument values should be strings, booleans, numbers, or nil (or arrays/hashes of those value types).
MSG TEXT
end end
opts = parsed_opts opts = parsed_opts

View File

@@ -59,7 +59,7 @@ class ReviewableSerializer < ApplicationSerializer
def self.create_attribute(name, field) def self.create_attribute(name, field)
attribute(name) attribute(name)
class_eval <<~GETTER class_eval <<~RUBY
def #{name} def #{name}
#{field} #{field}
end end
@@ -67,7 +67,7 @@ class ReviewableSerializer < ApplicationSerializer
def include_#{name}? def include_#{name}?
#{name}.present? #{name}.present?
end end
GETTER RUBY
end end
# This is easier than creating an AMS method for each attribute # This is easier than creating an AMS method for each attribute

View File

@@ -11,7 +11,7 @@ class UserActionManager
end end
[:notification, :post, :topic, :post_action].each do |type| [:notification, :post, :topic, :post_action].each do |type|
self.class_eval(<<~METHODS) self.class_eval(<<~RUBY)
def self.#{type}_created(*args) def self.#{type}_created(*args)
return if @disabled return if @disabled
#{type}_rows(*args).each { |row| UserAction.log_action!(row) } #{type}_rows(*args).each { |row| UserAction.log_action!(row) }
@@ -20,7 +20,7 @@ class UserActionManager
return if @disabled return if @disabled
#{type}_rows(*args).each { |row| UserAction.remove_action!(row) } #{type}_rows(*args).each { |row| UserAction.remove_action!(row) }
end end
METHODS RUBY
end end
private private

View File

@@ -39,7 +39,7 @@ if ENV["TRACE_PG_CONNECTIONS"]
def log_access(&blk) def log_access(&blk)
@access_log_mutex.synchronize do @access_log_mutex.synchronize do
if !@accessor_thread.nil? if !@accessor_thread.nil?
Rails.logger.error <<~STRING Rails.logger.error <<~TEXT
PG Clash: A connection is being accessed from two locations PG Clash: A connection is being accessed from two locations
#{@accessor_thread} was using the connection. Backtrace: #{@accessor_thread} was using the connection. Backtrace:
@@ -49,7 +49,7 @@ if ENV["TRACE_PG_CONNECTIONS"]
#{Thread.current} is now attempting to use the connection. Backtrace: #{Thread.current} is now attempting to use the connection. Backtrace:
#{Thread.current&.backtrace&.join("\n")} #{Thread.current&.backtrace&.join("\n")}
STRING TEXT
if ENV["ON_PG_CLASH"] == "byebug" if ENV["ON_PG_CLASH"] == "byebug"
require "byebug" require "byebug"

View File

@@ -5,7 +5,7 @@
if defined?(Rails::Server) && Rails.env.production? # Only run these checks when starting up a production server if defined?(Rails::Server) && Rails.env.production? # Only run these checks when starting up a production server
if ['localhost', 'production.localhost'].include?(Discourse.current_hostname) if ['localhost', 'production.localhost'].include?(Discourse.current_hostname)
puts <<END puts <<~TEXT
Discourse.current_hostname = '#{Discourse.current_hostname}' Discourse.current_hostname = '#{Discourse.current_hostname}'
@@ -13,20 +13,20 @@ if defined?(Rails::Server) && Rails.env.production? # Only run these checks when
so that it uses the hostname of your site. Otherwise you will so that it uses the hostname of your site. Otherwise you will
experience problems, like links in emails using #{Discourse.current_hostname}. experience problems, like links in emails using #{Discourse.current_hostname}.
END TEXT
raise "Invalid host_names in database.yml" raise "Invalid host_names in database.yml"
end end
if !Dir.glob(File.join(Rails.root, 'public', 'assets', 'application*.js')).present? if !Dir.glob(File.join(Rails.root, 'public', 'assets', 'application*.js')).present?
puts <<END puts <<~TEXT
Assets have not been precompiled. Please run the following command Assets have not been precompiled. Please run the following command
before starting the rails server in production mode: before starting the rails server in production mode:
rake assets:precompile rake assets:precompile
END TEXT
raise "Assets have not been precompiled" raise "Assets have not been precompiled"
end end

View File

@@ -11,10 +11,10 @@ class FixIncorrectUserHistory < ActiveRecord::Migration[4.2]
# this is a :auto_trust_level_change mislabeled as :check_email # this is a :auto_trust_level_change mislabeled as :check_email
# impersonate that was actually delete topic # impersonate that was actually delete topic
condition = <<CLAUSE condition = <<~SQL
(action = 16 AND previous_value in ('0','1','2','3','4')) OR (action = 16 AND previous_value in ('0','1','2','3','4')) OR
(action = 19 AND target_user_id IS NULL AND details IS NOT NULL) (action = 19 AND target_user_id IS NULL AND details IS NOT NULL)
CLAUSE SQL
first_wrong_id = execute("SELECT min(id) FROM user_histories WHERE #{condition}").values[0][0].to_i first_wrong_id = execute("SELECT min(id) FROM user_histories WHERE #{condition}").values[0][0].to_i
last_wrong_id = execute("SELECT max(id) FROM user_histories WHERE #{condition}").values[0][0].to_i last_wrong_id = execute("SELECT max(id) FROM user_histories WHERE #{condition}").values[0][0].to_i

View File

@@ -1011,13 +1011,13 @@ module Email
def forwarded_email_quote_forwarded(destination, user) def forwarded_email_quote_forwarded(destination, user)
embedded = embedded_email_raw embedded = embedded_email_raw
raw = <<~EOF raw = <<~MD
#{@before_embedded} #{@before_embedded}
[quote] [quote]
#{PlainTextToMarkdown.new(embedded).to_markdown} #{PlainTextToMarkdown.new(embedded).to_markdown}
[/quote] [/quote]
EOF MD
return true if forwarded_email_create_topic(destination: destination, user: user, raw: raw, title: subject) return true if forwarded_email_create_topic(destination: destination, user: user, raw: raw, title: subject)
end end

View File

@@ -18,7 +18,7 @@ class Barber::Precompiler
# very hacky but lets us use ES6. I'm ashamed of this code -RW # very hacky but lets us use ES6. I'm ashamed of this code -RW
transpiled = transpiled[transpiled.index('var RawHandlebars = ')...transpiled.index('export ')] transpiled = transpiled[transpiled.index('var RawHandlebars = ')...transpiled.index('export ')]
@precompiler = StringIO.new <<~END @precompiler = StringIO.new <<~JS
var __RawHandlebars; var __RawHandlebars;
(function() { (function() {
#{transpiled}; #{transpiled};
@@ -30,7 +30,7 @@ class Barber::Precompiler
return __RawHandlebars.precompile(string, false).toString(); return __RawHandlebars.precompile(string, false).toString();
} }
}; };
END JS
end end
@precompiler @precompiler
@@ -111,10 +111,10 @@ class Ember::Handlebars::Template
"define('#{module_name}', ['exports'], function(__exports__){ __exports__['default'] = #{template} });" "define('#{module_name}', ['exports'], function(__exports__){ __exports__['default'] = #{template} });"
when :global when :global
if raw if raw
return <<~RAW_TEMPLATE return <<~JS
var __t = #{template}; var __t = #{template};
requirejs('discourse-common/lib/raw-templates').addRawTemplate(#{path_for(template_name, config)}, __t); requirejs('discourse-common/lib/raw-templates').addRawTemplate(#{path_for(template_name, config)}, __t);
RAW_TEMPLATE JS
end end
target = global_template_target('Ember.TEMPLATES', template_name, config) target = global_template_target('Ember.TEMPLATES', template_name, config)

View File

@@ -121,7 +121,7 @@ class Migration::SafeMigrate
def self.protect!(sql) def self.protect!(sql)
if sql =~ /^\s*(?:drop\s+table|alter\s+table.*rename\s+to)\s+/i if sql =~ /^\s*(?:drop\s+table|alter\s+table.*rename\s+to)\s+/i
$stdout.puts("", <<~STR) $stdout.puts("", <<~TEXT)
WARNING WARNING
------------------------------------------------------------------------------------- -------------------------------------------------------------------------------------
An attempt was made to drop or rename a table in a migration An attempt was made to drop or rename a table in a migration
@@ -131,10 +131,10 @@ class Migration::SafeMigrate
This protection is in place to protect us against dropping tables that are currently This protection is in place to protect us against dropping tables that are currently
in use by live applications. in use by live applications.
STR TEXT
raise Discourse::InvalidMigration, "Attempt was made to drop a table" raise Discourse::InvalidMigration, "Attempt was made to drop a table"
elsif sql =~ /^\s*alter\s+table.*(?:rename|drop)\s+/i elsif sql =~ /^\s*alter\s+table.*(?:rename|drop)\s+/i
$stdout.puts("", <<~STR) $stdout.puts("", <<~TEXT)
WARNING WARNING
------------------------------------------------------------------------------------- -------------------------------------------------------------------------------------
An attempt was made to drop or rename a column in a migration An attempt was made to drop or rename a column in a migration
@@ -148,7 +148,7 @@ class Migration::SafeMigrate
This protection is in place to protect us against dropping columns that are currently This protection is in place to protect us against dropping columns that are currently
in use by live applications. in use by live applications.
STR TEXT
raise Discourse::InvalidMigration, "Attempt was made to rename or delete column" raise Discourse::InvalidMigration, "Attempt was made to rename or delete column"
end end
end end

View File

@@ -218,14 +218,14 @@ module Onebox
def card_html def card_html
escaped_url = ::Onebox::Helpers.normalize_url_for_output(data[:player]) escaped_url = ::Onebox::Helpers.normalize_url_for_output(data[:player])
<<~RAW <<~HTML
<iframe src="#{escaped_url}" <iframe src="#{escaped_url}"
width="#{data[:player_width] || "100%"}" width="#{data[:player_width] || "100%"}"
height="#{data[:player_height]}" height="#{data[:player_height]}"
scrolling="no" scrolling="no"
frameborder="0"> frameborder="0">
</iframe> </iframe>
RAW HTML
end end
def article_html def article_html

View File

@@ -24,7 +24,7 @@ def plugin_initialization_guard(&block)
end end
end.reverse.join("\n") end.reverse.join("\n")
STDERR.puts <<~MESSAGE STDERR.puts <<~TEXT
#{stack_trace} #{stack_trace}
** INCOMPATIBLE PLUGIN ** ** INCOMPATIBLE PLUGIN **
@@ -33,9 +33,9 @@ def plugin_initialization_guard(&block)
#{plugin_path} #{plugin_path}
Please try removing this plugin and rebuilding again! Please try removing this plugin and rebuilding again!
MESSAGE TEXT
else else
STDERR.puts <<~MESSAGE STDERR.puts <<~TEXT
** PLUGIN FAILURE ** ** PLUGIN FAILURE **
You are unable to build Discourse due to this error during plugin You are unable to build Discourse due to this error during plugin
@@ -44,7 +44,7 @@ def plugin_initialization_guard(&block)
#{error} #{error}
#{error.backtrace.join("\n")} #{error.backtrace.join("\n")}
MESSAGE TEXT
end end
exit 1 exit 1
end end

View File

@@ -34,23 +34,23 @@ module Stylesheet
contents = +"" contents = +""
if body_font.present? if body_font.present?
contents << <<~EOF contents << <<~CSS
#{font_css(body_font)} #{font_css(body_font)}
:root { :root {
--font-family: #{body_font[:stack]}; --font-family: #{body_font[:stack]};
} }
EOF CSS
end end
if heading_font.present? if heading_font.present?
contents << <<~EOF contents << <<~CSS
#{font_css(heading_font)} #{font_css(heading_font)}
:root { :root {
--heading-font-family: #{heading_font[:stack]}; --heading-font-family: #{heading_font[:stack]};
} }
EOF CSS
end end
contents contents
@@ -70,14 +70,14 @@ module Stylesheet
end end
contents << font_css(font) contents << font_css(font)
contents << <<~EOF contents << <<~CSS
.body-font-#{font[:key].tr("_", "-")} { .body-font-#{font[:key].tr("_", "-")} {
font-family: #{font[:stack]}; font-family: #{font[:stack]};
} }
.heading-font-#{font[:key].tr("_", "-")} h2 { .heading-font-#{font[:key].tr("_", "-")} h2 {
font-family: #{font[:stack]}; font-family: #{font[:stack]};
} }
EOF CSS
end end
contents contents
@@ -184,11 +184,11 @@ module Stylesheet
fields.map do |field| fields.map do |field|
value = field.value value = field.value
if value.present? if value.present?
contents << <<~COMMENT contents << <<~SCSS
// Theme: #{field.theme.name} // Theme: #{field.theme.name}
// Target: #{field.target_name} #{field.name} // Target: #{field.target_name} #{field.name}
// Last Edited: #{field.updated_at} // Last Edited: #{field.updated_at}
COMMENT SCSS
contents << value contents << value
end end
@@ -216,13 +216,13 @@ module Stylesheet
fonts_dir = UrlHelper.absolute("#{Discourse.base_path}/fonts") fonts_dir = UrlHelper.absolute("#{Discourse.base_path}/fonts")
font[:variants].each do |variant| font[:variants].each do |variant|
src = variant[:src] ? variant[:src] : "url(\"#{fonts_dir}/#{variant[:filename]}?v=#{DiscourseFonts::VERSION}\") format(\"#{variant[:format]}\")" src = variant[:src] ? variant[:src] : "url(\"#{fonts_dir}/#{variant[:filename]}?v=#{DiscourseFonts::VERSION}\") format(\"#{variant[:format]}\")"
contents << <<~EOF contents << <<~CSS
@font-face { @font-face {
font-family: #{font[:name]}; font-family: #{font[:name]};
src: #{src}; src: #{src};
font-weight: #{variant[:weight]}; font-weight: #{variant[:weight]};
} }
EOF CSS
end end
end end

View File

@@ -15,10 +15,10 @@ task 'assets:precompile:before' do
if EMBER_CLI && !(ENV["EMBER_CLI_COMPILE_DONE"] == "1") if EMBER_CLI && !(ENV["EMBER_CLI_COMPILE_DONE"] == "1")
# Using exec to free up Rails app memory during ember build # Using exec to free up Rails app memory during ember build
exec <<~SCRIPT exec <<~SH
NODE_OPTIONS='--max-old-space-size=2048' yarn --cwd app/assets/javascripts/discourse run ember build -prod && \ NODE_OPTIONS='--max-old-space-size=2048' yarn --cwd app/assets/javascripts/discourse run ember build -prod && \
EMBER_CLI_COMPILE_DONE=1 bin/rake assets:precompile EMBER_CLI_COMPILE_DONE=1 bin/rake assets:precompile
SCRIPT SH
end end
# Ensure we ALWAYS do a clean build # Ensure we ALWAYS do a clean build
@@ -124,9 +124,9 @@ def compress_node(from, to)
source_map_url = "#{File.basename(to)}.map" source_map_url = "#{File.basename(to)}.map"
base_source_map = assets_path + assets_additional_path base_source_map = assets_path + assets_additional_path
cmd = <<~EOS cmd = <<~SH
terser '#{assets_path}/#{from}' -m -c -o '#{to_path}' --source-map "base='#{base_source_map}',root='#{source_map_root}',url='#{source_map_url}',includeSources=true" terser '#{assets_path}/#{from}' -m -c -o '#{to_path}' --source-map "base='#{base_source_map}',root='#{source_map_root}',url='#{source_map_url}',includeSources=true"
EOS SH
STDERR.puts cmd STDERR.puts cmd
result = `#{cmd} 2>&1` result = `#{cmd} 2>&1`

View File

@@ -65,7 +65,7 @@ task 'emails:test', [:email] => [:environment] do |_, args|
smtp = Discourse::Application.config.action_mailer.smtp_settings smtp = Discourse::Application.config.action_mailer.smtp_settings
if smtp[:address].match(/smtp\.gmail\.com/) if smtp[:address].match(/smtp\.gmail\.com/)
puts <<~STR puts <<~TEXT
#{smtp} #{smtp}
============================== WARNING ============================== ============================== WARNING ==============================
@@ -75,7 +75,7 @@ task 'emails:test', [:email] => [:environment] do |_, args|
https://meta.discourse.org/t/discourse-aws-ec2-g-suite-troubleshooting/62931?u=pfaffman https://meta.discourse.org/t/discourse-aws-ec2-g-suite-troubleshooting/62931?u=pfaffman
========================= CONTINUING TEST ============================ ========================= CONTINUING TEST ============================
STR TEXT
end end
puts "Testing sending to #{email} using #{smtp[:address]}:#{smtp[:port]}, username:#{smtp[:user_name]} with #{smtp[:authentication]} auth." puts "Testing sending to #{email} using #{smtp[:address]}:#{smtp[:port]}, username:#{smtp[:user_name]} with #{smtp[:authentication]} auth."
@@ -93,21 +93,21 @@ task 'emails:test', [:email] => [:environment] do |_, args|
rescue Exception => e rescue Exception => e
if e.to_s.match(/execution expired/) if e.to_s.match(/execution expired/)
message = <<~STR message = <<~TEXT
======================================== ERROR ======================================== ======================================== ERROR ========================================
Connection to port #{smtp[:port]} failed. Connection to port #{smtp[:port]} failed.
====================================== SOLUTION ======================================= ====================================== SOLUTION =======================================
The most likely problem is that your server has outgoing SMTP traffic blocked. The most likely problem is that your server has outgoing SMTP traffic blocked.
If you are using a service like Mailgun or Sendgrid, try using port 2525. If you are using a service like Mailgun or Sendgrid, try using port 2525.
======================================================================================= =======================================================================================
STR TEXT
elsif e.to_s.match(/530.*STARTTLS/) elsif e.to_s.match(/530.*STARTTLS/)
# We can't run a preliminary test with STARTTLS, we'll just try sending the test email. # We can't run a preliminary test with STARTTLS, we'll just try sending the test email.
message = "OK" message = "OK"
elsif e.to_s.match(/535/) elsif e.to_s.match(/535/)
message = <<~STR message = <<~TEXT
======================================== ERROR ======================================== ======================================== ERROR ========================================
AUTHENTICATION FAILED AUTHENTICATION FAILED
@@ -117,10 +117,10 @@ task 'emails:test', [:email] => [:environment] do |_, args|
The most likely problem is that your SMTP username and/or Password is incorrect. The most likely problem is that your SMTP username and/or Password is incorrect.
Check them and try again. Check them and try again.
======================================================================================= =======================================================================================
STR TEXT
elsif e.to_s.match(/Connection refused/) elsif e.to_s.match(/Connection refused/)
message = <<~STR message = <<~TEXT
======================================== ERROR ======================================== ======================================== ERROR ========================================
CONNECTION REFUSED CONNECTION REFUSED
@@ -132,10 +132,10 @@ task 'emails:test', [:email] => [:environment] do |_, args|
Check the port and your networking configuration. Check the port and your networking configuration.
======================================================================================= =======================================================================================
STR TEXT
elsif e.to_s.match(/service not known/) elsif e.to_s.match(/service not known/)
message = <<~STR message = <<~TEXT
======================================== ERROR ======================================== ======================================== ERROR ========================================
SMTP SERVER NOT FOUND SMTP SERVER NOT FOUND
@@ -145,10 +145,10 @@ task 'emails:test', [:email] => [:environment] do |_, args|
The most likely problem is that the host name of your SMTP server is incorrect. The most likely problem is that the host name of your SMTP server is incorrect.
Check it and try again. Check it and try again.
======================================================================================= =======================================================================================
STR TEXT
else else
message = <<~STR message = <<~TEXT
======================================== ERROR ======================================== ======================================== ERROR ========================================
UNEXPECTED ERROR UNEXPECTED ERROR
@@ -160,7 +160,7 @@ task 'emails:test', [:email] => [:environment] do |_, args|
Please report the exact error message above to https://meta.discourse.org/ Please report the exact error message above to https://meta.discourse.org/
(And a solution, if you find one!) (And a solution, if you find one!)
======================================================================================= =======================================================================================
STR TEXT
end end
end end
if message == "OK" if message == "OK"
@@ -174,13 +174,13 @@ task 'emails:test', [:email] => [:environment] do |_, args|
email_log = Email::Sender.new(TestMailer.send_test(email), :test_message).send email_log = Email::Sender.new(TestMailer.send_test(email), :test_message).send
case email_log case email_log
when SkippedEmailLog when SkippedEmailLog
puts <<~STR puts <<~TEXT
Mail was not sent. Mail was not sent.
Reason: #{email_log.reason} Reason: #{email_log.reason}
STR TEXT
when EmailLog when EmailLog
puts <<~STR puts <<~TEXT
Mail accepted by SMTP server. Mail accepted by SMTP server.
Message-ID: #{email_log.message_id} Message-ID: #{email_log.message_id}
@@ -190,20 +190,20 @@ task 'emails:test', [:email] => [:environment] do |_, args|
If the message is not delivered it is not a problem with Discourse. If the message is not delivered it is not a problem with Discourse.
Check the SMTP server logs for the above Message ID to see why it Check the SMTP server logs for the above Message ID to see why it
failed to deliver the message. failed to deliver the message.
STR TEXT
when nil when nil
puts <<~STR puts <<~TEXT
Mail was not sent. Mail was not sent.
Verify the status of the `disable_emails` site setting. Verify the status of the `disable_emails` site setting.
STR TEXT
else else
puts <<~STR puts <<~TEXT
SCRIPT BUG: Got back a #{email_log.class} SCRIPT BUG: Got back a #{email_log.class}
#{email_log.inspect} #{email_log.inspect}
Mail may or may not have been sent. Check the destination mailbox. Mail may or may not have been sent. Check the destination mailbox.
STR TEXT
end end
rescue => error rescue => error
puts "Sending mail failed." puts "Sending mail failed."
@@ -211,11 +211,11 @@ task 'emails:test', [:email] => [:environment] do |_, args|
end end
if SiteSetting.disable_emails != 'no' if SiteSetting.disable_emails != 'no'
puts <<~STR puts <<~TEXT
### WARNING ### WARNING
The `disable_emails` site setting is currently set to #{SiteSetting.disable_emails}. The `disable_emails` site setting is currently set to #{SiteSetting.disable_emails}.
Consider changing it to 'no' before performing any further troubleshooting. Consider changing it to 'no' before performing any further troubleshooting.
STR TEXT
end end
end end

View File

@@ -22,7 +22,7 @@ def html_for_section(group)
" {{replace-emoji \":#{icon['name']}:\" (hash lazy=true#{class_attr})}}" " {{replace-emoji \":#{icon['name']}:\" (hash lazy=true#{class_attr})}}"
end end
<<~SECTION <<~HTML
<div class="section" data-section="#{group["name"]}"> <div class="section" data-section="#{group["name"]}">
<div class="section-header"> <div class="section-header">
<span class="title">{{i18n "emoji_picker.#{group["name"]}"}}</span> <span class="title">{{i18n "emoji_picker.#{group["name"]}"}}</span>
@@ -31,14 +31,14 @@ def html_for_section(group)
#{icons.join("\n").strip} #{icons.join("\n").strip}
</div> </div>
</div> </div>
SECTION HTML
end end
def write_template(path, task_name, template) def write_template(path, task_name, template)
header = <<~HEADER header = <<~JS
// DO NOT EDIT THIS FILE!!! // DO NOT EDIT THIS FILE!!!
// Update it by running `rake javascript:#{task_name}` // Update it by running `rake javascript:#{task_name}`
HEADER JS
basename = File.basename(path) basename = File.basename(path)
output_path = "#{Rails.root}/app/assets/javascripts/#{path}" output_path = "#{Rails.root}/app/assets/javascripts/#{path}"
@@ -50,10 +50,10 @@ def write_template(path, task_name, template)
end end
def write_hbs_template(path, task_name, template) def write_hbs_template(path, task_name, template)
header = <<~HEADER header = <<~HBS
{{!-- DO NOT EDIT THIS FILE!!! --}} {{!-- DO NOT EDIT THIS FILE!!! --}}
{{!-- Update it by running `rake javascript:#{task_name}` --}} {{!-- Update it by running `rake javascript:#{task_name}` --}}
HEADER HBS
basename = File.basename(path) basename = File.basename(path)
output_path = "#{Rails.root}/app/assets/javascripts/#{path}" output_path = "#{Rails.root}/app/assets/javascripts/#{path}"
@@ -244,11 +244,11 @@ task 'javascript:update_constants' => :environment do
groups_json = JSON.parse(File.read("lib/emoji/groups.json")) groups_json = JSON.parse(File.read("lib/emoji/groups.json"))
emoji_buttons = groups_json.map do |group| emoji_buttons = groups_json.map do |group|
<<~BUTTON <<~HTML
<button type="button" data-section="#{group["name"]}" {{action onCategorySelection "#{group["name"]}"}} class="btn btn-default category-button emoji"> <button type="button" data-section="#{group["name"]}" {{action onCategorySelection "#{group["name"]}"}} class="btn btn-default category-button emoji">
{{replace-emoji ":#{group["tabicon"]}:"}} {{replace-emoji ":#{group["tabicon"]}:"}}
</button> </button>
BUTTON HTML
end end
emoji_sections = groups_json.map { |group| html_for_section(group) } emoji_sections = groups_json.map { |group| html_for_section(group) }

View File

@@ -3,7 +3,7 @@
# Generates posts and topics # Generates posts and topics
class Populate < Thor class Populate < Thor
desc "posts", "Generate posts" desc "posts", "Generate posts"
long_desc <<-LONGDESC long_desc <<-MD
Create topics with any number of posts, or add posts to an existing topic. Create topics with any number of posts, or add posts to an existing topic.
Examples: Examples:
@@ -20,7 +20,7 @@ class Populate < Thor
> $ thor populate:posts -p 10 -n 5 > $ thor populate:posts -p 10 -n 5
LONGDESC MD
method_option :num_posts, aliases: '-n', type: :numeric, required: true, desc: "Number of posts to make" method_option :num_posts, aliases: '-n', type: :numeric, required: true, desc: "Number of posts to make"
method_option :users, aliases: '-u', type: :array, desc: "Usernames of users who will make the posts" method_option :users, aliases: '-u', type: :array, desc: "Usernames of users who will make the posts"
method_option :title, aliases: '-t', desc: "The title of the topic, if making a new topic" method_option :title, aliases: '-t', desc: "The title of the topic, if making a new topic"

View File

@@ -118,7 +118,7 @@ task "themes:qunit", :type, :value do |t, args|
type = args[:type] type = args[:type]
value = args[:value] value = args[:value]
if !%w(name url id).include?(type) || value.blank? if !%w(name url id).include?(type) || value.blank?
raise <<~MSG raise <<~TEXT
Wrong arguments type:#{type.inspect}, value:#{value.inspect}" Wrong arguments type:#{type.inspect}, value:#{value.inspect}"
Usage: Usage:
`bundle exec rake "themes:qunit[url,<theme_url>]"` `bundle exec rake "themes:qunit[url,<theme_url>]"`
@@ -126,7 +126,7 @@ task "themes:qunit", :type, :value do |t, args|
`bundle exec rake "themes:qunit[name,<theme_name>]"` `bundle exec rake "themes:qunit[name,<theme_name>]"`
OR OR
`bundle exec rake "themes:qunit[id,<theme_id>]"` `bundle exec rake "themes:qunit[id,<theme_id>]"`
MSG TEXT
end end
ENV["THEME_#{type.upcase}"] = value.to_s ENV["THEME_#{type.upcase}"] = value.to_s
ENV["QUNIT_RAILS_ENV"] ||= 'development' # qunit:test will switch to `test` by default ENV["QUNIT_RAILS_ENV"] ||= 'development' # qunit:test will switch to `test` by default

View File

@@ -165,11 +165,11 @@ def clean_up_uploads
exit 1 exit 1
end end
puts <<~OUTPUT puts <<~TEXT
This task will remove upload records and files permanently. This task will remove upload records and files permanently.
Would you like to take a full backup before the clean up? (Y/N) Would you like to take a full backup before the clean up? (Y/N)
OUTPUT TEXT
if STDIN.gets.chomp.downcase == 'y' if STDIN.gets.chomp.downcase == 'y'
puts "Starting backup..." puts "Starting backup..."
@@ -420,7 +420,7 @@ task "uploads:analyze", [:cache_path, :limit] => :environment do |_, args|
uploads_count = Upload.count uploads_count = Upload.count
optimized_images_count = OptimizedImage.count optimized_images_count = OptimizedImage.count
puts <<~REPORT puts <<~TEXT
Report for '#{current_db}' Report for '#{current_db}'
-----------#{'-' * current_db.length} -----------#{'-' * current_db.length}
Number of `Upload` records in DB: #{uploads_count} Number of `Upload` records in DB: #{uploads_count}
@@ -430,7 +430,7 @@ task "uploads:analyze", [:cache_path, :limit] => :environment do |_, args|
Number of images in uploads folder: #{paths_count} Number of images in uploads folder: #{paths_count}
------------------------------------#{'-' * paths_count.to_s.length} ------------------------------------#{'-' * paths_count.to_s.length}
REPORT TEXT
helper = Class.new do helper = Class.new do
include ActionView::Helpers::NumberHelper include ActionView::Helpers::NumberHelper

View File

@@ -28,13 +28,13 @@ describe PrettyText do
end end
it 'can replace spoilers in emails' do it 'can replace spoilers in emails' do
md = PrettyText.cook(<<~EOF) md = PrettyText.cook(<<~MD)
hello hello
[details="Summary"] [details="Summary"]
world world
[/details] [/details]
EOF MD
md = PrettyText.format_for_email(md, post) md = PrettyText.format_for_email(md, post)
html = "<p>hello</p>\n\nSummary <a href=\"#{post.full_url}\">(click for more details)</a>" html = "<p>hello</p>\n\nSummary <a href=\"#{post.full_url}\">(click for more details)</a>"
@@ -42,7 +42,7 @@ describe PrettyText do
end end
it 'properly handles multiple spoiler blocks in a post' do it 'properly handles multiple spoiler blocks in a post' do
md = PrettyText.cook(<<~EOF) md = PrettyText.cook(<<~MD)
[details="First"] [details="First"]
body secret stuff very long body secret stuff very long
[/details] [/details]
@@ -55,7 +55,7 @@ describe PrettyText do
[details="Third"] [details="Third"]
body secret stuff very long body secret stuff very long
[/details] [/details]
EOF MD
md = PrettyText.format_for_email(md, post) md = PrettyText.format_for_email(md, post)
expect(md).not_to include('secret stuff') expect(md).not_to include('secret stuff')
@@ -65,12 +65,12 @@ describe PrettyText do
end end
it 'escapes summary text' do it 'escapes summary text' do
md = PrettyText.cook(<<~EOF) md = PrettyText.cook(<<~MD)
<script>alert('hello')</script> <script>alert('hello')</script>
[details="<script>alert('hello')</script>"] [details="<script>alert('hello')</script>"]
<script>alert('hello')</script> <script>alert('hello')</script>
[/details] [/details]
EOF MD
md = PrettyText.format_for_email(md, post) md = PrettyText.format_for_email(md, post)
expect(md).not_to include('<script>') expect(md).not_to include('<script>')

View File

@@ -8,9 +8,9 @@ RSpec.describe "Local Dates" do
end end
it "should work without timezone" do it "should work without timezone" do
post = Fabricate(:post, raw: <<~TXT) post = Fabricate(:post, raw: <<~MD)
[date=2018-05-08 time=22:00 format="L LTS" timezones="Europe/Paris|America/Los_Angeles"] [date=2018-05-08 time=22:00 format="L LTS" timezones="Europe/Paris|America/Los_Angeles"]
TXT MD
cooked = post.cooked cooked = post.cooked
@@ -28,9 +28,9 @@ RSpec.describe "Local Dates" do
end end
it "should work with timezone" do it "should work with timezone" do
post = Fabricate(:post, raw: <<~TXT) post = Fabricate(:post, raw: <<~MD)
[date=2018-05-08 time=22:00 format="L LTS" timezone="Asia/Calcutta" timezones="Europe/Paris|America/Los_Angeles"] [date=2018-05-08 time=22:00 format="L LTS" timezone="Asia/Calcutta" timezones="Europe/Paris|America/Los_Angeles"]
TXT MD
cooked = post.cooked cooked = post.cooked
@@ -39,9 +39,9 @@ RSpec.describe "Local Dates" do
end end
it 'requires the right attributes to convert to a local date' do it 'requires the right attributes to convert to a local date' do
post = Fabricate(:post, raw: <<~TXT) post = Fabricate(:post, raw: <<~MD)
[date] [date]
TXT MD
cooked = post.cooked cooked = post.cooked
@@ -50,9 +50,9 @@ RSpec.describe "Local Dates" do
end end
it 'requires the right attributes to convert to a local date' do it 'requires the right attributes to convert to a local date' do
post = Fabricate(:post, raw: <<~TXT) post = Fabricate(:post, raw: <<~MD)
[date] [date]
TXT MD
cooked = post.cooked cooked = post.cooked

View File

@@ -163,11 +163,11 @@ module DiscourseNarrativeBot
def start_advanced_track def start_advanced_track
raw = I18n.t("#{I18N_KEY}.start_message", i18n_post_args(username: @user.username)) raw = I18n.t("#{I18N_KEY}.start_message", i18n_post_args(username: @user.username))
raw = <<~RAW raw = <<~MD
#{raw} #{raw}
#{instance_eval(&@next_instructions)} #{instance_eval(&@next_instructions)}
RAW MD
opts = { opts = {
title: I18n.t("#{I18N_KEY}.title"), title: I18n.t("#{I18N_KEY}.title"),
@@ -197,11 +197,11 @@ module DiscourseNarrativeBot
fake_delay fake_delay
raw = <<~RAW raw = <<~MD
#{I18n.t("#{I18N_KEY}.edit.reply", i18n_post_args)} #{I18n.t("#{I18N_KEY}.edit.reply", i18n_post_args)}
#{instance_eval(&@next_instructions)} #{instance_eval(&@next_instructions)}
RAW MD
reply_to(@post, raw) reply_to(@post, raw)
end end
@@ -227,11 +227,11 @@ module DiscourseNarrativeBot
fake_delay fake_delay
raw = <<~RAW raw = <<~MD
#{I18n.t("#{I18N_KEY}.delete.reply", i18n_post_args)} #{I18n.t("#{I18N_KEY}.delete.reply", i18n_post_args)}
#{instance_eval(&@next_instructions)} #{instance_eval(&@next_instructions)}
RAW MD
PostCreator.create!(self.discobot_user, PostCreator.create!(self.discobot_user,
raw: raw, raw: raw,
@@ -252,11 +252,11 @@ module DiscourseNarrativeBot
fake_delay fake_delay
raw = <<~RAW raw = <<~MD
#{I18n.t("#{I18N_KEY}.recover.reply", i18n_post_args(deletion_after: SiteSetting.delete_removed_posts_after))} #{I18n.t("#{I18N_KEY}.recover.reply", i18n_post_args(deletion_after: SiteSetting.delete_removed_posts_after))}
#{instance_eval(&@next_instructions)} #{instance_eval(&@next_instructions)}
RAW MD
PostCreator.create!(self.discobot_user, PostCreator.create!(self.discobot_user,
raw: raw, raw: raw,
@@ -279,11 +279,11 @@ module DiscourseNarrativeBot
return unless valid_topic?(topic_id) return unless valid_topic?(topic_id)
if Nokogiri::HTML5.fragment(@post.cooked).css('.hashtag').size > 0 if Nokogiri::HTML5.fragment(@post.cooked).css('.hashtag').size > 0
raw = <<~RAW raw = <<~MD
#{I18n.t("#{I18N_KEY}.category_hashtag.reply", i18n_post_args)} #{I18n.t("#{I18N_KEY}.category_hashtag.reply", i18n_post_args)}
#{instance_eval(&@next_instructions)} #{instance_eval(&@next_instructions)}
RAW MD
fake_delay fake_delay
reply_to(@post, raw) reply_to(@post, raw)
@@ -308,11 +308,11 @@ module DiscourseNarrativeBot
return unless valid_topic?(@topic_id) return unless valid_topic?(@topic_id)
fake_delay fake_delay
raw = <<~RAW raw = <<~MD
#{I18n.t("#{I18N_KEY}.change_topic_notification_level.reply", i18n_post_args)} #{I18n.t("#{I18N_KEY}.change_topic_notification_level.reply", i18n_post_args)}
#{instance_eval(&@next_instructions)} #{instance_eval(&@next_instructions)}
RAW MD
fake_delay fake_delay
@@ -330,11 +330,11 @@ module DiscourseNarrativeBot
return unless valid_topic?(topic_id) return unless valid_topic?(topic_id)
if Nokogiri::HTML5.fragment(@post.cooked).css(".poll").size > 0 if Nokogiri::HTML5.fragment(@post.cooked).css(".poll").size > 0
raw = <<~RAW raw = <<~MD
#{I18n.t("#{I18N_KEY}.poll.reply", i18n_post_args)} #{I18n.t("#{I18N_KEY}.poll.reply", i18n_post_args)}
#{instance_eval(&@next_instructions)} #{instance_eval(&@next_instructions)}
RAW MD
fake_delay fake_delay
reply_to(@post, raw) reply_to(@post, raw)

View File

@@ -175,11 +175,11 @@ module DiscourseNarrativeBot
MessageBus.publish('/new_user_narrative/tutorial_search', {}, user_ids: [@user.id]) MessageBus.publish('/new_user_narrative/tutorial_search', {}, user_ids: [@user.id])
raw = <<~RAW raw = <<~MD
#{post.raw} #{post.raw}
#{I18n.t("#{I18N_KEY}.search.hidden_message", i18n_post_args.merge(search_answer: NewUserNarrative.search_answer))} #{I18n.t("#{I18N_KEY}.search.hidden_message", i18n_post_args.merge(search_answer: NewUserNarrative.search_answer))}
RAW MD
PostRevisor.new(post, topic).revise!( PostRevisor.new(post, topic).revise!(
self.discobot_user, self.discobot_user,
@@ -206,11 +206,11 @@ module DiscourseNarrativeBot
) )
) )
raw = <<~RAW raw = <<~MD
#{raw} #{raw}
#{instance_eval(&@next_instructions)} #{instance_eval(&@next_instructions)}
RAW MD
title = I18n.t("#{I18N_KEY}.hello.title", title: SiteSetting.title) title = I18n.t("#{I18N_KEY}.hello.title", title: SiteSetting.title)
if SiteSetting.max_emojis_in_title == 0 if SiteSetting.max_emojis_in_title == 0
@@ -259,11 +259,11 @@ module DiscourseNarrativeBot
profile_page_url = url_helpers(:user_url, username: @user.username) profile_page_url = url_helpers(:user_url, username: @user.username)
bookmark_url = "#{profile_page_url}/activity/bookmarks" bookmark_url = "#{profile_page_url}/activity/bookmarks"
raw = <<~RAW raw = <<~MD
#{I18n.t("#{I18N_KEY}.bookmark.reply", i18n_post_args(bookmark_url: bookmark_url))} #{I18n.t("#{I18N_KEY}.bookmark.reply", i18n_post_args(bookmark_url: bookmark_url))}
#{instance_eval(&@next_instructions)} #{instance_eval(&@next_instructions)}
RAW MD
fake_delay fake_delay
@@ -279,11 +279,11 @@ module DiscourseNarrativeBot
@post.post_analyzer.cook(@post.raw, {}) @post.post_analyzer.cook(@post.raw, {})
if @post.post_analyzer.found_oneboxes? if @post.post_analyzer.found_oneboxes?
raw = <<~RAW raw = <<~MD
#{I18n.t("#{I18N_KEY}.onebox.reply", i18n_post_args)} #{I18n.t("#{I18N_KEY}.onebox.reply", i18n_post_args)}
#{instance_eval(&@next_instructions)} #{instance_eval(&@next_instructions)}
RAW MD
fake_delay fake_delay
@@ -315,11 +315,11 @@ module DiscourseNarrativeBot
fake_delay fake_delay
like_post(post) like_post(post)
raw = <<~RAW raw = <<~MD
#{I18n.t("#{I18N_KEY}.images.reply", i18n_post_args)} #{I18n.t("#{I18N_KEY}.images.reply", i18n_post_args)}
#{instance_eval(&@next_instructions)} #{instance_eval(&@next_instructions)}
RAW MD
reply = reply_to(@post, raw) reply = reply_to(@post, raw)
enqueue_timeout_job(@user) enqueue_timeout_job(@user)
@@ -350,11 +350,11 @@ module DiscourseNarrativeBot
set_state_data(:post_id, @post.id) set_state_data(:post_id, @post.id)
if get_state_data(:liked) if get_state_data(:liked)
raw = <<~RAW raw = <<~MD
#{I18n.t("#{I18N_KEY}.images.reply", i18n_post_args)} #{I18n.t("#{I18N_KEY}.images.reply", i18n_post_args)}
#{instance_eval(&@next_instructions)} #{instance_eval(&@next_instructions)}
RAW MD
like_post(@post) like_post(@post)
else else
@@ -405,11 +405,11 @@ module DiscourseNarrativeBot
) )
if post_liked if post_liked
raw = <<~RAW raw = <<~MD
#{I18n.t("#{I18N_KEY}.likes.reply", i18n_post_args)} #{I18n.t("#{I18N_KEY}.likes.reply", i18n_post_args)}
#{instance_eval(&@next_instructions)} #{instance_eval(&@next_instructions)}
RAW MD
fake_delay fake_delay
@@ -426,11 +426,11 @@ module DiscourseNarrativeBot
return unless valid_topic?(post_topic_id) return unless valid_topic?(post_topic_id)
if Nokogiri::HTML5.fragment(@post.cooked).css("b", "strong", "em", "i", ".bbcode-i", ".bbcode-b").size > 0 if Nokogiri::HTML5.fragment(@post.cooked).css("b", "strong", "em", "i", ".bbcode-i", ".bbcode-b").size > 0
raw = <<~RAW raw = <<~MD
#{I18n.t("#{I18N_KEY}.formatting.reply", i18n_post_args)} #{I18n.t("#{I18N_KEY}.formatting.reply", i18n_post_args)}
#{instance_eval(&@next_instructions)} #{instance_eval(&@next_instructions)}
RAW MD
fake_delay fake_delay
@@ -452,11 +452,11 @@ module DiscourseNarrativeBot
doc = Nokogiri::HTML5.fragment(@post.cooked) doc = Nokogiri::HTML5.fragment(@post.cooked)
if doc.css(".quote").size > 0 if doc.css(".quote").size > 0
raw = <<~RAW raw = <<~MD
#{I18n.t("#{I18N_KEY}.quoting.reply", i18n_post_args)} #{I18n.t("#{I18N_KEY}.quoting.reply", i18n_post_args)}
#{instance_eval(&@next_instructions)} #{instance_eval(&@next_instructions)}
RAW MD
fake_delay fake_delay
@@ -478,11 +478,11 @@ module DiscourseNarrativeBot
doc = Nokogiri::HTML5.fragment(@post.cooked) doc = Nokogiri::HTML5.fragment(@post.cooked)
if doc.css(".emoji").size > 0 if doc.css(".emoji").size > 0
raw = <<~RAW raw = <<~MD
#{I18n.t("#{I18N_KEY}.emoji.reply", i18n_post_args)} #{I18n.t("#{I18N_KEY}.emoji.reply", i18n_post_args)}
#{instance_eval(&@next_instructions)} #{instance_eval(&@next_instructions)}
RAW MD
fake_delay fake_delay
@@ -502,11 +502,11 @@ module DiscourseNarrativeBot
return unless valid_topic?(post_topic_id) return unless valid_topic?(post_topic_id)
if bot_mentioned?(@post) if bot_mentioned?(@post)
raw = <<~RAW raw = <<~MD
#{I18n.t("#{I18N_KEY}.mention.reply", i18n_post_args)} #{I18n.t("#{I18N_KEY}.mention.reply", i18n_post_args)}
#{instance_eval(&@next_instructions)} #{instance_eval(&@next_instructions)}
RAW MD
fake_delay fake_delay
@@ -552,11 +552,11 @@ module DiscourseNarrativeBot
return unless valid_topic?(post_topic_id) return unless valid_topic?(post_topic_id)
return unless @post.user.id == -2 return unless @post.user.id == -2
raw = <<~RAW raw = <<~MD
#{I18n.t("#{I18N_KEY}.flag.reply", i18n_post_args)} #{I18n.t("#{I18N_KEY}.flag.reply", i18n_post_args)}
#{instance_eval(&@next_instructions)} #{instance_eval(&@next_instructions)}
RAW MD
fake_delay fake_delay

View File

@@ -36,7 +36,7 @@ class Onebox::Engine::YoutubeOnebox
# Put in the LazyYT div instead of the iframe # Put in the LazyYT div instead of the iframe
escaped_title = ERB::Util.html_escape(video_title) escaped_title = ERB::Util.html_escape(video_title)
<<~EOF <<~HTML
<div class="onebox lazyYT lazyYT-container" <div class="onebox lazyYT lazyYT-container"
data-youtube-id="#{video_id}" data-youtube-id="#{video_id}"
data-youtube-title="#{escaped_title}" data-youtube-title="#{escaped_title}"
@@ -49,7 +49,7 @@ class Onebox::Engine::YoutubeOnebox
title="#{escaped_title}"> title="#{escaped_title}">
</a> </a>
</div> </div>
EOF HTML
else else
yt_onebox_to_html yt_onebox_to_html
end end

View File

@@ -39,12 +39,12 @@ describe NewPostManager do
end end
it 're-validates the poll when the approve_post event is triggered' do it 're-validates the poll when the approve_post event is triggered' do
invalid_raw_poll = <<~RAW invalid_raw_poll = <<~MD
[poll type=multiple min=0] [poll type=multiple min=0]
* 1 * 1
* 2 * 2
[/poll] [/poll]
RAW MD
result = NewPostManager.new(user, params).perform result = NewPostManager.new(user, params).perform

View File

@@ -1,12 +1,12 @@
# frozen_string_literal: true # frozen_string_literal: true
require "pg" require "pg"
usage = <<-END usage = <<-TEXT
Commands: Commands:
ruby db_timestamp_updater.rb yesterday <date> move all timestamps by x days so that <date> will be moved to yesterday ruby db_timestamp_updater.rb yesterday <date> move all timestamps by x days so that <date> will be moved to yesterday
ruby db_timestamp_updater.rb 100 move all timestamps forward by 100 days ruby db_timestamp_updater.rb 100 move all timestamps forward by 100 days
ruby db_timestamp_updater.rb -100 move all timestamps backward by 100 days ruby db_timestamp_updater.rb -100 move all timestamps backward by 100 days
END TEXT
class TimestampsUpdater class TimestampsUpdater
def initialize(schema, ignore_tables) def initialize(schema, ignore_tables)

View File

@@ -9,7 +9,7 @@ class DiscourseCLI < Thor
end end
desc "remap [--global,--regex] FROM TO", "Remap a string sequence across all tables" desc "remap [--global,--regex] FROM TO", "Remap a string sequence across all tables"
long_desc <<-LONGDESC long_desc <<-TEXT
Replace a string sequence FROM with TO across all tables. Replace a string sequence FROM with TO across all tables.
With --global option, the remapping is run on ***ALL*** With --global option, the remapping is run on ***ALL***
@@ -30,7 +30,7 @@ class DiscourseCLI < Thor
discourse remap talk.foo.com talk.bar.com # renaming a Discourse domain name discourse remap talk.foo.com talk.bar.com # renaming a Discourse domain name
discourse remap --regex "\[\/?color(=[^\]]*)*]" "" # removing "color" bbcodes discourse remap --regex "\[\/?color(=[^\]]*)*]" "" # removing "color" bbcodes
LONGDESC TEXT
option :global, type: :boolean option :global, type: :boolean
option :regex, type: :boolean option :regex, type: :boolean
def remap(from, to) def remap(from, to)

View File

@@ -85,11 +85,11 @@ def crawl_topics
begin begin
if start == 1 && find("h2").text == "Error 403" if start == 1 && find("h2").text == "Error 403"
exit_with_error(<<~MSG.red.bold) exit_with_error(<<~TEXT.red.bold)
Unable to find topics. Try running the script with the "--domain example.com" Unable to find topics. Try running the script with the "--domain example.com"
option if you are a G Suite user and your group's URL contains a path with option if you are a G Suite user and your group's URL contains a path with
your domain that looks like "/a/example.com". your domain that looks like "/a/example.com".
MSG TEXT
end end
rescue Selenium::WebDriver::Error::NoSuchElementError rescue Selenium::WebDriver::Error::NoSuchElementError
# Ignore this error. It simply means there wasn't an error. # Ignore this error. It simply means there wasn't an error.
@@ -151,10 +151,10 @@ def crawl_message(url, might_be_deleted)
@first_message_checked = true @first_message_checked = true
if content.match?(/From:.*\.\.\.@.*/i) && !@force_import if content.match?(/From:.*\.\.\.@.*/i) && !@force_import
exit_with_error(<<~MSG.red.bold) exit_with_error(<<~TEXT.red.bold)
It looks like you do not have permissions to see email addresses. Aborting. It looks like you do not have permissions to see email addresses. Aborting.
Use the --force option to import anyway. Use the --force option to import anyway.
MSG TEXT
end end
end end

View File

@@ -114,28 +114,28 @@ class ImportScripts::IpboardSQL < ImportScripts::Base
rescue Exception => e rescue Exception => e
puts '=' * 50 puts '=' * 50
puts e.message puts e.message
puts <<EOM puts <<~TEXT
Cannot log in to database. Cannot log in to database.
Hostname: #{DB_HOST} Hostname: #{DB_HOST}
Username: #{DB_USER} Username: #{DB_USER}
Password: #{DB_PW} Password: #{DB_PW}
database: #{DB_NAME} database: #{DB_NAME}
You should set these variables: You should set these variables:
export DB_HOST="localhost" export DB_HOST="localhost"
export DB_NAME="ipboard" export DB_NAME="ipboard"
export DB_PW="ipboard" export DB_PW="ipboard"
export DB_USER="ipboard" export DB_USER="ipboard"
export TABLE_PREFIX="ipb_" export TABLE_PREFIX="ipb_"
export IMPORT_AFTER="1970-01-01" export IMPORT_AFTER="1970-01-01"
export URL="http://example.com" export URL="http://example.com"
export UPLOADS= export UPLOADS=
export USERDIR="user" export USERDIR="user"
Exiting. Exiting.
EOM TEXT
exit exit
end end
end end

View File

@@ -43,25 +43,25 @@ class ImportScripts::Modx < ImportScripts::Base
rescue Exception => e rescue Exception => e
puts '=' * 50 puts '=' * 50
puts e.message puts e.message
puts <<EOM puts <<~TEXT
Cannot connect in to database. Cannot connect in to database.
Hostname: #{DB_HOST} Hostname: #{DB_HOST}
Username: #{DB_USER} Username: #{DB_USER}
Password: #{DB_PW} Password: #{DB_PW}
database: #{DB_NAME} database: #{DB_NAME}
Edit the script or set these environment variables: Edit the script or set these environment variables:
export DB_HOST="localhost" export DB_HOST="localhost"
export DB_NAME="modx" export DB_NAME="modx"
export DB_PW="modx" export DB_PW="modx"
export DB_USER="modx" export DB_USER="modx"
export TABLE_PREFIX="modx_" export TABLE_PREFIX="modx_"
export ATTACHMENT_DIR '/path/to/your/attachment/folder' export ATTACHMENT_DIR '/path/to/your/attachment/folder'
Exiting. Exiting.
EOM TEXT
exit exit
end end
@@ -446,12 +446,12 @@ FROM #{TABLE_PREFIX}discuss_users
# keep track of closed topics # keep track of closed topics
closed_topic_ids = [] closed_topic_ids = []
topics = mysql_query <<-MYSQL topics = mysql_query <<-SQL
SELECT t.threadid threadid, firstpostid, open SELECT t.threadid threadid, firstpostid, open
FROM #{TABLE_PREFIX}thread t FROM #{TABLE_PREFIX}thread t
JOIN #{TABLE_PREFIX}post p ON p.postid = t.firstpostid JOIN #{TABLE_PREFIX}post p ON p.postid = t.firstpostid
ORDER BY t.threadid ORDER BY t.threadid
MYSQL SQL
topics.each do |topic| topics.each do |topic|
topic_id = "thread-#{topic["threadid"]}" topic_id = "thread-#{topic["threadid"]}"
closed_topic_ids << topic_id if topic["open"] == 0 closed_topic_ids << topic_id if topic["open"] == 0

View File

@@ -58,27 +58,27 @@ class ImportScripts::MylittleforumSQL < ImportScripts::Base
rescue Exception => e rescue Exception => e
puts '=' * 50 puts '=' * 50
puts e.message puts e.message
puts <<EOM puts <<~TEXT
Cannot log in to database. Cannot log in to database.
Hostname: #{DB_HOST} Hostname: #{DB_HOST}
Username: #{DB_USER} Username: #{DB_USER}
Password: #{DB_PW} Password: #{DB_PW}
database: #{DB_NAME} database: #{DB_NAME}
You should set these variables: You should set these variables:
export DB_HOST="localhost" export DB_HOST="localhost"
export DB_NAME="mylittleforum" export DB_NAME="mylittleforum"
export DB_PW="" export DB_PW=""
export DB_USER="root" export DB_USER="root"
export TABLE_PREFIX="forum_" export TABLE_PREFIX="forum_"
export IMPORT_AFTER="1970-01-01" export IMPORT_AFTER="1970-01-01"
export IMAGE_BASE="http://www.example.com/forum" export IMAGE_BASE="http://www.example.com/forum"
export BASE="forum" export BASE="forum"
Exiting. Exiting.
EOM TEXT
exit exit
end end
end end

View File

@@ -26,10 +26,10 @@ module ImportScripts::PhpBB3
require_relative 'database_3_1' require_relative 'database_3_1'
Database_3_1.new(@database_client, @database_settings) Database_3_1.new(@database_client, @database_settings)
else else
raise UnsupportedVersionError, <<~MSG raise UnsupportedVersionError, <<~TEXT
Unsupported version (#{version}) of phpBB detected. Unsupported version (#{version}) of phpBB detected.
Currently only version 3.0, 3.1 and 3.2 are supported by this importer. Currently only version 3.0, 3.1 and 3.2 are supported by this importer.
MSG TEXT
end end
end end

View File

@@ -53,25 +53,25 @@ class ImportScripts::VBulletin < ImportScripts::Base
rescue Exception => e rescue Exception => e
puts '=' * 50 puts '=' * 50
puts e.message puts e.message
puts <<EOM puts <<~TEXT
Cannot connect in to database. Cannot connect in to database.
Hostname: #{DB_HOST} Hostname: #{DB_HOST}
Username: #{DB_USER} Username: #{DB_USER}
Password: #{DB_PW} Password: #{DB_PW}
database: #{DB_NAME} database: #{DB_NAME}
Edit the script or set these environment variables: Edit the script or set these environment variables:
export DB_HOST="localhost" export DB_HOST="localhost"
export DB_NAME="vbulletin" export DB_NAME="vbulletin"
export DB_PW="" export DB_PW=""
export DB_USER="root" export DB_USER="root"
export TABLE_PREFIX="vb_" export TABLE_PREFIX="vb_"
export ATTACHMENT_DIR '/path/to/your/attachment/folder' export ATTACHMENT_DIR '/path/to/your/attachment/folder'
Exiting. Exiting.
EOM TEXT
exit exit
end end
@@ -674,12 +674,12 @@ EOM
# keep track of closed topics # keep track of closed topics
closed_topic_ids = [] closed_topic_ids = []
topics = mysql_query <<-MYSQL topics = mysql_query <<-SQL
SELECT t.threadid threadid, firstpostid, open SELECT t.threadid threadid, firstpostid, open
FROM #{TABLE_PREFIX}thread t FROM #{TABLE_PREFIX}thread t
JOIN #{TABLE_PREFIX}post p ON p.postid = t.firstpostid JOIN #{TABLE_PREFIX}post p ON p.postid = t.firstpostid
ORDER BY t.threadid ORDER BY t.threadid
MYSQL SQL
topics.each do |topic| topics.each do |topic|
topic_id = "thread-#{topic["threadid"]}" topic_id = "thread-#{topic["threadid"]}"
closed_topic_ids << topic_id if topic["open"] == 0 closed_topic_ids << topic_id if topic["open"] == 0

View File

@@ -100,9 +100,9 @@ end
puts 'Done! File moves are staged and ready for commit.' puts 'Done! File moves are staged and ready for commit.'
puts 'Suggested commit message:' puts 'Suggested commit message:'
puts '-' * 20 puts '-' * 20
puts <<~MESSAGE puts <<~TEXT
DEV: Promote historic post_deploy migrations DEV: Promote historic post_deploy migrations
This commit promotes all post_deploy migrations which existed in Discourse #{previous_stable_version} (timestamp <= #{promote_threshold}) This commit promotes all post_deploy migrations which existed in Discourse #{previous_stable_version} (timestamp <= #{promote_threshold})
MESSAGE TEXT
puts '-' * 20 puts '-' * 20

View File

@@ -8,7 +8,7 @@ Fabricator(:incoming_email) do
imap_sync false imap_sync false
created_via 0 created_via 0
raw <<~RAW raw <<~EMAIL
Return-Path: <foo@example.com> Return-Path: <foo@example.com>
From: Foo <foo@example.com> From: Foo <foo@example.com>
To: someone@else.com To: someone@else.com
@@ -20,5 +20,5 @@ Fabricator(:incoming_email) do
Content-Transfer-Encoding: quoted-printable Content-Transfer-Encoding: quoted-printable
The body contains "Hello world" too. The body contains "Hello world" too.
RAW EMAIL
end end

View File

@@ -102,20 +102,20 @@ Fabricator(:post_with_uploads, from: :post) do
end end
Fabricator(:post_with_uploads_and_links, from: :post) do Fabricator(:post_with_uploads_and_links, from: :post) do
raw <<~RAW raw <<~MD
<a href="/#{Discourse.store.upload_path}/original/2X/2345678901234567.jpg">Link</a> <a href="/#{Discourse.store.upload_path}/original/2X/2345678901234567.jpg">Link</a>
<img src="/#{Discourse.store.upload_path}/original/1X/1234567890123456.jpg"> <img src="/#{Discourse.store.upload_path}/original/1X/1234567890123456.jpg">
<a href="http://www.google.com">Google</a> <a href="http://www.google.com">Google</a>
<img src="http://foo.bar/image.png"> <img src="http://foo.bar/image.png">
<a class="attachment" href="/#{Discourse.store.upload_path}/original/1X/af2c2618032c679333bebf745e75f9088748d737.txt">text.txt</a> (20 Bytes) <a class="attachment" href="/#{Discourse.store.upload_path}/original/1X/af2c2618032c679333bebf745e75f9088748d737.txt">text.txt</a> (20 Bytes)
:smile: :smile:
RAW MD
end end
Fabricator(:post_with_external_links, from: :post) do Fabricator(:post_with_external_links, from: :post) do
user user
topic topic
raw <<~RAW raw <<~MD
Here's a link to twitter: http://twitter.com Here's a link to twitter: http://twitter.com
And a link to google: http://google.com And a link to google: http://google.com
And a secure link to google: https://google.com And a secure link to google: https://google.com
@@ -123,7 +123,7 @@ Fabricator(:post_with_external_links, from: :post) do
And a markdown link with a period after it [codinghorror](http://www.codinghorror.com/blog). And a markdown link with a period after it [codinghorror](http://www.codinghorror.com/blog).
And one with a hash http://discourse.org#faq And one with a hash http://discourse.org#faq
And one with a two hash http://discourse.org#a#b And one with a two hash http://discourse.org#a#b
RAW MD
end end
Fabricator(:private_message_post, from: :post) do Fabricator(:private_message_post, from: :post) do

View File

@@ -25,13 +25,13 @@ describe 'Coding style' do
js_files = list_js_files('app/assets/javascripts') js_files = list_js_files('app/assets/javascripts')
offenses = grep_files(js_files, /this\.get\("\w+"\)/) offenses = grep_files(js_files, /this\.get\("\w+"\)/)
expect(offenses).to be_empty, <<~MSG expect(offenses).to be_empty, <<~TEXT
Do not use this.get("foo") accessor for single property, instead Do not use this.get("foo") accessor for single property, instead
prefer to use this.foo prefer to use this.foo
Offenses: Offenses:
#{offenses.join("\n")} #{offenses.join("\n")}
MSG TEXT
end end
end end
@@ -41,7 +41,7 @@ describe 'Coding style' do
constant_name_regex = /#{Regexp.escape(constant_name)}/ constant_name_regex = /#{Regexp.escape(constant_name)}/
offenses = files.reject { |file| is_valid?(file, method_name_regex, constant_name_regex) } offenses = files.reject { |file| is_valid?(file, method_name_regex, constant_name_regex) }
expect(offenses).to be_empty, <<~MSG expect(offenses).to be_empty, <<~TEXT
You need to use the constant #{constant_name} when you use You need to use the constant #{constant_name} when you use
#{method_name} in order to help with restoring backups. #{method_name} in order to help with restoring backups.
@@ -49,7 +49,7 @@ describe 'Coding style' do
Offenses: Offenses:
#{offenses.join("\n")} #{offenses.join("\n")}
MSG TEXT
end end
def is_valid?(file, method_name_regex, constant_name_regex) def is_valid?(file, method_name_regex, constant_name_regex)

View File

@@ -25,36 +25,36 @@ describe Jobs::OldKeysReminder do
expect(post.archetype).to eq(Archetype.private_message) expect(post.archetype).to eq(Archetype.private_message)
expect(post.topic.topic_allowed_users.map(&:user_id).sort).to eq([Discourse.system_user.id, admin.id, another_admin.id].sort) expect(post.topic.topic_allowed_users.map(&:user_id).sort).to eq([Discourse.system_user.id, admin.id, another_admin.id].sort)
expect(post.topic.title).to eq('Reminder about old credentials') expect(post.topic.title).to eq('Reminder about old credentials')
expect(post.raw).to eq(<<-MSG.rstrip) expect(post.raw).to eq(<<~TEXT.rstrip)
Hello! This is a routine yearly security reminder from your Discourse instance. Hello! This is a routine yearly security reminder from your Discourse instance.
As a courtesy, we wanted to let you know that the following credentials used on your Discourse instance have not been updated in more than two years: As a courtesy, we wanted to let you know that the following credentials used on your Discourse instance have not been updated in more than two years:
google_oauth2_client_secret - #{google_secret.updated_at.to_date.to_s(:db)} google_oauth2_client_secret - #{google_secret.updated_at.to_date.to_s(:db)}
github_client_secret - #{github_secret.updated_at.to_date.to_s(:db)} github_client_secret - #{github_secret.updated_at.to_date.to_s(:db)}
api key description - #{api_key.created_at.to_date.to_s(:db)} api key description - #{api_key.created_at.to_date.to_s(:db)}
No action is required at this time, however, it is considered good security practice to cycle all your important credentials every few years. No action is required at this time, however, it is considered good security practice to cycle all your important credentials every few years.
MSG TEXT
post.topic.destroy post.topic.destroy
freeze_time 4.years.from_now freeze_time 4.years.from_now
described_class.new.execute({}) described_class.new.execute({})
post = Post.last post = Post.last
expect(post.topic.title).to eq('Reminder about old credentials') expect(post.topic.title).to eq('Reminder about old credentials')
expect(post.raw).to eq(<<-MSG.rstrip) expect(post.raw).to eq(<<~TEXT.rstrip)
Hello! This is a routine yearly security reminder from your Discourse instance. Hello! This is a routine yearly security reminder from your Discourse instance.
As a courtesy, we wanted to let you know that the following credentials used on your Discourse instance have not been updated in more than two years: As a courtesy, we wanted to let you know that the following credentials used on your Discourse instance have not been updated in more than two years:
google_oauth2_client_secret - #{google_secret.updated_at.to_date.to_s(:db)} google_oauth2_client_secret - #{google_secret.updated_at.to_date.to_s(:db)}
github_client_secret - #{github_secret.updated_at.to_date.to_s(:db)} github_client_secret - #{github_secret.updated_at.to_date.to_s(:db)}
twitter_consumer_secret - #{recent_twitter_secret.updated_at.to_date.to_s(:db)} twitter_consumer_secret - #{recent_twitter_secret.updated_at.to_date.to_s(:db)}
api key description - #{api_key.created_at.to_date.to_s(:db)} api key description - #{api_key.created_at.to_date.to_s(:db)}
recent api key description - #{admin.username} - #{recent_api_key.created_at.to_date.to_s(:db)} recent api key description - #{admin.username} - #{recent_api_key.created_at.to_date.to_s(:db)}
No action is required at this time, however, it is considered good security practice to cycle all your important credentials every few years. No action is required at this time, however, it is considered good security practice to cycle all your important credentials every few years.
MSG TEXT
end end
it 'does not send message when send_old_credential_reminder_days is set to 0 or no old keys' do it 'does not send message when send_old_credential_reminder_days is set to 0 or no old keys' do

View File

@@ -122,12 +122,12 @@ describe Jobs::PullHotlinkedImages do
stub_request(:get, "http://test.localhost/uploads/short-url/z2QSs1KJWoj51uYhDjb6ifCzxH6.gif") stub_request(:get, "http://test.localhost/uploads/short-url/z2QSs1KJWoj51uYhDjb6ifCzxH6.gif")
.to_return(status: 200, body: "") .to_return(status: 200, body: "")
post = Fabricate(:post, raw: <<~RAW) post = Fabricate(:post, raw: <<~MD)
<h1></h1> <h1></h1>
<a href="https://somelink.com"> <a href="https://somelink.com">
<img alt="somelink" src="#{image_url}" /> <img alt="somelink" src="#{image_url}" />
</a> </a>
RAW MD
expect do expect do
Jobs::PullHotlinkedImages.new.execute(post_id: post.id) Jobs::PullHotlinkedImages.new.execute(post_id: post.id)
@@ -135,12 +135,12 @@ describe Jobs::PullHotlinkedImages do
upload = post.uploads.last upload = post.uploads.last
expect(post.reload.raw).to eq(<<~RAW.chomp) expect(post.reload.raw).to eq(<<~MD.chomp)
<h1></h1> <h1></h1>
<a href="https://somelink.com"> <a href="https://somelink.com">
![somelink](#{upload.short_url}) ![somelink](#{upload.short_url})
</a> </a>
RAW MD
end end
it 'replaces correct image URL' do it 'replaces correct image URL' do
@@ -371,12 +371,12 @@ describe Jobs::PullHotlinkedImages do
end end
it 'all combinations' do it 'all combinations' do
post = Fabricate(:post, raw: <<~BODY) post = Fabricate(:post, raw: <<~MD)
<img src='#{image_url}'> <img src='#{image_url}'>
#{url} #{url}
<img src='#{broken_image_url}'> <img src='#{broken_image_url}'>
<a href='#{url}'><img src='#{large_image_url}'></a> <a href='#{url}'><img src='#{large_image_url}'></a>
BODY MD
stub_image_size stub_image_size
2.times do 2.times do

View File

@@ -327,13 +327,13 @@ describe ContentSecurityPolicy do
it 'is extended automatically when themes reference external scripts' do it 'is extended automatically when themes reference external scripts' do
policy # call this first to make sure further actions clear the cache policy # call this first to make sure further actions clear the cache
theme.set_field(target: :common, name: "header", value: <<~SCRIPT) theme.set_field(target: :common, name: "header", value: <<~HTML)
<script src='https://example.com/myscript.js'></script> <script src='https://example.com/myscript.js'></script>
<script src='https://example.com/myscript2.js?with=query'></script> <script src='https://example.com/myscript2.js?with=query'></script>
<script src='//example2.com/protocol-less-script.js'></script> <script src='//example2.com/protocol-less-script.js'></script>
<script src='domain-only.com'></script> <script src='domain-only.com'></script>
<script>console.log('inline script')</script> <script>console.log('inline script')</script>
SCRIPT HTML
theme.set_field(target: :desktop, name: "header", value: "") theme.set_field(target: :desktop, name: "header", value: "")
theme.save! theme.save!

View File

@@ -14,10 +14,10 @@ describe Email::AuthenticationResults do
it "parses 'Service Provided, Authentication Done' correctly" do it "parses 'Service Provided, Authentication Done' correctly" do
# https://tools.ietf.org/html/rfc8601#appendix-B.3 # https://tools.ietf.org/html/rfc8601#appendix-B.3
results = described_class.new(<<~EOF results = described_class.new(<<~RAW
example.com; example.com;
spf=pass smtp.mailfrom=example.net spf=pass smtp.mailfrom=example.net
EOF RAW
).results ).results
expect(results[0][:authserv_id]).to eq "example.com" expect(results[0][:authserv_id]).to eq "example.com"
expect(results[0][:resinfo][0][:method]).to eq "spf" expect(results[0][:resinfo][0][:method]).to eq "spf"
@@ -30,15 +30,15 @@ describe Email::AuthenticationResults do
it "parses 'Service Provided, Several Authentications Done, Single MTA' correctly" do it "parses 'Service Provided, Several Authentications Done, Single MTA' correctly" do
# https://tools.ietf.org/html/rfc8601#appendix-B.4 # https://tools.ietf.org/html/rfc8601#appendix-B.4
results = described_class.new([<<~EOF , results = described_class.new([<<~RAW ,
example.com; example.com;
auth=pass (cram-md5) smtp.auth=sender@example.net; auth=pass (cram-md5) smtp.auth=sender@example.net;
spf=pass smtp.mailfrom=example.net spf=pass smtp.mailfrom=example.net
EOF RAW
<<~EOF , <<~RAW ,
example.com; iprev=pass example.com; iprev=pass
policy.iprev=192.0.2.200 policy.iprev=192.0.2.200
EOF RAW
]).results ]).results
expect(results[0][:authserv_id]).to eq "example.com" expect(results[0][:authserv_id]).to eq "example.com"
expect(results[0][:resinfo][0][:method]).to eq "auth" expect(results[0][:resinfo][0][:method]).to eq "auth"
@@ -64,15 +64,15 @@ describe Email::AuthenticationResults do
it "parses 'Service Provided, Several Authentications Done, Different MTAs' correctly" do it "parses 'Service Provided, Several Authentications Done, Different MTAs' correctly" do
# https://tools.ietf.org/html/rfc8601#appendix-B.5 # https://tools.ietf.org/html/rfc8601#appendix-B.5
results = described_class.new([<<~EOF , results = described_class.new([<<~RAW ,
example.com; example.com;
dkim=pass (good signature) header.d=example.com dkim=pass (good signature) header.d=example.com
EOF RAW
<<~EOF , <<~RAW ,
example.com; example.com;
auth=pass (cram-md5) smtp.auth=sender@example.com; auth=pass (cram-md5) smtp.auth=sender@example.com;
spf=fail smtp.mailfrom=example.com spf=fail smtp.mailfrom=example.com
EOF RAW
]).results ]).results
expect(results[0][:authserv_id]).to eq "example.com" expect(results[0][:authserv_id]).to eq "example.com"
@@ -99,17 +99,17 @@ describe Email::AuthenticationResults do
it "parses 'Service Provided, Multi-tiered Authentication Done' correctly" do it "parses 'Service Provided, Multi-tiered Authentication Done' correctly" do
# https://tools.ietf.org/html/rfc8601#appendix-B.6 # https://tools.ietf.org/html/rfc8601#appendix-B.6
results = described_class.new([<<~EOF , results = described_class.new([<<~RAW ,
example.com; example.com;
dkim=pass reason="good signature" dkim=pass reason="good signature"
header.i=@mail-router.example.net; header.i=@mail-router.example.net;
dkim=fail reason="bad signature" dkim=fail reason="bad signature"
header.i=@newyork.example.com header.i=@newyork.example.com
EOF RAW
<<~EOF , <<~RAW ,
example.net; example.net;
dkim=pass (good signature) header.i=@newyork.example.com dkim=pass (good signature) header.i=@newyork.example.com
EOF RAW
]).results ]).results
expect(results[0][:authserv_id]).to eq "example.com" expect(results[0][:authserv_id]).to eq "example.com"
@@ -136,12 +136,12 @@ describe Email::AuthenticationResults do
it "parses 'Comment-Heavy Example' correctly" do it "parses 'Comment-Heavy Example' correctly" do
# https://tools.ietf.org/html/rfc8601#appendix-B.7 # https://tools.ietf.org/html/rfc8601#appendix-B.7
results = described_class.new(<<~EOF results = described_class.new(<<~RAW
foo.example.net (foobar) 1 (baz); foo.example.net (foobar) 1 (baz);
dkim (Because I like it) / 1 (One yay) = (wait for it) fail dkim (Because I like it) / 1 (One yay) = (wait for it) fail
policy (A dot can go here) . (like that) expired policy (A dot can go here) . (like that) expired
(this surprised me) = (as I wasn't expecting it) 1362471462 (this surprised me) = (as I wasn't expecting it) 1362471462
EOF RAW
).results ).results
expect(results[0][:authserv_id]).to eq "foo.example.net" expect(results[0][:authserv_id]).to eq "foo.example.net"
@@ -163,12 +163,12 @@ describe Email::AuthenticationResults do
end end
it "parses header with multiple props correctly" do it "parses header with multiple props correctly" do
results = described_class.new(<<~EOF results = described_class.new(<<~RAW
mx.google.com; mx.google.com;
dkim=pass header.i=@email.example.com header.s=20111006 header.b=URn9MW+F; dkim=pass header.i=@email.example.com header.s=20111006 header.b=URn9MW+F;
spf=pass (google.com: domain of foo@b.email.example.com designates 1.2.3.4 as permitted sender) smtp.mailfrom=foo@b.email.example.com; spf=pass (google.com: domain of foo@b.email.example.com designates 1.2.3.4 as permitted sender) smtp.mailfrom=foo@b.email.example.com;
dmarc=pass (p=REJECT sp=REJECT dis=NONE) header.from=email.example.com dmarc=pass (p=REJECT sp=REJECT dis=NONE) header.from=email.example.com
EOF RAW
).results ).results
expect(results[0][:authserv_id]).to eq "mx.google.com" expect(results[0][:authserv_id]).to eq "mx.google.com"

View File

@@ -808,7 +808,7 @@ describe Email::Receiver do
post = Post.last post = Post.last
expect(post.user.email).to eq("ba@bar.com") expect(post.user.email).to eq("ba@bar.com")
expect(post.raw).to eq(<<~EOF.chomp expect(post.raw).to eq(<<~RAW.chomp)
@team, can you have a look at this email below? @team, can you have a look at this email below?
[quote] [quote]
@@ -823,8 +823,7 @@ describe Email::Receiver do
XoXo XoXo
[/quote] [/quote]
EOF RAW
)
end end
end end
@@ -976,7 +975,7 @@ describe Email::Receiver do
let!(:post) { Fabricate(:post, topic: topic) } let!(:post) { Fabricate(:post, topic: topic) }
def process_mail_with_message_id(message_id) def process_mail_with_message_id(message_id)
mail_string = <<~REPLY mail_string = <<~EMAIL
Return-Path: <two@foo.com> Return-Path: <two@foo.com>
From: Two <two@foo.com> From: Two <two@foo.com>
To: one@foo.com To: one@foo.com
@@ -989,7 +988,7 @@ describe Email::Receiver do
Content-Transfer-Encoding: 7bit Content-Transfer-Encoding: 7bit
This is email reply testing with Message-ID formats. This is email reply testing with Message-ID formats.
REPLY EMAIL
Email::Receiver.new(mail_string).process! Email::Receiver.new(mail_string).process!
end end
@@ -1867,7 +1866,7 @@ describe Email::Receiver do
context "#select_body" do context "#select_body" do
let(:email) { let(:email) {
<<~EOF <<~EMAIL
MIME-Version: 1.0 MIME-Version: 1.0
Date: Tue, 01 Jan 2019 00:00:00 +0300 Date: Tue, 01 Jan 2019 00:00:00 +0300
Subject: An email with whitespaces Subject: An email with whitespaces
@@ -1916,11 +1915,11 @@ describe Email::Receiver do
This is going to be stripped too. This is going to be stripped too.
Bye! Bye!
EOF EMAIL
} }
let(:stripped_text) { let(:stripped_text) {
<<~EOF <<~MD
This is a line that will be stripped This is a line that will be stripped
This is another line that will be stripped This is another line that will be stripped
@@ -1962,7 +1961,7 @@ describe Email::Receiver do
This is going to be stripped too. This is going to be stripped too.
Bye! Bye!
EOF MD
} }
it "strips lines if strip_incoming_email_lines is enabled" do it "strips lines if strip_incoming_email_lines is enabled" do
@@ -1976,7 +1975,7 @@ describe Email::Receiver do
it "works with empty mail body" do it "works with empty mail body" do
SiteSetting.strip_incoming_email_lines = true SiteSetting.strip_incoming_email_lines = true
email = <<~EOF email = <<~EMAIL
Date: Tue, 01 Jan 2019 00:00:00 +0300 Date: Tue, 01 Jan 2019 00:00:00 +0300
Subject: An email with whitespaces Subject: An email with whitespaces
From: Foo <foo@discourse.org> From: Foo <foo@discourse.org>
@@ -1986,7 +1985,7 @@ describe Email::Receiver do
-- --
my signature my signature
EOF EMAIL
receiver = Email::Receiver.new(email) receiver = Email::Receiver.new(email)
text, _elided, _format = receiver.select_body text, _elided, _format = receiver.select_body
@@ -2004,7 +2003,7 @@ describe Email::Receiver do
message_id: digest_message_id message_id: digest_message_id
)} )}
let(:email) { let(:email) {
<<~EOF <<~EMAIL
MIME-Version: 1.0 MIME-Version: 1.0
Date: Tue, 01 Jan 2019 00:00:00 +0300 Date: Tue, 01 Jan 2019 00:00:00 +0300
From: someone <#{user.email}> From: someone <#{user.email}>
@@ -2017,7 +2016,7 @@ describe Email::Receiver do
hello there! I like the digest! hello there! I like the digest!
EOF EMAIL
} }
before do before do
@@ -2034,7 +2033,7 @@ describe Email::Receiver do
let(:user) { Fabricate(:user) } let(:user) { Fabricate(:user) }
let(:group) { Fabricate(:group, users: [user]) } let(:group) { Fabricate(:group, users: [user]) }
let (:email_1) { <<~EOF let (:email_1) { <<~EMAIL
MIME-Version: 1.0 MIME-Version: 1.0
Date: Wed, 01 Jan 2019 12:00:00 +0200 Date: Wed, 01 Jan 2019 12:00:00 +0200
Message-ID: <7aN1uwcokt2xkfG3iYrpKmiuVhy4w9b5@mail.gmail.com> Message-ID: <7aN1uwcokt2xkfG3iYrpKmiuVhy4w9b5@mail.gmail.com>
@@ -2052,7 +2051,7 @@ describe Email::Receiver do
Vivamus semper lacinia scelerisque. Cras urna magna, porttitor nec Vivamus semper lacinia scelerisque. Cras urna magna, porttitor nec
libero quis, congue viverra sapien. Nulla sodales ac tellus a libero quis, congue viverra sapien. Nulla sodales ac tellus a
suscipit. suscipit.
EOF EMAIL
} }
let (:post_2) { let (:post_2) {
@@ -2064,7 +2063,7 @@ describe Email::Receiver do
) )
} }
let (:email_3) { <<~EOF let (:email_3) { <<~EMAIL
MIME-Version: 1.0 MIME-Version: 1.0
Date: Wed, 01 Jan 2019 12:00:00 +0200 Date: Wed, 01 Jan 2019 12:00:00 +0200
References: <7aN1uwcokt2xkfG3iYrpKmiuVhy4w9b5@mail.gmail.com> <topic/#{post_2.topic_id}/#{post_2.id}@test.localhost> References: <7aN1uwcokt2xkfG3iYrpKmiuVhy4w9b5@mail.gmail.com> <topic/#{post_2.topic_id}/#{post_2.id}@test.localhost>
@@ -2087,7 +2086,7 @@ describe Email::Receiver do
felis. Sed pellentesque, massa auctor venenatis gravida, risus lorem felis. Sed pellentesque, massa auctor venenatis gravida, risus lorem
iaculis mi, at hendrerit nisi turpis sit amet metus. Nulla egestas iaculis mi, at hendrerit nisi turpis sit amet metus. Nulla egestas
ante eget nisi luctus consectetur. ante eget nisi luctus consectetur.
EOF EMAIL
} }
def receive(email_string) def receive(email_string)

View File

@@ -16,28 +16,28 @@ describe EmailCook do
end end
it "doesn't add linebreaks to long lines" do it "doesn't add linebreaks to long lines" do
long = plaintext(<<~LONG_EMAIL) long = plaintext(<<~EMAIL)
Hello, Hello,
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nunc convallis volutpat Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nunc convallis volutpat
risus. Nulla ac faucibus quam, quis cursus lorem. Sed rutrum eget nunc sed accumsan. risus. Nulla ac faucibus quam, quis cursus lorem. Sed rutrum eget nunc sed accumsan.
Vestibulum feugiat mi vitae turpis tempor dignissim. Vestibulum feugiat mi vitae turpis tempor dignissim.
LONG_EMAIL EMAIL
long_cooked = (+<<~LONG_COOKED).strip! long_cooked = (+<<~HTML).strip!
Hello, Hello,
<br> <br>
<br>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nunc convallis volutpat <br>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nunc convallis volutpat
risus. Nulla ac faucibus quam, quis cursus lorem. Sed rutrum eget nunc sed accumsan. risus. Nulla ac faucibus quam, quis cursus lorem. Sed rutrum eget nunc sed accumsan.
Vestibulum feugiat mi vitae turpis tempor dignissim. Vestibulum feugiat mi vitae turpis tempor dignissim.
<br> <br>
LONG_COOKED HTML
expect(cook(long)).to eq(long_cooked) expect(cook(long)).to eq(long_cooked)
end end
it "replaces a blank line with 2 linebreaks" do it "replaces a blank line with 2 linebreaks" do
long = plaintext(<<~LONG_EMAIL) long = plaintext(<<~EMAIL)
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nunc convallis volutpat Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nunc convallis volutpat
risus. risus.
Nulla ac faucibus quam, quis cursus lorem. Sed rutrum eget nunc sed accumsan. Nulla ac faucibus quam, quis cursus lorem. Sed rutrum eget nunc sed accumsan.
@@ -45,9 +45,9 @@ describe EmailCook do
Vestibulum feugiat mi vitae turpis tempor dignissim. Vestibulum feugiat mi vitae turpis tempor dignissim.
Stet clita kasd gubergren. Stet clita kasd gubergren.
LONG_EMAIL EMAIL
long_cooked = (+<<~LONG_COOKED).strip! long_cooked = (+<<~HTML).strip!
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nunc convallis volutpat Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nunc convallis volutpat
risus. risus.
<br>Nulla ac faucibus quam, quis cursus lorem. Sed rutrum eget nunc sed accumsan. <br>Nulla ac faucibus quam, quis cursus lorem. Sed rutrum eget nunc sed accumsan.
@@ -56,13 +56,13 @@ describe EmailCook do
<br> <br>
<br>Stet clita kasd gubergren. <br>Stet clita kasd gubergren.
<br> <br>
LONG_COOKED HTML
expect(cook(long)).to eq(long_cooked) expect(cook(long)).to eq(long_cooked)
end end
it "escapes HTML" do it "escapes HTML" do
long = plaintext(<<~LONG_EMAIL) long = plaintext(<<~EMAIL)
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Lorem ipsum dolor sit amet, consectetur adipiscing elit.
<form name="f1" method="post" action="test.html" onsubmit="javascript:showAlert()"> <form name="f1" method="post" action="test.html" onsubmit="javascript:showAlert()">
@@ -70,9 +70,9 @@ describe EmailCook do
</form> </form>
Nunc convallis volutpat risus. Nunc convallis volutpat risus.
LONG_EMAIL EMAIL
long_cooked = (+<<~LONG_COOKED).strip! long_cooked = (+<<~HTML).strip!
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Lorem ipsum dolor sit amet, consectetur adipiscing elit.
<br> <br>
<br>&lt;form name=&quot;f1&quot; method=&quot;post&quot; action=&quot;test.html&quot; onsubmit=&quot;javascript:showAlert()&quot;&gt; <br>&lt;form name=&quot;f1&quot; method=&quot;post&quot; action=&quot;test.html&quot; onsubmit=&quot;javascript:showAlert()&quot;&gt;
@@ -81,29 +81,29 @@ describe EmailCook do
<br> <br>
<br>Nunc convallis volutpat risus. <br>Nunc convallis volutpat risus.
<br> <br>
LONG_COOKED HTML
expect(cook(long)).to eq(long_cooked) expect(cook(long)).to eq(long_cooked)
end end
it "replaces indentation of more than 2 spaces with corresponding amount of non-breaking spaces" do it "replaces indentation of more than 2 spaces with corresponding amount of non-breaking spaces" do
nbsp = "\u00A0" nbsp = "\u00A0"
long = plaintext(<<~LONG_EMAIL) long = plaintext(<<~EMAIL)
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Lorem ipsum dolor sit amet, consectetur adipiscing elit.
this is intended by 4 spaces this is intended by 4 spaces
this is intended by 1 space this is intended by 1 space
no indentation, but lots of spaces no indentation, but lots of spaces
LONG_EMAIL EMAIL
long_cooked = (+<<~LONG_COOKED).strip! long_cooked = (+<<~HTML).strip!
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Lorem ipsum dolor sit amet, consectetur adipiscing elit.
<br> <br>
<br>#{nbsp}#{nbsp}#{nbsp}#{nbsp}this is intended by 4 spaces <br>#{nbsp}#{nbsp}#{nbsp}#{nbsp}this is intended by 4 spaces
<br> this is intended by 1 space <br> this is intended by 1 space
<br>no indentation, but lots of spaces <br>no indentation, but lots of spaces
<br> <br>
LONG_COOKED HTML
expect(cook(long)).to eq(long_cooked) expect(cook(long)).to eq(long_cooked)
end end
@@ -129,7 +129,7 @@ describe EmailCook do
end end
it "it works and does not interpret Markdown in plaintext and elided" do it "it works and does not interpret Markdown in plaintext and elided" do
long = <<~LONG_EMAIL long = <<~EMAIL
[plaintext] [plaintext]
*Lorem ipsum* dolor sit amet, consectetur adipiscing elit. *Lorem ipsum* dolor sit amet, consectetur adipiscing elit.
[/plaintext] [/plaintext]
@@ -139,9 +139,9 @@ describe EmailCook do
[elided] [elided]
At vero eos *et accusam* et justo duo dolores et ea rebum. At vero eos *et accusam* et justo duo dolores et ea rebum.
[/elided] [/elided]
LONG_EMAIL EMAIL
long_cooked = <<~LONG_COOKED long_cooked = <<~HTML
*Lorem ipsum* dolor sit amet, consectetur adipiscing elit.<br> *Lorem ipsum* dolor sit amet, consectetur adipiscing elit.<br>
<br><img src='some_image.png' width='100' height='100'> <br><img src='some_image.png' width='100' height='100'>
<br><br> <br><br>
@@ -152,22 +152,22 @@ describe EmailCook do
At vero eos *et accusam* et justo duo dolores et ea rebum.<br> At vero eos *et accusam* et justo duo dolores et ea rebum.<br>
</details> </details>
LONG_COOKED HTML
expect(cook(long)).to eq(long_cooked) expect(cook(long)).to eq(long_cooked)
end end
it "works without attachments" do it "works without attachments" do
long = <<~LONG_EMAIL long = <<~EMAIL
[plaintext] [plaintext]
*Lorem ipsum* dolor sit amet, consectetur adipiscing elit. *Lorem ipsum* dolor sit amet, consectetur adipiscing elit.
[/plaintext] [/plaintext]
[elided] [elided]
At vero eos *et accusam* et justo duo dolores et ea rebum. At vero eos *et accusam* et justo duo dolores et ea rebum.
[/elided] [/elided]
LONG_EMAIL EMAIL
long_cooked = <<~LONG_COOKED long_cooked = <<~HTML
*Lorem ipsum* dolor sit amet, consectetur adipiscing elit.<br> *Lorem ipsum* dolor sit amet, consectetur adipiscing elit.<br>
<br><br> <br><br>
@@ -177,7 +177,7 @@ describe EmailCook do
At vero eos *et accusam* et justo duo dolores et ea rebum.<br> At vero eos *et accusam* et justo duo dolores et ea rebum.<br>
</details> </details>
LONG_COOKED HTML
expect(cook(long)).to eq(long_cooked) expect(cook(long)).to eq(long_cooked)
end end

View File

@@ -353,7 +353,7 @@ describe PrettyText do
expect(PrettyText.cook("[quote]\ntest")).not_to include('aside') expect(PrettyText.cook("[quote]\ntest")).not_to include('aside')
expect(PrettyText.cook("[quote]\ntest\n[/quote]z")).not_to include('aside') expect(PrettyText.cook("[quote]\ntest\n[/quote]z")).not_to include('aside')
nested = <<~QUOTE nested = <<~MD
[quote] [quote]
a a
[quote] [quote]
@@ -361,7 +361,7 @@ describe PrettyText do
[/quote] [/quote]
c c
[/quote] [/quote]
QUOTE MD
cooked = PrettyText.cook(nested) cooked = PrettyText.cook(nested)
expect(cooked.scan('aside').length).to eq(4) expect(cooked.scan('aside').length).to eq(4)
@@ -799,9 +799,9 @@ describe PrettyText do
context "emojis" do context "emojis" do
it "should remove broken emoji" do it "should remove broken emoji" do
html = <<~EOS html = <<~HTML
<img src=\"//localhost:3000/images/emoji/twitter/bike.png?v=#{Emoji::EMOJI_VERSION}\" title=\":bike:\" class=\"emoji\" alt=\":bike:\" loading=\"lazy\" width=\"20\" height=\"20\"> <img src=\"//localhost:3000/images/emoji/twitter/cat.png?v=#{Emoji::EMOJI_VERSION}\" title=\":cat:\" class=\"emoji\" alt=\":cat:\" loading=\"lazy\" width=\"20\" height=\"20\"> <img src=\"//localhost:3000/images/emoji/twitter/discourse.png?v=#{Emoji::EMOJI_VERSION}\" title=\":discourse:\" class=\"emoji\" alt=\":discourse:\" loading=\"lazy\" width=\"20\" height=\"20\"> <img src=\"//localhost:3000/images/emoji/twitter/bike.png?v=#{Emoji::EMOJI_VERSION}\" title=\":bike:\" class=\"emoji\" alt=\":bike:\" loading=\"lazy\" width=\"20\" height=\"20\"> <img src=\"//localhost:3000/images/emoji/twitter/cat.png?v=#{Emoji::EMOJI_VERSION}\" title=\":cat:\" class=\"emoji\" alt=\":cat:\" loading=\"lazy\" width=\"20\" height=\"20\"> <img src=\"//localhost:3000/images/emoji/twitter/discourse.png?v=#{Emoji::EMOJI_VERSION}\" title=\":discourse:\" class=\"emoji\" alt=\":discourse:\" loading=\"lazy\" width=\"20\" height=\"20\">
EOS HTML
expect(PrettyText.excerpt(html, 7)).to eq(":bike: &hellip;") expect(PrettyText.excerpt(html, 7)).to eq(":bike: &hellip;")
expect(PrettyText.excerpt(html, 8)).to eq(":bike: &hellip;") expect(PrettyText.excerpt(html, 8)).to eq(":bike: &hellip;")
expect(PrettyText.excerpt(html, 9)).to eq(":bike: &hellip;") expect(PrettyText.excerpt(html, 9)).to eq(":bike: &hellip;")
@@ -914,7 +914,7 @@ describe PrettyText do
end end
it "should not extract links inside oneboxes" do it "should not extract links inside oneboxes" do
onebox = <<~EOF onebox = <<~HTML
<aside class="onebox twitterstatus" data-onebox-src="https://twitter.com/EDBPostgres/status/1402528437441634306"> <aside class="onebox twitterstatus" data-onebox-src="https://twitter.com/EDBPostgres/status/1402528437441634306">
<header class="source"> <header class="source">
<a href="https://twitter.com/EDBPostgres/status/1402528437441634306" target="_blank" rel="noopener">twitter.com</a> <a href="https://twitter.com/EDBPostgres/status/1402528437441634306" target="_blank" rel="noopener">twitter.com</a>
@@ -924,7 +924,7 @@ describe PrettyText do
<div class="tweet">Example URL: <a target="_blank" href="https://example.com" rel="noopener">example.com</a></div> <div class="tweet">Example URL: <a target="_blank" href="https://example.com" rel="noopener">example.com</a></div>
</article> </article>
</aside> </aside>
EOF HTML
expect(PrettyText.extract_links(onebox).map(&:url)).to contain_exactly("https://twitter.com/EDBPostgres/status/1402528437441634306") expect(PrettyText.extract_links(onebox).map(&:url)).to contain_exactly("https://twitter.com/EDBPostgres/status/1402528437441634306")
end end
@@ -938,12 +938,13 @@ describe PrettyText do
end end
it "handles custom bbcode excerpt" do it "handles custom bbcode excerpt" do
raw = <<~RAW raw = <<~MD
[excerpt] [excerpt]
hello [site](https://site.com) hello [site](https://site.com)
[/excerpt] [/excerpt]
more stuff more stuff
RAW MD
post = Fabricate(:post, raw: raw) post = Fabricate(:post, raw: raw)
expect(post.excerpt).to eq("hello <a href=\"https://site.com\" rel=\"noopener nofollow ugc\">site</a>") expect(post.excerpt).to eq("hello <a href=\"https://site.com\" rel=\"noopener nofollow ugc\">site</a>")
end end
@@ -1929,17 +1930,17 @@ HTML
it "can properly allowlist iframes" do it "can properly allowlist iframes" do
SiteSetting.allowed_iframes = "https://bob.com/a|http://silly.com?EMBED=" SiteSetting.allowed_iframes = "https://bob.com/a|http://silly.com?EMBED="
raw = <<~IFRAMES raw = <<~HTML
<iframe src='https://www.google.com/maps/Embed?testing'></iframe> <iframe src='https://www.google.com/maps/Embed?testing'></iframe>
<iframe src='https://bob.com/a?testing'></iframe> <iframe src='https://bob.com/a?testing'></iframe>
<iframe src='HTTP://SILLY.COM?EMBED=111'></iframe> <iframe src='HTTP://SILLY.COM?EMBED=111'></iframe>
IFRAMES HTML
# we require explicit HTTPS here # we require explicit HTTPS here
html = <<~IFRAMES html = <<~HTML
<iframe src="https://bob.com/a?testing"></iframe> <iframe src="https://bob.com/a?testing"></iframe>
<iframe src="HTTP://SILLY.COM?EMBED=111"></iframe> <iframe src="HTTP://SILLY.COM?EMBED=111"></iframe>
IFRAMES HTML
cooked = PrettyText.cook(raw).strip cooked = PrettyText.cook(raw).strip

View File

@@ -551,9 +551,9 @@ describe Search do
result = Search.execute('search term') result = Search.execute('search term')
expect(result.posts.first.topic_title_headline).to eq(<<~TITLE.chomp) expect(result.posts.first.topic_title_headline).to eq(<<~HTML.chomp)
Very very very very very very very long topic title with our <span class=\"#{Search::HIGHLIGHT_CSS_CLASS}\">search</span> <span class=\"#{Search::HIGHLIGHT_CSS_CLASS}\">term</span> in the middle of the title Very very very very very very very long topic title with our <span class=\"#{Search::HIGHLIGHT_CSS_CLASS}\">search</span> <span class=\"#{Search::HIGHLIGHT_CSS_CLASS}\">term</span> in the middle of the title
TITLE HTML
end end
it "limits the search headline to #{Search::GroupedSearchResults::BLURB_LENGTH} characters" do it "limits the search headline to #{Search::GroupedSearchResults::BLURB_LENGTH} characters" do

View File

@@ -76,25 +76,25 @@ describe Discourse::VERSION do
end end
context "with a regular compatible list" do context "with a regular compatible list" do
let(:version_list) { <<~VERSION_LIST let(:version_list) { <<~YML
2.5.0.beta6: twofivebetasix 2.5.0.beta6: twofivebetasix
2.5.0.beta4: twofivebetafour 2.5.0.beta4: twofivebetafour
2.5.0.beta2: twofivebetatwo 2.5.0.beta2: twofivebetatwo
2.4.4.beta6: twofourfourbetasix 2.4.4.beta6: twofourfourbetasix
2.4.2.beta1: twofourtwobetaone 2.4.2.beta1: twofourtwobetaone
VERSION_LIST YML
} }
include_examples "test compatible resource" include_examples "test compatible resource"
end end
context "handle a compatible resource out of order" do context "handle a compatible resource out of order" do
let(:version_list) { <<~VERSION_LIST let(:version_list) { <<~YML
2.4.2.beta1: twofourtwobetaone 2.4.2.beta1: twofourtwobetaone
2.5.0.beta4: twofivebetafour 2.5.0.beta4: twofivebetafour
2.5.0.beta6: twofivebetasix 2.5.0.beta6: twofivebetasix
2.5.0.beta2: twofivebetatwo 2.5.0.beta2: twofivebetatwo
2.4.4.beta6: twofourfourbetasix 2.4.4.beta6: twofourfourbetasix
VERSION_LIST YML
} }
include_examples "test compatible resource" include_examples "test compatible resource"
end end

View File

@@ -28,7 +28,7 @@ describe GroupSmtpMailer do
end end
let(:email) do let(:email) do
<<~EOF <<~EMAIL
Delivered-To: bugs@gmail.com Delivered-To: bugs@gmail.com
MIME-Version: 1.0 MIME-Version: 1.0
From: John Doe <john@doe.com> From: John Doe <john@doe.com>
@@ -41,7 +41,7 @@ describe GroupSmtpMailer do
Hello, Hello,
How are you doing? How are you doing?
EOF EMAIL
end end
let(:receiver) do let(:receiver) do

View File

@@ -92,10 +92,10 @@ describe ThemeField do
<script>var b = 10</script> <script>var b = 10</script>
HTML HTML
extracted = <<~JavaScript extracted = <<~JS
var a = 10 var a = 10
var b = 10 var b = 10
JavaScript JS
theme_field = ThemeField.create!(theme_id: 1, target_id: 0, name: "header", value: html) theme_field = ThemeField.create!(theme_id: 1, target_id: 0, name: "header", value: html)
theme_field.ensure_baked! theme_field.ensure_baked!

View File

@@ -326,10 +326,10 @@ HTML
expect(scss).to include("font-size:30px") expect(scss).to include("font-size:30px")
# Escapes correctly. If not, compiling this would throw an exception # Escapes correctly. If not, compiling this would throw an exception
setting.value = <<~MULTILINE setting.value = <<~CSS
\#{$fakeinterpolatedvariable} \#{$fakeinterpolatedvariable}
andanothervalue 'withquotes'; margin: 0; andanothervalue 'withquotes'; margin: 0;
MULTILINE CSS
theme.set_field(target: :common, name: :scss, value: 'body {font-size: quote($font-size)}') theme.set_field(target: :common, name: :scss, value: 'body {font-size: quote($font-size)}')
theme.save! theme.save!

View File

@@ -390,10 +390,10 @@ describe TopicEmbed do
it "handles malformed links" do it "handles malformed links" do
url = "https://somesource.com" url = "https://somesource.com"
contents = <<~CONTENT contents = <<~HTML
hello world new post <a href="mailto:somemail@somewhere.org>">hello</a> hello world new post <a href="mailto:somemail@somewhere.org>">hello</a>
some image <img src="https:/><invalidimagesrc/"> some image <img src="https:/><invalidimagesrc/">
CONTENT HTML
raw = TopicEmbed.absolutize_urls(url, contents) raw = TopicEmbed.absolutize_urls(url, contents)
expect(raw).to eq(contents) expect(raw).to eq(contents)

View File

@@ -454,14 +454,14 @@ RSpec.describe Admin::BackupsController do
BackupRestore::S3BackupStore.any_instance.stubs(:temporary_upload_path).returns( BackupRestore::S3BackupStore.any_instance.stubs(:temporary_upload_path).returns(
"temp/default/#{test_bucket_prefix}/28fccf8259bbe75b873a2bd2564b778c/2u98j832nx93272x947823.gz" "temp/default/#{test_bucket_prefix}/28fccf8259bbe75b873a2bd2564b778c/2u98j832nx93272x947823.gz"
) )
create_multipart_result = <<~BODY create_multipart_result = <<~XML
<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n <?xml version=\"1.0\" encoding=\"UTF-8\"?>\n
<InitiateMultipartUploadResult> <InitiateMultipartUploadResult>
<Bucket>s3-backup-bucket</Bucket> <Bucket>s3-backup-bucket</Bucket>
<Key>temp/default/#{test_bucket_prefix}/28fccf8259bbe75b873a2bd2564b778c/2u98j832nx93272x947823.gz</Key> <Key>temp/default/#{test_bucket_prefix}/28fccf8259bbe75b873a2bd2564b778c/2u98j832nx93272x947823.gz</Key>
<UploadId>#{mock_multipart_upload_id}</UploadId> <UploadId>#{mock_multipart_upload_id}</UploadId>
</InitiateMultipartUploadResult> </InitiateMultipartUploadResult>
BODY XML
stub_request(:post, "https://s3-backup-bucket.s3.us-west-1.amazonaws.com/temp/default/#{test_bucket_prefix}/28fccf8259bbe75b873a2bd2564b778c/2u98j832nx93272x947823.gz?uploads"). stub_request(:post, "https://s3-backup-bucket.s3.us-west-1.amazonaws.com/temp/default/#{test_bucket_prefix}/28fccf8259bbe75b873a2bd2564b778c/2u98j832nx93272x947823.gz?uploads").
to_return(status: 200, body: create_multipart_result) to_return(status: 200, body: create_multipart_result)
end end

View File

@@ -20,7 +20,7 @@ describe 'groups' do
in: :query, in: :query,
type: :string, type: :string,
example: 'api @blake #support tags:api after:2021-06-04 in:unseen in:open order:latest_topic', example: 'api @blake #support tags:api after:2021-06-04 in:unseen in:open order:latest_topic',
description: <<~HEREDOC description: <<~MD
The query string needs to be url encoded and is made up of the following options: The query string needs to be url encoded and is made up of the following options:
- Search term. This is just a string. Usually it would be the first item in the query. - Search term. This is just a string. Usually it would be the first item in the query.
- `@<username>`: Use the `@` followed by the username to specify posts by this user. - `@<username>`: Use the `@` followed by the username to specify posts by this user.
@@ -44,7 +44,7 @@ describe 'groups' do
curl -i -sS -X GET -G "http://localhost:4200/search.json" \\ curl -i -sS -X GET -G "http://localhost:4200/search.json" \\
--data-urlencode 'q=wordpress @scossar #fun after:2020-01-01' --data-urlencode 'q=wordpress @scossar #fun after:2020-01-01'
``` ```
HEREDOC MD
) )
parameter name: :page, in: :query, type: :integer, example: 1 parameter name: :page, in: :query, type: :integer, example: 1

View File

@@ -52,7 +52,7 @@ describe 'uploads' do
tags 'Uploads' tags 'Uploads'
operationId 'generatePresignedPut' operationId 'generatePresignedPut'
consumes 'application/json' consumes 'application/json'
description <<~HEREDOC description <<~TEXT
Direct external uploads bypass the usual method of creating uploads Direct external uploads bypass the usual method of creating uploads
via the POST /uploads route, and upload directly to an external provider, via the POST /uploads route, and upload directly to an external provider,
which by default is S3. This route begins the process, and will return which by default is S3. This route begins the process, and will return
@@ -66,7 +66,7 @@ describe 'uploads' do
destination in the external storage service. destination in the external storage service.
#{direct_uploads_disclaimer} #{direct_uploads_disclaimer}
HEREDOC TEXT
expected_request_schema = load_spec_schema('upload_generate_presigned_put_request') expected_request_schema = load_spec_schema('upload_generate_presigned_put_request')
parameter name: :params, in: :body, schema: expected_request_schema parameter name: :params, in: :body, schema: expected_request_schema
@@ -108,7 +108,7 @@ describe 'uploads' do
tags 'Uploads' tags 'Uploads'
operationId 'completeExternalUpload' operationId 'completeExternalUpload'
consumes 'application/json' consumes 'application/json'
description <<~HEREDOC description <<~TEXT
Completes an external upload initialized with /get-presigned-put. The Completes an external upload initialized with /get-presigned-put. The
file will be moved from its temporary location in external storage to file will be moved from its temporary location in external storage to
a final destination in the S3 bucket. An Upload record will also be a final destination in the S3 bucket. An Upload record will also be
@@ -119,7 +119,7 @@ describe 'uploads' do
file was uploaded. The file size will be compared for the same reason. file was uploaded. The file size will be compared for the same reason.
#{direct_uploads_disclaimer} #{direct_uploads_disclaimer}
HEREDOC TEXT
expected_request_schema = load_spec_schema('upload_complete_external_upload_request') expected_request_schema = load_spec_schema('upload_complete_external_upload_request')
parameter name: :params, in: :body, schema: expected_request_schema parameter name: :params, in: :body, schema: expected_request_schema
@@ -154,12 +154,12 @@ describe 'uploads' do
tags 'Uploads' tags 'Uploads'
operationId 'createMultipartUpload' operationId 'createMultipartUpload'
consumes 'application/json' consumes 'application/json'
description <<~HEREDOC description <<~TEXT
Creates a multipart upload in the external storage provider, storing Creates a multipart upload in the external storage provider, storing
a temporary reference to the external upload similar to /get-presigned-put. a temporary reference to the external upload similar to /get-presigned-put.
#{direct_uploads_disclaimer} #{direct_uploads_disclaimer}
HEREDOC TEXT
expected_request_schema = load_spec_schema('upload_create_multipart_request') expected_request_schema = load_spec_schema('upload_create_multipart_request')
parameter name: :params, in: :body, schema: expected_request_schema parameter name: :params, in: :body, schema: expected_request_schema
@@ -200,7 +200,7 @@ describe 'uploads' do
tags 'Uploads' tags 'Uploads'
operationId 'batchPresignMultipartParts' operationId 'batchPresignMultipartParts'
consumes 'application/json' consumes 'application/json'
description <<~HEREDOC description <<~TEXT
Multipart uploads are uploaded in chunks or parts to individual presigned Multipart uploads are uploaded in chunks or parts to individual presigned
URLs, similar to the one generated by /generate-presigned-put. The part URLs, similar to the one generated by /generate-presigned-put. The part
numbers provided must be between 1 and 10000. The total number of parts numbers provided must be between 1 and 10000. The total number of parts
@@ -215,7 +215,7 @@ describe 'uploads' do
because this is needed to complete the multipart upload. because this is needed to complete the multipart upload.
#{direct_uploads_disclaimer} #{direct_uploads_disclaimer}
HEREDOC TEXT
expected_request_schema = load_spec_schema('upload_batch_presign_multipart_parts_request') expected_request_schema = load_spec_schema('upload_batch_presign_multipart_parts_request')
parameter name: :params, in: :body, schema: expected_request_schema parameter name: :params, in: :body, schema: expected_request_schema
@@ -255,13 +255,13 @@ describe 'uploads' do
tags 'Uploads' tags 'Uploads'
operationId 'abortMultipart' operationId 'abortMultipart'
consumes 'application/json' consumes 'application/json'
description <<~HEREDOC description <<~TEXT
This endpoint aborts the multipart upload initiated with /create-multipart. This endpoint aborts the multipart upload initiated with /create-multipart.
This should be used when cancelling the upload. It does not matter if parts This should be used when cancelling the upload. It does not matter if parts
were already uploaded into the external storage provider. were already uploaded into the external storage provider.
#{direct_uploads_disclaimer} #{direct_uploads_disclaimer}
HEREDOC TEXT
expected_request_schema = load_spec_schema('upload_abort_multipart_request') expected_request_schema = load_spec_schema('upload_abort_multipart_request')
parameter name: :params, in: :body, schema: expected_request_schema parameter name: :params, in: :body, schema: expected_request_schema
@@ -299,7 +299,7 @@ describe 'uploads' do
tags 'Uploads' tags 'Uploads'
operationId 'completeMultipart' operationId 'completeMultipart'
consumes 'application/json' consumes 'application/json'
description <<~HEREDOC description <<~TEXT
Completes the multipart upload in the external store, and copies the Completes the multipart upload in the external store, and copies the
file from its temporary location to its final location in the store. file from its temporary location to its final location in the store.
All of the parts must have been uploaded to the external storage provider. All of the parts must have been uploaded to the external storage provider.
@@ -307,7 +307,7 @@ describe 'uploads' do
to its final location. to its final location.
#{direct_uploads_disclaimer} #{direct_uploads_disclaimer}
HEREDOC TEXT
expected_request_schema = load_spec_schema('upload_complete_multipart_request') expected_request_schema = load_spec_schema('upload_complete_multipart_request')
parameter name: :params, in: :body, schema: expected_request_schema parameter name: :params, in: :body, schema: expected_request_schema

View File

@@ -125,13 +125,13 @@ RSpec.describe MetadataController do
end end
it 'returns the right output' do it 'returns the right output' do
SiteSetting.app_association_android = <<~EOF SiteSetting.app_association_android = <<~JSON
[{ [{
"relation": ["delegate_permission/common.handle_all_urls"], "relation": ["delegate_permission/common.handle_all_urls"],
"target" : { "namespace": "android_app", "package_name": "com.example.app", "target" : { "namespace": "android_app", "package_name": "com.example.app",
"sha256_cert_fingerprints": ["hash_of_app_certificate"] } "sha256_cert_fingerprints": ["hash_of_app_certificate"] }
}] }]
EOF JSON
get "/.well-known/assetlinks.json" get "/.well-known/assetlinks.json"
expect(response.headers["Cache-Control"]).to eq('max-age=60, private') expect(response.headers["Cache-Control"]).to eq('max-age=60, private')
@@ -150,13 +150,13 @@ RSpec.describe MetadataController do
end end
it 'returns the right output' do it 'returns the right output' do
SiteSetting.app_association_ios = <<~EOF SiteSetting.app_association_ios = <<~JSON
{ {
"applinks": { "applinks": {
"apps": [] "apps": []
} }
} }
EOF JSON
get "/apple-app-site-association" get "/apple-app-site-association"
expect(response.status).to eq(200) expect(response.status).to eq(200)

View File

@@ -831,14 +831,14 @@ describe UploadsController do
FileStore::S3Store.any_instance.stubs(:temporary_upload_path).returns( FileStore::S3Store.any_instance.stubs(:temporary_upload_path).returns(
"uploads/default/#{test_bucket_prefix}/temp/28fccf8259bbe75b873a2bd2564b778c/test.png" "uploads/default/#{test_bucket_prefix}/temp/28fccf8259bbe75b873a2bd2564b778c/test.png"
) )
create_multipart_result = <<~BODY create_multipart_result = <<~XML
<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n <?xml version=\"1.0\" encoding=\"UTF-8\"?>\n
<InitiateMultipartUploadResult> <InitiateMultipartUploadResult>
<Bucket>s3-upload-bucket</Bucket> <Bucket>s3-upload-bucket</Bucket>
<Key>uploads/default/#{test_bucket_prefix}/temp/28fccf8259bbe75b873a2bd2564b778c/test.png</Key> <Key>uploads/default/#{test_bucket_prefix}/temp/28fccf8259bbe75b873a2bd2564b778c/test.png</Key>
<UploadId>#{mock_multipart_upload_id}</UploadId> <UploadId>#{mock_multipart_upload_id}</UploadId>
</InitiateMultipartUploadResult> </InitiateMultipartUploadResult>
BODY XML
stub_request( stub_request(
:post, :post,
"https://s3-upload-bucket.s3.us-west-1.amazonaws.com/uploads/default/#{test_bucket_prefix}/temp/28fccf8259bbe75b873a2bd2564b778c/test.png?uploads" "https://s3-upload-bucket.s3.us-west-1.amazonaws.com/uploads/default/#{test_bucket_prefix}/temp/28fccf8259bbe75b873a2bd2564b778c/test.png?uploads"
@@ -948,7 +948,7 @@ describe UploadsController do
end end
def stub_list_multipart_request def stub_list_multipart_request
list_multipart_result = <<~BODY list_multipart_result = <<~XML
<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n <?xml version=\"1.0\" encoding=\"UTF-8\"?>\n
<ListPartsResult> <ListPartsResult>
<Bucket>s3-upload-bucket</Bucket> <Bucket>s3-upload-bucket</Bucket>
@@ -974,7 +974,7 @@ describe UploadsController do
</Owner> </Owner>
<StorageClass>STANDARD</StorageClass> <StorageClass>STANDARD</StorageClass>
</ListPartsResult> </ListPartsResult>
BODY XML
stub_request(:get, "https://s3-upload-bucket.s3.us-west-1.amazonaws.com/#{external_upload_stub.key}?max-parts=1&uploadId=#{mock_multipart_upload_id}").to_return({ status: 200, body: list_multipart_result }) stub_request(:get, "https://s3-upload-bucket.s3.us-west-1.amazonaws.com/#{external_upload_stub.key}?max-parts=1&uploadId=#{mock_multipart_upload_id}").to_return({ status: 200, body: list_multipart_result })
end end
@@ -1092,7 +1092,7 @@ describe UploadsController do
end end
def stub_list_multipart_request def stub_list_multipart_request
list_multipart_result = <<~BODY list_multipart_result = <<~XML
<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n <?xml version=\"1.0\" encoding=\"UTF-8\"?>\n
<ListPartsResult> <ListPartsResult>
<Bucket>s3-upload-bucket</Bucket> <Bucket>s3-upload-bucket</Bucket>
@@ -1118,7 +1118,7 @@ describe UploadsController do
</Owner> </Owner>
<StorageClass>STANDARD</StorageClass> <StorageClass>STANDARD</StorageClass>
</ListPartsResult> </ListPartsResult>
BODY XML
stub_request(:get, "#{upload_base_url}/#{external_upload_stub.key}?max-parts=1&uploadId=#{mock_multipart_upload_id}").to_return({ status: 200, body: list_multipart_result }) stub_request(:get, "#{upload_base_url}/#{external_upload_stub.key}?max-parts=1&uploadId=#{mock_multipart_upload_id}").to_return({ status: 200, body: list_multipart_result })
end end

View File

@@ -292,14 +292,14 @@ RSpec.describe ExternalUploadManager do
end end
def copy_object_result def copy_object_result
<<~BODY <<~XML
<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n <?xml version=\"1.0\" encoding=\"UTF-8\"?>\n
<CopyObjectResult <CopyObjectResult
xmlns=\"http://s3.amazonaws.com/doc/2006-03-01/\"> xmlns=\"http://s3.amazonaws.com/doc/2006-03-01/\">
<LastModified>2021-07-19T04:10:41.000Z</LastModified> <LastModified>2021-07-19T04:10:41.000Z</LastModified>
<ETag>&quot;#{etag}&quot;</ETag> <ETag>&quot;#{etag}&quot;</ETag>
</CopyObjectResult> </CopyObjectResult>
BODY XML
end end
def stub_copy_object def stub_copy_object

View File

@@ -175,9 +175,9 @@ describe PostAlerter do
post = Fabricate(:post, topic: pm, user: user1) post = Fabricate(:post, topic: pm, user: user1)
TopicUser.change(user1.id, pm.id, notification_level: TopicUser.notification_levels[:regular]) TopicUser.change(user1.id, pm.id, notification_level: TopicUser.notification_levels[:regular])
quote_raw = <<~STRING quote_raw = <<~MD
[quote="#{user1.username}, post:1, topic:#{pm.id}"]#{post.raw}[/quote] [quote="#{user1.username}, post:1, topic:#{pm.id}"]#{post.raw}[/quote]
STRING MD
expect { expect {
create_post_with_alerts( create_post_with_alerts(
@@ -190,9 +190,9 @@ describe PostAlerter do
group.add(admin) group.add(admin)
TopicUser.change(user2.id, pm.id, notification_level: TopicUser.notification_levels[:regular]) TopicUser.change(user2.id, pm.id, notification_level: TopicUser.notification_levels[:regular])
quote_raw = <<~STRING quote_raw = <<~MD
[quote="#{user2.username}, post:1, topic:#{pm.id}"]#{op.raw}[/quote] [quote="#{user2.username}, post:1, topic:#{pm.id}"]#{op.raw}[/quote]
STRING MD
expect { expect {
create_post_with_alerts( create_post_with_alerts(
@@ -1566,7 +1566,7 @@ describe PostAlerter do
end end
def create_post_with_incoming def create_post_with_incoming
raw_mail = <<~MAIL raw_mail = <<~EMAIL
From: Foo <foo@discourse.org> From: Foo <foo@discourse.org>
To: discourse@example.com To: discourse@example.com
Cc: bar@discourse.org, jim@othersite.com Cc: bar@discourse.org, jim@othersite.com
@@ -1578,7 +1578,7 @@ describe PostAlerter do
Content-Transfer-Encoding: 7bit Content-Transfer-Encoding: 7bit
This is the first email. This is the first email.
MAIL EMAIL
Email::Receiver.new(raw_mail, {}).process! Email::Receiver.new(raw_mail, {}).process!
end end
@@ -1716,7 +1716,7 @@ describe PostAlerter do
email = ActionMailer::Base.deliveries.last email = ActionMailer::Base.deliveries.last
# the reply post from someone who was emailed # the reply post from someone who was emailed
reply_raw_mail = <<~MAIL reply_raw_mail = <<~EMAIL
From: Bar <bar@discourse.org> From: Bar <bar@discourse.org>
To: discourse@example.com To: discourse@example.com
Cc: someothernewcc@baz.com, finalnewcc@doom.com Cc: someothernewcc@baz.com, finalnewcc@doom.com
@@ -1729,7 +1729,7 @@ describe PostAlerter do
Content-Transfer-Encoding: 7bit Content-Transfer-Encoding: 7bit
Hey here is my reply! Hey here is my reply!
MAIL EMAIL
reply_post_from_email = nil reply_post_from_email = nil
expect { expect {
@@ -1767,7 +1767,7 @@ describe PostAlerter do
email = ActionMailer::Base.deliveries.last email = ActionMailer::Base.deliveries.last
# the reply post from someone who was emailed # the reply post from someone who was emailed
reply_raw_mail = <<~MAIL reply_raw_mail = <<~EMAIL
From: Foo <foo@discourse.org> From: Foo <foo@discourse.org>
To: discourse@example.com To: discourse@example.com
Cc: someothernewcc@baz.com, finalnewcc@doom.com Cc: someothernewcc@baz.com, finalnewcc@doom.com
@@ -1780,7 +1780,7 @@ describe PostAlerter do
Content-Transfer-Encoding: 7bit Content-Transfer-Encoding: 7bit
I am ~~Commander Shepherd~~ the OP and I approve of this message. I am ~~Commander Shepherd~~ the OP and I approve of this message.
MAIL EMAIL
reply_post_from_email = nil reply_post_from_email = nil
expect { expect {
@@ -1811,7 +1811,7 @@ describe PostAlerter do
# this is a special case where we are not CC'ing on the original email, # this is a special case where we are not CC'ing on the original email,
# only on the follow up email # only on the follow up email
raw_mail = <<~MAIL raw_mail = <<~EMAIL
From: Foo <foo@discourse.org> From: Foo <foo@discourse.org>
To: discourse@example.com To: discourse@example.com
Subject: Full email group username flow Subject: Full email group username flow
@@ -1822,7 +1822,7 @@ describe PostAlerter do
Content-Transfer-Encoding: 7bit Content-Transfer-Encoding: 7bit
This is the first email. This is the first email.
MAIL EMAIL
incoming_email_post = Email::Receiver.new(raw_mail, {}).process! incoming_email_post = Email::Receiver.new(raw_mail, {}).process!
topic = incoming_email_post.topic topic = incoming_email_post.topic
@@ -1833,7 +1833,7 @@ describe PostAlerter do
email = ActionMailer::Base.deliveries.last email = ActionMailer::Base.deliveries.last
# the reply post from the OP, cc'ing new people in # the reply post from the OP, cc'ing new people in
reply_raw_mail = <<~MAIL reply_raw_mail = <<~EMAIL
From: Foo <foo@discourse.org> From: Foo <foo@discourse.org>
To: discourse@example.com To: discourse@example.com
Cc: someothernewcc@baz.com, finalnewcc@doom.com Cc: someothernewcc@baz.com, finalnewcc@doom.com
@@ -1846,7 +1846,7 @@ describe PostAlerter do
Content-Transfer-Encoding: 7bit Content-Transfer-Encoding: 7bit
I am inviting my mates to this email party. I am inviting my mates to this email party.
MAIL EMAIL
reply_post_from_email = nil reply_post_from_email = nil
expect { expect {

View File

@@ -14,7 +14,7 @@ def load_spec_schema(name)
end end
def api_docs_description def api_docs_description
<<~HEREDOC <<~MD
This page contains the documentation on how to use Discourse through API calls. This page contains the documentation on how to use Discourse through API calls.
> Note: For any endpoints not listed you can follow the > Note: For any endpoints not listed you can follow the
@@ -71,11 +71,11 @@ def api_docs_description
If an endpoint accepts a boolean be sure to specify it as a lowercase If an endpoint accepts a boolean be sure to specify it as a lowercase
`true` or `false` value unless noted otherwise. `true` or `false` value unless noted otherwise.
HEREDOC MD
end end
def direct_uploads_disclaimer def direct_uploads_disclaimer
<<~HEREDOC <<~MD
You must have the correct permissions and CORS settings configured in your You must have the correct permissions and CORS settings configured in your
external provider. We support AWS S3 as the default. See: external provider. We support AWS S3 as the default. See:
@@ -83,7 +83,7 @@ def direct_uploads_disclaimer
An external file store must be set up and `enable_direct_s3_uploads` must An external file store must be set up and `enable_direct_s3_uploads` must
be set to true for this endpoint to function. be set to true for this endpoint to function.
HEREDOC MD
end end
RSpec.configure do |config| RSpec.configure do |config|