diff --git a/app/assets/javascripts/discourse.js b/app/assets/javascripts/discourse.js
index f72cb294275..37a49d8d9ca 100644
--- a/app/assets/javascripts/discourse.js
+++ b/app/assets/javascripts/discourse.js
@@ -5,6 +5,10 @@ window.Discourse = Ember.Application.createWithMixins(Discourse.Ajax, {
rootElement: '#main',
_docTitle: document.title,
+ script: function(url) {
+ return $LAB.script(this.getURL(url));
+ },
+
getURL: function(url) {
if (!url) { return url; }
@@ -16,6 +20,12 @@ window.Discourse = Ember.Application.createWithMixins(Discourse.Ajax, {
u = u.substring(0, u.length-1);
}
if (url.indexOf(u) !== -1) return url;
+
+ if(u.length > 0 && url[0] !== "/") {
+ // we got to root this
+ url = "/" + url;
+ }
+
return u + url;
},
diff --git a/app/assets/javascripts/discourse/initializers/live-development.js.es6 b/app/assets/javascripts/discourse/initializers/live-development.js.es6
index df4b54c356b..fbfd5b7918c 100644
--- a/app/assets/javascripts/discourse/initializers/live-development.js.es6
+++ b/app/assets/javascripts/discourse/initializers/live-development.js.es6
@@ -41,7 +41,7 @@ export default {
// Reload handlebars
var js = me.name.replace(".hbs", "").replace("app/assets/javascripts", "/assets");
- $LAB.script(js + "?hash=" + me.hash).wait(function() {
+ Discourse.script(js + "?hash=" + me.hash).wait(function() {
var templateName;
templateName = js.replace(".js", "").replace("/assets/", "");
return _.each(Ember.View.views, function(view) {
diff --git a/app/assets/javascripts/discourse/lib/highlight-syntax.js.es6 b/app/assets/javascripts/discourse/lib/highlight-syntax.js.es6
index 784add9d223..6308a252659 100644
--- a/app/assets/javascripts/discourse/lib/highlight-syntax.js.es6
+++ b/app/assets/javascripts/discourse/lib/highlight-syntax.js.es6
@@ -3,7 +3,7 @@
export default function highlightSyntax($elem) {
const selector = Discourse.SiteSettings.autohighlight_all_code ? 'pre code' : 'pre code[class]';
$(selector, $elem).each(function(i, e) {
- return $LAB.script("/javascripts/highlight.pack.js").wait(function() {
+ return Discourse.script("/javascripts/highlight.pack.js").wait(function() {
return hljs.highlightBlock(e);
});
});
diff --git a/app/assets/javascripts/discourse/lib/lightbox.js.es6 b/app/assets/javascripts/discourse/lib/lightbox.js.es6
index b5f272180a8..a705ddf07ee 100644
--- a/app/assets/javascripts/discourse/lib/lightbox.js.es6
+++ b/app/assets/javascripts/discourse/lib/lightbox.js.es6
@@ -1,6 +1,6 @@
export default function($elem) {
$("a.lightbox", $elem).each(function(i, e) {
- $LAB.script("/javascripts/jquery.magnific-popup-min.js").wait(function() {
+ Discourse.script("/javascripts/jquery.magnific-popup-min.js").wait(function() {
var $e = $(e);
// do not lightbox spoiled images
if ($e.parents(".spoiler").length > 0 || $e.parents(".spoiled").length > 0) { return; }
diff --git a/app/assets/javascripts/discourse/routes/discourse_location.js b/app/assets/javascripts/discourse/routes/discourse_location.js
index b939e6c9539..22fcc3c80cf 100644
--- a/app/assets/javascripts/discourse/routes/discourse_location.js
+++ b/app/assets/javascripts/discourse/routes/discourse_location.js
@@ -204,6 +204,10 @@ Ember.DiscourseLocation = Ember.Object.extend({
if (url !== '') {
rootURL = rootURL.replace(/\/$/, '');
+
+ if (rootURL.length > 0 && url.indexOf(rootURL + "/") === 0){
+ rootURL = "";
+ }
}
return rootURL + url;
diff --git a/app/assets/javascripts/discourse/routes/discourse_route.js b/app/assets/javascripts/discourse/routes/discourse_route.js
index 926c457ea7b..17c3579436e 100644
--- a/app/assets/javascripts/discourse/routes/discourse_route.js
+++ b/app/assets/javascripts/discourse/routes/discourse_route.js
@@ -113,6 +113,12 @@ Discourse.Route.reopenClass({
}
});
+ if (Discourse.BaseUri) {
+ Discourse.Router.reopen({
+ rootURL: Discourse.BaseUri + "/"
+ });
+ }
+
Discourse.Router.map(function() {
var router = this;
diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb
index d36718044c2..0b7308ed570 100644
--- a/app/controllers/application_controller.rb
+++ b/app/controllers/application_controller.rb
@@ -88,7 +88,7 @@ class ApplicationController < ActionController::Base
if (request.format && request.format.json?) || request.xhr? || !request.get?
rescue_discourse_actions(:not_logged_in, 403, true)
else
- redirect_to "/"
+ redirect_to path("/")
end
end
@@ -379,7 +379,7 @@ class ApplicationController < ActionController::Base
# redirect user to the SSO page if we need to log in AND SSO is enabled
if SiteSetting.login_required?
if SiteSetting.enable_sso?
- redirect_to '/session/sso'
+ redirect_to path('/session/sso')
else
redirect_to :login
end
@@ -387,7 +387,7 @@ class ApplicationController < ActionController::Base
end
def block_if_readonly_mode
- return if request.fullpath.start_with?("/admin/backups")
+ return if request.fullpath.start_with?(path "/admin/backups")
raise Discourse::ReadOnly.new if !request.get? && Discourse.readonly_mode?
end
@@ -404,6 +404,10 @@ class ApplicationController < ActionController::Base
protected
+ def path(p)
+ "#{GlobalSetting.relative_url_root}#{p}"
+ end
+
def render_post_json(post, add_raw=true)
post_serializer = PostSerializer.new(post, scope: guardian, root: false)
post_serializer.add_raw = add_raw
diff --git a/app/controllers/categories_controller.rb b/app/controllers/categories_controller.rb
index c93ad37ab07..74219e7d995 100644
--- a/app/controllers/categories_controller.rb
+++ b/app/controllers/categories_controller.rb
@@ -7,7 +7,7 @@ class CategoriesController < ApplicationController
skip_before_filter :check_xhr, only: [:index, :redirect]
def redirect
- redirect_to "/c/#{params[:path]}"
+ redirect_to path("/c/#{params[:path]}")
end
def index
diff --git a/app/controllers/forums_controller.rb b/app/controllers/forums_controller.rb
index 505f98a2ff4..ea80a017fe5 100644
--- a/app/controllers/forums_controller.rb
+++ b/app/controllers/forums_controller.rb
@@ -17,7 +17,7 @@ class ForumsController < ApplicationController
end
def home_redirect
- redirect_to '/'
+ redirect_to path('/')
end
end
diff --git a/app/controllers/invites_controller.rb b/app/controllers/invites_controller.rb
index c5289ce4316..6756e4a2f51 100644
--- a/app/controllers/invites_controller.rb
+++ b/app/controllers/invites_controller.rb
@@ -19,13 +19,13 @@ class InvitesController < ApplicationController
topic = invite.topics.first
if topic.present?
- redirect_to "#{Discourse.base_uri}#{topic.relative_url}"
+ redirect_to path("#{topic.relative_url}")
return
end
end
end
- redirect_to "/"
+ redirect_to path("/")
end
def create
@@ -40,7 +40,7 @@ class InvitesController < ApplicationController
guardian.ensure_can_send_multiple_invites!(current_user)
end
- if Invite.invite_by_email(params[:email], current_user, topic=nil, group_ids)
+ if Invite.invite_by_email(params[:email], current_user, _topic=nil, group_ids)
render json: success_json
else
render json: failed_json, status: 422
@@ -77,13 +77,13 @@ class InvitesController < ApplicationController
topic = invite.topics.first
if topic.present?
- redirect_to "#{Discourse.base_uri}#{topic.relative_url}"
+ redirect_to path("#{topic.relative_url}")
return
end
end
end
- redirect_to "/"
+ redirect_to path("/")
end
def destroy
diff --git a/app/controllers/session_controller.rb b/app/controllers/session_controller.rb
index 6a1317cb75f..00dbbe1d74b 100644
--- a/app/controllers/session_controller.rb
+++ b/app/controllers/session_controller.rb
@@ -12,7 +12,7 @@ class SessionController < ApplicationController
def sso
if SiteSetting.enable_sso
- redirect_to DiscourseSingleSignOn.generate_url(params[:return_path] || '/')
+ redirect_to DiscourseSingleSignOn.generate_url(params[:return_path] || path('/'))
else
render nothing: true, status: 404
end
@@ -32,7 +32,7 @@ class SessionController < ApplicationController
redirect_to sso.to_url(sso.return_sso_url)
else
session[:sso_payload] = request.query_string
- redirect_to '/login'
+ redirect_to path('/login')
end
else
render nothing: true, status: 404
@@ -47,7 +47,7 @@ class SessionController < ApplicationController
raise "User #{params[:session_id]} not found" if user.blank?
log_on_user(user)
- redirect_to "/"
+ redirect_to path("/")
end
def sso_login
@@ -80,9 +80,9 @@ class SessionController < ApplicationController
if return_path !~ /^\/[^\/]/
begin
uri = URI(return_path)
- return_path = "/" unless uri.host == Discourse.current_hostname
+ return_path = path("/") unless uri.host == Discourse.current_hostname
rescue
- return_path = "/"
+ return_path = path("/")
end
end
diff --git a/app/controllers/static_controller.rb b/app/controllers/static_controller.rb
index 0b1bd1baa1a..532cb168b83 100644
--- a/app/controllers/static_controller.rb
+++ b/app/controllers/static_controller.rb
@@ -4,7 +4,7 @@ class StaticController < ApplicationController
skip_before_filter :verify_authenticity_token, only: [:enter]
def show
- return redirect_to('/') if current_user && params[:id] == 'login'
+ return redirect_to(path '/') if current_user && params[:id] == 'login'
map = {
"faq" => {redirect: "faq_url", topic_id: "guidelines_topic_id"},
@@ -60,15 +60,17 @@ class StaticController < ApplicationController
params.delete(:username)
params.delete(:password)
- destination = "/"
+ destination = path("/")
if params[:redirect].present? && !params[:redirect].match(login_path)
begin
forum_uri = URI(Discourse.base_url)
uri = URI(params[:redirect])
+
if uri.path.present? &&
(uri.host.blank? || uri.host == forum_uri.host) &&
uri.path !~ /\./
+
destination = uri.path
end
rescue URI::InvalidURIError
diff --git a/app/controllers/user_avatars_controller.rb b/app/controllers/user_avatars_controller.rb
index 919be6db10b..b8f1a34923b 100644
--- a/app/controllers/user_avatars_controller.rb
+++ b/app/controllers/user_avatars_controller.rb
@@ -58,7 +58,7 @@ class UserAvatarsController < ApplicationController
upload ||= user.uploaded_avatar if user.uploaded_avatar_id == version
if user.uploaded_avatar && !upload
- return redirect_to "/user_avatar/#{hostname}/#{user.username_lower}/#{size}/#{user.uploaded_avatar_id}.png"
+ return redirect_to path("/user_avatar/#{hostname}/#{user.username_lower}/#{size}/#{user.uploaded_avatar_id}.png")
elsif upload
original = Discourse.store.path_for(upload)
if Discourse.store.external? || File.exists?(original)
diff --git a/app/controllers/users_controller.rb b/app/controllers/users_controller.rb
index 5673c3298d4..98b4e3911be 100644
--- a/app/controllers/users_controller.rb
+++ b/app/controllers/users_controller.rb
@@ -142,7 +142,7 @@ class UsersController < ApplicationController
def my_redirect
if current_user.present? && params[:path] =~ /^[a-z\-\/]+$/
- redirect_to "/users/#{current_user.username}/#{params[:path]}"
+ redirect_to path("/users/#{current_user.username}/#{params[:path]}")
return
end
raise Discourse::NotFound.new
diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb
index 59c8bbb7f5c..b910470d4d7 100644
--- a/app/helpers/application_helper.rb
+++ b/app/helpers/application_helper.rb
@@ -23,6 +23,10 @@ module ApplicationHelper
end
end
+ def path(path)
+ "#{GlobalSetting.relative_url_root}#{path}"
+ end
+
def script(*args)
if SiteSetting.enable_cdn_js_debugging && GlobalSetting.cdn_url
tags = javascript_include_tag(*args, "crossorigin" => "anonymous")
diff --git a/app/views/application/_header.html.erb b/app/views/application/_header.html.erb
index d0f3c8b39fc..ddcb6da88fd 100644
--- a/app/views/application/_header.html.erb
+++ b/app/views/application/_header.html.erb
@@ -13,7 +13,7 @@
<% unless current_user %>
<% end %>
diff --git a/app/views/categories/index.html.erb b/app/views/categories/index.html.erb
index ad9266aa138..2b189514a6e 100644
--- a/app/views/categories/index.html.erb
+++ b/app/views/categories/index.html.erb
@@ -1,6 +1,6 @@
<% @list.categories.each do |c| %>
-
+
<%- if c.displayable_topics.present? %>
<% c.displayable_topics.each do |t| %>
diff --git a/app/views/exceptions/not_found.html.erb b/app/views/exceptions/not_found.html.erb
index 47ab572429d..c7030dec488 100644
--- a/app/views/exceptions/not_found.html.erb
+++ b/app/views/exceptions/not_found.html.erb
@@ -11,7 +11,7 @@
<% end %>
-
<%= t 'page_not_found.see_more' %>…
+
" class="btn"><%= t 'page_not_found.see_more' %>…
<%= t 'page_not_found.recent_topics' %>
@@ -21,7 +21,7 @@
<% end %>
- <%= t 'page_not_found.see_more' %>…
+ " class="btn"><%= t 'page_not_found.see_more' %>…
diff --git a/app/views/layouts/crawler.html.erb b/app/views/layouts/crawler.html.erb
index 9ef563c5544..c4d7dc14da8 100644
--- a/app/views/layouts/crawler.html.erb
+++ b/app/views/layouts/crawler.html.erb
@@ -19,7 +19,7 @@
<%= SiteCustomization.custom_header(session[:preview_style]) %>
<%- end %>
<%= yield %>
diff --git a/app/views/users/activate_account.html.erb b/app/views/users/activate_account.html.erb
index 3d33bb8270f..c0c1ab5287c 100644
--- a/app/views/users/activate_account.html.erb
+++ b/app/views/users/activate_account.html.erb
@@ -17,7 +17,7 @@
(function() {
function activateAccount() {
$('#activate-account-button').prop('disabled', true);
- $.ajax("/users/hp").then(function(hp) {
+ $.ajax("<%= path "/users/hp" %>").then(function(hp) {
$('#password_confirmation').val(hp.value);
$('#challenge').val(hp.challenge.split("").reverse().join(""));
$('#activate-account-form').submit();
diff --git a/app/views/users/password_reset.html.erb b/app/views/users/password_reset.html.erb
index dd77729fecf..585d2c8dd6b 100644
--- a/app/views/users/password_reset.html.erb
+++ b/app/views/users/password_reset.html.erb
@@ -20,7 +20,7 @@
<% else %>
-
<%= t('password_reset.continue', site_name: SiteSetting.title) %>
+
"><%= t('password_reset.continue', site_name: SiteSetting.title) %>
<% end %>
<% else %>
diff --git a/app/views/users/perform_account_activation.html.erb b/app/views/users/perform_account_activation.html.erb
index 7424cf6243a..09d66b2224f 100644
--- a/app/views/users/perform_account_activation.html.erb
+++ b/app/views/users/perform_account_activation.html.erb
@@ -12,7 +12,7 @@
<%= t 'activation.approval_required' %>
<% else %>
<%= t('activation.please_continue') %>
-
<%= t('activation.continue_button', site_name: SiteSetting.title) -%>
+
"><%= t('activation.continue_button', site_name: SiteSetting.title) -%>
<%= render partial: 'auto_redirect_home' %>
<% end %>
<%end%>
diff --git a/config/application.rb b/config/application.rb
index 8124238a788..193810ffcfc 100644
--- a/config/application.rb
+++ b/config/application.rb
@@ -156,6 +156,10 @@ module Discourse
require 'auth'
Discourse.activate_plugins! unless Rails.env.test? and ENV['LOAD_PLUGINS'] != "1"
+ if GlobalSetting.relative_url_root.present?
+ config.relative_url_root = GlobalSetting.relative_url_root
+ end
+
config.after_initialize do
# So open id logs somewhere sane
OpenID::Util.logger = Rails.logger
diff --git a/config/discourse_defaults.conf b/config/discourse_defaults.conf
index 248aff6421b..299e91fc2fc 100644
--- a/config/discourse_defaults.conf
+++ b/config/discourse_defaults.conf
@@ -119,3 +119,8 @@ new_version_emails = true
connection_reaper_age = 30
# run reap check every 30 seconds
connection_reaper_interval = 30
+
+# set to relative URL (for subdirectory hosting)
+# IMPORTANT: path must not include a trailing /
+# EG: /forum
+relative_url_root =
diff --git a/config/initializers/logster.rb b/config/initializers/logster.rb
index 48abc05567e..525512ae208 100644
--- a/config/initializers/logster.rb
+++ b/config/initializers/logster.rb
@@ -49,3 +49,6 @@ Logster.config.current_context = lambda{|env,&blk|
ActiveRecord::Base.connection_handler.clear_active_connections!
end
}
+
+# TODO logster should be able to do this automatically
+Logster.config.subdirectory = "#{GlobalSetting.relative_url_root}/logs"
diff --git a/lib/middleware/turbo_dev.rb b/lib/middleware/turbo_dev.rb
index 03b3c575385..d3b7eb3e1b4 100644
--- a/lib/middleware/turbo_dev.rb
+++ b/lib/middleware/turbo_dev.rb
@@ -17,11 +17,12 @@ module Middleware
end
def call(env)
- is_asset = (env['REQUEST_PATH'] =~ /^\/assets\//)
+ root = "#{GlobalSetting.relative_url_root}/assets/"
+ is_asset = env['REQUEST_PATH'] && env['REQUEST_PATH'].starts_with?(root)
# hack to bypass all middleware if serving assets, a lot faster 4.5 seconds -> 1.5 seconds
if (etag = env['HTTP_IF_NONE_MATCH']) && is_asset
- name = $'
+ name = env['REQUEST_PATH'][(root.length)..-1]
etag = etag.gsub "\"", ""
asset = Rails.application.assets.find_asset(name)
if asset && asset.digest == etag
diff --git a/lib/sass/discourse_stylesheets.rb b/lib/sass/discourse_stylesheets.rb
index 1ad84bc30e3..2b8195fcc22 100644
--- a/lib/sass/discourse_stylesheets.rb
+++ b/lib/sass/discourse_stylesheets.rb
@@ -114,12 +114,16 @@ class DiscourseStylesheets
"#{GlobalSetting.cdn_url}#{stylesheet_relpath}?__ws=#{Discourse.current_hostname}"
end
+ def root_path
+ "#{GlobalSetting.relative_url_root}/"
+ end
+
def stylesheet_relpath
- "/#{CACHE_PATH}/#{stylesheet_filename}"
+ "#{root_path}#{CACHE_PATH}/#{stylesheet_filename}"
end
def stylesheet_relpath_no_digest
- "/#{CACHE_PATH}/#{stylesheet_filename_no_digest}"
+ "#{root_path}#{CACHE_PATH}/#{stylesheet_filename_no_digest}"
end
def stylesheet_filename