diff --git a/src/Umbraco.Core/ApplicationContext.cs b/src/Umbraco.Core/ApplicationContext.cs index aae174106e..7b9a6a6b2b 100644 --- a/src/Umbraco.Core/ApplicationContext.cs +++ b/src/Umbraco.Core/ApplicationContext.cs @@ -10,6 +10,7 @@ using Umbraco.Core.Logging; using Umbraco.Core.ObjectResolution; using Umbraco.Core.Profiling; using Umbraco.Core.Services; +using Umbraco.Core.Sync; namespace Umbraco.Core @@ -220,18 +221,46 @@ namespace Umbraco.Core } /// - /// The original/first url that the web application executes + /// The application url. /// /// - /// we need to set the initial url in our ApplicationContext, this is so our keep alive service works and this must - /// exist on a global context because the keep alive service doesn't run in a web context. - /// we are NOT going to put a lock on this because locking will slow down the application and we don't really care - /// if two threads write to this at the exact same time during first page hit. - /// see: http://issues.umbraco.org/issue/U4-2059 + /// The application url is the url that should be used by services to talk to the application, + /// eg keep alive or scheduled publishing services. It must exist on a global context because + /// some of these services may not run within a web context. + /// The format of the application url is: + /// - has a scheme (http or https) + /// - has the SystemDirectories.Umbraco path + /// - does not end with a slash + /// It is initialized on the first request made to the server, by UmbracoModule.EnsureApplicationUrl: + /// - if umbracoSettings:settings/web.routing/@appUrl is set, use the value (new setting) + /// - if umbracoSettings:settings/scheduledTasks/@baseUrl is set, use the value (backward compatibility) + /// - otherwise, use the url of the (first) request. + /// Not locking, does not matter if several threads write to this. + /// See also issues: + /// - http://issues.umbraco.org/issue/U4-2059 + /// - http://issues.umbraco.org/issue/U4-6788 + /// - http://issues.umbraco.org/issue/U4-5728 + /// - http://issues.umbraco.org/issue/U4-5391 /// - internal string OriginalRequestUrl { get; set; } + internal string UmbracoApplicationUrl { + get + { + // if initialized, return + if (_umbracoApplicationUrl != null) return _umbracoApplicationUrl; + // try settings + ServerEnvironmentHelper.TrySetApplicationUrlFromSettings(this, ProfilingLogger.Logger, UmbracoConfig.For.UmbracoSettings()); + // and return what we have, may be null + return _umbracoApplicationUrl; + } + set + { + _umbracoApplicationUrl = value; + } + } + + internal string _umbracoApplicationUrl; // internal for tests private Lazy _configured; private void Init() diff --git a/src/Umbraco.Core/AsyncLock.cs b/src/Umbraco.Core/AsyncLock.cs index 0a9c79a80e..608b19a700 100644 --- a/src/Umbraco.Core/AsyncLock.cs +++ b/src/Umbraco.Core/AsyncLock.cs @@ -63,13 +63,13 @@ namespace Umbraco.Core // for anonymous semaphore, use the unique releaser, else create a new one return _semaphore != null ? _releaser // (IDisposable)new SemaphoreSlimReleaser(_semaphore) - : (IDisposable)new NamedSemaphoreReleaser(_semaphore2); + : new NamedSemaphoreReleaser(_semaphore2); } public Task LockAsync() { var wait = _semaphore != null - ? _semaphore.WaitAsync() + ? _semaphore.WaitAsync() : WaitOneAsync(_semaphore2); return wait.IsCompleted @@ -79,6 +79,19 @@ namespace Umbraco.Core TaskContinuationOptions.ExecuteSynchronously, TaskScheduler.Default); } + public Task LockAsync(int millisecondsTimeout) + { + var wait = _semaphore != null + ? _semaphore.WaitAsync(millisecondsTimeout) + : WaitOneAsync(_semaphore2, millisecondsTimeout); + + return wait.IsCompleted + ? _releaserTask ?? Task.FromResult(CreateReleaser()) // anonymous vs named + : wait.ContinueWith((_, state) => (((AsyncLock)state).CreateReleaser()), + this, CancellationToken.None, + TaskContinuationOptions.ExecuteSynchronously, TaskScheduler.Default); + } + public IDisposable Lock() { if (_semaphore != null) @@ -174,7 +187,7 @@ namespace Umbraco.Core // F# has a AwaitWaitHandle method that accepts a time out... and seems pretty complex... // version below should be OK - private static Task WaitOneAsync(WaitHandle handle) + private static Task WaitOneAsync(WaitHandle handle, int millisecondsTimeout = Timeout.Infinite) { var tcs = new TaskCompletionSource(); var callbackHandleInitLock = new object(); @@ -197,7 +210,7 @@ namespace Umbraco.Core } }, /*state:*/ null, - /*millisecondsTimeOutInterval:*/ Timeout.Infinite, + /*millisecondsTimeOutInterval:*/ millisecondsTimeout, /*executeOnlyOnce:*/ true); } diff --git a/src/Umbraco.Core/Configuration/UmbracoSettings/IWebRoutingSection.cs b/src/Umbraco.Core/Configuration/UmbracoSettings/IWebRoutingSection.cs index f3d42b6904..2998fc2f78 100644 --- a/src/Umbraco.Core/Configuration/UmbracoSettings/IWebRoutingSection.cs +++ b/src/Umbraco.Core/Configuration/UmbracoSettings/IWebRoutingSection.cs @@ -11,6 +11,8 @@ bool DisableFindContentByIdPath { get; } string UrlProviderMode { get; } + + string UmbracoApplicationUrl { get; } } } \ No newline at end of file diff --git a/src/Umbraco.Core/Configuration/UmbracoSettings/WebRoutingElement.cs b/src/Umbraco.Core/Configuration/UmbracoSettings/WebRoutingElement.cs index f5b71eb2c7..1ed9bc034c 100644 --- a/src/Umbraco.Core/Configuration/UmbracoSettings/WebRoutingElement.cs +++ b/src/Umbraco.Core/Configuration/UmbracoSettings/WebRoutingElement.cs @@ -30,8 +30,13 @@ namespace Umbraco.Core.Configuration.UmbracoSettings [ConfigurationProperty("urlProviderMode", DefaultValue = "AutoLegacy")] public string UrlProviderMode { - get { return (string)base["urlProviderMode"]; } + get { return (string) base["urlProviderMode"]; } } + [ConfigurationProperty("umbracoApplicationUrl", DefaultValue = null)] + public string UmbracoApplicationUrl + { + get { return (string)base["umbracoApplicationUrl"]; } + } } } \ No newline at end of file diff --git a/src/Umbraco.Core/CoreBootManager.cs b/src/Umbraco.Core/CoreBootManager.cs index 8f09fbc2bf..b188bf2232 100644 --- a/src/Umbraco.Core/CoreBootManager.cs +++ b/src/Umbraco.Core/CoreBootManager.cs @@ -76,7 +76,9 @@ namespace Umbraco.Core _profilingLogger = new ProfilingLogger(LoggerResolver.Current.Logger, ProfilerResolver.Current.Profiler); - _timer = _profilingLogger.TraceDuration("Umbraco application starting", "Umbraco application startup complete"); + _timer = _profilingLogger.TraceDuration( + "Umbraco application (" + UmbracoVersion.GetSemanticVersion() + ") starting", + "Umbraco application startup complete"); CreateApplicationCache(); diff --git a/src/Umbraco.Core/Logging/Logger.cs b/src/Umbraco.Core/Logging/Logger.cs index 58f696d82b..a362ae0a02 100644 --- a/src/Umbraco.Core/Logging/Logger.cs +++ b/src/Umbraco.Core/Logging/Logger.cs @@ -1,4 +1,5 @@ using System; +using System.Diagnostics; using System.IO; using System.Linq; using System.Threading; @@ -12,7 +13,16 @@ namespace Umbraco.Core.Logging /// Used for logging /// public class Logger : ILogger - { + { + private static string _processAndDomain; + + static Logger() + { + // these won't change and can go in a static variable + _processAndDomain = "P" + Process.GetCurrentProcess().Id + + "/D" + AppDomain.CurrentDomain.Id; + } + public Logger(FileInfo log4NetConfigFile) { XmlConfigurator.Configure(log4NetConfigFile); @@ -61,7 +71,10 @@ namespace Umbraco.Core.Logging /// private string PrefixThreadId(string generateMessageFormat) { - return "[Thread " + Thread.CurrentThread.ManagedThreadId + "] " + generateMessageFormat; + return "[" + _processAndDomain + + "/T" + Thread.CurrentThread.ManagedThreadId + + "] " + + generateMessageFormat; } public void Error(Type callingType, string message, Exception exception) diff --git a/src/Umbraco.Core/Persistence/Repositories/ContentPreviewRepository.cs b/src/Umbraco.Core/Persistence/Repositories/ContentPreviewRepository.cs index 67741c84d7..b368488833 100644 --- a/src/Umbraco.Core/Persistence/Repositories/ContentPreviewRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/ContentPreviewRepository.cs @@ -83,7 +83,7 @@ namespace Umbraco.Core.Persistence.Repositories NodeId = entity.Id, Timestamp = DateTime.Now, VersionId = entity.Version, - Xml = entity.Xml.ToString(SaveOptions.None) + Xml = entity.Xml.ToDataString() }; //We need to do a special InsertOrUpdate here because we know that the PreviewXmlDto table has a composite key and thus diff --git a/src/Umbraco.Core/Persistence/Repositories/ContentRepository.cs b/src/Umbraco.Core/Persistence/Repositories/ContentRepository.cs index 1e0d8884bd..3395b38b3d 100644 --- a/src/Umbraco.Core/Persistence/Repositories/ContentRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/ContentRepository.cs @@ -238,7 +238,7 @@ namespace Umbraco.Core.Persistence.Repositories var xmlItems = (from descendant in descendants let xml = serializer(descendant) - select new ContentXmlDto { NodeId = descendant.Id, Xml = xml.ToString(SaveOptions.None) }).ToArray(); + select new ContentXmlDto { NodeId = descendant.Id, Xml = xml.ToDataString() }).ToArray(); //bulk insert it into the database Database.BulkInsertRecords(xmlItems, tr); diff --git a/src/Umbraco.Core/Persistence/Repositories/ContentXmlRepository.cs b/src/Umbraco.Core/Persistence/Repositories/ContentXmlRepository.cs index a9d5850368..b242bb34f7 100644 --- a/src/Umbraco.Core/Persistence/Repositories/ContentXmlRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/ContentXmlRepository.cs @@ -81,8 +81,8 @@ namespace Umbraco.Core.Persistence.Repositories var poco = new ContentXmlDto { - NodeId = entity.Id, - Xml = entity.Xml.ToString(SaveOptions.None) + NodeId = entity.Id, + Xml = entity.Xml.ToDataString() }; //We need to do a special InsertOrUpdate here because we know that the ContentXmlDto table has a 1:1 relation diff --git a/src/Umbraco.Core/Persistence/Repositories/MediaRepository.cs b/src/Umbraco.Core/Persistence/Repositories/MediaRepository.cs index d2bf19448b..4a9c110359 100644 --- a/src/Umbraco.Core/Persistence/Repositories/MediaRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/MediaRepository.cs @@ -236,7 +236,7 @@ namespace Umbraco.Core.Persistence.Repositories var xmlItems = (from descendant in descendants let xml = serializer(descendant) - select new ContentXmlDto { NodeId = descendant.Id, Xml = xml.ToString(SaveOptions.None) }).ToArray(); + select new ContentXmlDto { NodeId = descendant.Id, Xml = xml.ToDataString() }).ToArray(); //bulk insert it into the database Database.BulkInsertRecords(xmlItems, tr); diff --git a/src/Umbraco.Core/Persistence/Repositories/MemberRepository.cs b/src/Umbraco.Core/Persistence/Repositories/MemberRepository.cs index 18d23d335d..02f5fb10c1 100644 --- a/src/Umbraco.Core/Persistence/Repositories/MemberRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/MemberRepository.cs @@ -451,7 +451,7 @@ namespace Umbraco.Core.Persistence.Repositories var xmlItems = (from descendant in descendants let xml = serializer(descendant) - select new ContentXmlDto { NodeId = descendant.Id, Xml = xml.ToString(SaveOptions.None) }).ToArray(); + select new ContentXmlDto { NodeId = descendant.Id, Xml = xml.ToDataString() }).ToArray(); //bulk insert it into the database Database.BulkInsertRecords(xmlItems, tr); diff --git a/src/Umbraco.Core/Services/ContentService.cs b/src/Umbraco.Core/Services/ContentService.cs index 1059f0e420..b7ad8c4d03 100644 --- a/src/Umbraco.Core/Services/ContentService.cs +++ b/src/Umbraco.Core/Services/ContentService.cs @@ -1632,7 +1632,7 @@ namespace Umbraco.Core.Services { var xml = _entitySerializer.Serialize(this, _dataTypeService, _userService, content); - var poco = new ContentXmlDto { NodeId = content.Id, Xml = xml.ToString(SaveOptions.None) }; + var poco = new ContentXmlDto { NodeId = content.Id, Xml = xml.ToDataString() }; var exists = uow.Database.FirstOrDefault("WHERE nodeId = @Id", new { Id = content.Id }) != null; diff --git a/src/Umbraco.Core/Services/MediaService.cs b/src/Umbraco.Core/Services/MediaService.cs index dcccf4d5d0..299d089319 100644 --- a/src/Umbraco.Core/Services/MediaService.cs +++ b/src/Umbraco.Core/Services/MediaService.cs @@ -1160,7 +1160,7 @@ namespace Umbraco.Core.Services //private void CreateAndSaveMediaXml(XElement xml, int id, UmbracoDatabase db) //{ - // var poco = new ContentXmlDto { NodeId = id, Xml = xml.ToString(SaveOptions.None) }; + // var poco = new ContentXmlDto { NodeId = id, Xml = xml.ToDataString() }; // var exists = db.FirstOrDefault("WHERE nodeId = @Id", new { Id = id }) != null; // int result = exists ? db.Update(poco) : Convert.ToInt32(db.Insert(poco)); //} diff --git a/src/Umbraco.Core/Sync/ServerEnvironmentHelper.cs b/src/Umbraco.Core/Sync/ServerEnvironmentHelper.cs index 1d5ee7855a..e79a0f9209 100644 --- a/src/Umbraco.Core/Sync/ServerEnvironmentHelper.cs +++ b/src/Umbraco.Core/Sync/ServerEnvironmentHelper.cs @@ -1,10 +1,9 @@ -using System; using System.Linq; using System.Web; -using System.Xml; using Umbraco.Core.Configuration; using Umbraco.Core.Configuration.UmbracoSettings; using Umbraco.Core.IO; +using Umbraco.Core.Logging; namespace Umbraco.Core.Sync { @@ -13,58 +12,73 @@ namespace Umbraco.Core.Sync /// internal static class ServerEnvironmentHelper { - /// - /// Returns the current umbraco base url for the current server depending on it's environment - /// status. This will attempt to determine the internal umbraco base url that can be used by the current - /// server to send a request to itself if it is in a load balanced environment. - /// - /// The full base url including schema (i.e. http://myserver:80/umbraco ) - or null if the url - /// cannot be determined at the moment (usually because the first request has not properly completed yet). - public static string GetCurrentServerUmbracoBaseUrl(ApplicationContext appContext, IUmbracoSettingsSection settings) + public static void TrySetApplicationUrlFromSettings(ApplicationContext appContext, ILogger logger, IUmbracoSettingsSection settings) { + // try umbracoSettings:settings/web.routing/@umbracoApplicationUrl + // which is assumed to: + // - end with SystemDirectories.Umbraco + // - contain a scheme + // - end or not with a slash, it will be taken care of + // eg "http://www.mysite.com/umbraco" + var url = settings.WebRouting.UmbracoApplicationUrl; + if (url.IsNullOrWhiteSpace() == false) + { + appContext.UmbracoApplicationUrl = url.TrimEnd('/'); + logger.Info("ApplicationUrl: " + appContext.UmbracoApplicationUrl + " (using web.routing/@umbracoApplicationUrl)"); + return; + } + + // try umbracoSettings:settings/scheduledTasks/@baseUrl + // which is assumed to: + // - end with SystemDirectories.Umbraco + // - NOT contain any scheme (because, legacy) + // - end or not with a slash, it will be taken care of + // eg "mysite.com/umbraco" + url = settings.ScheduledTasks.BaseUrl; + if (url.IsNullOrWhiteSpace() == false) + { + var ssl = GlobalSettings.UseSSL ? "s" : ""; + url = "http" + ssl + "://" + url; + appContext.UmbracoApplicationUrl = url.TrimEnd('/'); + logger.Info("ApplicationUrl: " + appContext.UmbracoApplicationUrl + " (using scheduledTasks/@baseUrl)"); + return; + } + + // try servers var status = GetStatus(settings); - if (status == CurrentServerEnvironmentStatus.Single) - { - // single install, return null if no config/original url, else use config/original url as base - // use http or https as appropriate - return GetBaseUrl(appContext, settings); - } + return; + // no server, nothing we can do var servers = settings.DistributedCall.Servers.ToArray(); + if (servers.Length == 0) + return; - if (servers.Any() == false) - { - // cannot be determined, return null if no config/original url, else use config/original url as base - // use http or https as appropriate - return GetBaseUrl(appContext, settings); - } - + // we have servers, look for this server foreach (var server in servers) { var appId = server.AppId; var serverName = server.ServerName; + // skip if no data if (appId.IsNullOrWhiteSpace() && serverName.IsNullOrWhiteSpace()) - { continue; - } + // if this server, build and return the url if ((appId.IsNullOrWhiteSpace() == false && appId.Trim().InvariantEquals(HttpRuntime.AppDomainAppId)) || (serverName.IsNullOrWhiteSpace() == false && serverName.Trim().InvariantEquals(NetworkHelper.MachineName))) { - //match by appId or computer name! return the url configured - return string.Format("{0}://{1}:{2}/{3}", + // match by appId or computer name, return the url configured + url = string.Format("{0}://{1}:{2}/{3}", server.ForceProtocol.IsNullOrWhiteSpace() ? "http" : server.ForceProtocol, server.ServerAddress, server.ForcePortnumber.IsNullOrWhiteSpace() ? "80" : server.ForcePortnumber, IOHelper.ResolveUrl(SystemDirectories.Umbraco).TrimStart('/')); + + appContext.UmbracoApplicationUrl = url.TrimEnd('/'); + logger.Info("ApplicationUrl: " + appContext.UmbracoApplicationUrl + " (using distributedCall/servers)"); } } - - // cannot be determined, return null if no config/original url, else use config/original url as base - // use http or https as appropriate - return GetBaseUrl(appContext, settings); } /// @@ -113,21 +127,5 @@ namespace Umbraco.Core.Sync return CurrentServerEnvironmentStatus.Slave; } - - private static string GetBaseUrl(ApplicationContext appContext, IUmbracoSettingsSection settings) - { - return ( - // is config empty? - settings.ScheduledTasks.BaseUrl.IsNullOrWhiteSpace() - // is the orig req empty? - ? appContext.OriginalRequestUrl.IsNullOrWhiteSpace() - // we've got nothing - ? null - //the orig req url is not null, use that - : string.Format("http{0}://{1}", GlobalSettings.UseSSL ? "s" : "", appContext.OriginalRequestUrl) - // the config has been specified, use that - : string.Format("http{0}://{1}", GlobalSettings.UseSSL ? "s" : "", settings.ScheduledTasks.BaseUrl)) - .EnsureEndsWith('/'); - } } } \ No newline at end of file diff --git a/src/Umbraco.Core/XmlExtensions.cs b/src/Umbraco.Core/XmlExtensions.cs index 200b845002..b81785eb7e 100644 --- a/src/Umbraco.Core/XmlExtensions.cs +++ b/src/Umbraco.Core/XmlExtensions.cs @@ -317,5 +317,25 @@ namespace Umbraco.Core } } + // this exists because + // new XElement("root", "a\nb").Value is "a\nb" but + // .ToString(SaveOptions.*) is "a\r\nb" and cannot figure out how to get rid of "\r" + // and when saving data we want nothing to change + // this method will produce a string that respects the \r and \n in the data value + public static string ToDataString(this XElement xml) + { + var settings = new XmlWriterSettings + { + OmitXmlDeclaration = true, + NewLineHandling = NewLineHandling.None, + Indent = false + }; + var output = new StringBuilder(); + using (var writer = XmlWriter.Create(output, settings)) + { + xml.WriteTo(writer); + } + return output.ToString(); + } } } \ No newline at end of file diff --git a/src/Umbraco.Tests/ServerEnvironmentHelperTests.cs b/src/Umbraco.Tests/ServerEnvironmentHelperTests.cs index 237c6cb17d..a1f71f4f0d 100644 --- a/src/Umbraco.Tests/ServerEnvironmentHelperTests.cs +++ b/src/Umbraco.Tests/ServerEnvironmentHelperTests.cs @@ -1,92 +1,105 @@ using System.Configuration; +using System.IO; using System.Linq; using Moq; using NUnit.Framework; using Umbraco.Core; using Umbraco.Core.Configuration.UmbracoSettings; +using Umbraco.Core.Logging; +using Umbraco.Core.Profiling; using Umbraco.Core.Sync; +using Umbraco.Tests.TestHelpers; namespace Umbraco.Tests { [TestFixture] public class ServerEnvironmentHelperTests { - [Test] - public void Get_Base_Url_Single_Server_Orig_Request_Url_No_SSL() + private ILogger _logger; + + // note: in tests, read appContext._umbracoApplicationUrl and not the property, + // because reading the property does run some code, as long as the field is null. + + [TestFixtureSetUp] + public void InitializeFixture() { - var appContext = new ApplicationContext(null) - { - OriginalRequestUrl = "test.com" - }; - - ConfigurationManager.AppSettings.Set("umbracoUseSSL", "false"); - - var result = ServerEnvironmentHelper.GetCurrentServerUmbracoBaseUrl( - appContext, - Mock.Of( - section => - section.DistributedCall == Mock.Of(callSection => callSection.Servers == Enumerable.Empty()) - && section.ScheduledTasks == Mock.Of())); - - - Assert.AreEqual("http://test.com/", result); + _logger = new Logger(new FileInfo(TestHelper.MapPathForTest("~/unit-test-log4net.config"))); } [Test] - public void Get_Base_Url_Single_Server_Orig_Request_Url_With_SSL() + public void SetApplicationUrlWhenNoSettings() { var appContext = new ApplicationContext(null) { - OriginalRequestUrl = "test.com" + UmbracoApplicationUrl = null // NOT set }; - ConfigurationManager.AppSettings.Set("umbracoUseSSL", "true"); + ConfigurationManager.AppSettings.Set("umbracoUseSSL", "true"); // does not make a diff here - var result = ServerEnvironmentHelper.GetCurrentServerUmbracoBaseUrl( - appContext, + ServerEnvironmentHelper.TrySetApplicationUrlFromSettings(appContext, _logger, Mock.Of( section => section.DistributedCall == Mock.Of(callSection => callSection.Servers == Enumerable.Empty()) + && section.WebRouting == Mock.Of(wrSection => wrSection.UmbracoApplicationUrl == (string) null) && section.ScheduledTasks == Mock.Of())); - Assert.AreEqual("https://test.com/", result); + // still NOT set + Assert.IsNull(appContext._umbracoApplicationUrl); } [Test] - public void Get_Base_Url_Single_Server_Via_Config_Url_No_SSL() + public void SetApplicationUrlFromDcSettingsNoSsl() { var appContext = new ApplicationContext(null); ConfigurationManager.AppSettings.Set("umbracoUseSSL", "false"); - var result = ServerEnvironmentHelper.GetCurrentServerUmbracoBaseUrl( - appContext, + ServerEnvironmentHelper.TrySetApplicationUrlFromSettings(appContext, _logger, Mock.Of( section => section.DistributedCall == Mock.Of(callSection => callSection.Servers == Enumerable.Empty()) - && section.ScheduledTasks == Mock.Of(tasksSection => tasksSection.BaseUrl == "mycoolhost.com/hello/world"))); + && section.WebRouting == Mock.Of(wrSection => wrSection.UmbracoApplicationUrl == (string) null) + && section.ScheduledTasks == Mock.Of(tasksSection => tasksSection.BaseUrl == "mycoolhost.com/hello/world/"))); - Assert.AreEqual("http://mycoolhost.com/hello/world/", result); + Assert.AreEqual("http://mycoolhost.com/hello/world", appContext._umbracoApplicationUrl); } [Test] - public void Get_Base_Url_Single_Server_Via_Config_Url_With_SSL() + public void SetApplicationUrlFromDcSettingsSsl() { var appContext = new ApplicationContext(null); ConfigurationManager.AppSettings.Set("umbracoUseSSL", "true"); - var result = ServerEnvironmentHelper.GetCurrentServerUmbracoBaseUrl( - appContext, + ServerEnvironmentHelper.TrySetApplicationUrlFromSettings(appContext, _logger, Mock.Of( section => section.DistributedCall == Mock.Of(callSection => callSection.Servers == Enumerable.Empty()) + && section.WebRouting == Mock.Of(wrSection => wrSection.UmbracoApplicationUrl == (string) null) && section.ScheduledTasks == Mock.Of(tasksSection => tasksSection.BaseUrl == "mycoolhost.com/hello/world"))); - Assert.AreEqual("https://mycoolhost.com/hello/world/", result); + Assert.AreEqual("https://mycoolhost.com/hello/world", appContext._umbracoApplicationUrl); + } + + [Test] + public void SetApplicationUrlFromWrSettingsSsl() + { + var appContext = new ApplicationContext(null); + + ConfigurationManager.AppSettings.Set("umbracoUseSSL", "true"); // does not make a diff here + + ServerEnvironmentHelper.TrySetApplicationUrlFromSettings(appContext, _logger, + Mock.Of( + section => + section.DistributedCall == Mock.Of(callSection => callSection.Servers == Enumerable.Empty()) + && section.WebRouting == Mock.Of(wrSection => wrSection.UmbracoApplicationUrl == "httpx://whatever.com/hello/world/") + && section.ScheduledTasks == Mock.Of(tasksSection => tasksSection.BaseUrl == "mycoolhost.com/hello/world"))); + + + Assert.AreEqual("httpx://whatever.com/hello/world", appContext._umbracoApplicationUrl); } } } \ No newline at end of file diff --git a/src/Umbraco.Web.UI/umbraco/config/lang/ja.xml b/src/Umbraco.Web.UI/umbraco/config/lang/ja.xml index cd0bfe03d7..237f0cc4b3 100644 --- a/src/Umbraco.Web.UI/umbraco/config/lang/ja.xml +++ b/src/Umbraco.Web.UI/umbraco/config/lang/ja.xml @@ -8,6 +8,7 @@ ドメインの割り当て 動作記録 ノードの参照 + ドキュメントタイプの変更 コピー 新規作成 パッケージの作成 @@ -26,6 +27,7 @@ 公開を止める 最新の情報に更新 サイトのリフレッシュ + 復元 アクセス権 以前の版に戻る 公開に送る @@ -34,22 +36,35 @@ 公開する 翻訳 更新 + 初期値 + アクセスが拒否されました ドメインの割り当て + ドメインの削除 + 適当でないノード名 適当でないホスト名 + そのホスト名は既に利用されています + 言語コード ドメイン ドメイン '%0%' が新たに割り当てられました ドメイン '%0%' は削除されました ドメイン '%0%' は既に割り当てられています - 例: yourdomain.com, www.yourdomain.com ドメイン '%0%' は更新されました ドメインの編集 + + Inherit + カルチャの割り当て + + ドメインの割り当て これらを表示 + 選択 + 現在のフォルダを選択 + その他のアクション 太字 インデント解除 フィールドから挿入 @@ -67,6 +82,7 @@ マクロの挿入 画像の挿入 関係性の編集 + リストに戻る 保存 保存及び公開 保存して承認に送る @@ -76,20 +92,45 @@ スタイルの表示 表の挿入 + + ドキュメントタイプを変更するには、まず有効なドキュメントタイプのリストから選択します + 確認および現在のドキュメントタイプからのマッピングを割り当て、保存します。 + コンテントは再公開されています + 現在のプロパティ + 現在のドキュメントタイプ + 有効な代替タイプが存在しないため変更することができません。選択されたコンテントの親の下に許可されたドキュメントタイプへのみ変更ができます + ドキュメントタイプを変更しました + プロパティを割り当てる + 割り当てるプロパティ + 新しいテンプレート + 新しいドキュメントタイプ + None + コンテント + ドキュメントタイプを変更する + プロパティが以下のように割り当てられました + から + 1つ以上のプロパティを割り当てられませんでした。プロパティが定義が重複しています + 有効なドキュメントタイプのみが表示されます + + 公開されました このページについて エイリアス (画像を電話でわかるように言葉で説明) 別名のリンク クリックでアイテムを編集する 作成者 + 作成者 + 更新者 作成日時 + このドキュメントが作成された日時 ドキュメントタイプ 変種中 公開終了日時 このページは公開後変更されています このページは公開されていません 公開日時 + リストに表示するアイテムはありません メディアタイプ メディアの項目へのリンク メンバーグループ @@ -99,24 +140,38 @@ タイトル プロパティ このページは公開されましたが、親ページの '%0%' が非公開のため閲覧できません + このコンテントは公開されていますがキャッシュされていません(内部エラー) 公開 公開状態 公開開始日時 + 公開停止日時 日時の消去 並び順が更新されました ノードをドラッグ、クリック、または列のヘッダーをクリックする事でノードを簡単にソートできます。SHIFT、CONTROLキーを使い複数のノードを選択する事もできます。 統計 タイトル (オプション) + 代替テキスト (オプション) 非公開 最終更新日時 + このドキュメントが最後に更新された日時 ファイルの消去 ページへのリンク + グループのメンバー + グループのメンバーではありません + 子コンテンツ + ターゲット + + + クリックしてアップロードする + ファイルをここへドロップ.. どこに新しい %0% を作りますか ここに作成 型とタイトルを選んでください + "document types".]]> + "media types".]]> ウェブサイトを参照する @@ -181,14 +236,32 @@ ]]> カルチャ名 + + ユーザー名を入力... + パスワードを入力... + %0%と命名します... + ここに名称を入力してください... + 検索する... + 条件で絞り込む... + タグを追加します... + + ルートノードとして許可する + これを有効にするとコンテンツとメディアツリーのルートレベルに作成することができます 子ノードとして許可するタイプ + Document Type Compositions 新規作成 削除 説明 新規見出し 見出し サムネイル + リストビューを有効にする + 子ノードをツリーに表示せずにリストビューに表示します + 現在のリストビュー + 有効なリストビューデータタイプ + カスタムリストビューを作成する + カスタムリストビューを削除する 値の前に追加 @@ -217,7 +290,8 @@ %0% は正しい書式ではありません - 注意! CodeMirrorが設定で有効かされていますが、 Internet Explorerでは不安定なので無効化してください。 + 指定されたファイルタイプは管理者のみに許可されます + 注意! CodeMirrorが設定で有効化されていますが、 Internet Explorerでは不安定なので無効化してください。 新しいプロパティ型のエイリアスと名前の両方を設定してください! 特定のファイルまたはフォルタの読み込み/書き込みアクセスに問題があります タイトルを入力してください @@ -232,10 +306,12 @@ このセルは結合されたものではないので分離する事はできません。 XSLTソースにエラーがあります 1つ以上のエラーがあるのでこのXSLTは保存できませんでした + このプロパティに使用されているデータタイプにエラーがあります Umbracoについて アクション + アクション選択 追加 エイリアス 確かですか? @@ -285,6 +361,7 @@ ログアウト マクロ 移動 + もっと 名前 新規 次へ @@ -304,6 +381,7 @@ 残り 名前の変更 更新 + この項目は必須です 再試行 許可 検索 @@ -313,7 +391,7 @@ サイズ 並べ替え - 探す型... + 検索... 更新 アップグレード @@ -327,6 +405,7 @@ はい フォルダー + 検索結果 背景色 @@ -348,10 +427,10 @@ ]]> 次へを押して続行してください。]]> データベースを見つけられません!"web.config"ファイルの中の"接続文字列"を確認してください。

-

続行するには"web.config"ファイルを編集(Visual Studioないし使い慣れたテキストエディタで)し、下の方にスクロールし、"UmbracoDbDSN"という名前のキーでデータベースの接続文字列を追加して保存します。

+

続行するには"web.config"ファイルを編集(Visual Studioないし使い慣れたテキストエディタで)し、下の方にスクロールし、"umbracoDbDSN"という名前のキーでデータベースの接続文字列を追加して保存します。

再施行ボタンをクリックして - 続けます。
+ 続けます。
より詳細にはこちらの web.config を編集します。

]]>
必要ならISPに連絡するなどしてみてください。 @@ -380,7 +459,7 @@

]]>
始めに、ビデオによる解説を見ましょう - 次へボタンをクリック(またはweb.configのUmbracoConfigurationStatusを編集)すると、あなたはここに示されるこのソフトウェアのライセンスを承諾したと見做されます。注意として、UmbracoはMITライセンスをフレームワークへ、フリーウェアライセンスをUIへ、それぞれ異なる2つのライセンスを採用しています。 + 次へボタンをクリック(またはweb.configのumbracoConfigurationStatusを編集)すると、あなたはここに示されるこのソフトウェアのライセンスを承諾したと見做されます。注意として、UmbracoはMITライセンスをフレームワークへ、フリーウェアライセンスをUIへ、それぞれ異なる2つのライセンスを採用しています。 まだインストールは完了していません。 影響するファイルとフォルダ Umbracoに必要なアクセス権の設定についての詳細はこちらをどうぞ @@ -408,7 +487,7 @@ スクラッチから始めたい どうしたらいいの?) + (どうしたらいいの?) 後からRunwayをインストールする事もできます。そうしたくなった時は、Developerセクションのパッケージへどうぞ。 ]]> Umbracoプラットフォームのクリーンセットアップが完了しました。この後はどうしますか? @@ -444,7 +523,7 @@ Runwayをインストールして作られた新しいウェブサイトがど 我々の認めるコミュニティから手助けを得られるでしょう。どうしたら簡単なサイトを構築できるか、どうしたらパッケージを使えるかについてのビデオや文書、またUmbracoの用語のクイックガイドも見る事ができます。]]> Umbraco %0% のインストールは完了、準備が整いました /web.config fileを手作業で編集し、'%0%'の下にあるUmbracoConfigurationStatusキーを設定してください。]]> + /web.config fileを手作業で編集し、'%0%'の下にあるumbracoConfigurationStatusキーを設定してください。]]> 今すぐ開始できます。
もしUmbracoの初心者なら、 私たちの初心者向けのたくさんの情報を参考にしてください。]]>
Umbracoの開始 @@ -453,7 +532,7 @@ Runwayをインストールして作られた新しいウェブサイトがど Umbraco Version 3 Umbraco Version 4 見る - Umbraco %0% の新規インストールまたは3.0からの更新について設定方法を案内します。 + umbraco %0% の新規インストールまたは3.0からの更新について設定方法を案内します。

"次へ"を押してウィザードを開始します。]]>
@@ -466,7 +545,16 @@ Runwayをインストールして作られた新しいウェブサイトがど 作業を保存して今すぐ更新 - © 2001 - %0%
umbraco.com

]]>
+ Happy super sunday + Happy manic monday + Happy tubular tuesday + Happy wonderful wednesday + Happy thunder thursday + Happy funky friday + Happy caturday + ウェブサイトにログインします。 + セッションタイムアウトしました。 + © 2001 - %0%
umbraco.org

]]>
Umbraco にようこそ。ユーザー名とパスワードを入力してください: @@ -549,7 +637,7 @@ Runwayをインストールして作られた新しいウェブサイトがど 本当にアンインストールしますか パッケージのアンインストールが終了しました パッケージが正常にアンインストールされました - パッケージのアンンストール + パッケージのアンインストール 注意: 全ての、文書やメディアなどに依存したアイテムを削除する場合はそれらの作業を一端止めてからアンインストールしなければシステムが不安定になる恐れがあります。 疑問点などあればパッケージの作者へ連絡してください。]]> @@ -558,6 +646,7 @@ Runwayをインストールして作られた新しいウェブサイトがど 更新の手順 このパッケージの更新があります。Umbracoのパッケージリポジトリから直接ダウンロードできます。 パッケージのバージョン + パッケージのバージョン履歴 パッケージのウェブサイトを見る @@ -585,14 +674,15 @@ Runwayをインストールして作られた新しいウェブサイトがど 単一のログインとパスワードで単純に保護したい場合に適します + - - - + ]]>
非公開の子ページも含めます 公開を進めています - 少々お待ちください... %1% ページ中 %0% ページが公開されました... @@ -615,6 +705,9 @@ Runwayをインストールして作られた新しいウェブサイトがど 新規ウィンドウで開く リンクを削除 + + リセット + 現在の版 の文字列は以前の版にはない部分で、緑の文字列は以前の版にのみある部分です。]]> @@ -640,6 +733,9 @@ Runwayをインストールして作られた新しいウェブサイトがど 統計 翻訳 ユーザー + ヘルプ + フォーム + アナリティクス 既定のテンプレート @@ -649,6 +745,7 @@ Runwayをインストールして作られた新しいウェブサイトがど ノードのタイプ タイプ スタイルシート + スクリプト スタイルシートのプロパティ タブ タブの名前 @@ -656,6 +753,9 @@ Runwayをインストールして作られた新しいウェブサイトがど マスターコンテンツタイプが有効 このコンテンツタイプの使用 マスターコンテンツタイプについては、マスターコンテンツタイプからのタブは表示されず、マスターコンテンツタイプでのみ編集することができます。 + このタブにはプロパティが定義されていません、上部のリンクから新しいプロパティを作成してください + マスタードキュメントタイプ + テンプレートを作成する ソートが完了しました。 @@ -684,6 +784,8 @@ Runwayをインストールして作られた新しいウェブサイトがど 変更を適用する為に公開する事を忘れないでください 承認へ送りました 変更は承認へと送られます + メディアを保存しました + メディアをエラーなく保存しました メンバーを保存しました スタイルシートのプロパティを保存しました スタイルシートを保存しました @@ -729,15 +831,53 @@ Runwayをインストールして作られた新しいウェブサイトがど コンテンツ領域プレースホルダーの挿入 dictionary item の挿入 マクロの挿入 - Umbraco ページフィールドの挿入 + umbraco ページフィールドの挿入 マスターテンプレート - Umbraco テンプレートタグのクイックガイド + umbraco テンプレートタグのクイックガイド テンプレート + + + 挿入するアイテムを選択する + ここからレイアウトを選択します + 最初の要素を追加します]]> + + クリックして埋め込む + クリックして画像を挿入する + キャプション... + ここに記入する... + + レイアウト + レイアウトは通常1つまたは2つの異なるレイアウトを必要とする、グリッドエディタの全体的な作業エリアです + レイアウトを追加する + 追加のセクションの横幅を設定し、レイアウトを調整する + + 行の構成 + 定義された構成の行が水平に配置されます + 行の構成を追加 + 追加のセルのセル幅を設定することで調整します + + + グリッドレイアウトの列を合計した数 + + 設定 + 編集者が設定できる項目 + + + スタイル + 編集者が設定できるスタイル + + 入力されたJSONが正しい場合のみ設定が保存されます + + すべてのエディタを許可する + すべての行の構成を許可する + + 代替フィールド 代替テキスト 大文字小文字変換 + エンコーディング フィールドの選択 改行コードの変換 改行コードをhtmlタグ <br> に変換する @@ -838,6 +978,7 @@ Runwayをインストールして作られた新しいウェブサイトがど スタイルシート テンプレート XSLT ファイル + アナリティクス 新しい更新があります @@ -864,6 +1005,7 @@ Runwayをインストールして作られた新しいウェブサイトがど セクション Umbracoへのアクセスを無効にする パスワード + パスワードのリセット パスワードが変更されました! 新しいパスワードの確認 新しいパスワードの入力 @@ -882,5 +1024,9 @@ Runwayをインストールして作られた新しいウェブサイトがど ユーザーの種類 ユーザーの種類 投稿者 + 翻訳者 + あなたのプロフィール + あなたの最新の履歴 + セッションの期限 diff --git a/src/Umbraco.Web/Scheduling/KeepAlive.cs b/src/Umbraco.Web/Scheduling/KeepAlive.cs index c1b43b57c6..a47f8aa72c 100644 --- a/src/Umbraco.Web/Scheduling/KeepAlive.cs +++ b/src/Umbraco.Web/Scheduling/KeepAlive.cs @@ -1,10 +1,8 @@ using System; using System.Net; using Umbraco.Core; -using Umbraco.Core.Configuration; using Umbraco.Core.Configuration.UmbracoSettings; using Umbraco.Core.Logging; -using Umbraco.Core.Sync; namespace Umbraco.Web.Scheduling { @@ -13,32 +11,28 @@ namespace Umbraco.Web.Scheduling public static void Start(ApplicationContext appContext, IUmbracoSettingsSection settings) { using (DisposableTimer.DebugDuration(() => "Keep alive executing", () => "Keep alive complete")) - { - var umbracoBaseUrl = ServerEnvironmentHelper.GetCurrentServerUmbracoBaseUrl( - appContext, - settings); - - if (string.IsNullOrWhiteSpace(umbracoBaseUrl)) + { + var umbracoAppUrl = appContext.UmbracoApplicationUrl; + if (umbracoAppUrl.IsNullOrWhiteSpace()) { LogHelper.Warn("No url for service (yet), skip."); + return; } - else - { - var url = string.Format("{0}ping.aspx", umbracoBaseUrl.EnsureEndsWith('/')); - try + var url = umbracoAppUrl + "/ping.aspx"; + + try + { + using (var wc = new WebClient()) { - using (var wc = new WebClient()) - { - wc.DownloadString(url); - } + wc.DownloadString(url); } - catch (Exception ee) - { - LogHelper.Error( - string.Format("Error in ping. The base url used in the request was: {0}, see http://our.umbraco.org/documentation/Using-Umbraco/Config-files/umbracoSettings/#ScheduledTasks documentation for details on setting a baseUrl if this is in error", umbracoBaseUrl) + } + catch (Exception ee) + { + LogHelper.Error( + string.Format("Error in ping. The base url used in the request was: {0}, see http://our.umbraco.org/documentation/Using-Umbraco/Config-files/umbracoSettings/#ScheduledTasks documentation for details on setting a baseUrl if this is in error", umbracoAppUrl) , ee); - } } } diff --git a/src/Umbraco.Web/Scheduling/ScheduledPublishing.cs b/src/Umbraco.Web/Scheduling/ScheduledPublishing.cs index 0f7cb8c205..d1dc8d1935 100644 --- a/src/Umbraco.Web/Scheduling/ScheduledPublishing.cs +++ b/src/Umbraco.Web/Scheduling/ScheduledPublishing.cs @@ -58,44 +58,41 @@ namespace Umbraco.Web.Scheduling _isPublishingRunning = true; - var umbracoBaseUrl = ServerEnvironmentHelper.GetCurrentServerUmbracoBaseUrl(_appContext, _settings); + var umbracoAppUrl = _appContext.UmbracoApplicationUrl; + if (umbracoAppUrl.IsNullOrWhiteSpace()) + { + LogHelper.Warn("No url for service (yet), skip."); + return; + } try { - - if (string.IsNullOrWhiteSpace(umbracoBaseUrl)) + var url = umbracoAppUrl + "/RestServices/ScheduledPublish/Index"; + using (var wc = new HttpClient()) { - LogHelper.Warn("No url for service (yet), skip."); - } - else - { - var url = string.Format("{0}RestServices/ScheduledPublish/Index", umbracoBaseUrl.EnsureEndsWith('/')); - using (var wc = new HttpClient()) + var request = new HttpRequestMessage() { - var request = new HttpRequestMessage() - { - RequestUri = new Uri(url), - Method = HttpMethod.Post, - Content = new StringContent(string.Empty) - }; - //pass custom the authorization header - request.Headers.Authorization = AdminTokenAuthorizeAttribute.GetAuthenticationHeaderValue(_appContext); + RequestUri = new Uri(url), + Method = HttpMethod.Post, + Content = new StringContent(string.Empty) + }; + //pass custom the authorization header + request.Headers.Authorization = AdminTokenAuthorizeAttribute.GetAuthenticationHeaderValue(_appContext); - try - { - var result = await wc.SendAsync(request, token); - } - catch (Exception ex) - { - LogHelper.Error("An error occurred calling scheduled publish url", ex); - } + try + { + var result = await wc.SendAsync(request, token); + } + catch (Exception ex) + { + LogHelper.Error("An error occurred calling scheduled publish url", ex); } } } catch (Exception ee) { LogHelper.Error( - string.Format("An error occurred with the scheduled publishing. The base url used in the request was: {0}, see http://our.umbraco.org/documentation/Using-Umbraco/Config-files/umbracoSettings/#ScheduledTasks documentation for details on setting a baseUrl if this is in error", umbracoBaseUrl) + string.Format("An error occurred with the scheduled publishing. The base url used in the request was: {0}, see http://our.umbraco.org/documentation/Using-Umbraco/Config-files/umbracoSettings/#ScheduledTasks documentation for details on setting a baseUrl if this is in error", umbracoAppUrl) , ee); } finally diff --git a/src/Umbraco.Web/UmbracoModule.cs b/src/Umbraco.Web/UmbracoModule.cs index f93347aa20..838dea94b8 100644 --- a/src/Umbraco.Web/UmbracoModule.cs +++ b/src/Umbraco.Web/UmbracoModule.cs @@ -36,37 +36,47 @@ namespace Umbraco.Web { #region HttpModule event handlers + private static void EnsureApplicationUrl(HttpRequestBase request) + { + var appctx = ApplicationContext.Current; + + // already initialized = ok + // note that getting ApplicationUrl will ALSO try the various settings + if (appctx.UmbracoApplicationUrl.IsNullOrWhiteSpace() == false) return; + + // so if we reach that point, nothing was configured + // use the current request as application url + + // if (HTTP and SSL not required) or (HTTPS and SSL required), + // use ports from request + // otherwise, + // if non-standard ports used, + // user may need to set baseUrl manually per + // http://our.umbraco.org/documentation/Using-Umbraco/Config-files/umbracoSettings/#ScheduledTasks + // TODO update the doc, prefer web.routing/@appUrl to scheduledTasks/@baseUrl + var port = (request.IsSecureConnection == false && GlobalSettings.UseSSL == false) + || (request.IsSecureConnection && GlobalSettings.UseSSL) + ? ":" + request.ServerVariables["SERVER_PORT"] + : ""; + + var ssl = GlobalSettings.UseSSL ? "s" : ""; // force, whatever the first request + var url = "http" + ssl + "://" + request.ServerVariables["SERVER_NAME"] + port + IOHelper.ResolveUrl(SystemDirectories.Umbraco); + + appctx.UmbracoApplicationUrl = UriUtility.TrimPathEndSlash(url); + LogHelper.Info("ApplicationUrl: " + appctx.UmbracoApplicationUrl + " (UmbracoModule request)"); + } + + /// /// Begins to process a request. /// /// static void BeginRequest(HttpContextBase httpContext) { + // ensure application url is initialized + EnsureApplicationUrl(httpContext.Request); - //we need to set the initial url in our ApplicationContext, this is so our keep alive service works and this must - //exist on a global context because the keep alive service doesn't run in a web context. - //we are NOT going to put a lock on this because locking will slow down the application and we don't really care - //if two threads write to this at the exact same time during first page hit. - //see: http://issues.umbraco.org/issue/U4-2059 - if (ApplicationContext.Current.OriginalRequestUrl.IsNullOrWhiteSpace()) - { - // If (HTTP and SSL not required) or (HTTPS and SSL required), use ports from request to configure OriginalRequestUrl. - // Otherwise, user may need to set baseUrl manually per http://our.umbraco.org/documentation/Using-Umbraco/Config-files/umbracoSettings/#ScheduledTasks if non-standard ports used. - if ((!httpContext.Request.IsSecureConnection && !GlobalSettings.UseSSL) || (httpContext.Request.IsSecureConnection && GlobalSettings.UseSSL)) - { - // Use port from request. - ApplicationContext.Current.OriginalRequestUrl = string.Format("{0}:{1}{2}", httpContext.Request.ServerVariables["SERVER_NAME"], httpContext.Request.ServerVariables["SERVER_PORT"], IOHelper.ResolveUrl(SystemDirectories.Umbraco)); - } - else - { - // Omit port entirely. - ApplicationContext.Current.OriginalRequestUrl = string.Format("{0}{1}", httpContext.Request.ServerVariables["SERVER_NAME"], IOHelper.ResolveUrl(SystemDirectories.Umbraco)); - } - - LogHelper.Info("Setting OriginalRequestUrl: " + ApplicationContext.Current.OriginalRequestUrl); - } - - // do not process if client-side request + // do not process if client-side request if (httpContext.Request.Url.IsClientSideRequest()) return; diff --git a/src/Umbraco.Web/umbraco.presentation/keepAliveService.cs b/src/Umbraco.Web/umbraco.presentation/keepAliveService.cs index 6d80d8bedd..abc3425f3f 100644 --- a/src/Umbraco.Web/umbraco.presentation/keepAliveService.cs +++ b/src/Umbraco.Web/umbraco.presentation/keepAliveService.cs @@ -18,9 +18,7 @@ namespace umbraco.presentation var appContext = (ApplicationContext) sender; - //TODO: This won't always work, in load balanced scenarios ping will not work because - // this original request url will be public and not internal to the server. - var url = string.Format("http://{0}/ping.aspx", appContext.OriginalRequestUrl); + var url = appContext.UmbracoApplicationUrl + "/ping.aspx"; try { using (var wc = new WebClient())