2013-02-06 03:16:51 +08:00
|
|
|
require 'current_user'
|
2013-02-13 19:04:43 +08:00
|
|
|
require 'canonical_url'
|
2013-02-06 03:16:51 +08:00
|
|
|
require_dependency 'guardian'
|
|
|
|
require_dependency 'unread'
|
|
|
|
require_dependency 'age_words'
|
2013-05-01 23:48:42 +08:00
|
|
|
require_dependency 'configurable_urls'
|
2014-01-09 11:08:42 +08:00
|
|
|
require_dependency 'mobile_detection'
|
2015-01-29 03:56:18 +08:00
|
|
|
require_dependency 'category_badge'
|
2015-03-10 03:24:16 +08:00
|
|
|
require_dependency 'global_path'
|
2015-03-24 02:12:11 +08:00
|
|
|
require_dependency 'canonical_url'
|
2013-02-06 03:16:51 +08:00
|
|
|
|
|
|
|
module ApplicationHelper
|
|
|
|
include CurrentUser
|
2013-02-13 19:04:43 +08:00
|
|
|
include CanonicalURL::Helpers
|
2013-05-01 23:48:42 +08:00
|
|
|
include ConfigurableUrls
|
2015-03-10 03:24:16 +08:00
|
|
|
include GlobalPath
|
2013-02-06 03:16:51 +08:00
|
|
|
|
2017-07-28 09:20:09 +08:00
|
|
|
def google_universal_analytics_json(ua_domain_name = nil)
|
2016-08-03 02:54:06 +08:00
|
|
|
result = {}
|
|
|
|
if ua_domain_name
|
|
|
|
result[:cookieDomain] = ua_domain_name.gsub(/^http(s)?:\/\//, '')
|
|
|
|
end
|
2015-04-08 01:09:49 +08:00
|
|
|
if current_user.present?
|
|
|
|
result[:userId] = current_user.id
|
|
|
|
end
|
2017-07-14 03:21:44 +08:00
|
|
|
if SiteSetting.ga_universal_auto_link_domains.present?
|
|
|
|
result[:allowLinker] = true
|
|
|
|
end
|
2015-04-08 01:09:49 +08:00
|
|
|
result.to_json.html_safe
|
|
|
|
end
|
|
|
|
|
2016-07-15 01:52:37 +08:00
|
|
|
def ga_universal_json
|
|
|
|
google_universal_analytics_json(SiteSetting.ga_universal_domain_name)
|
|
|
|
end
|
|
|
|
|
|
|
|
def google_tag_manager_json
|
2016-08-03 02:54:06 +08:00
|
|
|
google_universal_analytics_json
|
2016-07-15 01:52:37 +08:00
|
|
|
end
|
|
|
|
|
2014-10-24 10:38:00 +08:00
|
|
|
def shared_session_key
|
|
|
|
if SiteSetting.long_polling_base_url != '/'.freeze && current_user
|
|
|
|
sk = "shared_session_key"
|
|
|
|
return request.env[sk] if request.env[sk]
|
|
|
|
|
|
|
|
request.env[sk] = key = (session[sk] ||= SecureRandom.hex)
|
|
|
|
$redis.setex "#{sk}_#{key}", 7.days, current_user.id.to_s
|
|
|
|
key
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2017-04-17 23:52:43 +08:00
|
|
|
def preload_script(script)
|
|
|
|
path = asset_path("#{script}.js")
|
|
|
|
|
2016-12-05 10:57:09 +08:00
|
|
|
if GlobalSetting.cdn_url &&
|
|
|
|
GlobalSetting.cdn_url.start_with?("https") &&
|
|
|
|
ENV["COMPRESS_BROTLI"] == "1" &&
|
2016-12-05 12:10:54 +08:00
|
|
|
request.env["HTTP_ACCEPT_ENCODING"] =~ /br/
|
2017-07-28 09:20:09 +08:00
|
|
|
path.gsub!("#{GlobalSetting.cdn_url}/assets/", "#{GlobalSetting.cdn_url}/brotli_asset/")
|
2014-05-19 06:46:09 +08:00
|
|
|
end
|
2017-04-17 23:52:43 +08:00
|
|
|
"<link rel='preload' href='#{path}' as='script'/>
|
|
|
|
<script src='#{path}'></script>".html_safe
|
2014-05-15 10:59:26 +08:00
|
|
|
end
|
|
|
|
|
2013-05-03 14:43:11 +08:00
|
|
|
def discourse_csrf_tags
|
|
|
|
# anon can not have a CSRF token cause these are all pages
|
2013-06-06 06:23:43 +08:00
|
|
|
# that may be cached, causing a mismatch between session CSRF
|
2013-05-03 14:43:11 +08:00
|
|
|
# and CSRF on page and horrible impossible to debug login issues
|
|
|
|
if current_user
|
|
|
|
csrf_meta_tags
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2013-12-19 03:47:22 +08:00
|
|
|
def html_classes
|
2015-07-23 15:16:29 +08:00
|
|
|
"#{mobile_view? ? 'mobile-view' : 'desktop-view'} #{mobile_device? ? 'mobile-device' : 'not-mobile-device'} #{rtl_class} #{current_user ? '' : 'anon'}"
|
2014-08-27 19:38:03 +08:00
|
|
|
end
|
|
|
|
|
2016-07-05 00:10:52 +08:00
|
|
|
def body_classes
|
2017-06-16 02:20:04 +08:00
|
|
|
result = []
|
|
|
|
|
2016-07-05 00:10:52 +08:00
|
|
|
if @category && @category.url.present?
|
2017-06-16 02:20:04 +08:00
|
|
|
result << "category-#{@category.url.sub(/^\/c\//, '').gsub(/\//, '-')}"
|
|
|
|
end
|
|
|
|
|
|
|
|
if current_user.present? && primary_group_name = current_user.primary_group&.name
|
|
|
|
result << "primary-group-#{primary_group_name.downcase}"
|
2016-07-05 00:10:52 +08:00
|
|
|
end
|
2017-06-16 02:20:04 +08:00
|
|
|
|
|
|
|
result.join(' ')
|
2016-07-05 00:10:52 +08:00
|
|
|
end
|
|
|
|
|
2014-08-27 19:38:03 +08:00
|
|
|
def rtl_class
|
2016-02-07 03:49:39 +08:00
|
|
|
rtl? ? 'rtl' : ''
|
2013-12-19 03:47:22 +08:00
|
|
|
end
|
|
|
|
|
2013-07-03 08:43:52 +08:00
|
|
|
def escape_unicode(javascript)
|
|
|
|
if javascript
|
2013-12-30 11:05:25 +08:00
|
|
|
javascript = javascript.scrub
|
2013-09-10 14:01:36 +08:00
|
|
|
javascript.gsub!(/\342\200\250/u, '
')
|
|
|
|
javascript.gsub!(/(<\/)/u, '\u003C/')
|
|
|
|
javascript.html_safe
|
2013-07-03 08:43:52 +08:00
|
|
|
else
|
|
|
|
''
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2016-03-08 22:38:23 +08:00
|
|
|
def format_topic_title(title)
|
2016-11-23 01:37:22 +08:00
|
|
|
PrettyText.unescape_emoji strip_tags(title)
|
2015-10-15 15:59:29 +08:00
|
|
|
end
|
|
|
|
|
2013-02-06 03:16:51 +08: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
|
|
|
|
|
2016-11-11 07:16:24 +08:00
|
|
|
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-06 03:16:51 +08:00
|
|
|
def guardian
|
|
|
|
@guardian ||= Guardian.new(current_user)
|
|
|
|
end
|
|
|
|
|
|
|
|
def mini_profiler_enabled?
|
2013-03-05 08:42:44 +08:00
|
|
|
defined?(Rack::MiniProfiler) && admin?
|
2013-02-06 03:16:51 +08:00
|
|
|
end
|
|
|
|
|
|
|
|
def admin?
|
|
|
|
current_user.try(:admin?)
|
|
|
|
end
|
|
|
|
|
2013-05-02 13:15:17 +08:00
|
|
|
def moderator?
|
|
|
|
current_user.try(:moderator?)
|
|
|
|
end
|
|
|
|
|
2013-05-02 15:22:27 +08:00
|
|
|
def staff?
|
|
|
|
current_user.try(:staff?)
|
|
|
|
end
|
|
|
|
|
2015-05-20 13:56:54 +08:00
|
|
|
def rtl?
|
2016-12-11 07:57:48 +08:00
|
|
|
["ar", "ur", "fa_IR", "he"].include? I18n.locale.to_s
|
2015-05-20 13:56:54 +08:00
|
|
|
end
|
|
|
|
|
|
|
|
def user_locale
|
|
|
|
locale = current_user.locale if current_user && SiteSetting.allow_user_locale
|
|
|
|
# changing back to default shoves a blank string there
|
|
|
|
locale.present? ? locale : SiteSetting.default_locale
|
|
|
|
end
|
|
|
|
|
2013-03-09 04:04:37 +08:00
|
|
|
# Creates open graph and twitter card meta data
|
2017-07-28 09:20:09 +08:00
|
|
|
def crawlable_meta_data(opts = nil)
|
2013-03-09 04:04:37 +08:00
|
|
|
opts ||= {}
|
2015-10-02 00:24:07 +08:00
|
|
|
opts[:url] ||= "#{Discourse.base_url_no_prefix}#{request.fullpath}"
|
2013-03-08 06:31:06 +08:00
|
|
|
|
2016-09-19 19:41:12 +08:00
|
|
|
if opts[:image].blank? && (SiteSetting.default_opengraph_image_url.present? || SiteSetting.twitter_summary_large_image_url.present?)
|
|
|
|
opts[:twitter_summary_large_image] = SiteSetting.twitter_summary_large_image_url if SiteSetting.twitter_summary_large_image_url.present?
|
|
|
|
opts[:image] = SiteSetting.default_opengraph_image_url.present? ? SiteSetting.default_opengraph_image_url : SiteSetting.twitter_summary_large_image_url
|
2016-08-22 17:28:46 +08:00
|
|
|
elsif opts[:image].blank? && SiteSetting.apple_touch_icon_url.present?
|
|
|
|
opts[:image] = SiteSetting.apple_touch_icon_url
|
|
|
|
end
|
|
|
|
|
|
|
|
# Use the correct scheme for open graph image
|
2016-10-06 03:14:51 +08:00
|
|
|
if opts[:image].present?
|
|
|
|
if opts[:image].start_with?("//")
|
|
|
|
uri = URI(Discourse.base_url)
|
|
|
|
opts[:image] = "#{uri.scheme}:#{opts[:image]}"
|
|
|
|
elsif opts[:image].start_with?("/uploads/")
|
|
|
|
opts[:image] = "#{Discourse.base_url}#{opts[:image]}"
|
|
|
|
elsif GlobalSetting.relative_url_root && opts[:image].start_with?(GlobalSetting.relative_url_root)
|
|
|
|
opts[:image] = "#{Discourse.base_url_no_prefix}#{opts[:image]}"
|
|
|
|
end
|
2014-08-08 00:58:26 +08:00
|
|
|
end
|
|
|
|
|
2016-09-19 19:41:12 +08:00
|
|
|
# Add opengraph & twitter tags
|
2015-10-15 17:00:47 +08:00
|
|
|
result = []
|
|
|
|
result << tag(:meta, property: 'og:site_name', content: SiteSetting.title)
|
2014-08-08 00:58:26 +08:00
|
|
|
|
2016-09-19 19:41:12 +08:00
|
|
|
if opts[:twitter_summary_large_image].present?
|
|
|
|
result << tag(:meta, name: 'twitter:card', content: "summary_large_image")
|
|
|
|
result << tag(:meta, name: "twitter:image", content: opts[:twitter_summary_large_image])
|
|
|
|
elsif opts[:image].present?
|
|
|
|
result << tag(:meta, name: 'twitter:card', content: "summary")
|
|
|
|
result << tag(:meta, name: "twitter:image", content: opts[:image])
|
|
|
|
else
|
|
|
|
result << tag(:meta, name: 'twitter:card', content: "summary")
|
|
|
|
end
|
|
|
|
result << tag(:meta, property: "og:image", content: opts[:image]) if opts[:image].present?
|
|
|
|
|
|
|
|
[:url, :title, :description].each do |property|
|
2013-03-09 04:04:37 +08:00
|
|
|
if opts[property].present?
|
2013-03-09 05:17:56 +08:00
|
|
|
escape = (property != :image)
|
2017-02-23 05:24:05 +08:00
|
|
|
content = (property == :url ? opts[property] : gsub_emoji_to_unicode(opts[property]))
|
|
|
|
result << tag(:meta, { property: "og:#{property}", content: content }, nil, escape)
|
|
|
|
result << tag(:meta, { name: "twitter:#{property}", content: content }, nil, escape)
|
2013-03-09 04:04:37 +08:00
|
|
|
end
|
|
|
|
end
|
2013-03-08 06:31:06 +08:00
|
|
|
|
2016-01-07 19:18:05 +08:00
|
|
|
if opts[:read_time] && opts[:read_time] > 0 && opts[:like_count] && opts[:like_count] > 0
|
2015-12-28 20:52:31 +08:00
|
|
|
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"))
|
2015-12-29 06:36:02 +08:00
|
|
|
result << tag(:meta, name: 'twitter:data2', value: "#{opts[:like_count]} ❤")
|
2015-12-28 20:52:31 +08:00
|
|
|
end
|
|
|
|
|
2017-08-15 21:53:03 +08:00
|
|
|
if opts[:ignore_canonical]
|
|
|
|
result << tag(:meta, property: 'og:ignore_canonical', content: true)
|
|
|
|
end
|
|
|
|
|
2015-10-15 17:00:47 +08:00
|
|
|
result.join("\n")
|
2013-03-08 06:31:06 +08:00
|
|
|
end
|
|
|
|
|
2016-02-14 02:29:53 +08:00
|
|
|
def render_sitelinks_search_tag
|
|
|
|
json = {
|
2016-04-14 17:22:26 +08:00
|
|
|
'@context' => 'http://schema.org',
|
|
|
|
'@type' => 'WebSite',
|
2016-02-14 02:29:53 +08:00
|
|
|
url: Discourse.base_url,
|
|
|
|
potentialAction: {
|
2016-04-14 17:22:26 +08:00
|
|
|
'@type' => 'SearchAction',
|
2016-02-14 02:29:53 +08:00
|
|
|
target: "#{Discourse.base_url}/search?q={search_term_string}",
|
2016-04-14 17:22:26 +08:00
|
|
|
'query-input' => 'required name=search_term_string',
|
2016-02-14 02:29:53 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
content_tag(:script, MultiJson.dump(json).html_safe, type: 'application/ld+json'.freeze)
|
|
|
|
end
|
|
|
|
|
2017-02-23 05:24:05 +08:00
|
|
|
def gsub_emoji_to_unicode(str)
|
2017-07-22 02:24:28 +08:00
|
|
|
Emoji.gsub_emoji_to_unicode(str)
|
2017-02-23 05:24:05 +08:00
|
|
|
end
|
|
|
|
|
2015-02-26 00:35:47 +08:00
|
|
|
def application_logo_url
|
|
|
|
@application_logo_url ||= (mobile_view? && SiteSetting.mobile_logo_url) || SiteSetting.logo_url
|
|
|
|
end
|
|
|
|
|
2013-03-20 00:15:14 +08:00
|
|
|
def login_path
|
2013-11-14 23:41:16 +08:00
|
|
|
"#{Discourse::base_uri}/login"
|
2013-03-20 00:15:14 +08:00
|
|
|
end
|
2013-08-28 02:57:42 +08:00
|
|
|
|
2013-12-19 03:47:22 +08:00
|
|
|
def mobile_view?
|
2017-07-28 09:20:09 +08:00
|
|
|
MobileDetection.resolve_mobile_view!(request.user_agent, params, session)
|
2013-12-19 03:47:22 +08:00
|
|
|
end
|
|
|
|
|
2016-04-07 22:28:31 +08:00
|
|
|
def crawler_layout?
|
|
|
|
controller.try(:use_crawler_layout?)
|
|
|
|
end
|
|
|
|
|
2016-03-17 02:35:58 +08:00
|
|
|
def include_crawler_content?
|
2016-04-07 22:28:31 +08:00
|
|
|
crawler_layout? || !mobile_view?
|
2016-03-17 02:35:58 +08:00
|
|
|
end
|
|
|
|
|
2013-12-19 03:47:22 +08:00
|
|
|
def mobile_device?
|
2014-01-09 11:08:42 +08:00
|
|
|
MobileDetection.mobile_device?(request.user_agent)
|
2013-08-30 03:19:28 +08:00
|
|
|
end
|
2013-11-14 23:41:16 +08:00
|
|
|
|
|
|
|
def customization_disabled?
|
2017-04-15 01:35:12 +08:00
|
|
|
request.env[ApplicationController::NO_CUSTOM]
|
2016-11-21 13:46:02 +08:00
|
|
|
end
|
|
|
|
|
|
|
|
def allow_plugins?
|
2017-04-15 01:35:12 +08:00
|
|
|
!request.env[ApplicationController::NO_PLUGINS]
|
2016-11-21 13:46:02 +08:00
|
|
|
end
|
|
|
|
|
|
|
|
def allow_third_party_plugins?
|
2017-04-15 01:35:12 +08:00
|
|
|
allow_plugins? && !request.env[ApplicationController::ONLY_OFFICIAL]
|
2016-12-19 07:11:51 +08:00
|
|
|
end
|
|
|
|
|
|
|
|
def normalized_safe_mode
|
|
|
|
safe_mode = nil
|
2017-04-15 01:35:12 +08:00
|
|
|
(safe_mode ||= []) << ApplicationController::NO_CUSTOM if customization_disabled?
|
|
|
|
(safe_mode ||= []) << ApplicationController::NO_PLUGINS if !allow_plugins?
|
|
|
|
(safe_mode ||= []) << ApplicationController::ONLY_OFFICIAL if !allow_third_party_plugins?
|
2016-12-19 07:11:51 +08:00
|
|
|
if safe_mode
|
|
|
|
safe_mode.join(",").html_safe
|
|
|
|
end
|
2013-11-14 23:41:16 +08:00
|
|
|
end
|
|
|
|
|
2015-03-03 06:31:04 +08:00
|
|
|
def loading_admin?
|
|
|
|
controller.class.name.split("::").first == "Admin"
|
|
|
|
end
|
|
|
|
|
2017-07-28 09:20:09 +08:00
|
|
|
def category_badge(category, opts = nil)
|
2015-01-29 03:56:18 +08:00
|
|
|
CategoryBadge.html_for(category, opts).html_safe
|
|
|
|
end
|
2014-01-09 11:08:42 +08:00
|
|
|
|
2015-06-03 02:27:52 +08:00
|
|
|
def self.all_connectors
|
|
|
|
@all_connectors = Dir.glob("plugins/*/app/views/connectors/**/*.html.erb")
|
|
|
|
end
|
|
|
|
|
|
|
|
def server_plugin_outlet(name)
|
|
|
|
|
|
|
|
# Don't evaluate plugins in test
|
|
|
|
return "" if Rails.env.test?
|
|
|
|
|
|
|
|
matcher = Regexp.new("/connectors/#{name}/.*\.html\.erb$")
|
2017-07-28 09:20:09 +08:00
|
|
|
erbs = ApplicationHelper.all_connectors.select { |c| c =~ matcher }
|
2015-06-03 02:27:52 +08:00
|
|
|
return "" if erbs.blank?
|
|
|
|
|
|
|
|
result = ""
|
2017-07-28 09:20:09 +08:00
|
|
|
erbs.each { |erb| result << render(file: erb) }
|
2015-06-03 02:27:52 +08:00
|
|
|
result.html_safe
|
|
|
|
end
|
|
|
|
|
2016-12-05 20:31:43 +08:00
|
|
|
def topic_featured_link_domain(link)
|
|
|
|
begin
|
|
|
|
uri = URI.encode(link)
|
|
|
|
uri = URI.parse(uri)
|
|
|
|
uri = URI.parse("http://#{uri}") if uri.scheme.nil?
|
|
|
|
host = uri.host.downcase
|
|
|
|
host.start_with?('www.') ? host[4..-1] : host
|
|
|
|
rescue
|
|
|
|
''
|
|
|
|
end
|
|
|
|
end
|
2017-04-12 22:52:52 +08:00
|
|
|
|
|
|
|
def theme_key
|
|
|
|
if customization_disabled?
|
|
|
|
nil
|
|
|
|
else
|
2017-04-15 01:35:12 +08:00
|
|
|
request.env[:resolved_theme_key]
|
2017-04-12 22:52:52 +08:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2017-04-18 03:47:21 +08:00
|
|
|
def build_plugin_html(name)
|
|
|
|
return "" unless allow_plugins?
|
2017-04-19 00:35:19 +08:00
|
|
|
DiscoursePluginRegistry.build_html(name, controller) || ""
|
2017-04-18 03:47:21 +08:00
|
|
|
end
|
|
|
|
|
2017-04-12 22:52:52 +08:00
|
|
|
def theme_lookup(name)
|
|
|
|
lookup = Theme.lookup_field(theme_key, mobile_view? ? :mobile : :desktop, name)
|
|
|
|
lookup.html_safe if lookup
|
|
|
|
end
|
|
|
|
|
2017-07-28 09:20:09 +08:00
|
|
|
def discourse_stylesheet_link_tag(name, opts = {})
|
2017-04-12 22:52:52 +08:00
|
|
|
if opts.key?(:theme_key)
|
|
|
|
key = opts[:theme_key] unless customization_disabled?
|
|
|
|
else
|
|
|
|
key = theme_key
|
|
|
|
end
|
|
|
|
|
|
|
|
Stylesheet::Manager.stylesheet_link_tag(name, 'all', key)
|
|
|
|
end
|
2013-02-06 03:16:51 +08:00
|
|
|
end
|