DEV: Upgrade to Rails 7

This patch upgrades Rails to version 7.0.2.4.
This commit is contained in:
Loïc Guitaut 2022-03-21 15:28:52 +01:00 committed by Loïc Guitaut
parent 532f9cdb1a
commit 008b700a3f
99 changed files with 724 additions and 691 deletions

View File

@ -13,6 +13,7 @@ allowed:
ignored: ignored:
bundler: bundler:
- rchardet # Ruby terms - rchardet # Ruby terms
- strscan # Ruby
reviewed: reviewed:
bundler: bundler:
@ -31,10 +32,16 @@ reviewed:
- highline # GPL-2.0 OR Ruby terms - highline # GPL-2.0 OR Ruby terms
- htmlentities # MIT - htmlentities # MIT
- image_size # MIT - image_size # MIT
- io-wait # Ruby terms
- json # Ruby terms - json # Ruby terms
- jwt # MIT - jwt # MIT
- kgio # LGPL-2.1+ - kgio # LGPL-2.1+
- logstash-event # Apache-2.0 - logstash-event # Apache-2.0
- net-http # Ruby
- net-imap # Ruby
- net-pop # Ruby
- net-protocol # Ruby
- net-smtp # Ruby
- omniauth # MIT - omniauth # MIT
- openssl # Ruby terms - openssl # Ruby terms
- pg # Ruby terms - pg # Ruby terms
@ -44,5 +51,7 @@ reviewed:
- rubyzip # Ruby terms - rubyzip # Ruby terms
- sidekiq # LGPL (Sidekiq) - sidekiq # LGPL (Sidekiq)
- tilt - tilt
- timeout # Ruby
- unf # BSD-2-Clause - unf # BSD-2-Clause
- unicorn - unicorn
- uri # Ruby

10
Gemfile
View File

@ -18,7 +18,7 @@ else
# this allows us to include the bits of rails we use without pieces we do not. # this allows us to include the bits of rails we use without pieces we do not.
# #
# To issue a rails update bump the version number here # To issue a rails update bump the version number here
rails_version = '6.1.4.7' rails_version = '7.0.2.4'
gem 'actionmailer', rails_version gem 'actionmailer', rails_version
gem 'actionpack', rails_version gem 'actionpack', rails_version
gem 'actionview', rails_version gem 'actionview', rails_version
@ -68,7 +68,7 @@ gem 'http_accept_language', require: false
gem 'discourse-ember-rails', '0.18.6', require: 'ember-rails' gem 'discourse-ember-rails', '0.18.6', require: 'ember-rails'
gem 'discourse-ember-source', '~> 3.12.2' gem 'discourse-ember-source', '~> 3.12.2'
gem 'ember-handlebars-template', '0.8.0' gem 'ember-handlebars-template', '0.8.0'
gem 'discourse-fonts' gem 'discourse-fonts', require: 'discourse_fonts'
gem 'barber' gem 'barber'
@ -190,7 +190,7 @@ if ENV["ALLOW_DEV_POPULATE"] == "1"
gem 'discourse_dev_assets' gem 'discourse_dev_assets'
gem 'faker', "~> 2.16" gem 'faker', "~> 2.16"
else else
group :development do group :development, :test do
gem 'discourse_dev_assets' gem 'discourse_dev_assets'
gem 'faker', "~> 2.16" gem 'faker', "~> 2.16"
end end
@ -268,3 +268,7 @@ gem 'colored2', require: false
gem 'maxminddb' gem 'maxminddb'
gem 'rails_failover', require: false gem 'rails_failover', require: false
# workaround for faraday-net_http, see
# https://github.com/ruby/net-imap/issues/16#issuecomment-803086765
gem 'net-http'

View File

@ -8,22 +8,25 @@ GIT
GEM GEM
remote: https://rubygems.org/ remote: https://rubygems.org/
specs: specs:
actionmailer (6.1.4.7) actionmailer (7.0.2.4)
actionpack (= 6.1.4.7) actionpack (= 7.0.2.4)
actionview (= 6.1.4.7) actionview (= 7.0.2.4)
activejob (= 6.1.4.7) activejob (= 7.0.2.4)
activesupport (= 6.1.4.7) activesupport (= 7.0.2.4)
mail (~> 2.5, >= 2.5.4) mail (~> 2.5, >= 2.5.4)
net-imap
net-pop
net-smtp
rails-dom-testing (~> 2.0) rails-dom-testing (~> 2.0)
actionpack (6.1.4.7) actionpack (7.0.2.4)
actionview (= 6.1.4.7) actionview (= 7.0.2.4)
activesupport (= 6.1.4.7) activesupport (= 7.0.2.4)
rack (~> 2.0, >= 2.0.9) rack (~> 2.0, >= 2.2.0)
rack-test (>= 0.6.3) rack-test (>= 0.6.3)
rails-dom-testing (~> 2.0) rails-dom-testing (~> 2.0)
rails-html-sanitizer (~> 1.0, >= 1.2.0) rails-html-sanitizer (~> 1.0, >= 1.2.0)
actionview (6.1.4.7) actionview (7.0.2.4)
activesupport (= 6.1.4.7) activesupport (= 7.0.2.4)
builder (~> 3.1) builder (~> 3.1)
erubi (~> 1.4) erubi (~> 1.4)
rails-dom-testing (~> 2.0) rails-dom-testing (~> 2.0)
@ -32,20 +35,19 @@ GEM
actionview (>= 6.0.a) actionview (>= 6.0.a)
active_model_serializers (0.8.4) active_model_serializers (0.8.4)
activemodel (>= 3.0) activemodel (>= 3.0)
activejob (6.1.4.7) activejob (7.0.2.4)
activesupport (= 6.1.4.7) activesupport (= 7.0.2.4)
globalid (>= 0.3.6) globalid (>= 0.3.6)
activemodel (6.1.4.7) activemodel (7.0.2.4)
activesupport (= 6.1.4.7) activesupport (= 7.0.2.4)
activerecord (6.1.4.7) activerecord (7.0.2.4)
activemodel (= 6.1.4.7) activemodel (= 7.0.2.4)
activesupport (= 6.1.4.7) activesupport (= 7.0.2.4)
activesupport (6.1.4.7) activesupport (7.0.2.4)
concurrent-ruby (~> 1.0, >= 1.0.2) concurrent-ruby (~> 1.0, >= 1.0.2)
i18n (>= 1.6, < 2) i18n (>= 1.6, < 2)
minitest (>= 5.1) minitest (>= 5.1)
tzinfo (~> 2.0) tzinfo (~> 2.0)
zeitwerk (~> 2.3)
addressable (2.8.0) addressable (2.8.0)
public_suffix (>= 2.0.2, < 5.0) public_suffix (>= 2.0.2, < 5.0)
annotate (3.2.0) annotate (3.2.0)
@ -106,6 +108,7 @@ GEM
debug_inspector (1.1.0) debug_inspector (1.1.0)
diff-lcs (1.5.0) diff-lcs (1.5.0)
diffy (3.4.0) diffy (3.4.0)
digest (3.1.0)
discourse-ember-rails (0.18.6) discourse-ember-rails (0.18.6)
active_model_serializers active_model_serializers
ember-data-source (>= 1.0.0.beta.5) ember-data-source (>= 1.0.0.beta.5)
@ -185,6 +188,7 @@ GEM
progress (~> 3.0, >= 3.0.1) progress (~> 3.0, >= 3.0.1)
image_size (3.0.1) image_size (3.0.1)
in_threads (1.6.0) in_threads (1.6.0)
io-wait (0.2.1)
ipaddr (1.2.4) ipaddr (1.2.4)
jmespath (1.6.1) jmespath (1.6.1)
jquery-rails (4.4.0) jquery-rails (4.4.0)
@ -246,6 +250,24 @@ GEM
multi_xml (0.6.0) multi_xml (0.6.0)
multipart-post (2.1.1) multipart-post (2.1.1)
mustache (1.1.1) mustache (1.1.1)
net-http (0.2.0)
net-protocol
uri
net-imap (0.2.3)
digest
net-protocol
strscan
net-pop (0.1.1)
digest
net-protocol
timeout
net-protocol (0.1.2)
io-wait
timeout
net-smtp (0.3.1)
digest
net-protocol
timeout
nio4r (2.5.8) nio4r (2.5.8)
nokogiri (1.13.4) nokogiri (1.13.4)
mini_portile2 (~> 2.8.0) mini_portile2 (~> 2.8.0)
@ -332,12 +354,13 @@ GEM
rails_multisite (4.0.1) rails_multisite (4.0.1)
activerecord (> 5.0, < 7.1) activerecord (> 5.0, < 7.1)
railties (> 5.0, < 7.1) railties (> 5.0, < 7.1)
railties (6.1.4.7) railties (7.0.2.4)
actionpack (= 6.1.4.7) actionpack (= 7.0.2.4)
activesupport (= 6.1.4.7) activesupport (= 7.0.2.4)
method_source method_source
rake (>= 0.13) rake (>= 12.2)
thor (~> 1.0) thor (~> 1.0)
zeitwerk (~> 2.5)
rainbow (3.1.1) rainbow (3.1.1)
raindrops (0.20.0) raindrops (0.20.0)
rake (13.0.6) rake (13.0.6)
@ -452,9 +475,11 @@ GEM
sprockets (>= 3.0.0) sprockets (>= 3.0.0)
sshkey (2.0.0) sshkey (2.0.0)
stackprof (0.2.19) stackprof (0.2.19)
strscan (3.0.1)
test-prof (1.0.8) test-prof (1.0.8)
thor (1.2.1) thor (1.2.1)
tilt (2.0.10) tilt (2.0.10)
timeout (0.2.0)
tzinfo (2.0.4) tzinfo (2.0.4)
concurrent-ruby (~> 1.0) concurrent-ruby (~> 1.0)
uglifier (4.2.0) uglifier (4.2.0)
@ -467,6 +492,7 @@ GEM
kgio (~> 2.6) kgio (~> 2.6)
raindrops (~> 0.7) raindrops (~> 0.7)
uniform_notifier (1.16.0) uniform_notifier (1.16.0)
uri (0.11.0)
uri_template (0.7.0) uri_template (0.7.0)
webmock (3.14.0) webmock (3.14.0)
addressable (>= 2.8.0) addressable (>= 2.8.0)
@ -489,14 +515,14 @@ PLATFORMS
x86_64-linux x86_64-linux
DEPENDENCIES DEPENDENCIES
actionmailer (= 6.1.4.7) actionmailer (= 7.0.2.4)
actionpack (= 6.1.4.7) actionpack (= 7.0.2.4)
actionview (= 6.1.4.7) actionview (= 7.0.2.4)
actionview_precompiler actionview_precompiler
active_model_serializers (~> 0.8.3) active_model_serializers (~> 0.8.3)
activemodel (= 6.1.4.7) activemodel (= 7.0.2.4)
activerecord (= 6.1.4.7) activerecord (= 7.0.2.4)
activesupport (= 6.1.4.7) activesupport (= 7.0.2.4)
addressable addressable
annotate annotate
aws-sdk-s3 aws-sdk-s3
@ -556,6 +582,7 @@ DEPENDENCIES
mocha mocha
multi_json multi_json
mustache mustache
net-http
nokogiri nokogiri
oj oj
omniauth omniauth
@ -575,7 +602,7 @@ DEPENDENCIES
rack-protection rack-protection
rails_failover rails_failover
rails_multisite rails_multisite
railties (= 6.1.4.7) railties (= 7.0.2.4)
rake rake
rb-fsevent rb-fsevent
rbtrace rbtrace

View File

@ -68,7 +68,7 @@ class ApplicationController < ActionController::Base
def use_crawler_layout? def use_crawler_layout?
@use_crawler_layout ||= @use_crawler_layout ||=
request.user_agent && request.user_agent &&
(request.content_type.blank? || request.content_type.include?('html')) && (request.media_type.blank? || request.media_type.include?('html')) &&
!['json', 'rss'].include?(params[:format]) && !['json', 'rss'].include?(params[:format]) &&
(has_escaped_fragment? || params.key?("print") || show_browser_update? || (has_escaped_fragment? || params.key?("print") || show_browser_update? ||
CrawlerDetection.crawler?(request.user_agent, request.headers["HTTP_VIA"]) CrawlerDetection.crawler?(request.user_agent, request.headers["HTTP_VIA"])
@ -287,7 +287,7 @@ class ApplicationController < ActionController::Base
# cause category / topic was deleted # cause category / topic was deleted
if permalink.present? && permalink.target_url if permalink.present? && permalink.target_url
# permalink present, redirect to that URL # permalink present, redirect to that URL
redirect_with_client_support permalink.target_url, status: :moved_permanently redirect_with_client_support permalink.target_url, status: :moved_permanently, allow_other_host: true
return return
end end
end end
@ -834,7 +834,7 @@ class ApplicationController < ActionController::Base
end end
if UserApiKey.allowed_scopes.superset?(Set.new(["one_time_password"])) if UserApiKey.allowed_scopes.superset?(Set.new(["one_time_password"]))
redirect_to("#{params[:auth_redirect]}?otp=true") redirect_to("#{params[:auth_redirect]}?otp=true", allow_other_host: true)
return return
end end
end end

View File

@ -1,6 +1,9 @@
# frozen_string_literal: true # frozen_string_literal: true
class PostsController < ApplicationController class PostsController < ApplicationController
# Bug with Rails 7+
# see https://github.com/rails/rails/issues/44867
self._flash_types -= [:notice]
requires_login except: [ requires_login except: [
:show, :show,

View File

@ -33,7 +33,7 @@ class SessionController < ApplicationController
if SiteSetting.verbose_discourse_connect_logging if SiteSetting.verbose_discourse_connect_logging
Rails.logger.warn("Verbose SSO log: Started SSO process\n\n#{sso.diagnostics}") Rails.logger.warn("Verbose SSO log: Started SSO process\n\n#{sso.diagnostics}")
end end
redirect_to sso_url(sso) redirect_to sso_url(sso), allow_other_host: true
else else
render body: nil, status: 404 render body: nil, status: 404
end end
@ -69,14 +69,14 @@ class SessionController < ApplicationController
# for the login modal # for the login modal
cookies[:sso_destination_url] = data[:sso_redirect_url] cookies[:sso_destination_url] = data[:sso_redirect_url]
else else
redirect_to data[:sso_redirect_url] redirect_to data[:sso_redirect_url], allow_other_host: true
end end
elsif result.no_second_factors_enabled? elsif result.no_second_factors_enabled?
if request.xhr? if request.xhr?
# for the login modal # for the login modal
cookies[:sso_destination_url] = result.data[:sso_redirect_url] cookies[:sso_destination_url] = result.data[:sso_redirect_url]
else else
redirect_to result.data[:sso_redirect_url] redirect_to result.data[:sso_redirect_url], allow_other_host: true
end end
elsif result.second_factor_auth_completed? elsif result.second_factor_auth_completed?
redirect_url = result.data[:sso_redirect_url] redirect_url = result.data[:sso_redirect_url]
@ -169,7 +169,7 @@ class SessionController < ApplicationController
# they are already pre-approved because they have been invited # they are already pre-approved because they have been invited
if SiteSetting.must_approve_users? && !user.approved? && invite.blank? if SiteSetting.must_approve_users? && !user.approved? && invite.blank?
if SiteSetting.discourse_connect_not_approved_url.present? if SiteSetting.discourse_connect_not_approved_url.present?
redirect_to SiteSetting.discourse_connect_not_approved_url redirect_to SiteSetting.discourse_connect_not_approved_url, allow_other_host: true
else else
render_sso_error(text: I18n.t("discourse_connect.account_not_approved"), status: 403) render_sso_error(text: I18n.t("discourse_connect.account_not_approved"), status: 403)
end end
@ -220,7 +220,7 @@ class SessionController < ApplicationController
return_path = path("/") return_path = path("/")
end end
redirect_to return_path redirect_to return_path, allow_other_host: true
else else
render_sso_error(text: I18n.t("discourse_connect.not_found"), status: 500) render_sso_error(text: I18n.t("discourse_connect.not_found"), status: 500)
end end
@ -583,7 +583,7 @@ class SessionController < ApplicationController
redirect_url: redirect_url redirect_url: redirect_url
} }
else else
redirect_to redirect_url redirect_to redirect_url, allow_other_host: true
end end
end end

View File

@ -30,7 +30,7 @@ class StaticController < ApplicationController
if map.has_key?(@page) if map.has_key?(@page)
site_setting_key = map[@page][:redirect] site_setting_key = map[@page][:redirect]
url = SiteSetting.get(site_setting_key) if site_setting_key url = SiteSetting.get(site_setting_key) if site_setting_key
return redirect_to(url) if url.present? return redirect_to(url, allow_other_host: true) if url.present?
end end
# The /guidelines route ALWAYS shows our FAQ, ignoring the faq_url site setting. # The /guidelines route ALWAYS shows our FAQ, ignoring the faq_url site setting.

View File

@ -15,7 +15,7 @@ class SvgSpriteController < ApplicationController
theme_id = params[:theme_id].to_i if params[:theme_id].present? theme_id = params[:theme_id].to_i if params[:theme_id].present?
if SvgSprite.version(theme_id) != params[:version] if SvgSprite.version(theme_id) != params[:version]
return redirect_to UrlHelper.absolute((SvgSprite.path(theme_id))) return redirect_to UrlHelper.absolute((SvgSprite.path(theme_id))), allow_other_host: true
end end
svg_sprite = "window.__svg_sprite = #{SvgSprite.bundle(theme_id).inspect};" svg_sprite = "window.__svg_sprite = #{SvgSprite.bundle(theme_id).inspect};"

View File

@ -118,7 +118,7 @@ class UploadsController < ApplicationController
if Discourse.store.internal? if Discourse.store.internal?
send_file_local_upload(upload) send_file_local_upload(upload)
else else
redirect_to Discourse.store.url_for(upload, force_download: force_download?) redirect_to Discourse.store.url_for(upload, force_download: force_download?), allow_other_host: true
end end
else else
render_404 render_404
@ -149,7 +149,7 @@ class UploadsController < ApplicationController
# private, so we don't want to go to the CDN url just yet otherwise we # private, so we don't want to go to the CDN url just yet otherwise we
# will get a 403. if the upload is not secure we assume the ACL is public # will get a 403. if the upload is not secure we assume the ACL is public
signed_secure_url = Discourse.store.signed_url_for_path(path_with_ext) signed_secure_url = Discourse.store.signed_url_for_path(path_with_ext)
redirect_to upload.secure? ? signed_secure_url : Discourse.store.cdn_url(upload.url) redirect_to upload.secure? ? signed_secure_url : Discourse.store.cdn_url(upload.url), allow_other_host: true
end end
def handle_secure_upload_request(upload, path_with_ext = nil) def handle_secure_upload_request(upload, path_with_ext = nil)
@ -166,14 +166,14 @@ class UploadsController < ApplicationController
# url_for figures out the full URL, handling multisite DBs, # url_for figures out the full URL, handling multisite DBs,
# and will return a presigned URL for the upload # and will return a presigned URL for the upload
if path_with_ext.blank? if path_with_ext.blank?
return redirect_to Discourse.store.url_for(upload, force_download: force_download?) return redirect_to Discourse.store.url_for(upload, force_download: force_download?), allow_other_host: true
end end
redirect_to Discourse.store.signed_url_for_path( redirect_to Discourse.store.signed_url_for_path(
path_with_ext, path_with_ext,
expires_in: S3Helper::DOWNLOAD_URL_EXPIRES_AFTER_SECONDS, expires_in: S3Helper::DOWNLOAD_URL_EXPIRES_AFTER_SECONDS,
force_download: force_download? force_download: force_download?
) ), allow_other_host: true
end end
def metadata def metadata

View File

@ -97,7 +97,7 @@ class UserApiKeysController < ApplicationController
query_attributes << "oneTimePassword=#{CGI.escape(otp_payload)}" if scopes.include?("one_time_password") query_attributes << "oneTimePassword=#{CGI.escape(otp_payload)}" if scopes.include?("one_time_password")
uri.query = query_attributes.compact.join('&') uri.query = query_attributes.compact.join('&')
redirect_to(uri.to_s) redirect_to(uri.to_s, allow_other_host: true)
else else
respond_to do |format| respond_to do |format|
format.html { render :show } format.html { render :show }
@ -138,7 +138,7 @@ class UserApiKeysController < ApplicationController
otp_payload = one_time_password(public_key, current_user.username) otp_payload = one_time_password(public_key, current_user.username)
redirect_path = "#{params[:auth_redirect]}?oneTimePassword=#{CGI.escape(otp_payload)}" redirect_path = "#{params[:auth_redirect]}?oneTimePassword=#{CGI.escape(otp_payload)}"
redirect_to(redirect_path) redirect_to(redirect_path, allow_other_host: true)
end end
def revoke def revoke

View File

@ -112,7 +112,7 @@ class UserAvatarsController < ApplicationController
if !Discourse.avatar_sizes.include?(size) && Discourse.store.external? if !Discourse.avatar_sizes.include?(size) && Discourse.store.external?
closest = Discourse.avatar_sizes.to_a.min { |a, b| (size - a).abs <=> (size - b).abs } closest = Discourse.avatar_sizes.to_a.min { |a, b| (size - a).abs <=> (size - b).abs }
avatar_url = UserAvatar.local_avatar_url(hostname, user.encoded_username(lower: true), upload_id, closest) avatar_url = UserAvatar.local_avatar_url(hostname, user.encoded_username(lower: true), upload_id, closest)
return redirect_to cdn_path(avatar_url) return redirect_to cdn_path(avatar_url), allow_other_host: true
end end
upload = Upload.find_by(id: upload_id) if user&.user_avatar&.contains_upload?(upload_id) upload = Upload.find_by(id: upload_id) if user&.user_avatar&.contains_upload?(upload_id)
@ -120,7 +120,7 @@ class UserAvatarsController < ApplicationController
if user.uploaded_avatar && !upload if user.uploaded_avatar && !upload
avatar_url = UserAvatar.local_avatar_url(hostname, user.encoded_username(lower: true), user.uploaded_avatar_id, size) avatar_url = UserAvatar.local_avatar_url(hostname, user.encoded_username(lower: true), user.uploaded_avatar_id, size)
return redirect_to cdn_path(avatar_url) return redirect_to cdn_path(avatar_url), allow_other_host: true
elsif upload && optimized = get_optimized_image(upload, size) elsif upload && optimized = get_optimized_image(upload, size)
if optimized.local? if optimized.local?
optimized_path = Discourse.store.path_for(optimized) optimized_path = Discourse.store.path_for(optimized)

View File

@ -1024,7 +1024,7 @@ class UsersController < ApplicationController
if SiteSetting.enable_discourse_connect_provider && payload = cookies.delete(:sso_payload) if SiteSetting.enable_discourse_connect_provider && payload = cookies.delete(:sso_payload)
return redirect_to(session_sso_provider_url + "?" + payload) return redirect_to(session_sso_provider_url + "?" + payload)
elsif destination_url = cookies.delete(:destination_url) elsif destination_url = cookies.delete(:destination_url)
return redirect_to(destination_url) return redirect_to(destination_url, allow_other_host: true)
else else
return redirect_to(path('/')) return redirect_to(path('/'))
end end
@ -1086,7 +1086,7 @@ class UsersController < ApplicationController
if Wizard.user_requires_completion?(@user) if Wizard.user_requires_completion?(@user)
return redirect_to(wizard_path) return redirect_to(wizard_path)
elsif destination_url.present? elsif destination_url.present?
return redirect_to(destination_url) return redirect_to(destination_url, allow_other_host: true)
elsif SiteSetting.enable_discourse_connect_provider && payload = cookies.delete(:sso_payload) elsif SiteSetting.enable_discourse_connect_provider && payload = cookies.delete(:sso_payload)
return redirect_to(session_sso_provider_url + "?" + payload) return redirect_to(session_sso_provider_url + "?" + payload)
end end

View File

@ -1,7 +1,5 @@
# frozen_string_literal: true # frozen_string_literal: true
require_dependency 'email/sender'
module Jobs module Jobs
class GroupSmtpEmail < ::Jobs::Base class GroupSmtpEmail < ::Jobs::Base
include Skippable include Skippable

View File

@ -57,8 +57,8 @@ module Jobs
end end
def keys_list def keys_list
messages = old_site_settings_keys.map { |key| "#{key.name} - #{key.updated_at.to_date.to_s(:db)}" } messages = old_site_settings_keys.map { |key| "#{key.name} - #{key.updated_at.to_date.to_fs(:db)}" }
old_api_keys.each_with_object(messages) { |key, array| array << "#{[key.description, key.user&.username, key.created_at.to_date.to_s(:db)].compact.join(" - ")}" } old_api_keys.each_with_object(messages) { |key, array| array << "#{[key.description, key.user&.username, key.created_at.to_date.to_fs(:db)].compact.join(" - ")}" }
messages.join("\n") messages.join("\n")
end end
end end

View File

@ -1,7 +1,5 @@
# frozen_string_literal: true # frozen_string_literal: true
require_dependency 'email/message_builder'
class GroupSmtpMailer < ActionMailer::Base class GroupSmtpMailer < ActionMailer::Base
include Email::BuildEmailHelper include Email::BuildEmailHelper

View File

@ -1,6 +1,5 @@
# frozen_string_literal: true # frozen_string_literal: true
require_dependency 'global_path'
require 'csv' require 'csv'
require 'json_schemer' require 'json_schemer'

View File

@ -153,7 +153,9 @@ class TopicList
ft.topic_list = self ft.topic_list = self
end end
ActiveRecord::Associations::Preloader.new.preload(@topics, [:image_upload, topic_thumbnails: :optimized_image]) ActiveRecord::Associations::Preloader
.new(records: @topics, associations: [:image_upload, topic_thumbnails: :optimized_image])
.call
if preloaded_custom_fields.present? if preloaded_custom_fields.present?
Topic.preload_custom_fields(@topics, preloaded_custom_fields) Topic.preload_custom_fields(@topics, preloaded_custom_fields)

View File

@ -1,7 +1,5 @@
# frozen_string_literal: true # frozen_string_literal: true
require "i18n/i18n_interpolation_keys_finder"
class TranslationOverride < ActiveRecord::Base class TranslationOverride < ActiveRecord::Base
# Allowlist i18n interpolation keys that can be included when customizing translations # Allowlist i18n interpolation keys that can be included when customizing translations
ALLOWED_CUSTOM_INTERPOLATION_KEYS = { ALLOWED_CUSTOM_INTERPOLATION_KEYS = {

View File

@ -73,8 +73,8 @@ class Bookmarkable
# @param [Array] bookmarks The array of bookmarks after initial listing and filtering, note this is # @param [Array] bookmarks The array of bookmarks after initial listing and filtering, note this is
# array _not_ an ActiveRecord::Relation. # array _not_ an ActiveRecord::Relation.
def perform_preload(bookmarks) def perform_preload(bookmarks)
ActiveRecord::Associations::Preloader.new.preload( ActiveRecord::Associations::Preloader
Bookmark.select_type(bookmarks, model.to_s), { bookmarkable: preload_associations } .new(records: Bookmark.select_type(bookmarks, model.to_s), associations: [bookmarkable: preload_associations])
) .call
end end
end end

View File

@ -21,7 +21,7 @@ require 'action_mailer/railtie'
require 'sprockets/railtie' require 'sprockets/railtie'
# Plugin related stuff # Plugin related stuff
require_relative '../lib/plugin_initialization_guard' require_relative '../lib/plugin'
require_relative '../lib/discourse_event' require_relative '../lib/discourse_event'
require_relative '../lib/discourse_plugin_registry' require_relative '../lib/discourse_plugin_registry'
@ -31,7 +31,13 @@ require_relative '../lib/plugin_gem'
require_relative '../app/models/global_setting' require_relative '../app/models/global_setting'
GlobalSetting.configure! GlobalSetting.configure!
if GlobalSetting.load_plugins? if GlobalSetting.load_plugins?
require_relative '../lib/custom_setting_providers' # Support for plugins to register custom setting providers. They can do this
# by having a file, `register_provider.rb` in their root that will be run
# at this point.
Dir.glob(File.join(File.dirname(__FILE__), '../plugins', '*', "register_provider.rb")) do |p|
require p
end
end end
GlobalSetting.load_defaults GlobalSetting.load_defaults
if GlobalSetting.try(:cdn_url).present? && GlobalSetting.cdn_url !~ /^https?:\/\// if GlobalSetting.try(:cdn_url).present? && GlobalSetting.cdn_url !~ /^https?:\/\//
@ -85,14 +91,11 @@ module Discourse
# Application configuration should go into files in config/initializers # Application configuration should go into files in config/initializers
# -- all .rb files in that directory are automatically loaded. # -- all .rb files in that directory are automatically loaded.
# this pattern is somewhat odd but the reloader gets very require 'discourse'
# confused here if we load the deps without `lib` it thinks require 'js_locale_helper'
# discourse.rb is under the discourse folder incorrectly
require_dependency 'lib/discourse'
require_dependency 'lib/js_locale_helper'
# tiny file needed by site settings # tiny file needed by site settings
require_dependency 'lib/highlight_js/highlight_js' require 'highlight_js'
# we skip it cause we configure it in the initializer # we skip it cause we configure it in the initializer
# the railtie for message_bus would insert it in the # the railtie for message_bus would insert it in the
@ -109,120 +112,19 @@ module Discourse
# issue is image_optim crashes on missing dependencies # issue is image_optim crashes on missing dependencies
config.assets.image_optim = false config.assets.image_optim = false
config.autoloader = :zeitwerk
# Custom directories with classes and modules you want to be autoloadable. # Custom directories with classes and modules you want to be autoloadable.
config.autoload_paths += Dir["#{config.root}/lib"] config.autoload_paths << "#{root}/lib"
config.autoload_paths += Dir["#{config.root}/lib/common_passwords"] config.autoload_paths << "#{root}/lib/guardian"
config.autoload_paths += Dir["#{config.root}/lib/highlight_js"] config.autoload_paths << "#{root}/lib/i18n"
config.autoload_paths += Dir["#{config.root}/lib/i18n"] config.autoload_paths << "#{root}/lib/validators"
config.autoload_paths += Dir["#{config.root}/lib/validators/"]
Rails.autoloaders.main.ignore(Dir["#{config.root}/app/models/reports"])
Rails.autoloaders.main.ignore(Dir["#{config.root}/lib/freedom_patches"])
def watchable_args
files, dirs = super
# Skip the assets directory. It doesn't contain any .rb files, so watching it
# is just slowing things down and raising warnings about node_modules symlinks
app_file_extensions = dirs.delete("#{config.root}/app")
Dir["#{config.root}/app/*"].reject { |path| path.end_with? "/assets" }.each do |path|
dirs[path] = app_file_extensions
end
[files, dirs]
end
# Only load the plugins named here, in the order given (default is alphabetical). # Only load the plugins named here, in the order given (default is alphabetical).
# :all can be used as a placeholder for all plugins not explicitly named. # :all can be used as a placeholder for all plugins not explicitly named.
# config.plugins = [ :exception_notification, :ssl_requirement, :all ] # config.plugins = [ :exception_notification, :ssl_requirement, :all ]
config.assets.paths += %W(#{config.root}/config/locales #{config.root}/public/javascripts)
# Allows us to skip minification on some files # Allows us to skip minification on some files
config.assets.skip_minification = [] config.assets.skip_minification = []
# explicitly precompile any images in plugins ( /assets/images ) path
config.assets.precompile += [lambda do |filename, path|
path =~ /assets\/images/ && !%w(.js .css).include?(File.extname(filename))
end]
config.assets.precompile += %w{
vendor.js
admin.js
browser-detect.js
browser-update.js
break_string.js
ember_jquery.js
pretty-text-bundle.js
wizard-application.js
wizard-vendor.js
markdown-it-bundle.js
service-worker.js
google-tag-manager.js
google-universal-analytics-v3.js
google-universal-analytics-v4.js
start-discourse.js
print-page.js
omniauth-complete.js
activate-account.js
auto-redirect.js
wizard-start.js
locales/i18n.js
discourse/app/lib/webauthn.js
confirm-new-email/confirm-new-email.js
confirm-new-email/bootstrap.js
onpopstate-handler.js
embed-application.js
discourse/tests/active-plugins.js
admin-plugins.js
discourse/tests/test_starter.js
}
if EmberCli.enabled?
config.assets.precompile += %w{
discourse.js
test-support.js
test-helpers.js
scripts/discourse-test-listen-boot
scripts/discourse-boot
}
else
config.assets.precompile += %w{
application.js
discourse/tests/test-support-rails.js
discourse/tests/test-helpers-rails.js
vendor-theme-tests.js
}
end
# Precompile all available locales
unless GlobalSetting.try(:omit_base_locales)
Dir.glob("#{config.root}/app/assets/javascripts/locales/*.js.erb").each do |file|
config.assets.precompile << "locales/#{file.match(/([a-z_A-Z]+\.js)\.erb$/)[1]}"
end
end
# out of the box sprockets 3 grabs loose files that are hanging in assets,
# the exclusion list does not include hbs so you double compile all this stuff
initializer :fix_sprockets_loose_file_searcher, after: :set_default_precompile do |app|
app.config.assets.precompile.delete(Sprockets::Railtie::LOOSE_APP_ASSETS)
# We don't want application from node_modules, only from the root
app.config.assets.precompile.delete(/(?:\/|\\|\A)application\.(css|js)$/)
app.config.assets.precompile += ['application.js']
start_path = ::Rails.root.join("app/assets").to_s
exclude = ['.es6', '.hbs', '.hbr', '.js', '.css', '.lock', '.json', '.log', '.html', '']
app.config.assets.precompile << lambda do |logical_path, filename|
filename.start_with?(start_path) &&
!filename.include?("/node_modules/") &&
!filename.include?("/dist/") &&
!exclude.include?(File.extname(logical_path))
end
end
# Set Time.zone default to the specified zone and make Active Record auto-convert to this zone. # Set Time.zone default to the specified zone and make Active Record auto-convert to this zone.
# Run "rake -D time" for a list of tasks for finding time zone names. Default is UTC. # Run "rake -D time" for a list of tasks for finding time zone names. Default is UTC.
config.time_zone = 'UTC' config.time_zone = 'UTC'
@ -234,24 +136,6 @@ module Discourse
# Configure the default encoding used in templates for Ruby 1.9. # Configure the default encoding used in templates for Ruby 1.9.
config.encoding = 'utf-8' config.encoding = 'utf-8'
# Configure sensitive parameters which will be filtered from the log file.
config.filter_parameters += [
:password,
:pop3_polling_password,
:api_key,
:s3_secret_access_key,
:twitter_consumer_secret,
:facebook_app_secret,
:github_client_secret,
:second_factor_token,
]
# Enable the asset pipeline
config.assets.enabled = true
# Version of your assets, change this if you want to expire all your assets
config.assets.version = '1.2.5'
# see: http://stackoverflow.com/questions/11894180/how-does-one-correctly-add-custom-sql-dml-in-migrations/11894420#11894420 # see: http://stackoverflow.com/questions/11894180/how-does-one-correctly-add-custom-sql-dml-in-migrations/11894420#11894420
config.active_record.schema_format = :sql config.active_record.schema_format = :sql
@ -336,45 +220,21 @@ module Discourse
if Rails.env.test? && GlobalSetting.load_plugins? if Rails.env.test? && GlobalSetting.load_plugins?
Discourse.activate_plugins! Discourse.activate_plugins!
elsif GlobalSetting.load_plugins? elsif GlobalSetting.load_plugins?
plugin_initialization_guard do Plugin.initialization_guard do
Discourse.activate_plugins! Discourse.activate_plugins!
end end
end end
Discourse.find_plugin_js_assets(include_disabled: true).each do |file|
config.assets.precompile << "#{file}.js"
end
# Use discourse-fonts gem to symlink fonts and generate .scss file # Use discourse-fonts gem to symlink fonts and generate .scss file
fonts_path = File.join(config.root, 'public/fonts') fonts_path = File.join(config.root, 'public/fonts')
Discourse::Utils.atomic_ln_s(DiscourseFonts.path_for_fonts, fonts_path) Discourse::Utils.atomic_ln_s(DiscourseFonts.path_for_fonts, fonts_path)
require_dependency 'stylesheet/manager' require 'stylesheet/manager'
require_dependency 'svg_sprite/svg_sprite' require 'svg_sprite'
config.after_initialize do config.after_initialize do
# require common dependencies that are often required by plugins
# in the past observers would load them as side-effects
# correct behavior is for plugins to require stuff they need,
# however it would be a risky and breaking change not to require here
require_dependency 'category'
require_dependency 'post'
require_dependency 'topic'
require_dependency 'user'
require_dependency 'post_action'
require_dependency 'post_revision'
require_dependency 'notification'
require_dependency 'topic_user'
require_dependency 'topic_view'
require_dependency 'topic_list'
require_dependency 'group'
require_dependency 'user_field'
require_dependency 'post_action_type'
# Ensure that Discourse event triggers for web hooks are loaded
require_dependency 'web_hook'
# Load plugins # Load plugins
plugin_initialization_guard do Plugin.initialization_guard do
Discourse.plugins.each(&:notify_after_initialize) Discourse.plugins.each(&:notify_after_initialize)
end end

View File

@ -1,12 +1,12 @@
# frozen_string_literal: true # frozen_string_literal: true
# Load the rails application # Load the Rails application.
require File.expand_path('../application', __FILE__) require_relative "application"
# Initialize the rails application # Initialize the Rails application.
Discourse::Application.initialize! Rails.application.initialize!
# When in "dev" mode, ensure we won't be sending any emails # When in "dev" mode, ensure we won't be sending any emails
if Rails.env.development? && ActionMailer::Base.smtp_settings != { address: "localhost", port: 1025 } if Rails.env.development? && ActionMailer::Base.smtp_settings.slice(:address, :port) != { address: "localhost", port: 1025 }
fail "In development mode, you should be using mailhog otherwise you might end up sending thousands of digest emails" fail "In development mode, you should be using mailhog otherwise you might end up sending thousands of digest emails"
end end

View File

@ -36,5 +36,15 @@ Rails.autoloaders.each do |autoloader|
'onceoff' => 'Jobs', 'onceoff' => 'Jobs',
'regular' => 'Jobs', 'regular' => 'Jobs',
'scheduled' => 'Jobs', 'scheduled' => 'Jobs',
'google_oauth2_authenticator' => 'GoogleOAuth2Authenticator',
'omniauth_strategies' => 'OmniAuthStrategies',
'csrf_token_verifier' => 'CSRFTokenVerifier',
'html' => 'HTML',
'json' => 'JSON'
) )
end end
Rails.autoloaders.main.ignore("lib/tasks",
"lib/generators",
"lib/freedom_patches",
"lib/i18n/backend",
"lib/unicorn_logstash_patch.rb")

View File

@ -30,14 +30,14 @@ if defined?(RailsFailover::ActiveRecord)
return unless Rails.configuration.active_record_rails_failover return unless Rails.configuration.active_record_rails_failover
if Rails.configuration.multisite if Rails.configuration.multisite
if ActiveRecord::Base.current_role == ActiveRecord::Base.reading_role if ActiveRecord::Base.current_role == ActiveRecord.reading_role
RailsMultisite::ConnectionManagement.default_connection_handler = RailsMultisite::ConnectionManagement.default_connection_handler =
ActiveRecord::Base.connection_handlers[ActiveRecord::Base.reading_role] ActiveRecord::Base.connection_handlers[ActiveRecord.reading_role]
end end
end end
RailsFailover::ActiveRecord.on_failover do |role| RailsFailover::ActiveRecord.on_failover do |role|
if role == ActiveRecord::Base.writing_role # Multisite master if role == ActiveRecord.writing_role # Multisite master
RailsMultisite::ConnectionManagement.each_connection do RailsMultisite::ConnectionManagement.each_connection do
Discourse.enable_readonly_mode(Discourse::PG_READONLY_MODE_KEY) Discourse.enable_readonly_mode(Discourse::PG_READONLY_MODE_KEY)
end end
@ -47,16 +47,16 @@ if defined?(RailsFailover::ActiveRecord)
end end
# Test connection to the master, and trigger master failover if needed # Test connection to the master, and trigger master failover if needed
ActiveRecord::Base.connected_to(role: ActiveRecord::Base.writing_role) do ActiveRecord::Base.connected_to(role: ActiveRecord.writing_role) do
ActiveRecord::Base.connection.active? ActiveRecord::Base.connection.active?
rescue PG::ConnectionBad, PG::UnableToSend, PG::ServerError rescue PG::ConnectionBad, PG::UnableToSend, PG::ServerError
RailsFailover::ActiveRecord.verify_primary(ActiveRecord::Base.writing_role) RailsFailover::ActiveRecord.verify_primary(ActiveRecord.writing_role)
end end
end end
end end
RailsFailover::ActiveRecord.on_fallback do |role| RailsFailover::ActiveRecord.on_fallback do |role|
if role == ActiveRecord::Base.writing_role # Multisite master if role == ActiveRecord.writing_role # Multisite master
RailsMultisite::ConnectionManagement.each_connection do RailsMultisite::ConnectionManagement.each_connection do
Discourse.disable_readonly_mode(Discourse::PG_READONLY_MODE_KEY) Discourse.disable_readonly_mode(Discourse::PG_READONLY_MODE_KEY)
end end
@ -68,7 +68,7 @@ if defined?(RailsFailover::ActiveRecord)
if Rails.configuration.multisite if Rails.configuration.multisite
RailsMultisite::ConnectionManagement.default_connection_handler = RailsMultisite::ConnectionManagement.default_connection_handler =
ActiveRecord::Base.connection_handlers[ActiveRecord::Base.writing_role] ActiveRecord::Base.connection_handlers[ActiveRecord.writing_role]
end end
end end

View File

@ -6,17 +6,15 @@
Discourse.git_version Discourse.git_version
if GlobalSetting.skip_redis? if GlobalSetting.skip_redis?
# Requiring this file explicitly prevents it from being autoloaded and so the
# provider attribute is not cleared
require File.expand_path('../../../app/models/site_setting', __FILE__)
require 'site_settings/local_process_provider' require 'site_settings/local_process_provider'
Rails.cache = Discourse.cache Rails.cache = Discourse.cache
SiteSetting.provider = SiteSettings::LocalProcessProvider.new Rails.application.config.to_prepare do
SiteSetting.provider = SiteSettings::LocalProcessProvider.new
end
return return
end end
reload_settings = lambda { Rails.application.config.to_prepare do
RailsMultisite::ConnectionManagement.safe_each_connection do RailsMultisite::ConnectionManagement.safe_each_connection do
begin begin
SiteSetting.refresh! SiteSetting.refresh!
@ -28,12 +26,4 @@ reload_settings = lambda {
# This will happen when migrating a new database # This will happen when migrating a new database
end end
end end
}
reload_settings.call
if !Rails.configuration.cache_classes
ActiveSupport::Reloader.to_prepare do
reload_settings.call
end
end end

View File

@ -2,30 +2,32 @@
return if GlobalSetting.skip_db? return if GlobalSetting.skip_db?
# Some sanity checking so we don't count on an unindexed column on boot Rails.application.config.to_prepare do
begin # Some sanity checking so we don't count on an unindexed column on boot
if ActiveRecord::Base.connection.table_exists?(:users) && begin
User.limit(20).count < 20 && if ActiveRecord::Base.connection.table_exists?(:users) &&
User.where(admin: true).human_users.count == 0 User.limit(20).count < 20 &&
User.where(admin: true).human_users.count == 0
notice = notice =
if GlobalSetting.developer_emails.blank? if GlobalSetting.developer_emails.blank?
"Congratulations, you installed Discourse! Unfortunately, no administrator emails were defined during setup, so finalizing the configuration <a href='https://meta.discourse.org/t/create-admin-account-from-console/17274'>may be difficult</a>." "Congratulations, you installed Discourse! Unfortunately, no administrator emails were defined during setup, so finalizing the configuration <a href='https://meta.discourse.org/t/create-admin-account-from-console/17274'>may be difficult</a>."
else
emails = GlobalSetting.developer_emails.split(",")
if emails.length > 1
emails = emails[0..-2].join(', ') << " or #{emails[-1]} "
else else
emails = emails[0] emails = GlobalSetting.developer_emails.split(",")
if emails.length > 1
emails = emails[0..-2].join(', ') << " or #{emails[-1]} "
else
emails = emails[0]
end
"Congratulations, you installed Discourse! Register a new admin account with #{emails} to finalize configuration."
end end
"Congratulations, you installed Discourse! Register a new admin account with #{emails} to finalize configuration."
end
if notice != SiteSetting.global_notice if notice != SiteSetting.global_notice
SiteSetting.global_notice = notice SiteSetting.global_notice = notice
SiteSetting.has_login_hint = true SiteSetting.has_login_hint = true
end
end end
rescue ActiveRecord::NoDatabaseError
# Database might not have been created
end end
rescue ActiveRecord::NoDatabaseError
# Database might not have been created
end end

View File

@ -1,16 +1,18 @@
# frozen_string_literal: true # frozen_string_literal: true
if Rails.env.development? && SiteSetting.port.to_i > 0 Rails.application.config.to_prepare do
Onebox.options = { if Rails.env.development? && SiteSetting.port.to_i > 0
twitter_client: TwitterApi, Onebox.options = {
redirect_limit: 3, twitter_client: TwitterApi,
user_agent: "Discourse Forum Onebox v#{Discourse::VERSION::STRING}", redirect_limit: 3,
allowed_ports: [80, 443, SiteSetting.port.to_i] user_agent: "Discourse Forum Onebox v#{Discourse::VERSION::STRING}",
} allowed_ports: [80, 443, SiteSetting.port.to_i]
else }
Onebox.options = { else
twitter_client: TwitterApi, Onebox.options = {
redirect_limit: 3, twitter_client: TwitterApi,
user_agent: "Discourse Forum Onebox v#{Discourse::VERSION::STRING}" redirect_limit: 3,
} user_agent: "Discourse Forum Onebox v#{Discourse::VERSION::STRING}"
}
end
end end

View File

@ -2,30 +2,32 @@
return if GlobalSetting.skip_db? return if GlobalSetting.skip_db?
require_dependency 'webpush' Rails.application.config.to_prepare do
require 'webpush'
def generate_vapid_key? def generate_vapid_key?
SiteSetting.vapid_public_key.blank? || SiteSetting.vapid_public_key.blank? ||
SiteSetting.vapid_private_key.blank? || SiteSetting.vapid_private_key.blank? ||
SiteSetting.vapid_public_key_bytes.blank? || SiteSetting.vapid_public_key_bytes.blank? ||
SiteSetting.vapid_base_url != Discourse.base_url SiteSetting.vapid_base_url != Discourse.base_url
end end
SiteSetting.vapid_base_url = Discourse.base_url if SiteSetting.vapid_base_url.blank? SiteSetting.vapid_base_url = Discourse.base_url if SiteSetting.vapid_base_url.blank?
if generate_vapid_key? if generate_vapid_key?
vapid_key = Webpush.generate_key vapid_key = Webpush.generate_key
SiteSetting.vapid_public_key = vapid_key.public_key SiteSetting.vapid_public_key = vapid_key.public_key
SiteSetting.vapid_private_key = vapid_key.private_key SiteSetting.vapid_private_key = vapid_key.private_key
SiteSetting.vapid_public_key_bytes = Base64.urlsafe_decode64(SiteSetting.vapid_public_key).bytes.join("|") SiteSetting.vapid_public_key_bytes = Base64.urlsafe_decode64(SiteSetting.vapid_public_key).bytes.join("|")
SiteSetting.vapid_base_url = Discourse.base_url SiteSetting.vapid_base_url = Discourse.base_url
if ActiveRecord::Base.connection.table_exists?(:push_subscriptions) if ActiveRecord::Base.connection.table_exists?(:push_subscriptions)
PushSubscription.delete_all PushSubscription.delete_all
end
end
DiscourseEvent.on(:user_logged_out) do |user|
PushNotificationPusher.clear_subscriptions(user)
end end
end end
DiscourseEvent.on(:user_logged_out) do |user|
PushNotificationPusher.clear_subscriptions(user)
end

View File

@ -1,21 +1,21 @@
# frozen_string_literal: true # frozen_string_literal: true
# Be sure to restart your server when you modify this file. # Be sure to restart your server when you modify this file.
#
require_dependency 'discourse_cookie_store'
if Rails.env == "development" && SiteSetting.force_https Rails.application.config.session_store(
STDERR.puts
STDERR.puts "WARNING: force_https is enabled in dev"
STDERR.puts "It is very unlikely you are running HTTPS in dev."
STDERR.puts "Without HTTPS your session cookie will not work"
STDERR.puts "Try: bin/rails c"
STDERR.puts "SiteSetting.force_https = false"
STDERR.puts
end
Discourse::Application.config.session_store(
:discourse_cookie_store, :discourse_cookie_store,
key: '_forum_session', key: '_forum_session',
path: (Rails.application.config.relative_url_root.nil?) ? '/' : Rails.application.config.relative_url_root path: (Rails.application.config.relative_url_root.nil?) ? '/' : Rails.application.config.relative_url_root
) )
Rails.application.config.to_prepare do
if Rails.env.development? && SiteSetting.force_https
STDERR.puts
STDERR.puts "WARNING: force_https is enabled in dev"
STDERR.puts "It is very unlikely you are running HTTPS in dev."
STDERR.puts "Without HTTPS your session cookie will not work"
STDERR.puts "Try: bin/rails c"
STDERR.puts "SiteSetting.force_https = false"
STDERR.puts
end
end

View File

@ -1,117 +1,119 @@
# frozen_string_literal: true # frozen_string_literal: true
if (Rails.env.production? && SiteSetting.logging_provider == 'lograge') || (ENV["ENABLE_LOGRAGE"] == "1") Rails.application.config.to_prepare do
require 'lograge' if (Rails.env.production? && SiteSetting.logging_provider == 'lograge') || (ENV["ENABLE_LOGRAGE"] == "1")
require 'lograge'
if Rails.configuration.multisite if Rails.configuration.multisite
Rails.logger.formatter = ActiveSupport::Logger::SimpleFormatter.new Rails.logger.formatter = ActiveSupport::Logger::SimpleFormatter.new
end end
Rails.application.configure do Rails.application.configure do
config.lograge.enabled = true config.lograge.enabled = true
Lograge.ignore(lambda do |event| Lograge.ignore(lambda do |event|
# this is our hijack magic status, # this is our hijack magic status,
# no point logging this cause we log again # no point logging this cause we log again
# direct from hijack # direct from hijack
event.payload[:status] == 418 event.payload[:status] == 418
end) end)
config.lograge.custom_payload do |controller| config.lograge.custom_payload do |controller|
begin begin
username = username =
begin begin
if controller.respond_to?(:current_user) if controller.respond_to?(:current_user)
controller.current_user&.username controller.current_user&.username
end
rescue Discourse::InvalidAccess, Discourse::ReadOnly, ActiveRecord::ReadOnlyError
nil
end end
rescue Discourse::InvalidAccess, Discourse::ReadOnly, ActiveRecord::ReadOnlyError
nil
end
ip = ip =
begin begin
controller.request.remote_ip controller.request.remote_ip
rescue ActionDispatch::RemoteIp::IpSpoofAttackError rescue ActionDispatch::RemoteIp::IpSpoofAttackError
nil nil
end end
{ {
ip: ip, ip: ip,
username: username username: username
} }
rescue => e rescue => e
Rails.logger.warn("Failed to append custom payload: #{e.message}\n#{e.backtrace.join("\n")}") Rails.logger.warn("Failed to append custom payload: #{e.message}\n#{e.backtrace.join("\n")}")
{} {}
end
end end
end
config.lograge.custom_options = lambda do |event| config.lograge.custom_options = lambda do |event|
begin begin
exceptions = %w(controller action format id) exceptions = %w(controller action format id)
params = event.payload[:params].except(*exceptions) params = event.payload[:params].except(*exceptions)
if (file = params[:file]) && file.respond_to?(:headers) if (file = params[:file]) && file.respond_to?(:headers)
params[:file] = file.headers params[:file] = file.headers
end
if (files = params[:files]) && files.respond_to?(:map)
params[:files] = files.map do |f|
f.respond_to?(:headers) ? f.headers : f
end
end
output = {
params: params.to_query,
database: RailsMultisite::ConnectionManagement.current_db,
}
if data = (Thread.current[:_method_profiler] || event.payload[:timings])
sql = data[:sql]
if sql
output[:db] = sql[:duration] * 1000
output[:db_calls] = sql[:calls]
end
redis = data[:redis]
if redis
output[:redis] = redis[:duration] * 1000
output[:redis_calls] = redis[:calls]
end
net = data[:net]
if net
output[:net] = net[:duration] * 1000
output[:net_calls] = net[:calls]
end
end
output
rescue RateLimiter::LimitExceeded
# no idea who this is, but they are limited
{}
rescue => e
Rails.logger.warn("Failed to append custom options: #{e.message}\n#{e.backtrace.join("\n")}")
{}
end end
if (files = params[:files]) && files.respond_to?(:map)
params[:files] = files.map do |f|
f.respond_to?(:headers) ? f.headers : f
end
end
output = {
params: params.to_query,
database: RailsMultisite::ConnectionManagement.current_db,
}
if data = (Thread.current[:_method_profiler] || event.payload[:timings])
sql = data[:sql]
if sql
output[:db] = sql[:duration] * 1000
output[:db_calls] = sql[:calls]
end
redis = data[:redis]
if redis
output[:redis] = redis[:duration] * 1000
output[:redis_calls] = redis[:calls]
end
net = data[:net]
if net
output[:net] = net[:duration] * 1000
output[:net_calls] = net[:calls]
end
end
output
rescue RateLimiter::LimitExceeded
# no idea who this is, but they are limited
{}
rescue => e
Rails.logger.warn("Failed to append custom options: #{e.message}\n#{e.backtrace.join("\n")}")
{}
end end
end
if ENV["LOGSTASH_URI"] if ENV["LOGSTASH_URI"]
config.lograge.formatter = Lograge::Formatters::Logstash.new config.lograge.formatter = Lograge::Formatters::Logstash.new
require 'discourse_logstash_logger' require 'discourse_logstash_logger'
config.lograge.logger = DiscourseLogstashLogger.logger( config.lograge.logger = DiscourseLogstashLogger.logger(
uri: ENV['LOGSTASH_URI'], type: :rails uri: ENV['LOGSTASH_URI'], type: :rails
) )
# Remove ActiveSupport::Logger from the chain and replace with Lograge's # Remove ActiveSupport::Logger from the chain and replace with Lograge's
# logger # logger
Rails.logger.chained.pop Rails.logger.chained.pop
Rails.logger.chain(config.lograge.logger) Rails.logger.chain(config.lograge.logger)
end
end end
end end
end end

View File

@ -0,0 +1,99 @@
# frozen_string_literal: true
# Be sure to restart your server when you modify this file.
# Enable the asset pipeline
Rails.application.config.assets.enabled = true
# Version of your assets, change this if you want to expire all your assets.
Rails.application.config.assets.version = "1.2.5"
# Add additional assets to the asset load path.
Rails.application.config.assets.paths << "#{Rails.root}/config/locales"
Rails.application.config.assets.paths << "#{Rails.root}/public/javascripts"
# Precompile additional assets.
# application.js, application.css, and all non-JS/CSS in the app/assets
# folder are already added.
# explicitly precompile any images in plugins ( /assets/images ) path
Rails.application.config.assets.precompile += [lambda do |filename, path|
path =~ /assets\/images/ && !%w(.js .css).include?(File.extname(filename))
end]
Rails.application.config.assets.precompile += %w{
vendor.js
admin.js
browser-detect.js
browser-update.js
break_string.js
ember_jquery.js
pretty-text-bundle.js
wizard-application.js
wizard-vendor.js
markdown-it-bundle.js
service-worker.js
google-tag-manager.js
google-universal-analytics-v3.js
google-universal-analytics-v4.js
start-discourse.js
print-page.js
omniauth-complete.js
activate-account.js
auto-redirect.js
wizard-start.js
locales/i18n.js
discourse/app/lib/webauthn.js
confirm-new-email/confirm-new-email.js
confirm-new-email/bootstrap.js
onpopstate-handler.js
embed-application.js
discourse/tests/active-plugins.js
admin-plugins.js
discourse/tests/test_starter.js
}
if EmberCli.enabled?
Rails.application.config.assets.precompile += %w{
discourse.js
test-support.js
test-helpers.js
scripts/discourse-test-listen-boot
scripts/discourse-boot
}
else
Rails.application.config.assets.precompile += %w{
application.js
discourse/tests/test-support-rails.js
discourse/tests/test-helpers-rails.js
vendor-theme-tests.js
}
end
# Precompile all available locales
unless GlobalSetting.try(:omit_base_locales)
Dir.glob("#{Rails.root}/app/assets/javascripts/locales/*.js.erb").each do |file|
Rails.application.config.assets.precompile << "locales/#{file.match(/([a-z_A-Z]+\.js)\.erb$/)[1]}"
end
end
# out of the box sprockets 3 grabs loose files that are hanging in assets,
# the exclusion list does not include hbs so you double compile all this stuff
Rails.application.config.assets.precompile.delete(Sprockets::Railtie::LOOSE_APP_ASSETS)
# We don't want application from node_modules, only from the root
Rails.application.config.assets.precompile.delete(/(?:\/|\\|\A)application\.(css|js)$/)
Rails.application.config.assets.precompile += ['application.js']
start_path = ::Rails.root.join("app/assets").to_s
exclude = ['.es6', '.hbs', '.hbr', '.js', '.css', '.lock', '.json', '.log', '.html', '']
Rails.application.config.assets.precompile << lambda do |logical_path, filename|
filename.start_with?(start_path) &&
!filename.include?("/node_modules/") &&
!filename.include?("/dist/") &&
!exclude.include?(File.extname(logical_path))
end
Discourse.find_plugin_js_assets(include_disabled: true).each do |file|
Rails.application.config.assets.precompile << "#{file}.js"
end

View File

@ -0,0 +1,15 @@
# frozen_string_literal: true
# Be sure to restart your server when you modify this file.
# Configure sensitive parameters which will be filtered from the log file.
Rails.application.config.filter_parameters += [
:password,
:pop3_polling_password,
:api_key,
:s3_secret_access_key,
:twitter_consumer_secret,
:facebook_app_secret,
:github_client_secret,
:second_factor_token,
]

View File

@ -0,0 +1,106 @@
# frozen_string_literal: true
# Be sure to restart your server when you modify this file.
#
# This file eases your Rails 7.0 framework defaults upgrade.
#
# Uncomment each configuration one by one to switch to the new default.
# Once your application is ready to run with all new defaults, you can remove
# this file and set the `config.load_defaults` to `7.0`.
#
# Read the Guide for Upgrading Ruby on Rails for more info on each option.
# https://guides.rubyonrails.org/upgrading_ruby_on_rails.html
# `button_to` view helper will render `<button>` element, regardless of whether
# or not the content is passed as the first argument or as a block.
Rails.application.config.action_view.button_to_generates_button_tag = true
# `stylesheet_link_tag` view helper will not render the media attribute by default.
Rails.application.config.action_view.apply_stylesheet_media_default = false
# Change the digest class for the key generators to `OpenSSL::Digest::SHA256`.
# Changing this default means invalidate all encrypted messages generated by
# your application and, all the encrypted cookies. Only change this after you
# rotated all the messages using the key rotator.
#
# See upgrading guide for more information on how to build a rotator.
# https://guides.rubyonrails.org/v7.0/upgrading_ruby_on_rails.html
# Rails.application.config.active_support.key_generator_hash_digest_class = OpenSSL::Digest::SHA256
# Change the digest class for ActiveSupport::Digest.
# Changing this default means that for example Etags change and
# various cache keys leading to cache invalidation.
# Rails.application.config.active_support.hash_digest_class = OpenSSL::Digest::SHA256
# Don't override ActiveSupport::TimeWithZone.name and use the default Ruby
# implementation.
Rails.application.config.active_support.remove_deprecated_time_with_zone_name = true
# Change the format of the cache entry.
# Changing this default means that all new cache entries added to the cache
# will have a different format that is not supported by Rails 6.1 applications.
# Only change this value after your application is fully deployed to Rails 7.0
# and you have no plans to rollback.
# Rails.application.config.active_support.cache_format_version = 7.0
# Calls `Rails.application.executor.wrap` around test cases.
# This makes test cases behave closer to an actual request or job.
# Several features that are normally disabled in test, such as Active Record query cache
# and asynchronous queries will then be enabled.
Rails.application.config.active_support.executor_around_test_case = true
# Define the isolation level of most of Rails internal state.
# If you use a fiber based server or job processor, you should set it to `:fiber`.
# Otherwise the default of `:thread` if preferable.
Rails.application.config.active_support.isolation_level = :thread
# Set both the `:open_timeout` and `:read_timeout` values for `:smtp` delivery method.
Rails.application.config.action_mailer.smtp_timeout = 5
# Automatically infer `inverse_of` for associations with a scope.
Rails.application.config.active_record.automatic_scope_inversing = true
# Raise when running tests if fixtures contained foreign key violations
Rails.application.config.active_record.verify_foreign_keys_for_fixtures = true
# Disable partial inserts.
# This default means that all columns will be referenced in INSERT queries
# regardless of whether they have a default or not.
Rails.application.config.active_record.partial_inserts = false
#
# Protect from open redirect attacks in `redirect_back_or_to` and `redirect_to`.
Rails.application.config.action_controller.raise_on_open_redirects = true
# If you're upgrading and haven't set `cookies_serializer` previously, your cookie serializer
# was `:marshal`. Convert all cookies to JSON, using the `:hybrid` formatter.
#
# If you're confident all your cookies are JSON formatted, you can switch to the `:json` formatter.
#
# Continue to use `:marshal` for backward-compatibility with old cookies.
#
# If you have configured the serializer elsewhere, you can remove this.
#
# See https://guides.rubyonrails.org/action_controller_overview.html#cookies for more information.
Rails.application.config.action_dispatch.cookies_serializer = :marshal
# Enable parameter wrapping for JSON.
# Previously this was set in an initializer. It's fine to keep using that initializer if you've customized it.
# To disable parameter wrapping entirely, set this config to `false`.
# Rails.application.config.action_controller.wrap_parameters_by_default = true
# Specifies whether generated namespaced UUIDs follow the RFC 4122 standard for namespace IDs provided as a
# `String` to `Digest::UUID.uuid_v3` or `Digest::UUID.uuid_v5` method calls.
#
# See https://guides.rubyonrails.org/configuring.html#config-active-support-use-rfc4122-namespaced-uuids for
# more information.
Rails.application.config.active_support.use_rfc4122_namespaced_uuids = true
# Change the default headers to disable browsers' flawed legacy XSS protection.
Rails.application.config.action_dispatch.default_headers = {
"X-Frame-Options" => "SAMEORIGIN",
"X-XSS-Protection" => "0",
"X-Content-Type-Options" => "nosniff",
"X-Download-Options" => "noopen",
"X-Permitted-Cross-Domain-Policies" => "none",
"Referrer-Policy" => "strict-origin-when-cross-origin"
}

View File

@ -1,6 +1,6 @@
# frozen_string_literal: true # frozen_string_literal: true
require "common_passwords/common_passwords" require "common_passwords"
class ClearCommonPasswordsCache < ActiveRecord::Migration[4.2] class ClearCommonPasswordsCache < ActiveRecord::Migration[4.2]
def change def change

View File

@ -1,6 +1,6 @@
# frozen_string_literal: true # frozen_string_literal: true
require_dependency 'has_errors' require 'has_errors'
class Auth::GithubAuthenticator < Auth::ManagedAuthenticator class Auth::GithubAuthenticator < Auth::ManagedAuthenticator

View File

@ -1,59 +0,0 @@
# frozen_string_literal: true
class Auth::OAuth2Authenticator < Auth::Authenticator
def name
@name
end
# only option at the moment is :trusted
def initialize(name, opts = {})
Discourse.deprecate("OAuth2Authenticator is deprecated. Use `ManagedAuthenticator` and `UserAssociatedAccount` instead. For more information, see https://meta.discourse.org/t/106695", drop_from: '2.9.0', output_in_test: true)
@name = name
@opts = opts
end
def after_authenticate(auth_token)
result = Auth::Result.new
oauth2_provider = auth_token[:provider]
oauth2_uid = auth_token[:uid]
data = auth_token[:info]
result.email = email = data[:email]
result.name = name = data[:name]
oauth2_user_info = Oauth2UserInfo.find_by(uid: oauth2_uid, provider: oauth2_provider)
if !oauth2_user_info && @opts[:trusted] && user = User.find_by_email(email)
oauth2_user_info = Oauth2UserInfo.create(uid: oauth2_uid,
provider: oauth2_provider,
name: name,
email: email,
user: user)
end
result.user = oauth2_user_info.try(:user)
result.email_valid = @opts[:trusted]
result.extra_data = {
uid: oauth2_uid,
provider: oauth2_provider
}
result
end
def after_create_account(user, auth)
data = auth[:extra_data]
association = Oauth2UserInfo.find_or_initialize_by(provider: data[:provider], uid: data[:uid])
association.user = user
association.email = auth[:email]
association.save!
end
def description_for_user(user)
info = Oauth2UserInfo.find_by(user_id: user.id, provider: @name)
info&.email || info&.name || info&.uid || ""
end
end

View File

@ -75,7 +75,7 @@ class Auth::Result
def self.from_session_data(data, user:) def self.from_session_data(data, user:)
result = new result = new
data = data.symbolize_keys data = data.with_indifferent_access
SESSION_ATTRIBUTES.each { |att| result.public_send("#{att}=", data[att]) } SESSION_ATTRIBUTES.each { |att| result.public_send("#{att}=", data[att]) }
result.user = user result.user = user
result result

View File

@ -10,10 +10,10 @@ module BackupRestore
def self.create(opts = {}) def self.create(opts = {})
case opts[:location] || SiteSetting.backup_location case opts[:location] || SiteSetting.backup_location
when BackupLocationSiteSetting::LOCAL when BackupLocationSiteSetting::LOCAL
require_dependency "backup_restore/local_backup_store" require "backup_restore/local_backup_store"
BackupRestore::LocalBackupStore.new(opts) BackupRestore::LocalBackupStore.new(opts)
when BackupLocationSiteSetting::S3 when BackupLocationSiteSetting::S3
require_dependency "backup_restore/s3_backup_store" require "backup_restore/s3_backup_store"
BackupRestore::S3BackupStore.new(opts) BackupRestore::S3BackupStore.new(opts)
end end
end end

View File

@ -1,5 +1,5 @@
# frozen_string_literal: true # frozen_string_literal: true
require_dependency 'content_security_policy/default' require 'content_security_policy/default'
class ContentSecurityPolicy class ContentSecurityPolicy
class Builder class Builder

View File

@ -1,5 +1,5 @@
# frozen_string_literal: true # frozen_string_literal: true
require_dependency 'content_security_policy' require 'content_security_policy'
class ContentSecurityPolicy class ContentSecurityPolicy
class Default class Default

View File

@ -1,5 +1,5 @@
# frozen_string_literal: true # frozen_string_literal: true
require_dependency 'content_security_policy' require 'content_security_policy'
class ContentSecurityPolicy class ContentSecurityPolicy
class Middleware class Middleware

View File

@ -1,9 +0,0 @@
# frozen_string_literal: true
# Support for plugins to register custom setting providers. They can do this
# by having a file, `register_provider.rb` in their root that will be run
# at this point.
Dir.glob(File.join(File.dirname(__FILE__), '../plugins', '*', "register_provider.rb")) do |p|
require p
end

View File

@ -1,6 +1,6 @@
# frozen_string_literal: true # frozen_string_literal: true
require_dependency "migration/base_dropper" require "migration/base_dropper"
class DbHelper class DbHelper

View File

@ -2,8 +2,8 @@
require 'cache' require 'cache'
require 'open3' require 'open3'
require_dependency 'plugin/instance' require 'plugin/instance'
require_dependency 'version' require 'version'
module Discourse module Discourse
DB_POST_MIGRATE_PATH ||= "db/post_migrate" DB_POST_MIGRATE_PATH ||= "db/post_migrate"

View File

@ -1,6 +1,6 @@
# frozen_string_literal: true # frozen_string_literal: true
require_dependency 'file_store/base_store' require 'file_store/base_store'
module FileStore module FileStore

View File

@ -2,9 +2,9 @@
require "uri" require "uri"
require "mini_mime" require "mini_mime"
require_dependency "file_store/base_store" require "file_store/base_store"
require_dependency "s3_helper" require "s3_helper"
require_dependency "file_helper" require "file_helper"
module FileStore module FileStore

View File

@ -1,19 +0,0 @@
# frozen_string_literal: true
# Pulls in https://github.com/rails/rails/pull/42368 early since the query is
# definitely more efficient as it does not involved the PG planner.
# Remove once Rails 7 has been released.
module ActiveRecord
module ConnectionAdapters
class PostgreSQLAdapter
def active?
@lock.synchronize do
@connection.query ";"
end
true
rescue PG::Error
false
end
end
end
end

View File

@ -0,0 +1,50 @@
# frozen_string_literal: true
# This patch is a backport of https://github.com/rails/rails/pull/42350
# It fixes a bug introduced by Rails which affects reference columns marking
# them as integer instead of bigint.
#
# This should be deleted when version 7.0.3 is released.
module FreedomPatches
module ArReferencesFix
module SchemaDefinition
def index_options(table_name)
index_options = as_options(index)
# legacy reference index names are used on versions 6.0 and earlier
return index_options if options[:_uses_legacy_reference_index_name]
index_options[:name] ||= polymorphic_index_name(table_name) if polymorphic
index_options
end
ActiveRecord::ConnectionAdapters::ReferenceDefinition.prepend(self)
end
end
end
class ActiveRecord::Migration::Compatibility::V6_0
module TableDefinition
def references(*args, **options)
options[:_uses_legacy_reference_index_name] = true
super
end
alias :belongs_to :references
end
def add_reference(table_name, ref_name, **options)
if connection.adapter_name == "SQLite"
options[:type] = :integer
end
options[:_uses_legacy_reference_index_name] = true
super
end
alias :add_belongs_to :add_reference
def compatible_table_definition(t)
class << t
prepend TableDefinition
end
super
end
end

View File

@ -10,7 +10,7 @@ module RailsMultisite
break if !defined?(RailsFailover::ActiveRecord) break if !defined?(RailsFailover::ActiveRecord)
break if db == RailsMultisite::ConnectionManagement::DEFAULT break if db == RailsMultisite::ConnectionManagement::DEFAULT
reading_role = :"#{db}_#{ActiveRecord::Base.reading_role}" reading_role = :"#{db}_#{ActiveRecord.reading_role}"
spec = RailsMultisite::ConnectionManagement.connection_spec(db: db) spec = RailsMultisite::ConnectionManagement.connection_spec(db: db)
ActiveRecord::Base.connection_handlers[reading_role] ||= begin ActiveRecord::Base.connection_handlers[reading_role] ||= begin

View File

@ -59,13 +59,13 @@ class HtmlToMarkdown
before, after = parent.children.slice_when { |n| n == br }.to_a before, after = parent.children.slice_when { |n| n == br }.to_a
if before.size > 1 if before.size > 1
b = Nokogiri::XML::Node.new(parent.name, doc) b = Nokogiri::XML::Node.new(parent.name, doc.document)
before[0...-1].each { |c| b.add_child(c) } before[0...-1].each { |c| b.add_child(c) }
parent.previous = b if b.inner_html.present? parent.previous = b if b.inner_html.present?
end end
if after.present? if after.present?
a = Nokogiri::XML::Node.new(parent.name, doc) a = Nokogiri::XML::Node.new(parent.name, doc.document)
after.each { |c| a.add_child(c) } after.each { |c| a.add_child(c) }
parent.next = a if a.inner_html.present? parent.next = a if a.inner_html.present?
end end

View File

@ -1,6 +1,6 @@
# frozen_string_literal: true # frozen_string_literal: true
require_relative "locale_file_walker" require "locale_file_walker"
class DuplicateKeyFinder < LocaleFileWalker class DuplicateKeyFinder < LocaleFileWalker

View File

@ -1,9 +1,9 @@
# frozen_string_literal: true # frozen_string_literal: true
require_dependency "mobile_detection" require "mobile_detection"
require_dependency "crawler_detection" require "crawler_detection"
require_dependency "guardian" require "guardian"
require_dependency "http_language_parser" require "http_language_parser"
module Middleware module Middleware
class AnonymousCache class AnonymousCache

View File

@ -1,6 +1,6 @@
# frozen_string_literal: true # frozen_string_literal: true
require_dependency 'migration/base_dropper' require 'migration/base_dropper'
module Migration module Migration
class ColumnDropper class ColumnDropper

View File

@ -1,6 +1,6 @@
# frozen_string_literal: true # frozen_string_literal: true
require_dependency 'migration/base_dropper' require 'migration/base_dropper'
module Migration module Migration
class Migration::TableDropper class Migration::TableDropper

53
lib/plugin.rb Normal file
View File

@ -0,0 +1,53 @@
# frozen_string_literal: true
module Plugin
def self.initialization_guard(&block)
begin
block.call
rescue => error
plugins_directory = Rails.root + 'plugins'
if error.backtrace && error.backtrace_locations
plugin_path = error.backtrace_locations.lazy.map do |location|
Pathname.new(location.absolute_path)
.ascend
.lazy
.find { |path| path.parent == plugins_directory }
end.next
raise unless plugin_path
stack_trace = error.backtrace.each_with_index.inject([]) do |messages, (line, index)|
if index == 0
messages << "#{line}: #{error} (#{error.class})"
else
messages << "\t#{index}: from #{line}"
end
end.reverse.join("\n")
STDERR.puts <<~TEXT
#{stack_trace}
** INCOMPATIBLE PLUGIN **
You are unable to build Discourse due to errors in the plugin at
#{plugin_path}
Please try removing this plugin and rebuilding again!
TEXT
else
STDERR.puts <<~TEXT
** PLUGIN FAILURE **
You are unable to build Discourse due to this error during plugin
initialization:
#{error}
#{error.backtrace.join("\n")}
TEXT
end
exit 1
end
end
end

View File

@ -2,8 +2,8 @@
require 'digest/sha1' require 'digest/sha1'
require 'fileutils' require 'fileutils'
require_dependency 'plugin/metadata' require 'plugin/metadata'
require_dependency 'auth' require 'auth'
class Plugin::CustomEmoji class Plugin::CustomEmoji
CACHE_KEY ||= "plugin-emoji" CACHE_KEY ||= "plugin-emoji"

View File

@ -1,51 +0,0 @@
# frozen_string_literal: true
def plugin_initialization_guard(&block)
begin
block.call
rescue => error
plugins_directory = Rails.root + 'plugins'
if error.backtrace && error.backtrace_locations
plugin_path = error.backtrace_locations.lazy.map do |location|
Pathname.new(location.absolute_path)
.ascend
.lazy
.find { |path| path.parent == plugins_directory }
end.next
raise unless plugin_path
stack_trace = error.backtrace.each_with_index.inject([]) do |messages, (line, index)|
if index == 0
messages << "#{line}: #{error} (#{error.class})"
else
messages << "\t#{index}: from #{line}"
end
end.reverse.join("\n")
STDERR.puts <<~TEXT
#{stack_trace}
** INCOMPATIBLE PLUGIN **
You are unable to build Discourse due to errors in the plugin at
#{plugin_path}
Please try removing this plugin and rebuilding again!
TEXT
else
STDERR.puts <<~TEXT
** PLUGIN FAILURE **
You are unable to build Discourse due to this error during plugin
initialization:
#{error}
#{error.backtrace.join("\n")}
TEXT
end
exit 1
end
end

View File

@ -1,6 +1,6 @@
# frozen_string_literal: true # frozen_string_literal: true
require_dependency 'has_errors' require 'has_errors'
class PostActionResult class PostActionResult
include HasErrors include HasErrors

View File

@ -18,5 +18,5 @@ module RequireDependencyBackwardCompatibility
super super
end end
ActiveSupport::Dependencies::ZeitwerkIntegration::RequireDependency.prepend(self) Object.prepend(self)
end end

View File

@ -1,6 +1,6 @@
# frozen_string_literal: true # frozen_string_literal: true
require_dependency 'reviewable/collection' require 'reviewable/collection'
class Reviewable < ActiveRecord::Base class Reviewable < ActiveRecord::Base
class Actions < Reviewable::Collection class Actions < Reviewable::Collection

View File

@ -1,6 +1,6 @@
# frozen_string_literal: true # frozen_string_literal: true
require_dependency "s3_helper" require "s3_helper"
class S3CorsRulesets class S3CorsRulesets
ASSETS = { ASSETS = {

View File

@ -1,7 +1,6 @@
# frozen_string_literal: true # frozen_string_literal: true
require 'stylesheet/importer' require 'stylesheet/importer'
require 'stylesheet/functions'
module Stylesheet module Stylesheet

View File

@ -1,16 +0,0 @@
# frozen_string_literal: true
module Stylesheet
module ScssFunctions
def asset_url(path)
Discourse.deprecate("The `asset-url` SCSS function is deprecated. Use `absolute-image-url` instead.", drop_from: '2.9.0')
SassC::Script::Value::String.new("url('#{ActionController::Base.helpers.asset_url(path.value)}')")
end
def image_url(path)
Discourse.deprecate("The `image-url` SCSS function is deprecated. Use `absolute-image-url` instead.", drop_from: '2.9.0')
SassC::Script::Value::String.new("url('#{ActionController::Base.helpers.image_url(path.value)}')")
end
end
end
::SassC::Script::Functions.include(Stylesheet::ScssFunctions)

View File

@ -1,6 +1,6 @@
# frozen_string_literal: true # frozen_string_literal: true
require_dependency 'global_path' require 'global_path'
module Stylesheet module Stylesheet
class Importer class Importer

View File

@ -1,7 +1,7 @@
# frozen_string_literal: true # frozen_string_literal: true
require_dependency 'distributed_cache' require 'distributed_cache'
require_dependency 'stylesheet/compiler' require 'stylesheet/compiler'
module Stylesheet; end module Stylesheet; end

View File

@ -5,7 +5,7 @@ require "fileutils"
require "json" require "json"
require "nokogiri" require "nokogiri"
require "open-uri" require "open-uri"
require_dependency "file_helper" require "file_helper"
EMOJI_GROUPS_PATH ||= "lib/emoji/groups.json" EMOJI_GROUPS_PATH ||= "lib/emoji/groups.json"

View File

@ -1,6 +1,6 @@
# frozen_string_literal: true # frozen_string_literal: true
require_dependency "file_helper" require "file_helper"
task "images:compress" => :environment do task "images:compress" => :environment do
images = Dir.glob("#{Rails.root}/app/**/*.png") images = Dir.glob("#{Rails.root}/app/**/*.png")

View File

@ -1,6 +1,6 @@
# frozen_string_literal: true # frozen_string_literal: true
require_dependency "rake_helpers" require "rake_helpers"
def close_old_topics(category) def close_old_topics(category)
topics = Topic.where(closed: false, category_id: category.id) topics = Topic.where(closed: false, category_id: category.id)

View File

@ -8,7 +8,7 @@ require "base62"
# gather # # gather #
################################################################################ ################################################################################
require_dependency "rake_helpers" require "rake_helpers"
task "uploads:gather" => :environment do task "uploads:gather" => :environment do
ENV["RAILS_DB"] ? gather_uploads : gather_uploads_for_all_sites ENV["RAILS_DB"] ? gather_uploads : gather_uploads_for_all_sites

View File

@ -1,6 +1,6 @@
# frozen_string_literal: true # frozen_string_literal: true
require_dependency 'compression/zip' require 'compression/zip'
module ThemeStore; end module ThemeStore; end

View File

@ -1,6 +1,6 @@
# frozen_string_literal: true # frozen_string_literal: true
require_dependency 'compression/engine' require 'compression/engine'
module ThemeStore; end module ThemeStore; end

View File

@ -1,6 +1,6 @@
# frozen_string_literal: true # frozen_string_literal: true
require_dependency "file_helper" require "file_helper"
module Validators; end module Validators; end

View File

@ -1,7 +1,5 @@
# frozen_string_literal: true # frozen_string_literal: true
require "i18n/duplicate_key_finder"
def extract_locale(path) def extract_locale(path)
path[/\.([^.]{2,})\.yml$/, 1] path[/\.([^.]{2,})\.yml$/, 1]
end end

View File

@ -1,7 +1,5 @@
# frozen_string_literal: true # frozen_string_literal: true
require "i18n/duplicate_key_finder"
describe "site setting integrity checks" do describe "site setting integrity checks" do
let(:site_setting_file) { File.join(Rails.root, 'config', 'site_settings.yml') } let(:site_setting_file) { File.join(Rails.root, 'config', 'site_settings.yml') }
let(:yaml) { YAML.load_file(site_setting_file) } let(:yaml) { YAML.load_file(site_setting_file) }

View File

@ -1,13 +1,11 @@
# frozen_string_literal: true # frozen_string_literal: true
describe Jobs::CreateLinkedTopic do describe Jobs::CreateLinkedTopic do
it "returns when the post cannot be found" do it "returns when the post cannot be found" do
expect { Jobs::CreateLinkedTopic.new.perform(post_id: 1, sync_exec: true) }.not_to raise_error expect { Jobs::CreateLinkedTopic.new.perform(post_id: 1, sync_exec: true) }.not_to raise_error
end end
context 'with a post' do context 'with a post' do
fab!(:category) { Fabricate(:category) } fab!(:category) { Fabricate(:category) }
fab!(:topic) { Fabricate(:topic, category: category) } fab!(:topic) { Fabricate(:topic, category: category) }
fab!(:post) do fab!(:post) do
@ -54,5 +52,4 @@ describe Jobs::CreateLinkedTopic do
expect(linked_topic.sequence).to eq(2) expect(linked_topic.sequence).to eq(2)
end end
end end
end end

View File

@ -1,7 +1,6 @@
# frozen_string_literal: true # frozen_string_literal: true
describe Jobs::FeatureTopicUsers do describe Jobs::FeatureTopicUsers do
it "raises an error without a topic_id" do it "raises an error without a topic_id" do
expect { Jobs::FeatureTopicUsers.new.execute({}) }.to raise_error(Discourse::InvalidParameters) expect { Jobs::FeatureTopicUsers.new.execute({}) }.to raise_error(Discourse::InvalidParameters)
end end
@ -32,16 +31,13 @@ describe Jobs::FeatureTopicUsers do
Jobs::FeatureTopicUsers.new.execute(topic_id: topic.id) Jobs::FeatureTopicUsers.new.execute(topic_id: topic.id)
expect(topic.reload.featured_user_ids.include?(evil_trout.id)).to eq(false) expect(topic.reload.featured_user_ids.include?(evil_trout.id)).to eq(false)
end end
end end
context "participant count" do context "participant count" do
let!(:post) { create_post } let!(:post) { create_post }
let(:topic) { post.topic } let(:topic) { post.topic }
it "it works as expected" do it "it works as expected" do
# It has 1 participant after creation # It has 1 participant after creation
expect(topic.participant_count).to eq(1) expect(topic.participant_count).to eq(1)
@ -58,9 +54,6 @@ describe Jobs::FeatureTopicUsers do
create_post(topic: topic, user: Fabricate(:evil_trout)) create_post(topic: topic, user: Fabricate(:evil_trout))
Jobs::FeatureTopicUsers.new.execute(topic_id: topic.id) Jobs::FeatureTopicUsers.new.execute(topic_id: topic.id)
expect(topic.reload.participant_count).to eq(2) expect(topic.reload.participant_count).to eq(2)
end end
end end
end end

View File

@ -28,9 +28,9 @@ describe Jobs::OldKeysReminder do
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_fs(:db)}
github_client_secret - #{github_secret.updated_at.to_date.to_s(:db)} github_client_secret - #{github_secret.updated_at.to_date.to_fs(:db)}
api key description - #{api_key.created_at.to_date.to_s(:db)} api key description - #{api_key.created_at.to_date.to_fs(: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.
TEXT TEXT
@ -45,11 +45,11 @@ describe Jobs::OldKeysReminder do
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_fs(:db)}
github_client_secret - #{github_secret.updated_at.to_date.to_s(:db)} github_client_secret - #{github_secret.updated_at.to_date.to_fs(: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_fs(:db)}
api key description - #{api_key.created_at.to_date.to_s(:db)} api key description - #{api_key.created_at.to_date.to_fs(: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_fs(: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.
TEXT TEXT

View File

@ -1,13 +1,11 @@
# frozen_string_literal: true # frozen_string_literal: true
describe Jobs::ProcessPost do describe Jobs::ProcessPost do
it "returns when the post cannot be found" do it "returns when the post cannot be found" do
expect { Jobs::ProcessPost.new.perform(post_id: 1, sync_exec: true) }.not_to raise_error expect { Jobs::ProcessPost.new.perform(post_id: 1, sync_exec: true) }.not_to raise_error
end end
context 'with a post' do context 'with a post' do
fab!(:post) { Fabricate(:post) } fab!(:post) { Fabricate(:post) }
it 'does not erase posts when CookedPostProcessor malfunctions' do it 'does not erase posts when CookedPostProcessor malfunctions' do
@ -90,5 +88,4 @@ describe Jobs::ProcessPost do
expect(post.topic.reload.excerpt).to eq("Some OP content") expect(post.topic.reload.excerpt).to eq("Some OP content")
end end
end end
end end

View File

@ -1,7 +1,6 @@
# frozen_string_literal: true # frozen_string_literal: true
describe Jobs::PullHotlinkedImages do describe Jobs::PullHotlinkedImages do
let(:image_url) { "http://wiki.mozilla.org/images/2/2e/Longcat1.png" } let(:image_url) { "http://wiki.mozilla.org/images/2/2e/Longcat1.png" }
let(:broken_image_url) { "http://wiki.mozilla.org/images/2/2e/Longcat2.png" } let(:broken_image_url) { "http://wiki.mozilla.org/images/2/2e/Longcat2.png" }
let(:large_image_url) { "http://wiki.mozilla.org/images/2/2e/Longcat3.png" } let(:large_image_url) { "http://wiki.mozilla.org/images/2/2e/Longcat3.png" }

View File

@ -1,7 +1,6 @@
# frozen_string_literal: true # frozen_string_literal: true
describe Jobs::SendSystemMessage do describe Jobs::SendSystemMessage do
it "raises an error without a user_id" do it "raises an error without a user_id" do
expect { Jobs::SendSystemMessage.new.execute(message_type: 'welcome_invite') }.to raise_error(Discourse::InvalidParameters) expect { Jobs::SendSystemMessage.new.execute(message_type: 'welcome_invite') }.to raise_error(Discourse::InvalidParameters)
end end
@ -11,7 +10,6 @@ describe Jobs::SendSystemMessage do
end end
context 'with valid parameters' do context 'with valid parameters' do
fab!(:user) { Fabricate(:user) } fab!(:user) { Fabricate(:user) }
it "should call SystemMessage.create" do it "should call SystemMessage.create" do
@ -24,7 +22,5 @@ describe Jobs::SendSystemMessage do
SystemMessage.any_instance.expects(:create).with('post_hidden', options) SystemMessage.any_instance.expects(:create).with('post_hidden', options)
Jobs::SendSystemMessage.new.execute(user_id: user.id, message_type: 'post_hidden', message_options: options) Jobs::SendSystemMessage.new.execute(user_id: user.id, message_type: 'post_hidden', message_options: options)
end end
end end
end end

View File

@ -1,7 +1,5 @@
# frozen_string_literal: true # frozen_string_literal: true
require 'guardian'
describe Guardian do describe Guardian do
fab!(:user) { Fabricate(:user) } fab!(:user) { Fabricate(:user) }

View File

@ -193,7 +193,7 @@ describe Hijack do
Process.stubs(:clock_gettime).returns(1.0) Process.stubs(:clock_gettime).returns(1.0)
tester.hijack_test do tester.hijack_test do
Process.stubs(:clock_gettime).returns(2.0) Process.stubs(:clock_gettime).returns(2.0)
redirect_to 'http://awesome.com' redirect_to 'http://awesome.com', allow_other_host: true
end end
result = "HTTP/1.1 302 Found\r\nLocation: http://awesome.com\r\nContent-Type: text/html; charset=utf-8\r\nContent-Length: 84\r\nConnection: close\r\nX-Runtime: 1.000000\r\n\r\n<html><body>You are being <a href=\"http://awesome.com\">redirected</a>.</body></html>" result = "HTTP/1.1 302 Found\r\nLocation: http://awesome.com\r\nContent-Type: text/html; charset=utf-8\r\nContent-Length: 84\r\nConnection: close\r\nX-Runtime: 1.000000\r\n\r\n<html><body>You are being <a href=\"http://awesome.com\">redirected</a>.</body></html>"

View File

@ -1,7 +1,5 @@
# frozen_string_literal: true # frozen_string_literal: true
require "i18n/i18n_interpolation_keys_finder"
RSpec.describe I18nInterpolationKeysFinder do RSpec.describe I18nInterpolationKeysFinder do
describe '#find' do describe '#find' do
it 'should return the right keys' do it 'should return the right keys' do

View File

@ -1,7 +1,5 @@
# frozen_string_literal: true # frozen_string_literal: true
require 'onpdiff'
describe ONPDiff do describe ONPDiff do
describe "diff" do describe "diff" do

View File

@ -74,20 +74,6 @@ describe Stylesheet::Compiler do
end end
end end
it "supports asset-url" do
css, _map = Stylesheet::Compiler.compile(".body{background-image: asset-url('/images/favicons/github.png');}", "test.scss")
expect(css).to include("url('/images/favicons/github.png')")
expect(css).not_to include('asset-url')
end
it "supports image-url" do
css, _map = Stylesheet::Compiler.compile(".body{background-image: image-url('/favicons/github.png');}", "test.scss")
expect(css).to include("url('/favicons/github.png')")
expect(css).not_to include('image-url')
end
it "supports absolute-image-url" do it "supports absolute-image-url" do
scss = Stylesheet::Importer.new({}).prepended_scss scss = Stylesheet::Importer.new({}).prepended_scss
scss += ".body{background-image: absolute-image-url('/favicons/github.png');}" scss += ".body{background-image: absolute-image-url('/favicons/github.png');}"

View File

@ -1,7 +1,5 @@
# frozen_string_literal: true # frozen_string_literal: true
require 'validators/category_search_priority_weights_validator'
RSpec.describe CategorySearchPriorityWeightsValidator do RSpec.describe CategorySearchPriorityWeightsValidator do
it "should validate the results correctly" do it "should validate the results correctly" do
[1, 1.1].each do |value| [1, 1.1].each do |value|

View File

@ -1,8 +1,6 @@
# encoding: UTF-8 # encoding: UTF-8
# frozen_string_literal: true # frozen_string_literal: true
require 'validators/max_emojis_validator'
describe MaxEmojisValidator do describe MaxEmojisValidator do
# simulate Rails behavior (singleton) # simulate Rails behavior (singleton)

View File

@ -1,7 +1,6 @@
# encoding: UTF-8 # encoding: UTF-8
# frozen_string_literal: true # frozen_string_literal: true
require 'validators/quality_title_validator'
require 'ostruct' require 'ostruct'
module QualityTitleValidatorSpec module QualityTitleValidatorSpec

View File

@ -1,8 +1,6 @@
# encoding: UTF-8 # encoding: UTF-8
# frozen_string_literal: true # frozen_string_literal: true
require 'validators/topic_title_length_validator'
describe TopicTitleLengthValidator do describe TopicTitleLengthValidator do
# simulate Rails behavior (singleton) # simulate Rails behavior (singleton)

View File

@ -1,7 +1,5 @@
# frozen_string_literal: true # frozen_string_literal: true
require 'validators/topic_title_length_validator'
RSpec.describe UrlValidator do RSpec.describe UrlValidator do
let(:record) { Fabricate.build(:user_profile, user: Fabricate.build(:user)) } let(:record) { Fabricate.build(:user_profile, user: Fabricate.build(:user)) }
let(:validator) { described_class.new(attributes: :website) } let(:validator) { described_class.new(attributes: :website) }

View File

@ -3,8 +3,8 @@
describe GivenDailyLike do describe GivenDailyLike do
it 'no errors without a user' do it 'no errors without a user' do
expect(-> { GivenDailyLike.increment_for(nil) }).not_to raise_error expect { GivenDailyLike.increment_for(nil) }.not_to raise_error
expect(-> { GivenDailyLike.decrement_for(nil) }).not_to raise_error expect { GivenDailyLike.decrement_for(nil) }.not_to raise_error
end end
context 'with a user' do context 'with a user' do

View File

@ -487,7 +487,7 @@ end
def decrypt_auth_cookie(cookie) def decrypt_auth_cookie(cookie)
request = ActionDispatch::Request.new(create_request_env.merge("HTTP_COOKIE" => "_t=#{cookie}")) request = ActionDispatch::Request.new(create_request_env.merge("HTTP_COOKIE" => "_t=#{cookie}"))
request.cookie_jar.encrypted["_t"] request.cookie_jar.encrypted["_t"].with_indifferent_access
end end
class SpecSecureRandom class SpecSecureRandom

View File

@ -22,13 +22,22 @@ describe InvitesController do
end end
end end
it 'shows unobfuscated email if email data is present in authentication data' do context 'when email data is present in authentication data' do
ActionDispatch::Request.any_instance.stubs(:session).returns(authentication: { email: invite.email }) let(:store) { ActionDispatch::Session::CookieStore.new({}) }
get "/invites/#{invite.invite_key}" let(:session_stub) { ActionDispatch::Request::Session.create(store, ActionDispatch::TestRequest.create, {}) }
expect(response.status).to eq(200)
expect(response.body).to have_tag(:script, with: { src: "/assets/#{EmberCli.transform_name("application")}.js" }) before do
expect(response.body).to include(invite.email) session_stub[:authentication] = { email: invite.email }
expect(response.body).not_to include('i*****g@a***********e.ooo') ActionDispatch::Request.any_instance.stubs(:session).returns(session_stub)
end
it 'shows unobfuscated email' do
get "/invites/#{invite.invite_key}"
expect(response.status).to eq(200)
expect(response.body).to have_tag(:script, with: { src: "/assets/#{EmberCli.transform_name("application")}.js" })
expect(response.body).to include(invite.email)
expect(response.body).not_to include('i*****g@a***********e.ooo')
end
end end
it 'shows default user fields' do it 'shows default user fields' do

View File

@ -38,7 +38,7 @@ RSpec.describe EmailSettingsExceptionHandler do
end end
it "formats a Net::SMTPAuthenticationError with application-specific password Gmail error" do it "formats a Net::SMTPAuthenticationError with application-specific password Gmail error" do
exception = Net::SMTPAuthenticationError.new("Application-specific password required") exception = Net::SMTPAuthenticationError.new(nil, message: "Application-specific password required")
expect(subject.class.friendly_exception_message(exception, "smtp.gmail.com")).to eq( expect(subject.class.friendly_exception_message(exception, "smtp.gmail.com")).to eq(
I18n.t("email_settings.authentication_error_gmail_app_password") I18n.t("email_settings.authentication_error_gmail_app_password")
) )
@ -52,15 +52,15 @@ RSpec.describe EmailSettingsExceptionHandler do
end end
it "formats a Net::SMTPSyntaxError, Net::SMTPFatalError, and Net::SMTPUnknownError" do it "formats a Net::SMTPSyntaxError, Net::SMTPFatalError, and Net::SMTPUnknownError" do
exception = Net::SMTPSyntaxError.new("bad syntax") exception = Net::SMTPSyntaxError.new(nil, message: "bad syntax")
expect(subject.class.friendly_exception_message(exception, "smtp.test.com")).to eq( expect(subject.class.friendly_exception_message(exception, "smtp.test.com")).to eq(
I18n.t("email_settings.smtp_unhandled_error", message: exception.message) I18n.t("email_settings.smtp_unhandled_error", message: exception.message)
) )
exception = Net::SMTPFatalError.new("fatal") exception = Net::SMTPFatalError.new(nil, message: "fatal")
expect(subject.class.friendly_exception_message(exception, "smtp.test.com")).to eq( expect(subject.class.friendly_exception_message(exception, "smtp.test.com")).to eq(
I18n.t("email_settings.smtp_unhandled_error", message: exception.message) I18n.t("email_settings.smtp_unhandled_error", message: exception.message)
) )
exception = Net::SMTPUnknownError.new("unknown") exception = Net::SMTPUnknownError.new(nil, message: "unknown")
expect(subject.class.friendly_exception_message(exception, "smtp.test.com")).to eq( expect(subject.class.friendly_exception_message(exception, "smtp.test.com")).to eq(
I18n.t("email_settings.smtp_unhandled_error", message: exception.message) I18n.t("email_settings.smtp_unhandled_error", message: exception.message)
) )

View File

@ -112,7 +112,7 @@ RSpec.describe EmailSettingsValidator do
it "logs a warning if debug: true passed in and still raises the error" do it "logs a warning if debug: true passed in and still raises the error" do
Rails.logger.expects(:warn).with(regexp_matches(/\[EmailSettingsValidator\] Error encountered/)).at_least_once Rails.logger.expects(:warn).with(regexp_matches(/\[EmailSettingsValidator\] Error encountered/)).at_least_once
net_smtp_stub.stubs(:start).raises(Net::SMTPAuthenticationError, "invalid credentials") net_smtp_stub.stubs(:start).raises(Net::SMTPAuthenticationError, stub(message: "invalid credentials"))
expect { subject.class.validate_smtp(host: host, port: port, username: username, password: password, debug: true, domain: domain) }.to raise_error(Net::SMTPAuthenticationError) expect { subject.class.validate_smtp(host: host, port: port, username: username, password: password, debug: true, domain: domain) }.to raise_error(Net::SMTPAuthenticationError)
end end

View File

@ -1,12 +1,13 @@
# frozen_string_literal: true # frozen_string_literal: true
describe "users/omniauth_callbacks/failure.html.erb" do describe "users/omniauth_callbacks/failure.html.erb" do
before do
it "renders the failure page" do
flash[:error] = I18n.t("login.omniauth_error", strategy: 'test') flash[:error] = I18n.t("login.omniauth_error", strategy: 'test')
render
expect(rendered.match(I18n.t("login.omniauth_error.generic", strategy: 'test'))).not_to eq(nil)
end end
it "renders the failure page" do
render template: 'users/omniauth_callbacks/failure'
expect(rendered).to match I18n.t("login.omniauth_error.generic", strategy: 'test')
end
end end