Module: ApplicationHelper

Class Method Summary collapse

Instance Method Summary collapse

Methods included from GlobalPath

#cdn_path, #cdn_relative_path, #full_cdn_url, #path, #upload_cdn_path

Methods included from ConfigurableUrls

#faq_path, #privacy_policy_url, #tos_url

Methods included from CanonicalURL::Helpers

#canonical_link_tag

Methods included from CurrentUser

#clear_current_user, #current_user, has_auth_cookie?, #is_api?, #is_user_api?, #log_off_user, #log_on_user, lookup_from_env, #refresh_session

Class Method Details

.all_connectorsObject



504
505
506
# File 'app/helpers/application_helper.rb', line 504

def self.all_connectors
  @all_connectors = Dir.glob("plugins/*/app/views/connectors/**/*.html.erb")
end

.extra_body_classesObject



12
13
14
# File 'app/helpers/application_helper.rb', line 12

def self.extra_body_classes
  @extra_body_classes ||= Set.new
end

Instance Method Details

#add_resource_preload_list(resource_url, type) ⇒ Object



160
161
162
163
164
165
# File 'app/helpers/application_helper.rb', line 160

def add_resource_preload_list(resource_url, type)
  links =
    controller.instance_variable_get(:@asset_preload_links) ||
      controller.instance_variable_set(:@asset_preload_links, [])
  links << %Q(<#{resource_url}>; rel="preload"; as="#{type}")
end

#admin?Boolean

Returns:

  • (Boolean)


249
250
251
# File 'app/helpers/application_helper.rb', line 249

def admin?
  current_user.try(:admin?)
end

#age_words(secs) ⇒ Object



233
234
235
# File 'app/helpers/application_helper.rb', line 233

def age_words(secs)
  AgeWords.age_words(secs)
end

#allow_plugins?Boolean

Returns:

  • (Boolean)


475
476
477
# File 'app/helpers/application_helper.rb', line 475

def allow_plugins?
  !request.env[ApplicationController::NO_PLUGINS]
end

#allow_third_party_plugins?Boolean

Returns:

  • (Boolean)


479
480
481
# File 'app/helpers/application_helper.rb', line 479

def allow_third_party_plugins?
  allow_plugins? && !request.env[ApplicationController::NO_UNOFFICIAL_PLUGINS]
end

#anonymous_top_menu_itemsObject



776
777
778
# File 'app/helpers/application_helper.rb', line 776

def anonymous_top_menu_items
  Discourse.anonymous_top_menu_items.map(&:to_s)
end

#application_logo_dark_urlObject



404
405
406
407
408
409
410
411
412
413
414
415
# File 'app/helpers/application_helper.rb', line 404

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

#application_logo_urlObject



385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
# File 'app/helpers/application_helper.rb', line 385

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

#authentication_dataObject



780
781
782
783
784
785
786
787
788
789
# File 'app/helpers/application_helper.rb', line 780

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

#body_classesObject



185
186
187
188
189
190
191
192
193
194
195
196
# File 'app/helpers/application_helper.rb', line 185

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

#build_plugin_html(name) ⇒ Object



566
567
568
569
# File 'app/helpers/application_helper.rb', line 566

def build_plugin_html(name)
  return "" unless allow_plugins?
  DiscoursePluginRegistry.build_html(name, controller) || ""
end

#can_sign_up?Boolean

Returns:

  • (Boolean)


767
768
769
770
# File 'app/helpers/application_helper.rb', line 767

def can_sign_up?
  SiteSetting.allow_new_registrations && !SiteSetting.invite_only &&
    !SiteSetting.enable_discourse_connect
end

#category_badge(category, opts = nil) ⇒ Object



500
501
502
# File 'app/helpers/application_helper.rb', line 500

def category_badge(category, opts = nil)
  CategoryBadge.html_for(category, opts).html_safe
end

#client_side_setup_dataObject



705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
# File 'app/helpers/application_helper.rb', line 705

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,
    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,
  }

  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")
  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

#crawlable_meta_data(opts = nil) ⇒ Object

Creates open graph and twitter card meta data



270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
# File 'app/helpers/application_helper.rb', line 270

def (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

    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: opts[:site_name] || SiteSetting.title)
  result << tag(:meta, property: "og:type", content: "website")

  (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

#crawler_layout?Boolean

Returns:

  • (Boolean)


429
430
431
# File 'app/helpers/application_helper.rb', line 429

def crawler_layout?
  controller&.use_crawler_layout?
end

#csp_nonce_placeholderObject



68
69
70
# File 'app/helpers/application_helper.rb', line 68

def csp_nonce_placeholder
  ContentSecurityPolicy.nonce_placeholder(response.headers)
end

#current_homepageObject



562
563
564
# File 'app/helpers/application_helper.rb', line 562

def current_homepage
  current_user&.user_option&.homepage || HomepageHelper.resolve(request, current_user)
end

#customization_disabled?Boolean

Returns:

  • (Boolean)


453
454
455
# File 'app/helpers/application_helper.rb', line 453

def customization_disabled?
  request.env[ApplicationController::NO_THEMES]
end

#dark_color_scheme?Boolean

Returns:

  • (Boolean)


695
696
697
698
# File 'app/helpers/application_helper.rb', line 695

def dark_color_scheme?
  return false if scheme_id.blank?
  ColorScheme.find_by_id(scheme_id)&.is_dark?
end

#dark_scheme_idObject



557
558
559
560
# File 'app/helpers/application_helper.rb', line 557

def dark_scheme_id
  cookies[:dark_scheme_id] || current_user&.user_option&.dark_scheme_id ||
    SiteSetting.default_dark_mode_color_scheme_id
end

#discourse_color_scheme_meta_tagObject



681
682
683
684
685
686
687
688
689
690
691
692
693
# File 'app/helpers/application_helper.rb', line 681

def discourse_color_scheme_meta_tag
  scheme =
    if dark_scheme_id == -1
      # no automatic client-side switching
      dark_color_scheme? ? "dark" : "light"
    else
      # auto-switched based on browser setting
      "light dark"
    end
  <<~HTML.html_safe
      <meta name="color-scheme" content="#{scheme}">
    HTML
end

#discourse_color_scheme_stylesheetsObject



647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
# File 'app/helpers/application_helper.rb', line 647

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

#discourse_config_environment(testing: false) ⇒ Object



16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
# File 'app/helpers/application_helper.rb', line 16

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,
        String: 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

#discourse_csrf_tagsObject



167
168
169
170
171
172
# File 'app/helpers/application_helper.rb', line 167

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

#discourse_preload_color_scheme_stylesheetsObject



633
634
635
636
637
638
639
640
641
642
643
644
645
# File 'app/helpers/application_helper.rb', line 633

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


622
623
624
625
626
627
628
629
630
631
# File 'app/helpers/application_helper.rb', line 622

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

#discourse_stylesheet_preload_tag(name, opts = {}) ⇒ Object



611
612
613
614
615
616
617
618
619
620
# File 'app/helpers/application_helper.rb', line 611

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

#discourse_theme_color_meta_tagsObject



666
667
668
669
670
671
672
673
674
675
676
677
678
679
# File 'app/helpers/application_helper.rb', line 666

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

#escape_noscript(&block) ⇒ Object



756
757
758
# File 'app/helpers/application_helper.rb', line 756

def escape_noscript(&block)
  raw capture(&block).gsub(%r{<(/\s*noscript)}i, '&lt;\1')
end

#escape_unicode(javascript) ⇒ Object



210
211
212
213
214
215
216
217
218
219
# File 'app/helpers/application_helper.rb', line 210

def escape_unicode(javascript)
  if javascript
    javascript = javascript.scrub
    javascript.gsub!(/\342\200\250/u, "&#x2028;")
    javascript.gsub!(%r{(</)}u, '\u003C/')
    javascript
  else
    ""
  end
end

#format_topic_title(title) ⇒ Object



221
222
223
# File 'app/helpers/application_helper.rb', line 221

def format_topic_title(title)
  PrettyText.unescape_emoji strip_tags(title)
end

#ga_universal_jsonObject



60
61
62
# File 'app/helpers/application_helper.rb', line 60

def ga_universal_json
  google_universal_analytics_json(SiteSetting.ga_universal_domain_name)
end

#get_absolute_image_url(link) ⇒ Object



743
744
745
746
747
748
749
750
751
752
753
754
# File 'app/helpers/application_helper.rb', line 743

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

#google_tag_manager_jsonObject



64
65
66
# File 'app/helpers/application_helper.rb', line 64

def google_tag_manager_json
  google_universal_analytics_json
end

#google_universal_analytics_json(ua_domain_name = nil) ⇒ Object



52
53
54
55
56
57
58
# File 'app/helpers/application_helper.rb', line 52

def google_universal_analytics_json(ua_domain_name = nil)
  result = {}
  result[:cookieDomain] = ua_domain_name.gsub(%r{\Ahttp(s)?://}, "") if ua_domain_name
  result[:userId] = current_user.id if current_user.present?
  result[:allowLinker] = true if SiteSetting.ga_universal_auto_link_domains.present?
  result.to_json
end

#gsub_emoji_to_unicode(str) ⇒ Object



381
382
383
# File 'app/helpers/application_helper.rb', line 381

def gsub_emoji_to_unicode(str)
  Emoji.gsub_emoji_to_unicode(str)
end

#guardianObject



245
246
247
# File 'app/helpers/application_helper.rb', line 245

def guardian
  @guardian ||= Guardian.new(current_user)
end

#html_classesObject



174
175
176
177
178
179
180
181
182
183
# File 'app/helpers/application_helper.rb', line 174

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(" ")
end

#html_langObject



265
266
267
# File 'app/helpers/application_helper.rb', line 265

def html_lang
  (request ? I18n.locale.to_s : SiteSetting.default_locale).sub("_", "-")
end

#include_crawler_content?Boolean

Returns:

  • (Boolean)


433
434
435
436
437
438
439
# File 'app/helpers/application_helper.rb', line 433

def include_crawler_content?
  if current_user && !crawler_layout?
    params.key?(:print)
  else
    crawler_layout? || !mobile_view? || !modern_mobile_device?
  end
end

#include_ios_native_app_banner?Boolean

Returns:

  • (Boolean)


457
458
459
# File 'app/helpers/application_helper.rb', line 457

def include_ios_native_app_banner?
  current_user && current_user.trust_level >= 1 && SiteSetting.native_app_install_banner_ios
end

#include_splash_screen?Boolean

Returns:

  • (Boolean)


470
471
472
473
# File 'app/helpers/application_helper.rb', line 470

def include_splash_screen?
  # A bit basic for now but will be expanded later
  SiteSetting.splash_screen
end

#ios_app_argumentObject



461
462
463
464
465
466
467
468
# File 'app/helpers/application_helper.rb', line 461

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

#ios_device?Boolean

Returns:

  • (Boolean)


449
450
451
# File 'app/helpers/application_helper.rb', line 449

def ios_device?
  MobileDetection.ios_device?(request.user_agent)
end

#is_brotli_req?Boolean

Returns:

  • (Boolean)


83
84
85
# File 'app/helpers/application_helper.rb', line 83

def is_brotli_req?
  request.env["HTTP_ACCEPT_ENCODING"] =~ /br/
end

#is_gzip_req?Boolean

Returns:

  • (Boolean)


87
88
89
# File 'app/helpers/application_helper.rb', line 87

def is_gzip_req?
  request.env["HTTP_ACCEPT_ENCODING"] =~ /gzip/
end

#loading_admin?Boolean

Returns:

  • (Boolean)


493
494
495
496
497
498
# File 'app/helpers/application_helper.rb', line 493

def loading_admin?
  return false unless defined?(controller)
  return false if controller.class.name.blank?

  controller.class.name.split("::").first == "Admin"
end

#login_pathObject



421
422
423
# File 'app/helpers/application_helper.rb', line 421

def 
  "#{Discourse.base_path}/login"
end

#manifest_urlObject



760
761
762
763
764
765
# File 'app/helpers/application_helper.rb', line 760

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

#mobile_device?Boolean

Returns:

  • (Boolean)


445
446
447
# File 'app/helpers/application_helper.rb', line 445

def mobile_device?
  MobileDetection.mobile_device?(request.user_agent)
end

#mobile_view?Boolean

Returns:

  • (Boolean)


425
426
427
# File 'app/helpers/application_helper.rb', line 425

def mobile_view?
  MobileDetection.resolve_mobile_view!(request.user_agent, params, session)
end

#moderator?Boolean

Returns:

  • (Boolean)


253
254
255
# File 'app/helpers/application_helper.rb', line 253

def moderator?
  current_user.try(:moderator?)
end

#modern_mobile_device?Boolean

Returns:

  • (Boolean)


441
442
443
# File 'app/helpers/application_helper.rb', line 441

def modern_mobile_device?
  MobileDetection.modern_mobile_device?(request.user_agent)
end

#normalized_safe_modeObject



483
484
485
486
487
488
489
490
491
# File 'app/helpers/application_helper.rb', line 483

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(",")
end

#preload_script(script) ⇒ Object



131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
# File 'app/helpers/application_helper.rb', line 131

def preload_script(script)
  scripts = []

  if chunks = EmberCli.script_chunks[script]
    scripts.push(*chunks)
  else
    scripts.push(script)
  end

  scripts
    .map do |name|
      path = script_asset_path(name)
      preload_script_url(path, entrypoint: script)
    end
    .join("\n")
    .html_safe
end

#preload_script_url(url, entrypoint: nil) ⇒ Object



149
150
151
152
153
154
155
156
157
158
# File 'app/helpers/application_helper.rb', line 149

def preload_script_url(url, entrypoint: nil)
  entrypoint_attribute = entrypoint ? "data-discourse-entrypoint=\"#{entrypoint}\"" : ""
  nonce_attribute = "nonce=\"#{csp_nonce_placeholder}\""

  add_resource_preload_list(url, "script")

  <<~HTML.html_safe
    <script defer src="#{url}" #{entrypoint_attribute} #{nonce_attribute}></script>
  HTML
end

#preloaded_jsonObject



700
701
702
703
# File 'app/helpers/application_helper.rb', line 700

def preloaded_json
  return "{}" if @preloaded.blank?
  @preloaded.transform_values { |value| escape_unicode(value) }.to_json
end


364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
# File 'app/helpers/application_helper.rb', line 364

def render_sitelinks_search_tag
  if current_page?("/") || current_page?(Discourse.base_path)
    json = {
      "@context" => "http://schema.org",
      "@type" => "WebSite",
      :url => Discourse.base_url,
      :name => SiteSetting.title,
      :potentialAction => {
        "@type" => "SearchAction",
        :target => "#{Discourse.base_url}/search?q={search_term_string}",
        "query-input" => "required name=search_term_string",
      },
    }
    (:script, MultiJson.dump(json).html_safe, type: "application/ld+json")
  end
end

#replace_plugin_html(name) ⇒ Object

If there is plugin HTML return that, otherwise yield to the template



572
573
574
575
576
577
578
579
# File 'app/helpers/application_helper.rb', line 572

def replace_plugin_html(name)
  if (html = build_plugin_html(name)).present?
    html
  else
    yield
    nil
  end
end

#rss_creator(user) ⇒ Object



772
773
774
# File 'app/helpers/application_helper.rb', line 772

def rss_creator(user)
  user&.display_name
end

#rtl?Boolean

Returns:

  • (Boolean)


261
262
263
# File 'app/helpers/application_helper.rb', line 261

def rtl?
  Rtl::LOCALES.include? I18n.locale.to_s
end

#scheme_idObject



544
545
546
547
548
549
550
551
552
553
554
555
# File 'app/helpers/application_helper.rb', line 544

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

#script_asset_path(script) ⇒ Object



91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
# File 'app/helpers/application_helper.rb', line 91

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
  end

  path
end

#server_plugin_outlet(name, locals: {}) ⇒ Object



508
509
510
511
512
513
514
515
516
517
518
# File 'app/helpers/application_helper.rb', line 508

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

#shared_session_keyObject



72
73
74
75
76
77
78
79
80
81
# File 'app/helpers/application_helper.rb', line 72

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

#short_date(dt) ⇒ Object



237
238
239
240
241
242
243
# File 'app/helpers/application_helper.rb', line 237

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

#staff?Boolean

Returns:

  • (Boolean)


257
258
259
# File 'app/helpers/application_helper.rb', line 257

def staff?
  current_user.try(:staff?)
end

#stylesheet_managerObject



539
540
541
542
# File 'app/helpers/application_helper.rb', line 539

def stylesheet_manager
  return @stylesheet_manager if defined?(@stylesheet_manager)
  @stylesheet_manager = Stylesheet::Manager.new(theme_id: theme_id)
end

#text_size_classObject



198
199
200
201
202
203
204
205
206
207
208
# File 'app/helpers/application_helper.rb', line 198

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

#theme_idObject



531
532
533
534
535
536
537
# File 'app/helpers/application_helper.rb', line 531

def theme_id
  if customization_disabled?
    nil
  else
    request.env[:resolved_theme_id]
  end
end

#theme_js_lookupObject



601
602
603
604
605
606
607
608
609
# File 'app/helpers/application_helper.rb', line 601

def theme_js_lookup
  Theme.lookup_field(
    theme_id,
    :extra_js,
    nil,
    skip_transformation: request.env[:skip_theme_ids_transformation].present?,
    csp_nonce: csp_nonce_placeholder,
  )
end

#theme_lookup(name) ⇒ Object



581
582
583
584
585
586
587
588
589
# File 'app/helpers/application_helper.rb', line 581

def theme_lookup(name)
  Theme.lookup_field(
    theme_id,
    mobile_view? ? :mobile : :desktop,
    name,
    skip_transformation: request.env[:skip_theme_ids_transformation].present?,
    csp_nonce: csp_nonce_placeholder,
  )
end

#theme_translations_lookupObject



591
592
593
594
595
596
597
598
599
# File 'app/helpers/application_helper.rb', line 591

def theme_translations_lookup
  Theme.lookup_field(
    theme_id,
    :translations,
    I18n.locale,
    skip_transformation: request.env[:skip_theme_ids_transformation].present?,
    csp_nonce: csp_nonce_placeholder,
  )
end


520
521
522
523
524
525
526
527
528
529
# File 'app/helpers/application_helper.rb', line 520

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

#waving_hand_urlObject



417
418
419
# File 'app/helpers/application_helper.rb', line 417

def waving_hand_url
  UrlHelper.cook_url(Emoji.url_for(":wave:t#{rand(2..6)}:"))
end

#with_format(format, &block) ⇒ Object



225
226
227
228
229
230
231
# File 'app/helpers/application_helper.rb', line 225

def with_format(format, &block)
  old_formats = formats
  self.formats = [format]
  block.call
  self.formats = old_formats
  nil
end