discourse/app/helpers/application_helper.rb

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

760 lines
22 KiB
Ruby
Raw Normal View History

# coding: utf-8
# frozen_string_literal: true
2013-02-05 14:16:51 -05:00
require "current_user"
require "canonical_url"
2013-02-05 14:16:51 -05:00
module ApplicationHelper
include CurrentUser
include CanonicalURL::Helpers
include ConfigurableUrls
include GlobalPath
def self.extra_body_classes
@extra_body_classes ||= Set.new
end
2013-02-05 14:16:51 -05:00
def discourse_config_environment(testing: false)
# TODO: Can this come from Ember CLI somehow?
config = {
modulePrefix: "discourse",
environment: Rails.env,
rootURL: Discourse.base_path,
locationType: "history",
historySupportMiddleware: false,
EmberENV: {
FEATURES: {
},
EXTEND_PROTOTYPES: {
Date: false,
},
_APPLICATION_TEMPLATE_WRAPPER: false,
_DEFAULT_ASYNC_OBSERVERS: true,
_JQUERY_INTEGRATION: true,
},
APP: {
name: "discourse",
version: "#{Discourse::VERSION::STRING} #{Discourse.git_version}",
exportApplicationGlobal: true,
},
}
if testing
config[:environment] = "test"
config[:locationType] = "none"
config[:APP][:autoboot] = false
config[:APP][:rootElement] = "#ember-testing"
end
config.to_json
end
def google_universal_analytics_json(ua_domain_name = nil)
result = {}
result[:cookieDomain] = ua_domain_name.gsub(%r{\Ahttp(s)?://}, "") if ua_domain_name
2015-04-07 13:09:49 -04:00
result[:userId] = current_user.id if current_user.present?
result[:allowLinker] = true if SiteSetting.ga_universal_auto_link_domains.present?
result.to_json
2015-04-07 13:09:49 -04:00
end
def ga_universal_json
google_universal_analytics_json(SiteSetting.ga_universal_domain_name)
end
def google_tag_manager_json
google_universal_analytics_json
end
def self.google_tag_manager_nonce
@gtm_nonce ||= SecureRandom.hex
end
def shared_session_key
if SiteSetting.long_polling_base_url != "/" && current_user
sk = "shared_session_key"
return request.env[sk] if request.env[sk]
request.env[sk] = key = (session[sk] ||= SecureRandom.hex)
Discourse.redis.setex "#{sk}_#{key}", 7.days, current_user.id.to_s
key
end
end
def is_brotli_req?
request.env["HTTP_ACCEPT_ENCODING"] =~ /br/
end
def is_gzip_req?
request.env["HTTP_ACCEPT_ENCODING"] =~ /gzip/
end
2018-11-15 16:13:18 -05:00
def script_asset_path(script)
path = ActionController::Base.helpers.asset_path("#{script}.js")
if GlobalSetting.use_s3? && GlobalSetting.s3_cdn_url
resolved_s3_asset_cdn_url =
GlobalSetting.s3_asset_cdn_url.presence || GlobalSetting.s3_cdn_url
if GlobalSetting.cdn_url
folder = ActionController::Base.config.relative_url_root || "/"
path =
path.gsub(
File.join(GlobalSetting.cdn_url, folder, "/"),
File.join(resolved_s3_asset_cdn_url, "/"),
)
else
# we must remove the subfolder path here, assets are uploaded to s3
# without it getting involved
if ActionController::Base.config.relative_url_root
path = path.sub(ActionController::Base.config.relative_url_root, "")
end
path = "#{resolved_s3_asset_cdn_url}#{path}"
end
# assets needed for theme testing are not compressed because they take a fair
# amount of time to compress (+30 seconds) during rebuilds/deploys when the
# vast majority of sites will never need them, so it makes more sense to serve
# them uncompressed instead of making everyone's rebuild/deploy take +30 more
# seconds.
if !script.start_with?("discourse/tests/")
if is_brotli_req?
path = path.gsub(/\.([^.]+)\z/, '.br.\1')
elsif is_gzip_req?
path = path.gsub(/\.([^.]+)\z/, '.gz.\1')
end
end
elsif GlobalSetting.cdn_url&.start_with?("https") && is_brotli_req? &&
Rails.env != "development"
path = path.gsub("#{GlobalSetting.cdn_url}/assets/", "#{GlobalSetting.cdn_url}/brotli_asset/")
end
2018-11-15 16:13:18 -05:00
path
end
DEV: Allow Ember CLI assets to be used by development Rails app (#16511) Previously, accessing the Rails app directly in development mode would give you assets from our 'legacy' Ember asset pipeline. The only way to run with Ember CLI assets was to run ember-cli as a proxy. This was quite limiting when working on things which are bypassed when using the ember-cli proxy (e.g. changes to `application.html.erb`). Also, since `ember-auto-import` introduced chunking, visiting `/theme-qunit` under Ember CLI was failing to include all necessary chunks. This commit teaches Sprockets about our Ember CLI assets so that they can be used in development mode, and are automatically collected up under `/public/assets` during `assets:precompile`. As a bonus, this allows us to remove all the custom manifest modification from `assets:precompile`. The key changes are: - Introduce a shared `EmberCli.enabled?` helper - When ember-cli is enabled, add ember-cli `/dist/assets` as the top-priority Rails asset directory - Have ember-cli output a `chunks.json` manifest, and teach `preload_script` to read it and append the correct chunks to their associated `afterFile` - Remove most custom ember-cli logic from the `assets:precompile` step. Instead, rely on Rails to take care of pulling the 'precompiled' assets into the `public/assets` directory. Move the 'renaming' logic to runtime, so it can be used in development mode as well. - Remove fingerprinting from `ember-cli-build`, and allow Rails to take care of things Long-term, we may want to replace Sprockets with the lighter-weight Propshaft. The changes made in this commit have been made with that long-term goal in mind. tldr: when you visit the rails app directly, you'll now be served the current ember-cli assets. To keep these up-to-date make sure either `ember serve`, or `ember build --watch` is running. If you really want to load the old non-ember-cli assets, then you should start the server with `EMBER_CLI_PROD_ASSETS=0`. (the legacy asset pipeline will be removed very soon)
2022-04-21 11:26:34 -04:00
def preload_script(script)
scripts = [script]
if chunks = EmberCli.script_chunks[script]
DEV: Allow Ember CLI assets to be used by development Rails app (#16511) Previously, accessing the Rails app directly in development mode would give you assets from our 'legacy' Ember asset pipeline. The only way to run with Ember CLI assets was to run ember-cli as a proxy. This was quite limiting when working on things which are bypassed when using the ember-cli proxy (e.g. changes to `application.html.erb`). Also, since `ember-auto-import` introduced chunking, visiting `/theme-qunit` under Ember CLI was failing to include all necessary chunks. This commit teaches Sprockets about our Ember CLI assets so that they can be used in development mode, and are automatically collected up under `/public/assets` during `assets:precompile`. As a bonus, this allows us to remove all the custom manifest modification from `assets:precompile`. The key changes are: - Introduce a shared `EmberCli.enabled?` helper - When ember-cli is enabled, add ember-cli `/dist/assets` as the top-priority Rails asset directory - Have ember-cli output a `chunks.json` manifest, and teach `preload_script` to read it and append the correct chunks to their associated `afterFile` - Remove most custom ember-cli logic from the `assets:precompile` step. Instead, rely on Rails to take care of pulling the 'precompiled' assets into the `public/assets` directory. Move the 'renaming' logic to runtime, so it can be used in development mode as well. - Remove fingerprinting from `ember-cli-build`, and allow Rails to take care of things Long-term, we may want to replace Sprockets with the lighter-weight Propshaft. The changes made in this commit have been made with that long-term goal in mind. tldr: when you visit the rails app directly, you'll now be served the current ember-cli assets. To keep these up-to-date make sure either `ember serve`, or `ember build --watch` is running. If you really want to load the old non-ember-cli assets, then you should start the server with `EMBER_CLI_PROD_ASSETS=0`. (the legacy asset pipeline will be removed very soon)
2022-04-21 11:26:34 -04:00
scripts.push(*chunks)
end
scripts
.map do |name|
DEV: Allow Ember CLI assets to be used by development Rails app (#16511) Previously, accessing the Rails app directly in development mode would give you assets from our 'legacy' Ember asset pipeline. The only way to run with Ember CLI assets was to run ember-cli as a proxy. This was quite limiting when working on things which are bypassed when using the ember-cli proxy (e.g. changes to `application.html.erb`). Also, since `ember-auto-import` introduced chunking, visiting `/theme-qunit` under Ember CLI was failing to include all necessary chunks. This commit teaches Sprockets about our Ember CLI assets so that they can be used in development mode, and are automatically collected up under `/public/assets` during `assets:precompile`. As a bonus, this allows us to remove all the custom manifest modification from `assets:precompile`. The key changes are: - Introduce a shared `EmberCli.enabled?` helper - When ember-cli is enabled, add ember-cli `/dist/assets` as the top-priority Rails asset directory - Have ember-cli output a `chunks.json` manifest, and teach `preload_script` to read it and append the correct chunks to their associated `afterFile` - Remove most custom ember-cli logic from the `assets:precompile` step. Instead, rely on Rails to take care of pulling the 'precompiled' assets into the `public/assets` directory. Move the 'renaming' logic to runtime, so it can be used in development mode as well. - Remove fingerprinting from `ember-cli-build`, and allow Rails to take care of things Long-term, we may want to replace Sprockets with the lighter-weight Propshaft. The changes made in this commit have been made with that long-term goal in mind. tldr: when you visit the rails app directly, you'll now be served the current ember-cli assets. To keep these up-to-date make sure either `ember serve`, or `ember build --watch` is running. If you really want to load the old non-ember-cli assets, then you should start the server with `EMBER_CLI_PROD_ASSETS=0`. (the legacy asset pipeline will be removed very soon)
2022-04-21 11:26:34 -04:00
path = script_asset_path(name)
preload_script_url(path)
end
.join("\n")
.html_safe
end
def preload_script_url(url)
add_resource_preload_list(url, "script")
if GlobalSetting.preload_link_header
<<~HTML.html_safe
<script defer src="#{url}"></script>
HTML
else
<<~HTML.html_safe
<link rel="preload" href="#{url}" as="script">
<script defer src="#{url}"></script>
HTML
end
end
def add_resource_preload_list(resource_url, type)
if !@links_to_preload.nil?
@links_to_preload << %Q(<#{resource_url}>; rel="preload"; as="#{type}")
end
end
def discourse_csrf_tags
# anon can not have a CSRF token cause these are all pages
# that may be cached, causing a mismatch between session CSRF
# and CSRF on page and horrible impossible to debug login issues
csrf_meta_tags if current_user
end
def html_classes
list = []
list << (mobile_view? ? "mobile-view" : "desktop-view")
list << (mobile_device? ? "mobile-device" : "not-mobile-device")
list << "ios-device" if ios_device?
list << "rtl" if rtl?
list << text_size_class
list << "anon" unless current_user
list.join(" ")
2014-08-27 07:38:03 -04:00
end
def body_classes
result = ApplicationHelper.extra_body_classes.to_a
result << "category-#{@category.slug_path.join("-")}" if @category && @category.url.present?
if current_user.present? && current_user.primary_group_id &&
primary_group_name = Group.where(id: current_user.primary_group_id).pick(:name)
result << "primary-group-#{primary_group_name.downcase}"
end
result.join(" ")
end
def text_size_class
requested_cookie_size, cookie_seq = cookies[:text_size]&.split("|")
server_seq = current_user&.user_option&.text_size_seq
if cookie_seq && server_seq && cookie_seq.to_i >= server_seq &&
UserOption.text_sizes.keys.include?(requested_cookie_size&.to_sym)
cookie_size = requested_cookie_size
end
size = cookie_size || current_user&.user_option&.text_size || SiteSetting.default_text_size
"text-size-#{size}"
end
def escape_unicode(javascript)
if javascript
javascript = javascript.scrub
2013-09-10 02:01:36 -04:00
javascript.gsub!(/\342\200\250/u, "&#x2028;")
javascript.gsub!(%r{(</)}u, '\u003C/')
javascript
else
""
end
end
def format_topic_title(title)
2016-11-22 12:37:22 -05:00
PrettyText.unescape_emoji strip_tags(title)
2015-10-15 03:59:29 -04:00
end
2013-02-05 14:16:51 -05:00
def with_format(format, &block)
old_formats = formats
self.formats = [format]
block.call
self.formats = old_formats
nil
end
def age_words(secs)
AgeWords.age_words(secs)
end
def short_date(dt)
if dt.year == Time.now.year
I18n.l(dt, format: :short_no_year)
else
I18n.l(dt, format: :date_only)
end
end
2013-02-05 14:16:51 -05:00
def guardian
@guardian ||= Guardian.new(current_user)
end
def admin?
current_user.try(:admin?)
end
def moderator?
current_user.try(:moderator?)
end
def staff?
current_user.try(:staff?)
end
def rtl?
%w[ar ur fa_IR he].include? I18n.locale.to_s
end
def html_lang
(request ? I18n.locale.to_s : SiteSetting.default_locale).sub("_", "-")
end
# Creates open graph and twitter card meta data
def crawlable_meta_data(opts = nil)
opts ||= {}
opts[:url] ||= "#{Discourse.base_url_no_prefix}#{request.fullpath}"
# if slug generation method is encoded, non encoded urls can sneak in
# via bots
url = opts[:url]
if url.encoding.name != "UTF-8" || !url.valid_encoding?
opts[:url] = url.dup.force_encoding("UTF-8").scrub!
end
if opts[:image].blank?
twitter_summary_large_image_url = SiteSetting.site_twitter_summary_large_image_url
if twitter_summary_large_image_url.present?
opts[:twitter_summary_large_image] = twitter_summary_large_image_url
end
FEATURE: Automatically generate optimized site metadata icons (#7372) This change automatically resizes icons for various purposes. Admins can now upload `logo` and `logo_small`, and everything else will be auto-generated. Specific icons can still be uploaded separately if required. ## Core - Adds an SiteIconManager module which manages automatic resizing and fallback - Icons are looked up in the OptimizedImage table at runtime, and then cached in Redis. If the resized version is missing for some reason, then most icons will fall back to the original files. Some icons (e.g. PWA Manifest) will return `nil` (because an incorrectly sized icon is worse than a missing icon). - `SiteSetting.site_large_icon_url` will return the optimized version, including any fallback. `SiteSetting.large_icon` continues to return the upload object. This means that (almost) no changes are required in core/plugins to support this new system. - Icons are resized whenever a relevant site setting is changed, and during post-deploy migrations ## Wizard - Allows `requiresRefresh` wizard steps to reload data via AJAX instead of a full page reload - Add placeholders to the **icons** step of the wizard, which automatically update from the "Square Logo" - Various copy updates to support the changes - Remove the "upload-time" resizing for `large_icon`. This is no longer required. ## Site Settings UX - Move logo/icon settings under a new "Branding" tab - Various copy changes to support the changes - Adds placeholder support to the `image-uploader` component - Automatically reloads site settings after saving. This allows setting placeholders to change based on changes to other settings - Upload site settings will be assigned a placeholder if SiteIconManager `responds_to?` an icon of the same name ## Dashboard Warnings - Remove PWA icon and PWA title warnings. Both are now handled automatically. ## Bonus - Updated the sketch logos to use @awesomerobot's new high-res designs
2019-05-01 09:44:45 -04:00
opts[:image] = SiteSetting.site_opengraph_image_url
end
# Use the correct scheme for opengraph/twitter image
opts[:image] = get_absolute_image_url(opts[:image]) if opts[:image].present?
opts[:twitter_summary_large_image] = get_absolute_image_url(
opts[:twitter_summary_large_image],
) if opts[:twitter_summary_large_image].present?
result = []
result << tag(:meta, property: "og:site_name", content: SiteSetting.title)
result << tag(:meta, property: "og:type", content: "website")
generate_twitter_card_metadata(result, opts)
result << tag(:meta, property: "og:image", content: opts[:image]) if opts[:image].present?
%i[url title description].each do |property|
if opts[property].present?
content = (property == :url ? opts[property] : gsub_emoji_to_unicode(opts[property]))
result << tag(:meta, { property: "og:#{property}", content: content }, nil, true)
result << tag(:meta, { name: "twitter:#{property}", content: content }, nil, true)
end
end
Array
.wrap(opts[:breadcrumbs])
.each do |breadcrumb|
result << tag(:meta, property: "og:article:section", content: breadcrumb[:name])
result << tag(:meta, property: "og:article:section:color", content: breadcrumb[:color])
end
Array
.wrap(opts[:tags])
.each { |tag_name| result << tag(:meta, property: "og:article:tag", content: tag_name) }
if opts[:read_time] && opts[:read_time] > 0 && opts[:like_count] && opts[:like_count] > 0
result << tag(:meta, name: "twitter:label1", value: I18n.t("reading_time"))
result << tag(:meta, name: "twitter:data1", value: "#{opts[:read_time]} mins 🕑")
result << tag(:meta, name: "twitter:label2", value: I18n.t("likes"))
result << tag(:meta, name: "twitter:data2", value: "#{opts[:like_count]}")
end
if opts[:published_time]
result << tag(:meta, property: "article:published_time", content: opts[:published_time])
end
result << tag(:meta, property: "og:ignore_canonical", content: true) if opts[:ignore_canonical]
result.join("\n")
end
private def generate_twitter_card_metadata(result, opts)
img_url =
(
if opts[:twitter_summary_large_image].present?
opts[:twitter_summary_large_image]
else
opts[:image]
end
)
# Twitter does not allow SVGs, see https://developer.twitter.com/en/docs/twitter-for-websites/cards/overview/markup
if img_url.ends_with?(".svg")
img_url = SiteSetting.site_logo_url.ends_with?(".svg") ? nil : SiteSetting.site_logo_url
end
if opts[:twitter_summary_large_image].present? && img_url.present?
result << tag(:meta, name: "twitter:card", content: "summary_large_image")
result << tag(:meta, name: "twitter:image", content: img_url)
elsif opts[:image].present? && img_url.present?
result << tag(:meta, name: "twitter:card", content: "summary")
result << tag(:meta, name: "twitter:image", content: img_url)
else
result << tag(:meta, name: "twitter:card", content: "summary")
end
end
def render_sitelinks_search_tag
if current_page?("/") || current_page?(Discourse.base_path)
json = {
"@context" => "http://schema.org",
"@type" => "WebSite",
:url => Discourse.base_url,
:potentialAction => {
"@type" => "SearchAction",
:target => "#{Discourse.base_url}/search?q={search_term_string}",
"query-input" => "required name=search_term_string",
},
}
content_tag(:script, MultiJson.dump(json).html_safe, type: "application/ld+json")
end
end
def gsub_emoji_to_unicode(str)
2017-07-21 14:24:28 -04:00
Emoji.gsub_emoji_to_unicode(str)
end
def application_logo_url
@application_logo_url ||=
begin
if mobile_view?
if dark_color_scheme? && SiteSetting.site_mobile_logo_dark_url.present?
SiteSetting.site_mobile_logo_dark_url
elsif SiteSetting.site_mobile_logo_url.present?
SiteSetting.site_mobile_logo_url
end
else
if dark_color_scheme? && SiteSetting.site_logo_dark_url.present?
SiteSetting.site_logo_dark_url
else
SiteSetting.site_logo_url
end
end
end
end
def application_logo_dark_url
@application_logo_dark_url ||=
begin
if dark_scheme_id != -1
if mobile_view? && SiteSetting.site_mobile_logo_dark_url != application_logo_url
SiteSetting.site_mobile_logo_dark_url
elsif !mobile_view? && SiteSetting.site_logo_dark_url != application_logo_url
SiteSetting.site_logo_dark_url
end
end
end
end
def login_path
"#{Discourse.base_path}/login"
end
def mobile_view?
MobileDetection.resolve_mobile_view!(request.user_agent, params, session)
end
def crawler_layout?
controller&.use_crawler_layout?
end
def include_crawler_content?
crawler_layout? || !mobile_view? || !modern_mobile_device?
end
def modern_mobile_device?
MobileDetection.modern_mobile_device?(request.user_agent)
end
def mobile_device?
MobileDetection.mobile_device?(request.user_agent)
end
2013-11-14 10:41:16 -05:00
def ios_device?
MobileDetection.ios_device?(request.user_agent)
end
2013-11-14 10:41:16 -05:00
def customization_disabled?
request.env[ApplicationController::NO_THEMES]
end
def include_ios_native_app_banner?
current_user && current_user.trust_level >= 1 && SiteSetting.native_app_install_banner_ios
end
def ios_app_argument
# argument only makes sense for DiscourseHub app
if SiteSetting.ios_app_id == "1173672076"
", app-argument=discourse://new?siteUrl=#{Discourse.base_url}"
else
""
end
end
def include_splash_screen?
# A bit basic for now but will be expanded later
SiteSetting.splash_screen
end
def allow_plugins?
!request.env[ApplicationController::NO_PLUGINS]
end
def allow_third_party_plugins?
allow_plugins? && !request.env[ApplicationController::NO_UNOFFICIAL_PLUGINS]
end
def normalized_safe_mode
safe_mode = []
safe_mode << ApplicationController::NO_THEMES if customization_disabled?
safe_mode << ApplicationController::NO_PLUGINS if !allow_plugins?
safe_mode << ApplicationController::NO_UNOFFICIAL_PLUGINS if !allow_third_party_plugins?
safe_mode.join(",")
2013-11-14 10:41:16 -05:00
end
def loading_admin?
return false unless defined?(controller)
return false if controller.class.name.blank?
controller.class.name.split("::").first == "Admin"
end
def category_badge(category, opts = nil)
CategoryBadge.html_for(category, opts).html_safe
end
def self.all_connectors
@all_connectors = Dir.glob("plugins/*/app/views/connectors/**/*.html.erb")
end
def server_plugin_outlet(name, locals: {})
return "" if !GlobalSetting.load_plugins?
matcher = Regexp.new("/connectors/#{name}/.*\.html\.erb$")
erbs = ApplicationHelper.all_connectors.select { |c| c =~ matcher }
return "" if erbs.blank?
result = +""
erbs.each { |erb| result << render(inline: File.read(erb), locals: locals) }
result.html_safe
end
def topic_featured_link_domain(link)
begin
uri = UrlHelper.encode_and_parse(link)
uri = URI.parse("http://#{uri}") if uri.scheme.nil?
host = uri.host.downcase
host.start_with?("www.") ? host[4..-1] : host
rescue StandardError
""
end
end
def theme_id
if customization_disabled?
nil
else
request.env[:resolved_theme_id]
end
end
def stylesheet_manager
return @stylesheet_manager if defined?(@stylesheet_manager)
@stylesheet_manager = Stylesheet::Manager.new(theme_id: theme_id)
end
def scheme_id
return @scheme_id if defined?(@scheme_id)
custom_user_scheme_id = cookies[:color_scheme_id] || current_user&.user_option&.color_scheme_id
if custom_user_scheme_id && ColorScheme.find_by_id(custom_user_scheme_id)
return custom_user_scheme_id
end
return if theme_id.blank?
@scheme_id = Theme.where(id: theme_id).pick(:color_scheme_id)
end
def dark_scheme_id
cookies[:dark_scheme_id] || current_user&.user_option&.dark_scheme_id ||
SiteSetting.default_dark_mode_color_scheme_id
end
def current_homepage
current_user&.user_option&.homepage || SiteSetting.anonymous_homepage
end
def build_plugin_html(name)
return "" unless allow_plugins?
DiscoursePluginRegistry.build_html(name, controller) || ""
end
# If there is plugin HTML return that, otherwise yield to the template
def replace_plugin_html(name)
if (html = build_plugin_html(name)).present?
html
else
yield
nil
end
end
def theme_lookup(name)
FEATURE: Introduce theme/component QUnit tests (take 2) (#12661) This commit allows themes and theme components to have QUnit tests. To add tests to your theme/component, create a top-level directory in your theme and name it `test`, and Discourse will save all the files in that directory (and its sub-directories) as "tests files" in the database. While tests files/directories are not required to be organized in a specific way, we recommend that you follow Discourse core's tests [structure](https://github.com/discourse/discourse/tree/master/app/assets/javascripts/discourse/tests). Writing theme tests should be identical to writing plugins or core tests; all the `import` statements and APIs that you see in core (or plugins) to define/setup tests should just work in themes. You do need a working Discourse install to run theme tests, and you have 2 ways to run theme tests: * In the browser at the `/qunit` route. `/qunit` will run tests of all active themes/components as well as core and plugins. The `/qunit` now accepts a `theme_name` or `theme_url` params that you can use to run tests of a specific theme/component like so: `/qunit?theme_name=<your_theme_name>`. * In the command line using the `themes:qunit` rake task. This take is meant to run tests of a single theme/component so you need to provide it with a theme name or URL like so: `bundle exec rake themes:qunit[name=<theme_name>]` or `bundle exec rake themes:qunit[url=<theme_url>]`. There are some refactors to how Discourse processes JavaScript that comes with themes/components, and these refactors may break your JS customizations; see https://meta.discourse.org/t/upcoming-core-changes-that-may-break-some-themes-components-april-12/186252?u=osama for details on how you can check if your themes/components are affected and what you need to do to fix them. This commit also improves theme error handling in Discourse. We will now be able to catch errors that occur when theme initializers are run and prevent them from breaking the site and other themes/components.
2021-04-12 08:02:58 -04:00
Theme.lookup_field(
theme_id,
FEATURE: Introduce theme/component QUnit tests (take 2) (#12661) This commit allows themes and theme components to have QUnit tests. To add tests to your theme/component, create a top-level directory in your theme and name it `test`, and Discourse will save all the files in that directory (and its sub-directories) as "tests files" in the database. While tests files/directories are not required to be organized in a specific way, we recommend that you follow Discourse core's tests [structure](https://github.com/discourse/discourse/tree/master/app/assets/javascripts/discourse/tests). Writing theme tests should be identical to writing plugins or core tests; all the `import` statements and APIs that you see in core (or plugins) to define/setup tests should just work in themes. You do need a working Discourse install to run theme tests, and you have 2 ways to run theme tests: * In the browser at the `/qunit` route. `/qunit` will run tests of all active themes/components as well as core and plugins. The `/qunit` now accepts a `theme_name` or `theme_url` params that you can use to run tests of a specific theme/component like so: `/qunit?theme_name=<your_theme_name>`. * In the command line using the `themes:qunit` rake task. This take is meant to run tests of a single theme/component so you need to provide it with a theme name or URL like so: `bundle exec rake themes:qunit[name=<theme_name>]` or `bundle exec rake themes:qunit[url=<theme_url>]`. There are some refactors to how Discourse processes JavaScript that comes with themes/components, and these refactors may break your JS customizations; see https://meta.discourse.org/t/upcoming-core-changes-that-may-break-some-themes-components-april-12/186252?u=osama for details on how you can check if your themes/components are affected and what you need to do to fix them. This commit also improves theme error handling in Discourse. We will now be able to catch errors that occur when theme initializers are run and prevent them from breaking the site and other themes/components.
2021-04-12 08:02:58 -04:00
mobile_view? ? :mobile : :desktop,
name,
skip_transformation: request.env[:skip_theme_ids_transformation].present?,
)
end
def theme_translations_lookup
FEATURE: Introduce theme/component QUnit tests (take 2) (#12661) This commit allows themes and theme components to have QUnit tests. To add tests to your theme/component, create a top-level directory in your theme and name it `test`, and Discourse will save all the files in that directory (and its sub-directories) as "tests files" in the database. While tests files/directories are not required to be organized in a specific way, we recommend that you follow Discourse core's tests [structure](https://github.com/discourse/discourse/tree/master/app/assets/javascripts/discourse/tests). Writing theme tests should be identical to writing plugins or core tests; all the `import` statements and APIs that you see in core (or plugins) to define/setup tests should just work in themes. You do need a working Discourse install to run theme tests, and you have 2 ways to run theme tests: * In the browser at the `/qunit` route. `/qunit` will run tests of all active themes/components as well as core and plugins. The `/qunit` now accepts a `theme_name` or `theme_url` params that you can use to run tests of a specific theme/component like so: `/qunit?theme_name=<your_theme_name>`. * In the command line using the `themes:qunit` rake task. This take is meant to run tests of a single theme/component so you need to provide it with a theme name or URL like so: `bundle exec rake themes:qunit[name=<theme_name>]` or `bundle exec rake themes:qunit[url=<theme_url>]`. There are some refactors to how Discourse processes JavaScript that comes with themes/components, and these refactors may break your JS customizations; see https://meta.discourse.org/t/upcoming-core-changes-that-may-break-some-themes-components-april-12/186252?u=osama for details on how you can check if your themes/components are affected and what you need to do to fix them. This commit also improves theme error handling in Discourse. We will now be able to catch errors that occur when theme initializers are run and prevent them from breaking the site and other themes/components.
2021-04-12 08:02:58 -04:00
Theme.lookup_field(
theme_id,
FEATURE: Introduce theme/component QUnit tests (take 2) (#12661) This commit allows themes and theme components to have QUnit tests. To add tests to your theme/component, create a top-level directory in your theme and name it `test`, and Discourse will save all the files in that directory (and its sub-directories) as "tests files" in the database. While tests files/directories are not required to be organized in a specific way, we recommend that you follow Discourse core's tests [structure](https://github.com/discourse/discourse/tree/master/app/assets/javascripts/discourse/tests). Writing theme tests should be identical to writing plugins or core tests; all the `import` statements and APIs that you see in core (or plugins) to define/setup tests should just work in themes. You do need a working Discourse install to run theme tests, and you have 2 ways to run theme tests: * In the browser at the `/qunit` route. `/qunit` will run tests of all active themes/components as well as core and plugins. The `/qunit` now accepts a `theme_name` or `theme_url` params that you can use to run tests of a specific theme/component like so: `/qunit?theme_name=<your_theme_name>`. * In the command line using the `themes:qunit` rake task. This take is meant to run tests of a single theme/component so you need to provide it with a theme name or URL like so: `bundle exec rake themes:qunit[name=<theme_name>]` or `bundle exec rake themes:qunit[url=<theme_url>]`. There are some refactors to how Discourse processes JavaScript that comes with themes/components, and these refactors may break your JS customizations; see https://meta.discourse.org/t/upcoming-core-changes-that-may-break-some-themes-components-april-12/186252?u=osama for details on how you can check if your themes/components are affected and what you need to do to fix them. This commit also improves theme error handling in Discourse. We will now be able to catch errors that occur when theme initializers are run and prevent them from breaking the site and other themes/components.
2021-04-12 08:02:58 -04:00
:translations,
I18n.locale,
skip_transformation: request.env[:skip_theme_ids_transformation].present?,
)
end
def theme_js_lookup
FEATURE: Introduce theme/component QUnit tests (take 2) (#12661) This commit allows themes and theme components to have QUnit tests. To add tests to your theme/component, create a top-level directory in your theme and name it `test`, and Discourse will save all the files in that directory (and its sub-directories) as "tests files" in the database. While tests files/directories are not required to be organized in a specific way, we recommend that you follow Discourse core's tests [structure](https://github.com/discourse/discourse/tree/master/app/assets/javascripts/discourse/tests). Writing theme tests should be identical to writing plugins or core tests; all the `import` statements and APIs that you see in core (or plugins) to define/setup tests should just work in themes. You do need a working Discourse install to run theme tests, and you have 2 ways to run theme tests: * In the browser at the `/qunit` route. `/qunit` will run tests of all active themes/components as well as core and plugins. The `/qunit` now accepts a `theme_name` or `theme_url` params that you can use to run tests of a specific theme/component like so: `/qunit?theme_name=<your_theme_name>`. * In the command line using the `themes:qunit` rake task. This take is meant to run tests of a single theme/component so you need to provide it with a theme name or URL like so: `bundle exec rake themes:qunit[name=<theme_name>]` or `bundle exec rake themes:qunit[url=<theme_url>]`. There are some refactors to how Discourse processes JavaScript that comes with themes/components, and these refactors may break your JS customizations; see https://meta.discourse.org/t/upcoming-core-changes-that-may-break-some-themes-components-april-12/186252?u=osama for details on how you can check if your themes/components are affected and what you need to do to fix them. This commit also improves theme error handling in Discourse. We will now be able to catch errors that occur when theme initializers are run and prevent them from breaking the site and other themes/components.
2021-04-12 08:02:58 -04:00
Theme.lookup_field(
theme_id,
FEATURE: Introduce theme/component QUnit tests (take 2) (#12661) This commit allows themes and theme components to have QUnit tests. To add tests to your theme/component, create a top-level directory in your theme and name it `test`, and Discourse will save all the files in that directory (and its sub-directories) as "tests files" in the database. While tests files/directories are not required to be organized in a specific way, we recommend that you follow Discourse core's tests [structure](https://github.com/discourse/discourse/tree/master/app/assets/javascripts/discourse/tests). Writing theme tests should be identical to writing plugins or core tests; all the `import` statements and APIs that you see in core (or plugins) to define/setup tests should just work in themes. You do need a working Discourse install to run theme tests, and you have 2 ways to run theme tests: * In the browser at the `/qunit` route. `/qunit` will run tests of all active themes/components as well as core and plugins. The `/qunit` now accepts a `theme_name` or `theme_url` params that you can use to run tests of a specific theme/component like so: `/qunit?theme_name=<your_theme_name>`. * In the command line using the `themes:qunit` rake task. This take is meant to run tests of a single theme/component so you need to provide it with a theme name or URL like so: `bundle exec rake themes:qunit[name=<theme_name>]` or `bundle exec rake themes:qunit[url=<theme_url>]`. There are some refactors to how Discourse processes JavaScript that comes with themes/components, and these refactors may break your JS customizations; see https://meta.discourse.org/t/upcoming-core-changes-that-may-break-some-themes-components-april-12/186252?u=osama for details on how you can check if your themes/components are affected and what you need to do to fix them. This commit also improves theme error handling in Discourse. We will now be able to catch errors that occur when theme initializers are run and prevent them from breaking the site and other themes/components.
2021-04-12 08:02:58 -04:00
:extra_js,
nil,
skip_transformation: request.env[:skip_theme_ids_transformation].present?,
)
end
def discourse_stylesheet_preload_tag(name, opts = {})
manager =
if opts.key?(:theme_id)
Stylesheet::Manager.new(theme_id: customization_disabled? ? nil : opts[:theme_id])
else
stylesheet_manager
end
manager.stylesheet_preload_tag(name, "all")
end
def discourse_stylesheet_link_tag(name, opts = {})
manager =
if opts.key?(:theme_id)
Stylesheet::Manager.new(theme_id: customization_disabled? ? nil : opts[:theme_id])
else
stylesheet_manager
end
manager.stylesheet_link_tag(name, "all", self.method(:add_resource_preload_list))
end
def discourse_preload_color_scheme_stylesheets
result = +""
result << stylesheet_manager.color_scheme_stylesheet_preload_tag(scheme_id, "all")
if dark_scheme_id != -1
result << stylesheet_manager.color_scheme_stylesheet_preload_tag(
dark_scheme_id,
"(prefers-color-scheme: dark)",
)
end
result.html_safe
end
def discourse_color_scheme_stylesheets
result = +""
result << stylesheet_manager.color_scheme_stylesheet_link_tag(
scheme_id,
"all",
self.method(:add_resource_preload_list),
)
if dark_scheme_id != -1
result << stylesheet_manager.color_scheme_stylesheet_link_tag(
dark_scheme_id,
"(prefers-color-scheme: dark)",
self.method(:add_resource_preload_list),
)
end
result.html_safe
end
def discourse_theme_color_meta_tags
result = +""
if dark_scheme_id != -1
result << <<~HTML
<meta name="theme-color" media="(prefers-color-scheme: light)" content="##{ColorScheme.hex_for_name("header_background", scheme_id)}">
<meta name="theme-color" media="(prefers-color-scheme: dark)" content="##{ColorScheme.hex_for_name("header_background", dark_scheme_id)}">
HTML
else
result << <<~HTML
<meta name="theme-color" media="all" content="##{ColorScheme.hex_for_name("header_background", scheme_id)}">
HTML
end
result.html_safe
end
def dark_color_scheme?
return false if scheme_id.blank?
ColorScheme.find_by_id(scheme_id)&.is_dark?
end
def preloaded_json
return "{}" if @preloaded.blank?
@preloaded.transform_values { |value| escape_unicode(value) }.to_json
end
def client_side_setup_data
setup_data = {
cdn: Rails.configuration.action_controller.asset_host,
base_url: Discourse.base_url,
base_uri: Discourse.base_path,
environment: Rails.env,
letter_avatar_version: LetterAvatar.version,
2018-11-15 16:13:18 -05:00
markdown_it_url: script_asset_path("markdown-it-bundle"),
service_worker_url: "service-worker.js",
default_locale: SiteSetting.default_locale,
asset_version: Discourse.assets_digest,
disable_custom_css: loading_admin?,
highlight_js_path: HighlightJs.path,
svg_sprite_path: SvgSprite.path(theme_id),
enable_js_error_reporting: GlobalSetting.enable_js_error_reporting,
color_scheme_is_dark: dark_color_scheme?,
user_color_scheme_id: scheme_id,
user_dark_scheme_id: dark_scheme_id,
}
Upgrade to FontAwesome 5 (take two) (#6673) * Add missing icons to set * Revert FA5 revert This reverts commit 42572ff * use new SVG syntax in locales * Noscript page changes (remove login button, center "powered by" footer text) * Cast wider net for SVG icons in settings - include any _icon setting for SVG registry (offers better support for plugin settings) - let themes store multiple pipe-delimited icons in a setting - also replaces broken onebox image icon with SVG reference in cooked post processor * interpolate icons in locales * Fix composer whisper icon alignment * Add support for stacked icons * SECURITY: enforce hostname to match discourse hostname This ensures that the hostname rails uses for various helpers always matches the Discourse hostname * load SVG sprite with pre-initializers * FIX: enable caching on SVG sprites * PERF: use JSONP for SVG sprites so they are served from CDN This avoids needing to deal with CORS for loading of the SVG Note, added the svg- prefix to the filename so we can quickly tell in dev tools what the file is * Add missing SVG sprite JSONP script to CSP * Upgrade to FA 5.5.0 * Add support for all FA4.7 icons - adds complete frontend and backend for renamed FA4.7 icons - improves performance of SvgSprite.bundle and SvgSprite.all_icons * Fix group avatar flair preview - adds an endpoint at /svg-sprites/search/:keyword - adds frontend ajax call that pulls icon in avatar flair preview even when it is not in subset * Remove FA 4.7 font files
2018-11-26 16:49:57 -05:00
if Rails.env.development?
setup_data[:svg_icon_list] = SvgSprite.all_icons(theme_id)
setup_data[:debug_preloaded_app_data] = true if ENV["DEBUG_PRELOADED_APP_DATA"]
setup_data[:mb_last_file_change_id] = MessageBus.last_id("/file-change")
Upgrade to FontAwesome 5 (take two) (#6673) * Add missing icons to set * Revert FA5 revert This reverts commit 42572ff * use new SVG syntax in locales * Noscript page changes (remove login button, center "powered by" footer text) * Cast wider net for SVG icons in settings - include any _icon setting for SVG registry (offers better support for plugin settings) - let themes store multiple pipe-delimited icons in a setting - also replaces broken onebox image icon with SVG reference in cooked post processor * interpolate icons in locales * Fix composer whisper icon alignment * Add support for stacked icons * SECURITY: enforce hostname to match discourse hostname This ensures that the hostname rails uses for various helpers always matches the Discourse hostname * load SVG sprite with pre-initializers * FIX: enable caching on SVG sprites * PERF: use JSONP for SVG sprites so they are served from CDN This avoids needing to deal with CORS for loading of the SVG Note, added the svg- prefix to the filename so we can quickly tell in dev tools what the file is * Add missing SVG sprite JSONP script to CSP * Upgrade to FA 5.5.0 * Add support for all FA4.7 icons - adds complete frontend and backend for renamed FA4.7 icons - improves performance of SvgSprite.bundle and SvgSprite.all_icons * Fix group avatar flair preview - adds an endpoint at /svg-sprites/search/:keyword - adds frontend ajax call that pulls icon in avatar flair preview even when it is not in subset * Remove FA 4.7 font files
2018-11-26 16:49:57 -05:00
end
if guardian.can_enable_safe_mode? && params["safe_mode"]
setup_data[:safe_mode] = normalized_safe_mode
end
if SiteSetting.Upload.enable_s3_uploads
setup_data[:s3_cdn] = SiteSetting.Upload.s3_cdn_url.presence
setup_data[:s3_base_url] = SiteSetting.Upload.s3_base_url
end
setup_data
end
def get_absolute_image_url(link)
absolute_url = link
if link.start_with?("//")
uri = URI(Discourse.base_url)
absolute_url = "#{uri.scheme}:#{link}"
elsif link.start_with?("/uploads/", "/images/", "/user_avatar/")
absolute_url = "#{Discourse.base_url}#{link}"
elsif GlobalSetting.relative_url_root && link.start_with?(GlobalSetting.relative_url_root)
absolute_url = "#{Discourse.base_url_no_prefix}#{link}"
end
absolute_url
end
def manifest_url
# If you want the `manifest_url` to be different for a specific action,
# in the action set @manifest_url = X. Originally added for chat to add a
# separate manifest
@manifest_url || "#{Discourse.base_path}/manifest.webmanifest"
end
def can_sign_up?
SiteSetting.allow_new_registrations && !SiteSetting.invite_only &&
FEATURE: Rename 'Discourse SSO' to DiscourseConnect (#11978) The 'Discourse SSO' protocol is being rebranded to DiscourseConnect. This should help to reduce confusion when 'SSO' is used in the generic sense. This commit aims to: - Rename `sso_` site settings. DiscourseConnect specific ones are prefixed `discourse_connect_`. Generic settings are prefixed `auth_` - Add (server-side-only) backwards compatibility for the old setting names, with deprecation notices - Copy `site_settings` database records to the new names - Rename relevant translation keys - Update relevant translations This commit does **not** aim to: - Rename any Ruby classes or methods. This might be done in a future commit - Change any URLs. This would break existing integrations - Make any changes to the protocol. This would break existing integrations - Change any functionality. Further normalization across DiscourseConnect and other auth methods will be done separately The risks are: - There is no backwards compatibility for site settings on the client-side. Accessing auth-related site settings in Javascript is fairly rare, and an error on the client side would not be security-critical. - If a plugin is monkey-patching parts of the auth process, changes to locale keys could cause broken error messages. This should also be unlikely. The old site setting names remain functional, so security-related overrides will remain working. A follow-up commit will be made with a post-deploy migration to delete the old `site_settings` rows.
2021-02-08 05:04:33 -05:00
!SiteSetting.enable_discourse_connect
end
def rss_creator(user)
user&.display_name
end
def authentication_data
return @authentication_data if defined?(@authentication_data)
@authentication_data =
begin
value = cookies[:authentication_data]
cookies.delete(:authentication_data, path: Discourse.base_path("/")) if value
current_user ? nil : value
end
end
2013-02-05 14:16:51 -05:00
end