2015-08-24 21:54:23 -04:00
|
|
|
require_dependency 'distributed_memoizer'
|
|
|
|
require_dependency 'file_helper'
|
|
|
|
|
2013-02-05 14:16:51 -05:00
|
|
|
class StaticController < ApplicationController
|
|
|
|
|
2013-06-04 18:32:36 -04:00
|
|
|
skip_before_filter :check_xhr, :redirect_to_login_if_required
|
2016-12-04 21:57:09 -05:00
|
|
|
skip_before_filter :verify_authenticity_token, only: [:brotli_asset, :cdn_asset, :enter, :favicon]
|
2013-02-05 14:16:51 -05:00
|
|
|
|
2016-01-19 16:53:46 -05:00
|
|
|
PAGES_WITH_EMAIL_PARAM = ['login', 'password_reset', 'signup']
|
|
|
|
|
2013-02-05 14:16:51 -05:00
|
|
|
def show
|
2016-01-19 16:53:46 -05:00
|
|
|
return redirect_to(path '/') if current_user && (params[:id] == 'login' || params[:id] == 'signup')
|
2013-10-30 16:37:22 -04:00
|
|
|
|
2013-06-27 03:15:59 -04:00
|
|
|
map = {
|
2014-07-24 14:27:34 -04:00
|
|
|
"faq" => {redirect: "faq_url", topic_id: "guidelines_topic_id"},
|
|
|
|
"tos" => {redirect: "tos_url", topic_id: "tos_topic_id"},
|
|
|
|
"privacy" => {redirect: "privacy_policy_url", topic_id: "privacy_topic_id"}
|
2013-06-27 03:15:59 -04:00
|
|
|
}
|
|
|
|
|
2014-07-24 14:27:34 -04:00
|
|
|
@page = params[:id]
|
2013-02-05 14:16:51 -05:00
|
|
|
|
2014-07-24 14:27:34 -04:00
|
|
|
if map.has_key?(@page)
|
|
|
|
site_setting_key = map[@page][:redirect]
|
2013-06-27 03:15:59 -04:00
|
|
|
url = SiteSetting.send(site_setting_key)
|
|
|
|
return redirect_to(url) unless url.blank?
|
|
|
|
end
|
2013-06-18 10:52:04 -04:00
|
|
|
|
2014-07-10 12:58:34 -04:00
|
|
|
# The /guidelines route ALWAYS shows our FAQ, ignoring the faq_url site setting.
|
2014-07-24 14:27:34 -04:00
|
|
|
@page = 'faq' if @page == 'guidelines'
|
2014-07-10 12:58:34 -04:00
|
|
|
|
2013-02-05 14:16:51 -05:00
|
|
|
# Don't allow paths like ".." or "/" or anything hacky like that
|
2014-07-24 14:27:34 -04:00
|
|
|
@page.gsub!(/[^a-z0-9\_\-]/, '')
|
2013-02-05 14:16:51 -05:00
|
|
|
|
2014-07-24 14:27:34 -04:00
|
|
|
if map.has_key?(@page)
|
|
|
|
@topic = Topic.find_by_id(SiteSetting.send(map[@page][:topic_id]))
|
|
|
|
raise Discourse::NotFound unless @topic
|
2015-05-31 21:40:52 -04:00
|
|
|
@title = @topic.title
|
2014-07-24 14:27:34 -04:00
|
|
|
@body = @topic.posts.first.cooked
|
2014-07-10 12:58:34 -04:00
|
|
|
@faq_overriden = !SiteSetting.faq_url.blank?
|
2014-07-24 14:27:34 -04:00
|
|
|
render :show, layout: !request.xhr?, formats: [:html]
|
2013-02-05 14:16:51 -05:00
|
|
|
return
|
|
|
|
end
|
|
|
|
|
2014-10-17 23:27:33 -04:00
|
|
|
if I18n.exists?("static.#{@page}")
|
2014-10-18 02:17:20 -04:00
|
|
|
render text: I18n.t("static.#{@page}"), layout: !request.xhr?, formats: [:html]
|
2014-10-17 23:27:33 -04:00
|
|
|
return
|
|
|
|
end
|
|
|
|
|
2016-01-19 16:53:46 -05:00
|
|
|
if PAGES_WITH_EMAIL_PARAM.include?(@page) && params[:email]
|
|
|
|
cookies[:email] = { value: params[:email], expires: 1.day.from_now }
|
|
|
|
end
|
|
|
|
|
2014-07-26 17:16:08 -04:00
|
|
|
file = "static/#{@page}.#{I18n.locale}"
|
|
|
|
file = "static/#{@page}.en" if lookup_context.find_all("#{file}.html").empty?
|
|
|
|
file = "static/#{@page}" if lookup_context.find_all("#{file}.html").empty?
|
|
|
|
|
|
|
|
if lookup_context.find_all("#{file}.html").any?
|
|
|
|
render file, layout: !request.xhr?, formats: [:html]
|
|
|
|
return
|
|
|
|
end
|
|
|
|
|
2013-05-19 20:29:49 -04:00
|
|
|
raise Discourse::NotFound
|
2013-02-05 14:16:51 -05:00
|
|
|
end
|
|
|
|
|
2013-03-13 10:22:56 -04:00
|
|
|
# This method just redirects to a given url.
|
|
|
|
# It's used when an ajax login was successful but we want the browser to see
|
|
|
|
# a post of a login form so that it offers to remember your password.
|
|
|
|
def enter
|
|
|
|
params.delete(:username)
|
|
|
|
params.delete(:password)
|
|
|
|
|
2015-03-08 20:45:36 -04:00
|
|
|
destination = path("/")
|
2014-08-28 17:45:13 -04:00
|
|
|
|
|
|
|
if params[:redirect].present? && !params[:redirect].match(login_path)
|
|
|
|
begin
|
|
|
|
forum_uri = URI(Discourse.base_url)
|
|
|
|
uri = URI(params[:redirect])
|
2015-03-08 20:45:36 -04:00
|
|
|
|
2014-10-30 11:31:44 -04:00
|
|
|
if uri.path.present? &&
|
|
|
|
(uri.host.blank? || uri.host == forum_uri.host) &&
|
|
|
|
uri.path !~ /\./
|
2015-03-08 20:45:36 -04:00
|
|
|
|
2014-08-28 17:45:13 -04:00
|
|
|
destination = uri.path
|
2016-08-16 01:10:32 -04:00
|
|
|
destination = "#{uri.path}?#{uri.query}" if uri.path =~ /new-topic/ || uri.path =~ /new-message/ || uri.path =~ /user-api-key/
|
2014-08-28 17:45:13 -04:00
|
|
|
end
|
|
|
|
rescue URI::InvalidURIError
|
|
|
|
# Do nothing if the URI is invalid
|
2013-06-04 18:34:54 -04:00
|
|
|
end
|
2014-08-28 17:45:13 -04:00
|
|
|
end
|
|
|
|
|
|
|
|
redirect_to destination
|
2013-06-04 18:34:54 -04:00
|
|
|
end
|
2014-05-18 18:46:09 -04:00
|
|
|
|
2015-08-24 21:54:23 -04:00
|
|
|
# We need to be able to draw our favicon on a canvas
|
|
|
|
# and pull it off the canvas into a data uri
|
|
|
|
# This can work by ensuring people set all the right CORS
|
|
|
|
# settings in the CDN asset, BUT its annoying and error prone
|
|
|
|
# instead we cache the favicon in redis and serve it out real quick with
|
|
|
|
# a huge expiry, we also cache these assets in nginx so it bypassed if needed
|
|
|
|
def favicon
|
|
|
|
|
2015-08-24 22:05:15 -04:00
|
|
|
data = DistributedMemoizer.memoize('favicon' + SiteSetting.favicon_url, 60*30) do
|
|
|
|
begin
|
2016-02-24 22:50:34 -05:00
|
|
|
file = FileHelper.download(SiteSetting.favicon_url, 50.kilobytes, "favicon.png", true)
|
2015-08-24 22:05:15 -04:00
|
|
|
data = file.read
|
|
|
|
file.unlink
|
|
|
|
data
|
|
|
|
rescue => e
|
2016-04-05 14:42:24 -04:00
|
|
|
AdminDashboardData.add_problem_message('dashboard.bad_favicon_url', 1800)
|
|
|
|
Rails.logger.debug("Invalid favicon_url #{SiteSetting.favicon_url}: #{e}\n#{e.backtrace}")
|
2015-08-24 22:05:15 -04:00
|
|
|
""
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
if data.bytesize == 0
|
2015-12-22 02:08:27 -05:00
|
|
|
@@default_favicon ||= File.read(Rails.root + "public/images/default-favicon.png")
|
|
|
|
response.headers["Content-Length"] = @@default_favicon.bytesize.to_s
|
|
|
|
render text: @@default_favicon, content_type: "image/png"
|
2015-08-24 22:05:15 -04:00
|
|
|
else
|
|
|
|
expires_in 1.year, public: true
|
|
|
|
response.headers["Expires"] = 1.year.from_now.httpdate
|
|
|
|
response.headers["Content-Length"] = data.bytesize.to_s
|
|
|
|
response.headers["Last-Modified"] = Time.new('2000-01-01').httpdate
|
|
|
|
render text: data, content_type: "image/png"
|
2015-08-24 21:54:23 -04:00
|
|
|
end
|
2016-12-04 21:57:09 -05:00
|
|
|
end
|
|
|
|
|
|
|
|
def brotli_asset
|
|
|
|
path = File.expand_path(Rails.root + "public/assets/" + params[:path])
|
|
|
|
path += ".br"
|
|
|
|
|
|
|
|
# SECURITY what if path has /../
|
|
|
|
raise Discourse::NotFound unless path.start_with?(Rails.root.to_s + "/public/assets")
|
|
|
|
|
|
|
|
opts = { disposition: nil }
|
|
|
|
opts[:type] = "application/javascript" if path =~ /\.js.br$/
|
2015-08-24 21:54:23 -04:00
|
|
|
|
2016-12-04 21:57:09 -05:00
|
|
|
response.headers["Expires"] = 1.year.from_now.httpdate
|
2016-12-04 23:10:54 -05:00
|
|
|
response.headers["Cache-Control"] = 'max-age=31557600, public'
|
2016-12-04 21:57:09 -05:00
|
|
|
response.headers["Content-Encoding"] = 'br'
|
|
|
|
begin
|
|
|
|
response.headers["Last-Modified"] = File.ctime(path).httpdate
|
|
|
|
response.headers["Content-Length"] = File.size(path).to_s
|
|
|
|
rescue Errno::ENOENT
|
|
|
|
raise Discourse::NotFound
|
|
|
|
end
|
|
|
|
|
2016-12-05 00:08:36 -05:00
|
|
|
expires_in 1.year, public: true, must_revalidate: false
|
|
|
|
|
2016-12-04 21:57:09 -05:00
|
|
|
if File.exists?(path)
|
|
|
|
send_file(path, opts)
|
|
|
|
else
|
|
|
|
raise Discourse::NotFound
|
|
|
|
end
|
2015-08-24 21:54:23 -04:00
|
|
|
end
|
|
|
|
|
2014-10-22 09:39:51 -04:00
|
|
|
|
2014-05-18 18:46:09 -04:00
|
|
|
def cdn_asset
|
2014-07-10 03:29:38 -04:00
|
|
|
path = File.expand_path(Rails.root + "public/assets/" + params[:path])
|
|
|
|
|
|
|
|
# SECURITY what if path has /../
|
2014-10-22 09:39:51 -04:00
|
|
|
raise Discourse::NotFound unless path.start_with?(Rails.root.to_s + "/public/assets")
|
2014-07-10 03:29:38 -04:00
|
|
|
|
2014-05-18 18:46:09 -04:00
|
|
|
expires_in 1.year, public: true
|
2014-10-21 00:59:16 -04:00
|
|
|
|
|
|
|
response.headers["Expires"] = 1.year.from_now.httpdate
|
2015-02-16 17:54:45 -05:00
|
|
|
response.headers["Access-Control-Allow-Origin"] = params[:origin] if params[:origin]
|
2014-10-21 00:59:16 -04:00
|
|
|
|
2014-07-08 00:48:20 -04:00
|
|
|
begin
|
|
|
|
response.headers["Last-Modified"] = File.ctime(path).httpdate
|
2014-10-21 01:17:13 -04:00
|
|
|
response.headers["Content-Length"] = File.size(path).to_s
|
2014-07-08 00:48:20 -04:00
|
|
|
rescue Errno::ENOENT
|
|
|
|
raise Discourse::NotFound
|
|
|
|
end
|
2014-10-21 00:59:16 -04:00
|
|
|
|
2014-10-22 09:39:51 -04:00
|
|
|
opts = { disposition: nil }
|
2014-10-21 00:59:16 -04:00
|
|
|
opts[:type] = "application/javascript" if path =~ /\.js$/
|
2014-07-10 02:32:06 -04:00
|
|
|
|
|
|
|
# we must disable acceleration otherwise NGINX strips
|
|
|
|
# access control headers
|
2014-07-10 03:01:21 -04:00
|
|
|
request.env['sendfile.type'] = ''
|
2015-11-21 09:20:39 -05:00
|
|
|
# TODO send_file chunks which kills caching, need to render text here
|
2014-05-18 18:46:09 -04:00
|
|
|
send_file(path, opts)
|
|
|
|
end
|
2014-10-22 09:39:51 -04:00
|
|
|
|
2013-02-07 10:45:24 -05:00
|
|
|
end
|