mirror of
https://github.com/OrchardCMS/Orchard.git
synced 2025-09-23 04:43:35 +08:00
Implementing the Warmup module
- Global.asax.cs looks for static pages in ~\App_Data\Warmup - Orchard.Warmup module contains methods to generate those pages based on scheduled times, and content publishing --HG-- branch : dev
This commit is contained in:
@@ -169,6 +169,8 @@
|
|||||||
<Compile Include="Users\ShellSettingsUtility.cs" />
|
<Compile Include="Users\ShellSettingsUtility.cs" />
|
||||||
<Compile Include="Values.cs" />
|
<Compile Include="Values.cs" />
|
||||||
<Compile Include="Users\Services\MembershipServiceTests.cs" />
|
<Compile Include="Users\Services\MembershipServiceTests.cs" />
|
||||||
|
<Compile Include="Warmup\WebDownloaderTests.cs" />
|
||||||
|
<Compile Include="Warmup\WarmupUpdaterTests.cs" />
|
||||||
<Compile Include="Widgets\RuleEngine\UrlRuleProviderTest.cs" />
|
<Compile Include="Widgets\RuleEngine\UrlRuleProviderTest.cs" />
|
||||||
<Compile Include="Widgets\Services\WidgetsServiceTest.cs" />
|
<Compile Include="Widgets\Services\WidgetsServiceTest.cs" />
|
||||||
<Compile Include="Widgets\WidgetsTests.cs" />
|
<Compile Include="Widgets\WidgetsTests.cs" />
|
||||||
@@ -254,6 +256,10 @@
|
|||||||
<Project>{79AED36E-ABD0-4747-93D3-8722B042454B}</Project>
|
<Project>{79AED36E-ABD0-4747-93D3-8722B042454B}</Project>
|
||||||
<Name>Orchard.Users</Name>
|
<Name>Orchard.Users</Name>
|
||||||
</ProjectReference>
|
</ProjectReference>
|
||||||
|
<ProjectReference Include="..\Orchard.Web\Modules\Orchard.WarmUp\Orchard.Warmup.csproj">
|
||||||
|
<Project>{9CD5C81F-5828-4384-8474-2E2BE71D5EDD}</Project>
|
||||||
|
<Name>Orchard.Warmup</Name>
|
||||||
|
</ProjectReference>
|
||||||
<ProjectReference Include="..\Orchard.Web\Modules\Orchard.Widgets\Orchard.Widgets.csproj">
|
<ProjectReference Include="..\Orchard.Web\Modules\Orchard.Widgets\Orchard.Widgets.csproj">
|
||||||
<Project>{194D3CCC-1153-474D-8176-FDE8D7D0D0BD}</Project>
|
<Project>{194D3CCC-1153-474D-8176-FDE8D7D0D0BD}</Project>
|
||||||
<Name>Orchard.Widgets</Name>
|
<Name>Orchard.Widgets</Name>
|
||||||
|
281
src/Orchard.Tests.Modules/Warmup/WarmupUpdaterTests.cs
Normal file
281
src/Orchard.Tests.Modules/Warmup/WarmupUpdaterTests.cs
Normal file
@@ -0,0 +1,281 @@
|
|||||||
|
using System;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Net;
|
||||||
|
using System.Xml;
|
||||||
|
using Autofac;
|
||||||
|
using Moq;
|
||||||
|
using NUnit.Framework;
|
||||||
|
using Orchard.Environment.Warmup;
|
||||||
|
using Orchard.FileSystems.AppData;
|
||||||
|
using Orchard.FileSystems.LockFile;
|
||||||
|
using Orchard.Services;
|
||||||
|
using Orchard.Tests.FileSystems.AppData;
|
||||||
|
using Orchard.Tests.Stubs;
|
||||||
|
using Orchard.Tests.UI.Navigation;
|
||||||
|
using Orchard.Warmup.Models;
|
||||||
|
using Orchard.Warmup.Services;
|
||||||
|
|
||||||
|
namespace Orchard.Tests.Modules.Warmup {
|
||||||
|
public class WarmupUpdaterTests {
|
||||||
|
protected IContainer _container;
|
||||||
|
private IWarmupUpdater _warmupUpdater;
|
||||||
|
private IAppDataFolder _appDataFolder;
|
||||||
|
private ILockFileManager _lockFileManager;
|
||||||
|
private StubClock _clock;
|
||||||
|
private Mock<IWebDownloader> _webDownloader;
|
||||||
|
private IOrchardServices _orchardServices;
|
||||||
|
private WarmupSettingsPart _settings;
|
||||||
|
|
||||||
|
private readonly string _basePath = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName());
|
||||||
|
|
||||||
|
private string _warmupFilename, _lockFilename;
|
||||||
|
private const string WarmupFolder = "Warmup";
|
||||||
|
|
||||||
|
[TestFixtureTearDown]
|
||||||
|
public void Clean() {
|
||||||
|
if (Directory.Exists(_basePath)) {
|
||||||
|
Directory.Delete(_basePath, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[SetUp]
|
||||||
|
public void Init() {
|
||||||
|
if (Directory.Exists(_basePath)) {
|
||||||
|
Directory.Delete(_basePath, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
Directory.CreateDirectory(_basePath);
|
||||||
|
_appDataFolder = AppDataFolderTests.CreateAppDataFolder(_basePath);
|
||||||
|
_webDownloader = new Mock<IWebDownloader>();
|
||||||
|
_orchardServices = new StubOrchardServices();
|
||||||
|
((StubWorkContextAccessor.WorkContextImpl.StubSite) _orchardServices.WorkContext.CurrentSite).BaseUrl = "http://orchardproject.net";
|
||||||
|
|
||||||
|
_settings = new WarmupSettingsPart { Record = new WarmupSettingsPartRecord() };
|
||||||
|
_orchardServices.WorkContext.CurrentSite.ContentItem.Weld(_settings);
|
||||||
|
|
||||||
|
var builder = new ContainerBuilder();
|
||||||
|
builder.RegisterInstance(_appDataFolder).As<IAppDataFolder>();
|
||||||
|
builder.RegisterInstance(_orchardServices).As<IOrchardServices>();
|
||||||
|
builder.RegisterType<DefaultLockFileManager>().As<ILockFileManager>();
|
||||||
|
builder.RegisterType<WarmupUpdater>().As<IWarmupUpdater>();
|
||||||
|
builder.RegisterType<StubClock>().As<IClock>();
|
||||||
|
builder.RegisterInstance(_clock = new StubClock()).As<IClock>();
|
||||||
|
builder.RegisterInstance(_webDownloader.Object).As<IWebDownloader>();
|
||||||
|
_container = builder.Build();
|
||||||
|
|
||||||
|
_lockFileManager = _container.Resolve<ILockFileManager>();
|
||||||
|
_warmupUpdater = _container.Resolve<IWarmupUpdater>();
|
||||||
|
|
||||||
|
_warmupFilename = _appDataFolder.Combine(WarmupFolder, "warmup.txt");
|
||||||
|
_lockFilename = _appDataFolder.Combine(WarmupFolder, "warmup.txt.lock");
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void ShouldDoNothingWhenNoUrlsAreSpecified() {
|
||||||
|
_warmupUpdater.EnsureGenerate();
|
||||||
|
Assert.That(_appDataFolder.ListFiles(WarmupFolder).Count(), Is.EqualTo(0));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void StampFileShouldBeDeletedToForceAnUpdate() {
|
||||||
|
_appDataFolder.CreateFile(_warmupFilename, "");
|
||||||
|
_warmupUpdater.Generate();
|
||||||
|
Assert.That(_appDataFolder.ListFiles(WarmupFolder).Count(), Is.EqualTo(0));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void GenerateShouldNotRunIfLocked() {
|
||||||
|
_appDataFolder.CreateFile(_warmupFilename, "");
|
||||||
|
ILockFile lockFile = null;
|
||||||
|
_lockFileManager.TryAcquireLock(_lockFilename, ref lockFile);
|
||||||
|
using(lockFile) {
|
||||||
|
_warmupUpdater.Generate();
|
||||||
|
// warmup file + lock file
|
||||||
|
Assert.That(_appDataFolder.ListFiles(WarmupFolder).Count(), Is.EqualTo(2));
|
||||||
|
}
|
||||||
|
|
||||||
|
_warmupUpdater.Generate();
|
||||||
|
Assert.That(_appDataFolder.ListFiles(WarmupFolder).Count(), Is.EqualTo(0));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void ShouldDownloadConfiguredUrls() {
|
||||||
|
_settings.Urls = @" /
|
||||||
|
/About";
|
||||||
|
_webDownloader
|
||||||
|
.Setup(w => w.Download("http://orchardproject.net/"))
|
||||||
|
.Returns(new DownloadResult { Content = "Foo", StatusCode = HttpStatusCode.OK });
|
||||||
|
|
||||||
|
_webDownloader
|
||||||
|
.Setup(w => w.Download("http://orchardproject.net/About"))
|
||||||
|
.Returns(new DownloadResult { Content = "Bar", StatusCode = HttpStatusCode.OK });
|
||||||
|
|
||||||
|
_warmupUpdater.Generate();
|
||||||
|
var files = _appDataFolder.ListFiles(WarmupFolder).ToList();
|
||||||
|
|
||||||
|
// warmup + content files
|
||||||
|
Assert.That(files, Has.Some.Matches<string>(x => x == _appDataFolder.Combine(WarmupFolder, "warmup.txt")));
|
||||||
|
Assert.That(files, Has.Some.Matches<string>(x => x == _appDataFolder.Combine(WarmupFolder, WarmupUtility.EncodeUrl("http://orchardproject.net/"))));
|
||||||
|
Assert.That(files, Has.Some.Matches<string>(x => x == _appDataFolder.Combine(WarmupFolder, WarmupUtility.EncodeUrl("http://orchardproject.net/About"))));
|
||||||
|
|
||||||
|
var homepageContent = _appDataFolder.ReadFile(_appDataFolder.Combine(WarmupFolder, WarmupUtility.EncodeUrl("http://orchardproject.net/")));
|
||||||
|
var aboutcontent = _appDataFolder.ReadFile(_appDataFolder.Combine(WarmupFolder, WarmupUtility.EncodeUrl("http://orchardproject.net/About")));
|
||||||
|
|
||||||
|
Assert.That(homepageContent, Is.EqualTo("Foo"));
|
||||||
|
Assert.That(aboutcontent, Is.EqualTo("Bar"));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void ShouldCreateFilesForOkStatusOnly() {
|
||||||
|
_settings.Urls = @" /
|
||||||
|
/About";
|
||||||
|
_webDownloader
|
||||||
|
.Setup(w => w.Download("http://orchardproject.net/"))
|
||||||
|
.Returns(new DownloadResult { Content = "Foo", StatusCode = HttpStatusCode.OK });
|
||||||
|
|
||||||
|
_webDownloader
|
||||||
|
.Setup(w => w.Download("http://orchardproject.net/About"))
|
||||||
|
.Returns(new DownloadResult { Content = "Bar", StatusCode = HttpStatusCode.NotFound });
|
||||||
|
|
||||||
|
_warmupUpdater.Generate();
|
||||||
|
var files = _appDataFolder.ListFiles(WarmupFolder).ToList();
|
||||||
|
|
||||||
|
// warmup + content file
|
||||||
|
Assert.That(files, Has.Some.Matches<string>(x => x == _appDataFolder.Combine(WarmupFolder, "warmup.txt")));
|
||||||
|
Assert.That(files, Has.Some.Matches<string>(x => x == _appDataFolder.Combine(WarmupFolder, WarmupUtility.EncodeUrl("http://orchardproject.net/"))));
|
||||||
|
Assert.That(files, Has.None.Matches<string>(x => x == _appDataFolder.Combine(WarmupFolder, WarmupUtility.EncodeUrl("http://orchardproject.net/About"))));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void ShouldProcessValidRequestsOnly() {
|
||||||
|
_settings.Urls = @" /
|
||||||
|
<>@\\";
|
||||||
|
_webDownloader
|
||||||
|
.Setup(w => w.Download("http://orchardproject.net/"))
|
||||||
|
.Returns(new DownloadResult { Content = "Foo", StatusCode = HttpStatusCode.OK });
|
||||||
|
|
||||||
|
_warmupUpdater.Generate();
|
||||||
|
var files = _appDataFolder.ListFiles(WarmupFolder).ToList();
|
||||||
|
|
||||||
|
// warmup + content file
|
||||||
|
Assert.That(files.Count, Is.EqualTo(2));
|
||||||
|
Assert.That(files, Has.Some.Matches<string>(x => x == _appDataFolder.Combine(WarmupFolder, "warmup.txt")));
|
||||||
|
Assert.That(files, Has.Some.Matches<string>(x => x == _appDataFolder.Combine(WarmupFolder, WarmupUtility.EncodeUrl("http://orchardproject.net/"))));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void WarmupFileShouldContainUtcNow() {
|
||||||
|
_settings.Urls = @"/";
|
||||||
|
|
||||||
|
_webDownloader
|
||||||
|
.Setup(w => w.Download("http://orchardproject.net/"))
|
||||||
|
.Returns(new DownloadResult { Content = "Foo", StatusCode = HttpStatusCode.OK });
|
||||||
|
|
||||||
|
_warmupUpdater.Generate();
|
||||||
|
|
||||||
|
var warmupContent = _appDataFolder.ReadFile(_warmupFilename);
|
||||||
|
Assert.That(warmupContent, Is.EqualTo(XmlConvert.ToString(_clock.UtcNow, XmlDateTimeSerializationMode.Utc)));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void ShouldNotProcessIfDelayHasNotExpired() {
|
||||||
|
_settings.Urls = @"/";
|
||||||
|
_settings.Delay = 90;
|
||||||
|
_settings.Scheduled = true;
|
||||||
|
|
||||||
|
_webDownloader
|
||||||
|
.Setup(w => w.Download("http://orchardproject.net/"))
|
||||||
|
.Returns(new DownloadResult { Content = "Foo", StatusCode = HttpStatusCode.OK });
|
||||||
|
|
||||||
|
_webDownloader
|
||||||
|
.Setup(w => w.Download("http://orchardproject.net/About"))
|
||||||
|
.Returns(new DownloadResult { Content = "Bar", StatusCode = HttpStatusCode.OK });
|
||||||
|
|
||||||
|
_warmupUpdater.Generate();
|
||||||
|
|
||||||
|
var warmupContent = _appDataFolder.ReadFile(_warmupFilename);
|
||||||
|
Assert.That(warmupContent, Is.EqualTo(XmlConvert.ToString(_clock.UtcNow, XmlDateTimeSerializationMode.Utc)));
|
||||||
|
|
||||||
|
_settings.Urls = @" /
|
||||||
|
/About";
|
||||||
|
_clock.Advance(TimeSpan.FromMinutes(89));
|
||||||
|
_warmupUpdater.EnsureGenerate();
|
||||||
|
|
||||||
|
var files = _appDataFolder.ListFiles(WarmupFolder).ToList();
|
||||||
|
Assert.That(files, Has.None.Matches<string>(x => x == _appDataFolder.Combine(WarmupFolder, WarmupUtility.EncodeUrl("http://orchardproject.net/About"))));
|
||||||
|
|
||||||
|
warmupContent = _appDataFolder.ReadFile(_warmupFilename);
|
||||||
|
Assert.That(warmupContent, Is.Not.EqualTo(XmlConvert.ToString(_clock.UtcNow, XmlDateTimeSerializationMode.Utc)));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void ShouldProcessIfDelayHasExpired() {
|
||||||
|
_settings.Urls = @"/";
|
||||||
|
_settings.Delay = 90;
|
||||||
|
_settings.Scheduled = true;
|
||||||
|
|
||||||
|
_webDownloader
|
||||||
|
.Setup(w => w.Download("http://orchardproject.net/"))
|
||||||
|
.Returns(new DownloadResult { Content = "Foo", StatusCode = HttpStatusCode.OK });
|
||||||
|
|
||||||
|
_webDownloader
|
||||||
|
.Setup(w => w.Download("http://orchardproject.net/About"))
|
||||||
|
.Returns(new DownloadResult { Content = "Bar", StatusCode = HttpStatusCode.OK });
|
||||||
|
|
||||||
|
_warmupUpdater.Generate();
|
||||||
|
|
||||||
|
var warmupContent = _appDataFolder.ReadFile(_warmupFilename);
|
||||||
|
Assert.That(warmupContent, Is.EqualTo(XmlConvert.ToString(_clock.UtcNow, XmlDateTimeSerializationMode.Utc)));
|
||||||
|
|
||||||
|
_settings.Urls = @" /
|
||||||
|
/About";
|
||||||
|
_clock.Advance(TimeSpan.FromMinutes(91));
|
||||||
|
_warmupUpdater.EnsureGenerate();
|
||||||
|
|
||||||
|
var files = _appDataFolder.ListFiles(WarmupFolder).ToList();
|
||||||
|
Assert.That(files, Has.Some.Matches<string>(x => x == _appDataFolder.Combine(WarmupFolder, WarmupUtility.EncodeUrl("http://orchardproject.net/About"))));
|
||||||
|
|
||||||
|
warmupContent = _appDataFolder.ReadFile(_warmupFilename);
|
||||||
|
Assert.That(warmupContent, Is.EqualTo(XmlConvert.ToString(_clock.UtcNow, XmlDateTimeSerializationMode.Utc)));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void ShouldGenerateNonWwwVersions() {
|
||||||
|
_settings.Urls = @" /
|
||||||
|
/About";
|
||||||
|
|
||||||
|
((StubWorkContextAccessor.WorkContextImpl.StubSite)_orchardServices.WorkContext.CurrentSite).BaseUrl = "http://www.orchardproject.net";
|
||||||
|
|
||||||
|
_webDownloader
|
||||||
|
.Setup(w => w.Download("http://www.orchardproject.net/"))
|
||||||
|
.Returns(new DownloadResult { Content = "Foo", StatusCode = HttpStatusCode.OK });
|
||||||
|
|
||||||
|
_webDownloader
|
||||||
|
.Setup(w => w.Download("http://www.orchardproject.net/About"))
|
||||||
|
.Returns(new DownloadResult { Content = "Bar", StatusCode = HttpStatusCode.OK });
|
||||||
|
|
||||||
|
_warmupUpdater.Generate();
|
||||||
|
var files = _appDataFolder.ListFiles(WarmupFolder).ToList();
|
||||||
|
|
||||||
|
// warmup + content files
|
||||||
|
Assert.That(files, Has.Some.Matches<string>(x => x == _appDataFolder.Combine(WarmupFolder, "warmup.txt")));
|
||||||
|
Assert.That(files, Has.Some.Matches<string>(x => x == _appDataFolder.Combine(WarmupFolder, WarmupUtility.EncodeUrl("http://www.orchardproject.net/"))));
|
||||||
|
Assert.That(files, Has.Some.Matches<string>(x => x == _appDataFolder.Combine(WarmupFolder, WarmupUtility.EncodeUrl("http://www.orchardproject.net/About"))));
|
||||||
|
Assert.That(files, Has.Some.Matches<string>(x => x == _appDataFolder.Combine(WarmupFolder, WarmupUtility.EncodeUrl("http://orchardproject.net/"))));
|
||||||
|
Assert.That(files, Has.Some.Matches<string>(x => x == _appDataFolder.Combine(WarmupFolder, WarmupUtility.EncodeUrl("http://orchardproject.net/About"))));
|
||||||
|
|
||||||
|
var homepageContent = _appDataFolder.ReadFile(_appDataFolder.Combine(WarmupFolder, WarmupUtility.EncodeUrl("http://orchardproject.net/")));
|
||||||
|
var aboutcontent = _appDataFolder.ReadFile(_appDataFolder.Combine(WarmupFolder, WarmupUtility.EncodeUrl("http://orchardproject.net/About")));
|
||||||
|
|
||||||
|
var wwwhomepageContent = _appDataFolder.ReadFile(_appDataFolder.Combine(WarmupFolder, WarmupUtility.EncodeUrl("http://www.orchardproject.net/")));
|
||||||
|
var wwwaboutcontent = _appDataFolder.ReadFile(_appDataFolder.Combine(WarmupFolder, WarmupUtility.EncodeUrl("http://www.orchardproject.net/About")));
|
||||||
|
|
||||||
|
Assert.That(homepageContent, Is.EqualTo("Foo"));
|
||||||
|
Assert.That(wwwhomepageContent, Is.EqualTo("Foo"));
|
||||||
|
Assert.That(aboutcontent, Is.EqualTo("Bar"));
|
||||||
|
Assert.That(wwwaboutcontent, Is.EqualTo("Bar"));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
37
src/Orchard.Tests.Modules/Warmup/WebDownloaderTests.cs
Normal file
37
src/Orchard.Tests.Modules/Warmup/WebDownloaderTests.cs
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
using System.Net;
|
||||||
|
using NUnit.Framework;
|
||||||
|
using Orchard.Warmup.Services;
|
||||||
|
|
||||||
|
namespace Orchard.Tests.Modules.Warmup {
|
||||||
|
public class WebDownloaderTests {
|
||||||
|
private readonly IWebDownloader _webDownloader = new WebDownloader();
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void ShouldReturnNullWhenUrlIsEmpty() {
|
||||||
|
Assert.That(_webDownloader.Download(null), Is.Null);
|
||||||
|
Assert.That(_webDownloader.Download(""), Is.Null);
|
||||||
|
Assert.That(_webDownloader.Download(" "), Is.Null);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void ShouldReturnNullWhenUrlIsInvalid() {
|
||||||
|
Assert.That(_webDownloader.Download("froutfrout|yepyep"), Is.Null);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void StatusCodeShouldBe404ForUnexistingResources() {
|
||||||
|
var download = _webDownloader.Download("http://www.microsoft.com/yepyep");
|
||||||
|
Assert.That(download, Is.Not.Null);
|
||||||
|
Assert.That(download.StatusCode, Is.EqualTo(HttpStatusCode.NotFound));
|
||||||
|
Assert.That(download.Content, Is.Null);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void StatusCodeShouldBe200ForValidRequests() {
|
||||||
|
var download = _webDownloader.Download("http://www.microsoft.com/");
|
||||||
|
Assert.That(download, Is.Not.Null);
|
||||||
|
Assert.That(download.StatusCode, Is.EqualTo(HttpStatusCode.OK));
|
||||||
|
Assert.That(download.Content, Is.Not.Empty);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
28
src/Orchard.Tests/Environment/WarmUp/WarmUpUtilityTests.cs
Normal file
28
src/Orchard.Tests/Environment/WarmUp/WarmUpUtilityTests.cs
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
using System;
|
||||||
|
using NUnit.Framework;
|
||||||
|
using Orchard.Environment.Warmup;
|
||||||
|
|
||||||
|
namespace Orchard.Tests.Environment.Warmup {
|
||||||
|
[TestFixture]
|
||||||
|
public class WarmUpUtilityTests {
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void EmptyStringsAreNotAllowed() {
|
||||||
|
Assert.Throws<ArgumentException>(() => WarmupUtility.EncodeUrl(""));
|
||||||
|
Assert.Throws<ArgumentException>(() => WarmupUtility.EncodeUrl(null));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void EncodedUrlsShouldBeValidFilenames() {
|
||||||
|
Assert.That(WarmupUtility.EncodeUrl("http://www.microsoft.com"), Is.EqualTo("http_3A_2F_2Fwww_2Emicrosoft_2Ecom"));
|
||||||
|
Assert.That(WarmupUtility.EncodeUrl("http://www.microsoft.com/foo?bar=baz"), Is.EqualTo("http_3A_2F_2Fwww_2Emicrosoft_2Ecom_2Ffoo_3Fbar_3Dbaz"));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void EncodedUrlsShouldPreserveQueryStrings() {
|
||||||
|
Assert.That(WarmupUtility.EncodeUrl("http://www.microsoft.com/foo?bar=baz"), Is.StringContaining("bar"));
|
||||||
|
Assert.That(WarmupUtility.EncodeUrl("http://www.microsoft.com/foo?bar=baz"), Is.StringContaining("baz"));
|
||||||
|
Assert.That(WarmupUtility.EncodeUrl("http://www.microsoft.com/foo?bar=baz"), Is.StringContaining("foo"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -239,6 +239,7 @@
|
|||||||
<Compile Include="Environment\RunningShellTableTests.cs" />
|
<Compile Include="Environment\RunningShellTableTests.cs" />
|
||||||
<Compile Include="Environment\StubHostEnvironment.cs" />
|
<Compile Include="Environment\StubHostEnvironment.cs" />
|
||||||
<Compile Include="Environment\Utility\Build.cs" />
|
<Compile Include="Environment\Utility\Build.cs" />
|
||||||
|
<Compile Include="Environment\WarmUp\WarmUpUtilityTests.cs" />
|
||||||
<Compile Include="FileSystems\AppData\AppDataFolderTests.cs" />
|
<Compile Include="FileSystems\AppData\AppDataFolderTests.cs" />
|
||||||
<Compile Include="Environment\Configuration\DefaultTenantManagerTests.cs" />
|
<Compile Include="Environment\Configuration\DefaultTenantManagerTests.cs" />
|
||||||
<Compile Include="Environment\DefaultCompositionStrategyTests.cs" />
|
<Compile Include="Environment\DefaultCompositionStrategyTests.cs" />
|
||||||
|
@@ -71,10 +71,12 @@ namespace Orchard.Tests.Stubs {
|
|||||||
set { throw new NotImplementedException(); }
|
set { throw new NotImplementedException(); }
|
||||||
}
|
}
|
||||||
|
|
||||||
public int PageSize {
|
public int PageSize{
|
||||||
get { throw new NotImplementedException(); }
|
get { throw new NotImplementedException(); }
|
||||||
set { throw new NotImplementedException(); }
|
set { throw new NotImplementedException(); }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public string BaseUrl { get; set;}
|
||||||
}
|
}
|
||||||
|
|
||||||
public class StubUser : IUser {
|
public class StubUser : IUser {
|
||||||
|
@@ -140,8 +140,15 @@ namespace Orchard.Tests.UI.Navigation {
|
|||||||
get { throw new NotImplementedException(); }
|
get { throw new NotImplementedException(); }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private WorkContext _workContext;
|
||||||
public WorkContext WorkContext {
|
public WorkContext WorkContext {
|
||||||
get { return new StubWorkContextAccessor(_lifetimeScope).GetContext(); }
|
get {
|
||||||
|
if(_workContext == null) {
|
||||||
|
_workContext = new StubWorkContextAccessor(_lifetimeScope).GetContext();
|
||||||
|
}
|
||||||
|
|
||||||
|
return _workContext;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -64,27 +64,26 @@ namespace Orchard.Core.Settings.Controllers {
|
|||||||
var site = _siteService.GetSiteSettings();
|
var site = _siteService.GetSiteSettings();
|
||||||
dynamic model = Services.ContentManager.UpdateEditor(site, this, groupInfoId);
|
dynamic model = Services.ContentManager.UpdateEditor(site, this, groupInfoId);
|
||||||
|
|
||||||
|
GroupInfo groupInfo = null;
|
||||||
|
|
||||||
if (!string.IsNullOrWhiteSpace(groupInfoId)) {
|
if (!string.IsNullOrWhiteSpace(groupInfoId)) {
|
||||||
if (model == null) {
|
if (model == null) {
|
||||||
Services.TransactionManager.Cancel();
|
Services.TransactionManager.Cancel();
|
||||||
return HttpNotFound();
|
return HttpNotFound();
|
||||||
}
|
}
|
||||||
|
|
||||||
var groupInfo = Services.ContentManager.GetEditorGroupInfo(site, groupInfoId);
|
groupInfo = Services.ContentManager.GetEditorGroupInfo(site, groupInfoId);
|
||||||
if (groupInfo == null) {
|
if (groupInfo == null) {
|
||||||
Services.TransactionManager.Cancel();
|
Services.TransactionManager.Cancel();
|
||||||
return HttpNotFound();
|
return HttpNotFound();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!ModelState.IsValid) {
|
|
||||||
Services.TransactionManager.Cancel();
|
|
||||||
model.GroupInfo = groupInfo;
|
|
||||||
|
|
||||||
// Casting to avoid invalid (under medium trust) reflection over the protected View method and force a static invocation.
|
|
||||||
return View((object) model);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else {
|
|
||||||
|
if (!ModelState.IsValid) {
|
||||||
|
Services.TransactionManager.Cancel();
|
||||||
|
model.GroupInfo = groupInfo;
|
||||||
|
|
||||||
|
// Casting to avoid invalid (under medium trust) reflection over the protected View method and force a static invocation.
|
||||||
return View((object)model);
|
return View((object)model);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -1,9 +1,11 @@
|
|||||||
using JetBrains.Annotations;
|
using System.Net;
|
||||||
|
using JetBrains.Annotations;
|
||||||
using Orchard.ContentManagement;
|
using Orchard.ContentManagement;
|
||||||
using Orchard.ContentManagement.Drivers;
|
using Orchard.ContentManagement.Drivers;
|
||||||
using Orchard.Core.Settings.Models;
|
using Orchard.Core.Settings.Models;
|
||||||
using Orchard.Core.Settings.ViewModels;
|
using Orchard.Core.Settings.ViewModels;
|
||||||
using Orchard.Localization.Services;
|
using Orchard.Localization.Services;
|
||||||
|
using Orchard.Logging;
|
||||||
using Orchard.Settings;
|
using Orchard.Settings;
|
||||||
using System;
|
using System;
|
||||||
using Orchard.Security;
|
using Orchard.Security;
|
||||||
@@ -16,16 +18,24 @@ namespace Orchard.Core.Settings.Drivers {
|
|||||||
private readonly ISiteService _siteService;
|
private readonly ISiteService _siteService;
|
||||||
private readonly ICultureManager _cultureManager;
|
private readonly ICultureManager _cultureManager;
|
||||||
private readonly IMembershipService _membershipService;
|
private readonly IMembershipService _membershipService;
|
||||||
|
private readonly INotifier _notifier;
|
||||||
|
|
||||||
public SiteSettingsPartDriver(ISiteService siteService, ICultureManager cultureManager, IMembershipService membershipService, INotifier notifier) {
|
public SiteSettingsPartDriver(
|
||||||
|
ISiteService siteService,
|
||||||
|
ICultureManager cultureManager,
|
||||||
|
IMembershipService membershipService,
|
||||||
|
INotifier notifier) {
|
||||||
_siteService = siteService;
|
_siteService = siteService;
|
||||||
_cultureManager = cultureManager;
|
_cultureManager = cultureManager;
|
||||||
_membershipService = membershipService;
|
_membershipService = membershipService;
|
||||||
|
_notifier = notifier;
|
||||||
|
|
||||||
T = NullLocalizer.Instance;
|
T = NullLocalizer.Instance;
|
||||||
|
Logger = NullLogger.Instance;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Localizer T { get; set; }
|
public Localizer T { get; set; }
|
||||||
|
public ILogger Logger { get; set; }
|
||||||
|
|
||||||
protected override string Prefix { get { return "SiteSettings"; } }
|
protected override string Prefix { get { return "SiteSettings"; } }
|
||||||
|
|
||||||
@@ -48,6 +58,8 @@ namespace Orchard.Core.Settings.Drivers {
|
|||||||
SiteCultures = _cultureManager.ListCultures()
|
SiteCultures = _cultureManager.ListCultures()
|
||||||
};
|
};
|
||||||
|
|
||||||
|
var previousBaseUrl = model.Site.BaseUrl;
|
||||||
|
|
||||||
updater.TryUpdateModel(model, Prefix, null, null);
|
updater.TryUpdateModel(model, Prefix, null, null);
|
||||||
|
|
||||||
// ensures the super user is fully empty
|
// ensures the super user is fully empty
|
||||||
@@ -62,6 +74,27 @@ namespace Orchard.Core.Settings.Drivers {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// ensure the base url is absolute if provided
|
||||||
|
if (!String.IsNullOrWhiteSpace(model.Site.BaseUrl)) {
|
||||||
|
if (!model.Site.BaseUrl.ToLower().StartsWith("http")) {
|
||||||
|
updater.AddModelError("BaseUrl", T("The base url must be absolute."));
|
||||||
|
}
|
||||||
|
// if the base url has been modified, try to ping it
|
||||||
|
else if (!String.Equals(previousBaseUrl, model.Site.BaseUrl, StringComparison.OrdinalIgnoreCase)) {
|
||||||
|
try {
|
||||||
|
var request = WebRequest.Create(model.Site.BaseUrl) as HttpWebRequest;
|
||||||
|
if (request != null) {
|
||||||
|
using (request.GetResponse() as HttpWebResponse) {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception e) {
|
||||||
|
_notifier.Warning(T("The base url you entered could not be requested from current location."));
|
||||||
|
Logger.Warning(e, "Could not query base url: {0}", model.Site.BaseUrl);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return ContentShape("Parts_Settings_SiteSettingsPart",
|
return ContentShape("Parts_Settings_SiteSettingsPart",
|
||||||
() => shapeHelper.EditorTemplate(TemplateName: "Parts.Settings.SiteSettingsPart", Model: model, Prefix: Prefix));
|
() => shapeHelper.EditorTemplate(TemplateName: "Parts.Settings.SiteSettingsPart", Model: model, Prefix: Prefix));
|
||||||
}
|
}
|
||||||
|
@@ -95,5 +95,14 @@ namespace Orchard.Core.Settings {
|
|||||||
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public int UpdateFrom1() {
|
||||||
|
SchemaBuilder.AlterTable("SiteSettingsPartRecord",
|
||||||
|
table => table
|
||||||
|
.AddColumn<string>("BaseUrl", c => c.WithLength(255))
|
||||||
|
);
|
||||||
|
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -1,4 +1,5 @@
|
|||||||
using Orchard.ContentManagement;
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
using Orchard.ContentManagement;
|
||||||
using Orchard.Settings;
|
using Orchard.Settings;
|
||||||
|
|
||||||
namespace Orchard.Core.Settings.Models {
|
namespace Orchard.Core.Settings.Models {
|
||||||
@@ -42,5 +43,11 @@ namespace Orchard.Core.Settings.Models {
|
|||||||
get { return Record.PageSize; }
|
get { return Record.PageSize; }
|
||||||
set { Record.PageSize = value; }
|
set { Record.PageSize = value; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[StringLength(255)]
|
||||||
|
public string BaseUrl {
|
||||||
|
get { return Record.BaseUrl; }
|
||||||
|
set { Record.BaseUrl = value; }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,4 +1,5 @@
|
|||||||
using Orchard.ContentManagement.Records;
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
using Orchard.ContentManagement.Records;
|
||||||
using Orchard.Settings;
|
using Orchard.Settings;
|
||||||
|
|
||||||
namespace Orchard.Core.Settings.Models {
|
namespace Orchard.Core.Settings.Models {
|
||||||
@@ -24,5 +25,8 @@ namespace Orchard.Core.Settings.Models {
|
|||||||
public virtual ResourceDebugMode ResourceDebugMode { get; set; }
|
public virtual ResourceDebugMode ResourceDebugMode { get; set; }
|
||||||
|
|
||||||
public virtual int PageSize { get; set; }
|
public virtual int PageSize { get; set; }
|
||||||
|
|
||||||
|
[StringLength(255)]
|
||||||
|
public virtual string BaseUrl { get; set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -1,6 +1,5 @@
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Web.Mvc;
|
using System.Web.Mvc;
|
||||||
using Orchard.ContentManagement;
|
|
||||||
using Orchard.Core.Settings.Models;
|
using Orchard.Core.Settings.Models;
|
||||||
using Orchard.Settings;
|
using Orchard.Settings;
|
||||||
|
|
||||||
@@ -8,7 +7,6 @@ namespace Orchard.Core.Settings.ViewModels {
|
|||||||
public class SiteSettingsPartViewModel {
|
public class SiteSettingsPartViewModel {
|
||||||
public SiteSettingsPart Site { get; set; }
|
public SiteSettingsPart Site { get; set; }
|
||||||
public IEnumerable<string> SiteCultures { get; set; }
|
public IEnumerable<string> SiteCultures { get; set; }
|
||||||
|
|
||||||
|
|
||||||
[HiddenInput(DisplayValue = false)]
|
[HiddenInput(DisplayValue = false)]
|
||||||
public int Id {
|
public int Id {
|
||||||
@@ -16,33 +14,38 @@ namespace Orchard.Core.Settings.ViewModels {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public string PageTitleSeparator {
|
public string PageTitleSeparator {
|
||||||
get { return Site.Record.PageTitleSeparator; }
|
get { return Site.PageTitleSeparator; }
|
||||||
set { Site.Record.PageTitleSeparator = value; }
|
set { Site.PageTitleSeparator = value; }
|
||||||
}
|
}
|
||||||
|
|
||||||
public string SiteName {
|
public string SiteName {
|
||||||
get { return Site.Record.SiteName; }
|
get { return Site.SiteName; }
|
||||||
set { Site.Record.SiteName = value; }
|
set { Site.SiteName = value; }
|
||||||
}
|
}
|
||||||
|
|
||||||
public string SiteCulture {
|
public string SiteCulture {
|
||||||
get { return Site.Record.SiteCulture; }
|
get { return Site.SiteCulture; }
|
||||||
set { Site.Record.SiteCulture = value; }
|
set { Site.SiteCulture = value; }
|
||||||
}
|
}
|
||||||
|
|
||||||
public string SuperUser {
|
public string SuperUser {
|
||||||
get { return Site.As<SiteSettingsPart>().Record.SuperUser; }
|
get { return Site.SuperUser; }
|
||||||
set { Site.As<SiteSettingsPart>().Record.SuperUser = value; }
|
set { Site.SuperUser = value; }
|
||||||
}
|
}
|
||||||
|
|
||||||
public ResourceDebugMode ResourceDebugMode {
|
public ResourceDebugMode ResourceDebugMode {
|
||||||
get { return Site.As<SiteSettingsPart>().ResourceDebugMode; }
|
get { return Site.ResourceDebugMode; }
|
||||||
set { Site.As<SiteSettingsPart>().ResourceDebugMode = value; }
|
set { Site.ResourceDebugMode = value; }
|
||||||
}
|
}
|
||||||
|
|
||||||
public int PageSize {
|
public int PageSize {
|
||||||
get { return Site.As<SiteSettingsPart>().PageSize; }
|
get { return Site.PageSize; }
|
||||||
set { Site.As<SiteSettingsPart>().PageSize = value; }
|
set { Site.PageSize = value; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public string BaseUrl {
|
||||||
|
get { return Site.BaseUrl; }
|
||||||
|
set { Site.BaseUrl = value; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -38,7 +38,13 @@
|
|||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<label for="DefaultPageSize">@T("Default number of items per page")</label>
|
<label for="DefaultPageSize">@T("Default number of items per page")</label>
|
||||||
@Html.TextBoxFor(m => m.PageSize, new { @class = "textMedium" })
|
@Html.TextBoxFor(m => m.PageSize, new { @class = "text-small" })
|
||||||
<span class="hint">@T("Determines the default number of items that are shown per page.")</span>
|
<span class="hint">@T("Determines the default number of items that are shown per page.")</span>
|
||||||
</div>
|
</div>
|
||||||
|
<div>
|
||||||
|
<label for="@Html.FieldIdFor(m => m.BaseUrl)">@T("Base url ")</label>
|
||||||
|
@Html.TextBoxFor(m => m.BaseUrl, new { @class = "textMedium" })
|
||||||
|
<span class="hint">@T("Enter the fully qualified base url of your website.")</span>
|
||||||
|
<span class="hint">@T("e.g., http://localhost:30320/orchardlocal, http://www.yourdomain.com")</span>
|
||||||
|
</div>
|
||||||
</fieldset>
|
</fieldset>
|
@@ -1,35 +1,106 @@
|
|||||||
using System.Web;
|
using System;
|
||||||
|
using System.IO;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Web;
|
||||||
|
using System.Web.Hosting;
|
||||||
using System.Web.Mvc;
|
using System.Web.Mvc;
|
||||||
using System.Web.Routing;
|
using System.Web.Routing;
|
||||||
using Autofac;
|
using Autofac;
|
||||||
using Orchard.Environment;
|
using Orchard.Environment;
|
||||||
|
using Orchard.Environment.Warmup;
|
||||||
|
using Orchard.Utility.Extensions;
|
||||||
|
|
||||||
namespace Orchard.Web {
|
namespace Orchard.Web {
|
||||||
// Note: For instructions on enabling IIS6 or IIS7 classic mode,
|
// Note: For instructions on enabling IIS6 or IIS7 classic mode,
|
||||||
// visit http://go.microsoft.com/?LinkId=9394801
|
// visit http://go.microsoft.com/?LinkId=9394801
|
||||||
|
|
||||||
public class MvcApplication : HttpApplication {
|
public class MvcApplication : HttpApplication {
|
||||||
private static IOrchardHost _host;
|
private static StartupResult _startupResult;
|
||||||
|
private static EventWaitHandle _waitHandle;
|
||||||
|
|
||||||
public static void RegisterRoutes(RouteCollection routes) {
|
public static void RegisterRoutes(RouteCollection routes) {
|
||||||
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
|
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void Application_Start() {
|
protected void Application_Start() {
|
||||||
RegisterRoutes(RouteTable.Routes);
|
LaunchStartupThread();
|
||||||
|
}
|
||||||
|
|
||||||
_host = OrchardStarter.CreateHost(MvcSingletons);
|
/// <summary>
|
||||||
_host.Initialize();
|
/// Initializes Orchard's Host in a separate thread
|
||||||
|
/// </summary>
|
||||||
|
private static void LaunchStartupThread() {
|
||||||
|
_startupResult = new StartupResult();
|
||||||
|
_waitHandle = new AutoResetEvent(false);
|
||||||
|
|
||||||
|
ThreadPool.QueueUserWorkItem(
|
||||||
|
state => {
|
||||||
|
try {
|
||||||
|
RegisterRoutes(RouteTable.Routes);
|
||||||
|
var host = OrchardStarter.CreateHost(MvcSingletons);
|
||||||
|
host.Initialize();
|
||||||
|
_startupResult.Host = host;
|
||||||
|
}
|
||||||
|
catch (Exception e) {
|
||||||
|
_startupResult.Error = e;
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
_waitHandle.Set();
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void Application_BeginRequest() {
|
protected void Application_BeginRequest() {
|
||||||
Context.Items["originalHttpContext"] = Context;
|
// Host is still starting up?
|
||||||
|
if (_startupResult.Host == null && _startupResult.Error == null) {
|
||||||
|
|
||||||
_host.BeginRequest();
|
// use the url as it was requested by the client
|
||||||
|
// the real url might be different if it has been translated (proxy, load balancing, ...)
|
||||||
|
var url = Request.ToUrlString();
|
||||||
|
var virtualFileCopy = "~/App_Data/WarmUp/" + WarmupUtility.EncodeUrl(url.Trim('/'));
|
||||||
|
var localCopy = HostingEnvironment.MapPath(virtualFileCopy);
|
||||||
|
|
||||||
|
if (File.Exists(localCopy)) {
|
||||||
|
|
||||||
|
// result should not be cached, even on proxies
|
||||||
|
Context.Response.Cache.SetExpires(DateTime.UtcNow.AddDays(-1));
|
||||||
|
Context.Response.Cache.SetValidUntilExpires(false);
|
||||||
|
Context.Response.Cache.SetRevalidation(HttpCacheRevalidation.AllCaches);
|
||||||
|
Context.Response.Cache.SetCacheability(HttpCacheability.NoCache);
|
||||||
|
Context.Response.Cache.SetNoStore();
|
||||||
|
|
||||||
|
Context.Response.ContentType = "text/html";
|
||||||
|
|
||||||
|
Context.Response.WriteFile(localCopy);
|
||||||
|
Context.Response.End();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// there is no local copy and the host is not running
|
||||||
|
// wait for the host to initialize
|
||||||
|
_waitHandle.WaitOne();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if (_startupResult.Error != null) {
|
||||||
|
// Host startup resulted in an error
|
||||||
|
|
||||||
|
// Throw error once, and restart launch (machine state may have changed
|
||||||
|
// so we need to simulate a "restart".
|
||||||
|
var error = _startupResult.Error;
|
||||||
|
LaunchStartupThread();
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
|
||||||
|
Context.Items["originalHttpContext"] = Context;
|
||||||
|
_startupResult.Host.BeginRequest();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void Application_EndRequest() {
|
protected void Application_EndRequest() {
|
||||||
_host.EndRequest();
|
// Only notify if the host has started up
|
||||||
|
if (_startupResult.Host != null) {
|
||||||
|
_startupResult.Host.EndRequest();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void MvcSingletons(ContainerBuilder builder) {
|
static void MvcSingletons(ContainerBuilder builder) {
|
||||||
@@ -37,6 +108,5 @@ namespace Orchard.Web {
|
|||||||
builder.Register(ctx => ModelBinders.Binders).SingleInstance();
|
builder.Register(ctx => ModelBinders.Binders).SingleInstance();
|
||||||
builder.Register(ctx => ViewEngines.Engines).SingleInstance();
|
builder.Register(ctx => ViewEngines.Engines).SingleInstance();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -182,6 +182,10 @@ namespace Orchard.Setup {
|
|||||||
get { return SiteSettingsPartRecord.DefaultPageSize; }
|
get { return SiteSettingsPartRecord.DefaultPageSize; }
|
||||||
set { throw new NotImplementedException(); }
|
set { throw new NotImplementedException(); }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public string BaseUrl {
|
||||||
|
get { return ""; }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
17
src/Orchard.Web/Modules/Orchard.Warmup/AdminMenu.cs
Normal file
17
src/Orchard.Web/Modules/Orchard.Warmup/AdminMenu.cs
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
using Orchard.Localization;
|
||||||
|
using Orchard.Security;
|
||||||
|
using Orchard.UI.Navigation;
|
||||||
|
|
||||||
|
namespace Orchard.Warmup {
|
||||||
|
public class AdminMenu : INavigationProvider {
|
||||||
|
public Localizer T { get; set; }
|
||||||
|
public string MenuName { get { return "admin"; } }
|
||||||
|
|
||||||
|
public void GetNavigation(NavigationBuilder builder) {
|
||||||
|
builder
|
||||||
|
.Add(T("Settings"), menu => menu
|
||||||
|
.Add(T("Warmup" ), "10.0", item => item.Action("Index", "Admin", new { area = "Orchard.Warmup" }).Permission(StandardPermissions.SiteOwner))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,29 @@
|
|||||||
|
using Orchard.Commands;
|
||||||
|
using Orchard.Warmup.Services;
|
||||||
|
|
||||||
|
namespace Orchard.Warmup.Commands {
|
||||||
|
public class WarmupCommands : DefaultOrchardCommandHandler {
|
||||||
|
private readonly IWarmupUpdater _warmupUpdater;
|
||||||
|
|
||||||
|
[OrchardSwitch]
|
||||||
|
public bool Force { get; set; }
|
||||||
|
|
||||||
|
public WarmupCommands(IWarmupUpdater warmupUpdater) {
|
||||||
|
_warmupUpdater = warmupUpdater;
|
||||||
|
}
|
||||||
|
|
||||||
|
[CommandName("warmup generate")]
|
||||||
|
[CommandHelp("warmup generate [/Force:true] \r\n\t Generates all the static pages for the warmup feature.")]
|
||||||
|
[OrchardSwitches("Force")]
|
||||||
|
public string Generate() {
|
||||||
|
if(Force) {
|
||||||
|
_warmupUpdater.Generate();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
_warmupUpdater.EnsureGenerate();
|
||||||
|
}
|
||||||
|
|
||||||
|
return "Generation finished";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,78 @@
|
|||||||
|
using System.Web.Mvc;
|
||||||
|
using Orchard.ContentManagement;
|
||||||
|
using Orchard.Core.Contents.Controllers;
|
||||||
|
using Orchard.Localization;
|
||||||
|
using Orchard.Security;
|
||||||
|
using Orchard.Warmup.Models;
|
||||||
|
using Orchard.UI.Notify;
|
||||||
|
using Orchard.Warmup.Services;
|
||||||
|
|
||||||
|
namespace Orchard.Warmup.Controllers {
|
||||||
|
public class AdminController : Controller, IUpdateModel {
|
||||||
|
private readonly IWarmupUpdater _warmupUpdater;
|
||||||
|
|
||||||
|
public AdminController(IOrchardServices services, IWarmupUpdater warmupUpdater) {
|
||||||
|
_warmupUpdater = warmupUpdater;
|
||||||
|
Services = services;
|
||||||
|
|
||||||
|
T = NullLocalizer.Instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
public IOrchardServices Services { get; set; }
|
||||||
|
public Localizer T { get; set; }
|
||||||
|
|
||||||
|
public ActionResult Index() {
|
||||||
|
if (!Services.Authorizer.Authorize(StandardPermissions.SiteOwner, T("Not authorized to manage settings")))
|
||||||
|
return new HttpUnauthorizedResult();
|
||||||
|
|
||||||
|
var warmupPart = Services.WorkContext.CurrentSite.As<WarmupSettingsPart>();
|
||||||
|
return View(warmupPart);
|
||||||
|
}
|
||||||
|
|
||||||
|
[FormValueRequired("submit")]
|
||||||
|
[HttpPost, ActionName("Index")]
|
||||||
|
public ActionResult IndexPost() {
|
||||||
|
if (!Services.Authorizer.Authorize(StandardPermissions.SiteOwner, T("Not authorized to manage settings")))
|
||||||
|
return new HttpUnauthorizedResult();
|
||||||
|
|
||||||
|
var warmupPart = Services.WorkContext.CurrentSite.As<WarmupSettingsPart>();
|
||||||
|
|
||||||
|
if(TryUpdateModel(warmupPart)) {
|
||||||
|
Services.Notifier.Information(T("Warmup updated successfully."));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (warmupPart.Scheduled) {
|
||||||
|
if (warmupPart.Delay <= 0) {
|
||||||
|
AddModelError("Delay", T("Delay must be greater than zero."));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return View(warmupPart);
|
||||||
|
}
|
||||||
|
|
||||||
|
[FormValueRequired("submit.Generate")]
|
||||||
|
[HttpPost, ActionName("Index")]
|
||||||
|
public ActionResult IndexPostGenerate() {
|
||||||
|
var result = IndexPost();
|
||||||
|
|
||||||
|
if (ModelState.IsValid) {
|
||||||
|
_warmupUpdater.Generate();
|
||||||
|
Services.Notifier.Information(T("Static pages have been generated."));
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IUpdateModel.TryUpdateModel<TModel>(TModel model, string prefix, string[] includeProperties, string[] excludeProperties) {
|
||||||
|
return TryUpdateModel(model, prefix, includeProperties, excludeProperties);
|
||||||
|
}
|
||||||
|
|
||||||
|
void IUpdateModel.AddModelError(string key, LocalizedString errorMessage) {
|
||||||
|
ModelState.AddModelError(key, errorMessage.ToString());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void AddModelError(string key, LocalizedString errorMessage) {
|
||||||
|
ModelState.AddModelError(key, errorMessage.ToString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,28 @@
|
|||||||
|
using Orchard.ContentManagement.Handlers;
|
||||||
|
using Orchard.ContentManagement;
|
||||||
|
using Orchard.Warmup.Models;
|
||||||
|
using Orchard.Warmup.Services;
|
||||||
|
|
||||||
|
namespace Orchard.Warmup.Handlers {
|
||||||
|
/// <summary>
|
||||||
|
/// Intercepts the ContentHandler events to create warmup static pages
|
||||||
|
/// whenever some content is published
|
||||||
|
/// </summary>
|
||||||
|
public class WarmupContentHandler : ContentHandler {
|
||||||
|
private readonly IOrchardServices _orchardServices;
|
||||||
|
private readonly IWarmupUpdater _warmupUpdater;
|
||||||
|
|
||||||
|
public WarmupContentHandler(IOrchardServices orchardServices, IWarmupUpdater warmupUpdater) {
|
||||||
|
_orchardServices = orchardServices;
|
||||||
|
_warmupUpdater = warmupUpdater;
|
||||||
|
|
||||||
|
OnPublished<ContentPart>(Generate);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Generate(PublishContentContext context, ContentPart part) {
|
||||||
|
if(_orchardServices.WorkContext.CurrentSite.As<WarmupSettingsPart>().OnPublish) {
|
||||||
|
_warmupUpdater.Generate();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,14 @@
|
|||||||
|
using JetBrains.Annotations;
|
||||||
|
using Orchard.Data;
|
||||||
|
using Orchard.ContentManagement.Handlers;
|
||||||
|
using Orchard.Warmup.Models;
|
||||||
|
|
||||||
|
namespace Orchard.Warmup.Handlers {
|
||||||
|
[UsedImplicitly]
|
||||||
|
public class WarmupSettingsPartHandler : ContentHandler {
|
||||||
|
public WarmupSettingsPartHandler(IRepository<WarmupSettingsPartRecord> repository) {
|
||||||
|
Filters.Add(new ActivatingFilter<WarmupSettingsPart>("Site"));
|
||||||
|
Filters.Add(StorageFilter.For(repository));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
18
src/Orchard.Web/Modules/Orchard.Warmup/Migrations.cs
Normal file
18
src/Orchard.Web/Modules/Orchard.Warmup/Migrations.cs
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
using Orchard.Data.Migration;
|
||||||
|
|
||||||
|
namespace Orchard.Warmup {
|
||||||
|
public class Migrations : DataMigrationImpl {
|
||||||
|
public int Create() {
|
||||||
|
SchemaBuilder.CreateTable("WarmupSettingsPartRecord",
|
||||||
|
table => table
|
||||||
|
.ContentPartRecord()
|
||||||
|
.Column<string>("Urls", column => column.Unlimited())
|
||||||
|
.Column<bool>("Scheduled")
|
||||||
|
.Column<int>("Delay")
|
||||||
|
.Column<bool>("OnPublish")
|
||||||
|
);
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,26 @@
|
|||||||
|
using Orchard.ContentManagement;
|
||||||
|
|
||||||
|
namespace Orchard.Warmup.Models {
|
||||||
|
public class WarmupSettingsPart : ContentPart<WarmupSettingsPartRecord> {
|
||||||
|
|
||||||
|
public string Urls {
|
||||||
|
get { return Record.Urls; }
|
||||||
|
set { Record.Urls = value; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool Scheduled {
|
||||||
|
get { return Record.Scheduled; }
|
||||||
|
set { Record.Scheduled = value; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public int Delay {
|
||||||
|
get { return Record.Delay; }
|
||||||
|
set { Record.Delay = value; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool OnPublish {
|
||||||
|
get { return Record.OnPublish; }
|
||||||
|
set { Record.OnPublish = value; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,16 @@
|
|||||||
|
using Orchard.ContentManagement.Records;
|
||||||
|
using Orchard.Data.Conventions;
|
||||||
|
|
||||||
|
namespace Orchard.Warmup.Models {
|
||||||
|
public class WarmupSettingsPartRecord : ContentPartRecord {
|
||||||
|
public WarmupSettingsPartRecord() {
|
||||||
|
Delay = 90;
|
||||||
|
}
|
||||||
|
|
||||||
|
[StringLengthMax]
|
||||||
|
public virtual string Urls { get; set; }
|
||||||
|
public virtual bool Scheduled { get; set; }
|
||||||
|
public virtual int Delay { get; set; }
|
||||||
|
public virtual bool OnPublish { get; set; }
|
||||||
|
}
|
||||||
|
}
|
12
src/Orchard.Web/Modules/Orchard.Warmup/Module.txt
Normal file
12
src/Orchard.Web/Modules/Orchard.Warmup/Module.txt
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
Name: Orchard.Warmup
|
||||||
|
AntiForgery: enabled
|
||||||
|
Author: The Orchard Team
|
||||||
|
Website: http://orchardproject.net
|
||||||
|
Version: 1.0
|
||||||
|
OrchardVersion: 1.0
|
||||||
|
Description: Provides a mecanism to generate a static version of pages for being used during application warm up.
|
||||||
|
Features:
|
||||||
|
Orchard.Warmup:
|
||||||
|
Description: Generates the static version of specific pages periodically.
|
||||||
|
Name: Warmup
|
||||||
|
Category: Hosting
|
142
src/Orchard.Web/Modules/Orchard.Warmup/Orchard.Warmup.csproj
Normal file
142
src/Orchard.Web/Modules/Orchard.Warmup/Orchard.Warmup.csproj
Normal file
@@ -0,0 +1,142 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||||
|
<PropertyGroup>
|
||||||
|
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||||
|
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
||||||
|
<ProductVersion>9.0.30729</ProductVersion>
|
||||||
|
<SchemaVersion>2.0</SchemaVersion>
|
||||||
|
<ProjectGuid>{9CD5C81F-5828-4384-8474-2E2BE71D5EDD}</ProjectGuid>
|
||||||
|
<ProjectTypeGuids>{349c5851-65df-11da-9384-00065b846f21};{fae04ec0-301f-11d3-bf4b-00c04f79efbc}</ProjectTypeGuids>
|
||||||
|
<OutputType>Library</OutputType>
|
||||||
|
<AppDesignerFolder>Properties</AppDesignerFolder>
|
||||||
|
<RootNamespace>Orchard.Warmup</RootNamespace>
|
||||||
|
<AssemblyName>Orchard.Warmup</AssemblyName>
|
||||||
|
<TargetFrameworkVersion>v4.0</TargetFrameworkVersion>
|
||||||
|
<MvcBuildViews>false</MvcBuildViews>
|
||||||
|
<FileUpgradeFlags>
|
||||||
|
</FileUpgradeFlags>
|
||||||
|
<OldToolsVersion>3.5</OldToolsVersion>
|
||||||
|
<UpgradeBackupLocation />
|
||||||
|
<TargetFrameworkProfile />
|
||||||
|
<UseIISExpress>false</UseIISExpress>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||||
|
<DebugSymbols>true</DebugSymbols>
|
||||||
|
<DebugType>full</DebugType>
|
||||||
|
<Optimize>false</Optimize>
|
||||||
|
<OutputPath>bin\</OutputPath>
|
||||||
|
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||||
|
<ErrorReport>prompt</ErrorReport>
|
||||||
|
<WarningLevel>4</WarningLevel>
|
||||||
|
<CodeAnalysisRuleSet>AllRules.ruleset</CodeAnalysisRuleSet>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
||||||
|
<DebugType>pdbonly</DebugType>
|
||||||
|
<Optimize>true</Optimize>
|
||||||
|
<OutputPath>bin\</OutputPath>
|
||||||
|
<DefineConstants>TRACE</DefineConstants>
|
||||||
|
<ErrorReport>prompt</ErrorReport>
|
||||||
|
<WarningLevel>4</WarningLevel>
|
||||||
|
<CodeAnalysisRuleSet>AllRules.ruleset</CodeAnalysisRuleSet>
|
||||||
|
</PropertyGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<Reference Include="Microsoft.CSharp" />
|
||||||
|
<Reference Include="System" />
|
||||||
|
<Reference Include="System.Data" />
|
||||||
|
<Reference Include="System.ComponentModel.DataAnnotations">
|
||||||
|
<RequiredTargetFramework>3.5</RequiredTargetFramework>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="System.Web.DynamicData" />
|
||||||
|
<Reference Include="System.Web.Mvc, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
|
||||||
|
<SpecificVersion>False</SpecificVersion>
|
||||||
|
<HintPath>..\..\..\..\lib\aspnetmvc\System.Web.Mvc.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="System.Web" />
|
||||||
|
<Reference Include="System.Web.Abstractions" />
|
||||||
|
<Reference Include="System.Web.Routing" />
|
||||||
|
<Reference Include="System.Xml" />
|
||||||
|
<Reference Include="System.Configuration" />
|
||||||
|
<Reference Include="System.Xml.Linq" />
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<Content Include="Web.config" />
|
||||||
|
<Content Include="Views\Web.config" />
|
||||||
|
<Content Include="Scripts\Web.config" />
|
||||||
|
<Content Include="Styles\Web.config" />
|
||||||
|
<Content Include="Properties\AssemblyInfo.cs" />
|
||||||
|
<Content Include="Module.txt" />
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\..\..\Orchard\Orchard.Framework.csproj">
|
||||||
|
<Project>{2D1D92BB-4555-4CBE-8D0E-63563D6CE4C6}</Project>
|
||||||
|
<Name>Orchard.Framework</Name>
|
||||||
|
</ProjectReference>
|
||||||
|
<ProjectReference Include="..\..\Core\Orchard.Core.csproj">
|
||||||
|
<Project>{9916839C-39FC-4CEB-A5AF-89CA7E87119F}</Project>
|
||||||
|
<Name>Orchard.Core</Name>
|
||||||
|
</ProjectReference>
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<Compile Include="AdminMenu.cs" />
|
||||||
|
<Compile Include="Commands\WarmupCommands.cs" />
|
||||||
|
<Compile Include="Controllers\AdminController.cs" />
|
||||||
|
<Compile Include="Handlers\WarmupContentHandler.cs" />
|
||||||
|
<Compile Include="Handlers\WarmupSettingsPartHandler.cs" />
|
||||||
|
<Compile Include="Migrations.cs" />
|
||||||
|
<Compile Include="Models\WarmupSettingsPart.cs" />
|
||||||
|
<Compile Include="Models\WarmupSettingsPartRecord.cs" />
|
||||||
|
<Compile Include="Services\WebDownloader.cs" />
|
||||||
|
<Compile Include="Services\IWarmupUpdater.cs" />
|
||||||
|
<Compile Include="Services\IWebDownloader.cs" />
|
||||||
|
<Compile Include="Services\SettingsBanner.cs" />
|
||||||
|
<Compile Include="Services\WarmupTask.cs" />
|
||||||
|
<Compile Include="Services\WarmupUpdater.cs" />
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup />
|
||||||
|
<ItemGroup>
|
||||||
|
<Content Include="Views\EditorTemplates\Parts.Warmup.SiteSettings.cshtml" />
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<None Include="Placement.info" />
|
||||||
|
<Content Include="Views\Admin\Index.cshtml" />
|
||||||
|
</ItemGroup>
|
||||||
|
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
|
||||||
|
<Import Project="$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v10.0\WebApplications\Microsoft.WebApplication.targets" />
|
||||||
|
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
|
||||||
|
Other similar extension points exist, see Microsoft.Common.targets.
|
||||||
|
<Target Name="BeforeBuild">
|
||||||
|
</Target> -->
|
||||||
|
<Target Name="AfterBuild" DependsOnTargets="AfterBuildCompiler">
|
||||||
|
<PropertyGroup>
|
||||||
|
<AreasManifestDir>$(ProjectDir)\..\Manifests</AreasManifestDir>
|
||||||
|
</PropertyGroup>
|
||||||
|
<!-- If this is an area child project, uncomment the following line:
|
||||||
|
<CreateAreaManifest AreaName="$(AssemblyName)" AreaType="Child" AreaPath="$(ProjectDir)" ManifestPath="$(AreasManifestDir)" ContentFiles="@(Content)" />
|
||||||
|
-->
|
||||||
|
<!-- If this is an area parent project, uncomment the following lines:
|
||||||
|
<CreateAreaManifest AreaName="$(AssemblyName)" AreaType="Parent" AreaPath="$(ProjectDir)" ManifestPath="$(AreasManifestDir)" ContentFiles="@(Content)" />
|
||||||
|
<CopyAreaManifests ManifestPath="$(AreasManifestDir)" CrossCopy="false" RenameViews="true" />
|
||||||
|
-->
|
||||||
|
</Target>
|
||||||
|
<Target Name="AfterBuildCompiler" Condition="'$(MvcBuildViews)'=='true'">
|
||||||
|
<AspNetCompiler VirtualPath="temp" PhysicalPath="$(ProjectDir)\..\$(ProjectName)" />
|
||||||
|
</Target>
|
||||||
|
<ProjectExtensions>
|
||||||
|
<VisualStudio>
|
||||||
|
<FlavorProperties GUID="{349c5851-65df-11da-9384-00065b846f21}">
|
||||||
|
<WebProjectProperties>
|
||||||
|
<UseIIS>False</UseIIS>
|
||||||
|
<AutoAssignPort>True</AutoAssignPort>
|
||||||
|
<DevelopmentServerPort>45979</DevelopmentServerPort>
|
||||||
|
<DevelopmentServerVPath>/</DevelopmentServerVPath>
|
||||||
|
<IISUrl>
|
||||||
|
</IISUrl>
|
||||||
|
<NTLMAuthentication>False</NTLMAuthentication>
|
||||||
|
<UseCustomServer>True</UseCustomServer>
|
||||||
|
<CustomServerUrl>http://orchard.codeplex.com</CustomServerUrl>
|
||||||
|
<SaveServerSettingsInUserFile>False</SaveServerSettingsInUserFile>
|
||||||
|
</WebProjectProperties>
|
||||||
|
</FlavorProperties>
|
||||||
|
</VisualStudio>
|
||||||
|
</ProjectExtensions>
|
||||||
|
</Project>
|
3
src/Orchard.Web/Modules/Orchard.Warmup/Placement.info
Normal file
3
src/Orchard.Web/Modules/Orchard.Warmup/Placement.info
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
<Placement>
|
||||||
|
<Place Parts_Warmup_SiteSettings="Content:10"/>
|
||||||
|
</Placement>
|
@@ -0,0 +1,34 @@
|
|||||||
|
using System.Reflection;
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
|
// General Information about an assembly is controlled through the following
|
||||||
|
// set of attributes. Change these attribute values to modify the information
|
||||||
|
// associated with an assembly.
|
||||||
|
[assembly: AssemblyTitle("Orchard.Warmup")]
|
||||||
|
[assembly: AssemblyDescription("")]
|
||||||
|
[assembly: AssemblyConfiguration("")]
|
||||||
|
[assembly: AssemblyProduct("Orchard")]
|
||||||
|
[assembly: AssemblyCopyright("")]
|
||||||
|
[assembly: AssemblyTrademark("")]
|
||||||
|
[assembly: AssemblyCulture("")]
|
||||||
|
|
||||||
|
// Setting ComVisible to false makes the types in this assembly not visible
|
||||||
|
// to COM components. If you need to access a type in this assembly from
|
||||||
|
// COM, set the ComVisible attribute to true on that type.
|
||||||
|
[assembly: ComVisible(false)]
|
||||||
|
|
||||||
|
// The following GUID is for the ID of the typelib if this project is exposed to COM
|
||||||
|
[assembly: Guid("3484b5d3-de81-4a46-bfa6-c1d02a029184")]
|
||||||
|
|
||||||
|
// Version information for an assembly consists of the following four values:
|
||||||
|
//
|
||||||
|
// Major Version
|
||||||
|
// Minor Version
|
||||||
|
// Build Number
|
||||||
|
// Revision
|
||||||
|
//
|
||||||
|
// You can specify all the values or you can default the Revision and Build Numbers
|
||||||
|
// by using the '*' as shown below:
|
||||||
|
[assembly: AssemblyVersion("1.0.0.0")]
|
||||||
|
[assembly: AssemblyFileVersion("1.0.0.0")]
|
21
src/Orchard.Web/Modules/Orchard.Warmup/Scripts/Web.config
Normal file
21
src/Orchard.Web/Modules/Orchard.Warmup/Scripts/Web.config
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<configuration>
|
||||||
|
<appSettings>
|
||||||
|
<add key="webpages:Enabled" value="false" />
|
||||||
|
</appSettings>
|
||||||
|
<system.web>
|
||||||
|
<httpHandlers>
|
||||||
|
<!-- iis6 - for any request in this location, return via managed static file handler -->
|
||||||
|
<add path="*" verb="*" type="System.Web.StaticFileHandler" />
|
||||||
|
</httpHandlers>
|
||||||
|
</system.web>
|
||||||
|
<system.webServer>
|
||||||
|
<handlers accessPolicy="Script,Read">
|
||||||
|
<!--
|
||||||
|
iis7 - for any request to a file exists on disk, return it via native http module.
|
||||||
|
accessPolicy 'Script' is to allow for a managed 404 page.
|
||||||
|
-->
|
||||||
|
<add name="StaticFile" path="*" verb="*" modules="StaticFileModule" preCondition="integratedMode" resourceType="File" requireAccess="Read" />
|
||||||
|
</handlers>
|
||||||
|
</system.webServer>
|
||||||
|
</configuration>
|
@@ -0,0 +1,13 @@
|
|||||||
|
namespace Orchard.Warmup.Services {
|
||||||
|
public interface IWarmupUpdater : IDependency {
|
||||||
|
/// <summary>
|
||||||
|
/// Forces a regeneration of all static pages
|
||||||
|
/// </summary>
|
||||||
|
void Generate();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Generates static pages if needed
|
||||||
|
/// </summary>
|
||||||
|
void EnsureGenerate();
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,13 @@
|
|||||||
|
|
||||||
|
using System.Net;
|
||||||
|
|
||||||
|
namespace Orchard.Warmup.Services {
|
||||||
|
public class DownloadResult {
|
||||||
|
public HttpStatusCode StatusCode { get; set; }
|
||||||
|
public string Content { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public interface IWebDownloader : IDependency {
|
||||||
|
DownloadResult Download(string url);
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,25 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
using Orchard.ContentManagement;
|
||||||
|
using Orchard.Environment.Configuration;
|
||||||
|
using Orchard.Localization;
|
||||||
|
using Orchard.UI.Admin.Notification;
|
||||||
|
using Orchard.UI.Notify;
|
||||||
|
|
||||||
|
namespace Orchard.Warmup.Services {
|
||||||
|
public class SettingsBanner: INotificationProvider {
|
||||||
|
private readonly IOrchardServices _orchardServices;
|
||||||
|
|
||||||
|
public SettingsBanner(IOrchardServices orchardServices) {
|
||||||
|
_orchardServices = orchardServices;
|
||||||
|
T = NullLocalizer.Instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Localizer T { get; set; }
|
||||||
|
|
||||||
|
public IEnumerable<NotifyEntry> GetNotifications() {
|
||||||
|
if ( string.IsNullOrWhiteSpace(_orchardServices.WorkContext.CurrentSite.BaseUrl)) {
|
||||||
|
yield return new NotifyEntry { Message = T("The Warmup feature needs the Base Url site setting to be set." ), Type = NotifyType.Warning };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,25 @@
|
|||||||
|
using Orchard.ContentManagement;
|
||||||
|
using Orchard.Tasks;
|
||||||
|
using Orchard.Warmup.Models;
|
||||||
|
|
||||||
|
namespace Orchard.Warmup.Services {
|
||||||
|
public class WarmupTask : IBackgroundTask {
|
||||||
|
private readonly IOrchardServices _orchardServices;
|
||||||
|
private readonly IWarmupUpdater _warmupUpdater;
|
||||||
|
|
||||||
|
public WarmupTask(IOrchardServices orchardServices, IWarmupUpdater warmupUpdater) {
|
||||||
|
_orchardServices = orchardServices;
|
||||||
|
_warmupUpdater = warmupUpdater;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Sweep() {
|
||||||
|
var part = _orchardServices.WorkContext.CurrentSite.As<WarmupSettingsPart>();
|
||||||
|
|
||||||
|
if (!part.Scheduled) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_warmupUpdater.EnsureGenerate();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
151
src/Orchard.Web/Modules/Orchard.Warmup/Services/WarmupUpdater.cs
Normal file
151
src/Orchard.Web/Modules/Orchard.Warmup/Services/WarmupUpdater.cs
Normal file
@@ -0,0 +1,151 @@
|
|||||||
|
using System;
|
||||||
|
using System.IO;
|
||||||
|
using System.Net;
|
||||||
|
using System.Web;
|
||||||
|
using System.Xml;
|
||||||
|
using Orchard.ContentManagement;
|
||||||
|
using Orchard.Environment.Warmup;
|
||||||
|
using Orchard.FileSystems.AppData;
|
||||||
|
using Orchard.FileSystems.LockFile;
|
||||||
|
using Orchard.Logging;
|
||||||
|
using Orchard.Services;
|
||||||
|
using Orchard.Warmup.Models;
|
||||||
|
|
||||||
|
namespace Orchard.Warmup.Services {
|
||||||
|
public class WarmupUpdater : IWarmupUpdater {
|
||||||
|
private readonly IOrchardServices _orchardServices;
|
||||||
|
private readonly ILockFileManager _lockFileManager;
|
||||||
|
private readonly IClock _clock;
|
||||||
|
private readonly IAppDataFolder _appDataFolder;
|
||||||
|
private readonly IWebDownloader _webDownloader;
|
||||||
|
private const string BaseFolder = "Warmup";
|
||||||
|
private const string WarmupFilename = "warmup.txt";
|
||||||
|
private readonly string _lockFilename;
|
||||||
|
|
||||||
|
public WarmupUpdater(
|
||||||
|
IOrchardServices orchardServices,
|
||||||
|
ILockFileManager lockFileManager,
|
||||||
|
IClock clock,
|
||||||
|
IAppDataFolder appDataFolder,
|
||||||
|
IWebDownloader webDownloader) {
|
||||||
|
_orchardServices = orchardServices;
|
||||||
|
_lockFileManager = lockFileManager;
|
||||||
|
_clock = clock;
|
||||||
|
_appDataFolder = appDataFolder;
|
||||||
|
_webDownloader = webDownloader;
|
||||||
|
_lockFilename = _appDataFolder.Combine(BaseFolder, WarmupFilename + ".lock");
|
||||||
|
|
||||||
|
Logger = NullLogger.Instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ILogger Logger { get; set; }
|
||||||
|
|
||||||
|
public void EnsureGenerate() {
|
||||||
|
var baseUrl = _orchardServices.WorkContext.CurrentSite.BaseUrl;
|
||||||
|
var part = _orchardServices.WorkContext.CurrentSite.As<WarmupSettingsPart>();
|
||||||
|
|
||||||
|
// do nothing while the base url setting is not defined, or if there is no page defined
|
||||||
|
if (String.IsNullOrWhiteSpace(baseUrl) || String.IsNullOrWhiteSpace(part.Urls)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// prevent multiple appdomains from rebuilding the static page concurrently (e.g., command line)
|
||||||
|
ILockFile lockFile = null;
|
||||||
|
if (!_lockFileManager.TryAcquireLock(_lockFilename, ref lockFile)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
using (lockFile) {
|
||||||
|
|
||||||
|
// check if we need to regenerate the pages by reading the last time it has been done
|
||||||
|
// 1- if the warmup file doesn't exists, generate the pages
|
||||||
|
// 2- otherwise, if the scheduled generation option is on, check if the delay is over
|
||||||
|
var warmupPath = _appDataFolder.Combine(BaseFolder, WarmupFilename);
|
||||||
|
if(_appDataFolder.FileExists(warmupPath)) {
|
||||||
|
try {
|
||||||
|
var warmupContent = _appDataFolder.ReadFile(warmupPath);
|
||||||
|
var expired = XmlConvert.ToDateTimeOffset(warmupContent).AddMinutes(part.Delay);
|
||||||
|
if (expired > _clock.UtcNow) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch {
|
||||||
|
// invalid file, delete continue processing
|
||||||
|
_appDataFolder.DeleteFile(warmupPath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// delete existing static page files
|
||||||
|
foreach (var filename in _appDataFolder.ListFiles(BaseFolder)) {
|
||||||
|
var prefix = _appDataFolder.Combine(BaseFolder, "http");
|
||||||
|
|
||||||
|
// delete only static page files
|
||||||
|
if (!filename.StartsWith(prefix, StringComparison.OrdinalIgnoreCase)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
_appDataFolder.DeleteFile(filename);
|
||||||
|
}
|
||||||
|
catch(Exception e) {
|
||||||
|
// ignore files which could not be deleted
|
||||||
|
Logger.Error(e, "Could not delete file {0}", filename);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// loop over every relative url to generate the contents
|
||||||
|
using (var urlReader = new StringReader(part.Urls)) {
|
||||||
|
string relativeUrl;
|
||||||
|
while (null != (relativeUrl = urlReader.ReadLine())) {
|
||||||
|
string url = null;
|
||||||
|
relativeUrl = relativeUrl.Trim();
|
||||||
|
|
||||||
|
try {
|
||||||
|
url = VirtualPathUtility.RemoveTrailingSlash(baseUrl) + relativeUrl;
|
||||||
|
var download = _webDownloader.Download(url);
|
||||||
|
|
||||||
|
if (download != null && download.StatusCode == HttpStatusCode.OK) {
|
||||||
|
var filename = WarmupUtility.EncodeUrl(url);
|
||||||
|
var path = _appDataFolder.Combine(BaseFolder, filename);
|
||||||
|
_appDataFolder.CreateFile(path, download.Content);
|
||||||
|
|
||||||
|
// if the base url contains http://www, then also render the www-less one
|
||||||
|
|
||||||
|
if (url.StartsWith("http://www.", StringComparison.OrdinalIgnoreCase)) {
|
||||||
|
url = "http://" + url.Substring("http://www.".Length);
|
||||||
|
filename = WarmupUtility.EncodeUrl(url);
|
||||||
|
path = _appDataFolder.Combine(BaseFolder, filename);
|
||||||
|
_appDataFolder.CreateFile(path, download.Content);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception e) {
|
||||||
|
Logger.Error(e, "Could not extract warmup page content for: ", url);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// finally write the time the generation has been executed
|
||||||
|
_appDataFolder.CreateFile(warmupPath, XmlConvert.ToString(_clock.UtcNow, XmlDateTimeSerializationMode.Utc));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Generate() {
|
||||||
|
// prevent multiple appdomains from rebuilding the static page concurrently (e.g., command line)
|
||||||
|
ILockFile lockFile = null;
|
||||||
|
if (!_lockFileManager.TryAcquireLock(_lockFilename, ref lockFile)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
using (lockFile) {
|
||||||
|
var warmupPath = _appDataFolder.Combine(BaseFolder, WarmupFilename);
|
||||||
|
if (_appDataFolder.FileExists(warmupPath)) {
|
||||||
|
_appDataFolder.DeleteFile(warmupPath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
EnsureGenerate();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,49 @@
|
|||||||
|
using System;
|
||||||
|
using System.IO;
|
||||||
|
using System.Net;
|
||||||
|
using Orchard.Logging;
|
||||||
|
|
||||||
|
namespace Orchard.Warmup.Services {
|
||||||
|
public class WebDownloader : IWebDownloader {
|
||||||
|
public WebDownloader() {
|
||||||
|
Logger = NullLogger.Instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ILogger Logger { get; set; }
|
||||||
|
|
||||||
|
public DownloadResult Download(string url) {
|
||||||
|
if(String.IsNullOrWhiteSpace(url)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
var request = WebRequest.Create(url) as HttpWebRequest;
|
||||||
|
if (request != null) {
|
||||||
|
using (var response = request.GetResponse() as HttpWebResponse) {
|
||||||
|
if (response != null) {
|
||||||
|
using (var stream = response.GetResponseStream()) {
|
||||||
|
if (stream != null) {
|
||||||
|
using (var sr = new StreamReader(stream)) {
|
||||||
|
return new DownloadResult {Content = sr.ReadToEnd(), StatusCode = response.StatusCode};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
catch (WebException e) {
|
||||||
|
if(e.Response as HttpWebResponse != null) {
|
||||||
|
return new DownloadResult { StatusCode = ((HttpWebResponse)e.Response).StatusCode };
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
catch(Exception e) {
|
||||||
|
Logger.Error(e, "An error occured while downloading url: {0}", url);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
21
src/Orchard.Web/Modules/Orchard.Warmup/Styles/Web.config
Normal file
21
src/Orchard.Web/Modules/Orchard.Warmup/Styles/Web.config
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<configuration>
|
||||||
|
<appSettings>
|
||||||
|
<add key="webpages:Enabled" value="false" />
|
||||||
|
</appSettings>
|
||||||
|
<system.web>
|
||||||
|
<httpHandlers>
|
||||||
|
<!-- iis6 - for any request in this location, return via managed static file handler -->
|
||||||
|
<add path="*" verb="*" type="System.Web.StaticFileHandler" />
|
||||||
|
</httpHandlers>
|
||||||
|
</system.web>
|
||||||
|
<system.webServer>
|
||||||
|
<handlers accessPolicy="Script,Read">
|
||||||
|
<!--
|
||||||
|
iis7 - for any request to a file exists on disk, return it via native http module.
|
||||||
|
accessPolicy 'Script' is to allow for a managed 404 page.
|
||||||
|
-->
|
||||||
|
<add name="StaticFile" path="*" verb="*" modules="StaticFileModule" preCondition="integratedMode" resourceType="File" requireAccess="Read" />
|
||||||
|
</handlers>
|
||||||
|
</system.webServer>
|
||||||
|
</configuration>
|
@@ -0,0 +1,37 @@
|
|||||||
|
@model Orchard.Warmup.Models.WarmupSettingsPart
|
||||||
|
@using Orchard.Utility.Extensions;
|
||||||
|
@using Orchard.Warmup.Models;
|
||||||
|
|
||||||
|
@{ Layout.Title = T("Warmup").ToString(); }
|
||||||
|
|
||||||
|
@using (Html.BeginFormAntiForgeryPost()) {
|
||||||
|
@Html.ValidationSummary()
|
||||||
|
|
||||||
|
<fieldset>
|
||||||
|
<div>
|
||||||
|
<label for="@Html.FieldIdFor(m => m.Urls)">@T("Urls for which static warmup pages will be generated")</label>
|
||||||
|
@Html.TextAreaFor(m => m.Urls, new { @class = "textMedium" })
|
||||||
|
<span class="hint">@T("This must be a set of relative paths, e.g., /, /About")</span>
|
||||||
|
</div>
|
||||||
|
</fieldset>
|
||||||
|
<fieldset>
|
||||||
|
<div>
|
||||||
|
@Html.EditorFor(m => m.Scheduled)
|
||||||
|
<label class="forcheckbox" for="@Html.FieldIdFor(m => m.Scheduled)">@T("Generate warmup pages periodically")</label>
|
||||||
|
</div>
|
||||||
|
<div data-controllerid="@Html.FieldIdFor(m => m.Scheduled)">
|
||||||
|
@T("Every")
|
||||||
|
@Html.TextBoxFor(m => m.Delay, new { @class = "text-small" })
|
||||||
|
@T("minutes")
|
||||||
|
@Html.ValidationMessage("Delay", "*")
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
@Html.EditorFor(m => m.OnPublish)
|
||||||
|
<label class="forcheckbox" for="@Html.FieldIdFor(m => m.OnPublish)">@T("Generate warmup pages each time some content is published")</label>
|
||||||
|
</div>
|
||||||
|
</fieldset>
|
||||||
|
<fieldset>
|
||||||
|
<button class="primaryAction" name="submit" value="@T("Save")" type="submit">@T("Save")</button>
|
||||||
|
<button class="primaryAction" name="submit.Generate" value="@T("Save and generate")" type="submit">@T("Save and generate")</button>
|
||||||
|
</fieldset>
|
||||||
|
}
|
@@ -0,0 +1,28 @@
|
|||||||
|
@model Orchard.Warmup.Models.WarmupSettingsPartRecord
|
||||||
|
@using Orchard.Utility.Extensions;
|
||||||
|
@using Orchard.Warmup.Models;
|
||||||
|
|
||||||
|
<fieldset>
|
||||||
|
<legend>@T("Warmup")</legend>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<label for="@Html.FieldIdFor(m => m.Urls)">@T("Urls for which static warm up pages will be generated")</label>
|
||||||
|
@Html.TextAreaFor(m => m.Urls, new { @class = "textMedium" })
|
||||||
|
@Html.ValidationMessage("Urls", "*")
|
||||||
|
<span class="hint">@T("This must be a set of virtual paths, e.g., ~/, ~/About")</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
@Html.EditorFor(m => m.Scheduled)
|
||||||
|
<label class="forcheckbox" for="@Html.FieldIdFor(m => m.Scheduled)">@T("Generate warmup pages periodically")</label>
|
||||||
|
</div>
|
||||||
|
<div data-controllerid="@Html.FieldIdFor(m => m.Scheduled)>
|
||||||
|
<label for="@Html.FieldIdFor(m => m.Urls)">@T("Delay to generate pages")</label>
|
||||||
|
@Html.TextBoxFor(m => m.Delay, new { @class = "" }) @T("minutes")
|
||||||
|
@Html.ValidationMessage("Delay", "*")
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
@Html.EditorFor(m => m.OnPublish)
|
||||||
|
<label class="forcheckbox" for="@Html.FieldIdFor(m => m.OnPublish)">@T("Generate warmup pages each time some content is published")</label>
|
||||||
|
</div>
|
||||||
|
</fieldset>
|
41
src/Orchard.Web/Modules/Orchard.Warmup/Views/Web.config
Normal file
41
src/Orchard.Web/Modules/Orchard.Warmup/Views/Web.config
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
<?xml version="1.0"?>
|
||||||
|
<configuration>
|
||||||
|
<appSettings>
|
||||||
|
<add key="webpages:Enabled" value="false" />
|
||||||
|
</appSettings>
|
||||||
|
<system.web>
|
||||||
|
<httpHandlers>
|
||||||
|
</httpHandlers>
|
||||||
|
|
||||||
|
<!--
|
||||||
|
Enabling request validation in view pages would cause validation to occur
|
||||||
|
after the input has already been processed by the controller. By default
|
||||||
|
MVC performs request validation before a controller processes the input.
|
||||||
|
To change this behavior apply the ValidateInputAttribute to a
|
||||||
|
controller or action.
|
||||||
|
-->
|
||||||
|
<pages
|
||||||
|
validateRequest="false"
|
||||||
|
pageParserFilterType="System.Web.Mvc.ViewTypeParserFilter, System.Web.Mvc, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL"
|
||||||
|
pageBaseType="System.Web.Mvc.ViewPage, System.Web.Mvc, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL"
|
||||||
|
userControlBaseType="System.Web.Mvc.ViewUserControl, System.Web.Mvc, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
|
||||||
|
<controls>
|
||||||
|
<add assembly="System.Web.Mvc, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL" namespace="System.Web.Mvc" tagPrefix="mvc" />
|
||||||
|
</controls>
|
||||||
|
</pages>
|
||||||
|
</system.web>
|
||||||
|
|
||||||
|
<system.webServer>
|
||||||
|
<validation validateIntegratedModeConfiguration="false"/>
|
||||||
|
<handlers>
|
||||||
|
</handlers>
|
||||||
|
</system.webServer>
|
||||||
|
<runtime>
|
||||||
|
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
|
||||||
|
<dependentAssembly>
|
||||||
|
<assemblyIdentity name="System.Web.Mvc" publicKeyToken="31bf3856ad364e35" />
|
||||||
|
<bindingRedirect oldVersion="2.0.0.0" newVersion="3.0.0.0" />
|
||||||
|
</dependentAssembly>
|
||||||
|
</assemblyBinding>
|
||||||
|
</runtime>
|
||||||
|
</configuration>
|
39
src/Orchard.Web/Modules/Orchard.Warmup/Web.config
Normal file
39
src/Orchard.Web/Modules/Orchard.Warmup/Web.config
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
<?xml version="1.0"?>
|
||||||
|
<configuration>
|
||||||
|
|
||||||
|
<configSections>
|
||||||
|
<sectionGroup name="system.web.webPages.razor" type="System.Web.WebPages.Razor.Configuration.RazorWebSectionGroup, System.Web.WebPages.Razor, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35">
|
||||||
|
<remove name="host" />
|
||||||
|
<remove name="pages" />
|
||||||
|
<section name="host" type="System.Web.WebPages.Razor.Configuration.HostSection, System.Web.WebPages.Razor, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" requirePermission="false" />
|
||||||
|
<section name="pages" type="System.Web.WebPages.Razor.Configuration.RazorPagesSection, System.Web.WebPages.Razor, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" requirePermission="false" />
|
||||||
|
</sectionGroup>
|
||||||
|
</configSections>
|
||||||
|
|
||||||
|
<system.web.webPages.razor>
|
||||||
|
<host factoryType="System.Web.Mvc.MvcWebRazorHostFactory, System.Web.Mvc, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" />
|
||||||
|
<pages pageBaseType="Orchard.Mvc.ViewEngines.Razor.WebViewPage">
|
||||||
|
<namespaces>
|
||||||
|
<add namespace="System.Web.Mvc" />
|
||||||
|
<add namespace="System.Web.Mvc.Ajax" />
|
||||||
|
<add namespace="System.Web.Mvc.Html" />
|
||||||
|
<add namespace="System.Web.Routing" />
|
||||||
|
<add namespace="System.Linq"/>
|
||||||
|
<add namespace="System.Collections.Generic"/>
|
||||||
|
<add namespace="Orchard.Mvc.Html"/>
|
||||||
|
</namespaces>
|
||||||
|
</pages>
|
||||||
|
</system.web.webPages.razor>
|
||||||
|
|
||||||
|
<system.web>
|
||||||
|
<compilation targetFramework="4.0">
|
||||||
|
<assemblies>
|
||||||
|
<add assembly="System.Web.Abstractions, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/>
|
||||||
|
<add assembly="System.Web.Routing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/>
|
||||||
|
<add assembly="System.Data.Linq, Version=4.0.0.0, Culture=neutral, PublicKeyToken=B77A5C561934E089"/>
|
||||||
|
<add assembly="System.Web.Mvc, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" />
|
||||||
|
</assemblies>
|
||||||
|
</compilation>
|
||||||
|
</system.web>
|
||||||
|
|
||||||
|
</configuration>
|
@@ -18,6 +18,7 @@
|
|||||||
<OldToolsVersion>3.5</OldToolsVersion>
|
<OldToolsVersion>3.5</OldToolsVersion>
|
||||||
<UpgradeBackupLocation />
|
<UpgradeBackupLocation />
|
||||||
<TargetFrameworkProfile />
|
<TargetFrameworkProfile />
|
||||||
|
<UseIISExpress>false</UseIISExpress>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||||
<DebugSymbols>true</DebugSymbols>
|
<DebugSymbols>true</DebugSymbols>
|
||||||
@@ -48,11 +49,68 @@
|
|||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Content Include="Scripts\jquery-1.5.1.js" />
|
<Content Include="Scripts\jquery-1.5.1.js" />
|
||||||
<Content Include="Scripts\jquery-1.5.1.min.js" />
|
<Content Include="Scripts\jquery-1.5.1.min.js" />
|
||||||
|
<Content Include="Scripts\jquery.effects.blind.js" />
|
||||||
|
<Content Include="Scripts\jquery.effects.blind.min.js" />
|
||||||
|
<Content Include="Scripts\jquery.effects.bounce.js" />
|
||||||
|
<Content Include="Scripts\jquery.effects.bounce.min.js" />
|
||||||
|
<Content Include="Scripts\jquery.effects.clip.js" />
|
||||||
|
<Content Include="Scripts\jquery.effects.clip.min.js" />
|
||||||
|
<Content Include="Scripts\jquery.effects.core.js" />
|
||||||
|
<Content Include="Scripts\jquery.effects.core.min.js" />
|
||||||
|
<Content Include="Scripts\jquery.effects.drop.js" />
|
||||||
|
<Content Include="Scripts\jquery.effects.drop.min.js" />
|
||||||
|
<Content Include="Scripts\jquery.effects.explode.js" />
|
||||||
|
<Content Include="Scripts\jquery.effects.explode.min.js" />
|
||||||
|
<Content Include="Scripts\jquery.effects.fade.js" />
|
||||||
|
<Content Include="Scripts\jquery.effects.fade.min.js" />
|
||||||
|
<Content Include="Scripts\jquery.effects.fold.js" />
|
||||||
|
<Content Include="Scripts\jquery.effects.fold.min.js" />
|
||||||
|
<Content Include="Scripts\jquery.effects.highlight.js" />
|
||||||
|
<Content Include="Scripts\jquery.effects.highlight.min.js" />
|
||||||
|
<Content Include="Scripts\jquery.effects.pulsate.js" />
|
||||||
|
<Content Include="Scripts\jquery.effects.pulsate.min.js" />
|
||||||
|
<Content Include="Scripts\jquery.effects.scale.js" />
|
||||||
|
<Content Include="Scripts\jquery.effects.scale.min.js" />
|
||||||
|
<Content Include="Scripts\jquery.effects.shake.js" />
|
||||||
|
<Content Include="Scripts\jquery.effects.shake.min.js" />
|
||||||
|
<Content Include="Scripts\jquery.effects.slide.js" />
|
||||||
|
<Content Include="Scripts\jquery.effects.slide.min.js" />
|
||||||
|
<Content Include="Scripts\jquery.effects.transfer.js" />
|
||||||
|
<Content Include="Scripts\jquery.effects.transfer.min.js" />
|
||||||
|
<Content Include="Scripts\jquery.ui.accordion.js" />
|
||||||
|
<Content Include="Scripts\jquery.ui.accordion.min.js" />
|
||||||
|
<Content Include="Scripts\jquery.ui.autocomplete.js" />
|
||||||
|
<Content Include="Scripts\jquery.ui.autocomplete.min.js" />
|
||||||
|
<Content Include="Scripts\jquery.ui.button.js" />
|
||||||
|
<Content Include="Scripts\jquery.ui.button.min.js" />
|
||||||
<Content Include="Scripts\jquery.ui.core.js" />
|
<Content Include="Scripts\jquery.ui.core.js" />
|
||||||
|
<Content Include="Scripts\jquery.ui.core.min.js" />
|
||||||
<Content Include="Scripts\jquery.ui.datepicker.js" />
|
<Content Include="Scripts\jquery.ui.datepicker.js" />
|
||||||
|
<Content Include="Scripts\jquery.ui.datepicker.min.js" />
|
||||||
|
<Content Include="Scripts\jquery.ui.dialog.js" />
|
||||||
|
<Content Include="Scripts\jquery.ui.dialog.min.js" />
|
||||||
|
<Content Include="Scripts\jquery.ui.draggable.js" />
|
||||||
|
<Content Include="Scripts\jquery.ui.draggable.min.js" />
|
||||||
|
<Content Include="Scripts\jquery.ui.droppable.js" />
|
||||||
|
<Content Include="Scripts\jquery.ui.droppable.min.js" />
|
||||||
|
<Content Include="Scripts\jquery.ui.mouse.js" />
|
||||||
|
<Content Include="Scripts\jquery.ui.mouse.min.js" />
|
||||||
|
<Content Include="Scripts\jquery.ui.position.js" />
|
||||||
|
<Content Include="Scripts\jquery.ui.position.min.js" />
|
||||||
|
<Content Include="Scripts\jquery.ui.progressbar.js" />
|
||||||
|
<Content Include="Scripts\jquery.ui.progressbar.min.js" />
|
||||||
|
<Content Include="Scripts\jquery.ui.resizable.js" />
|
||||||
|
<Content Include="Scripts\jquery.ui.resizable.min.js" />
|
||||||
|
<Content Include="Scripts\jquery.ui.selectable.js" />
|
||||||
|
<Content Include="Scripts\jquery.ui.selectable.min.js" />
|
||||||
|
<Content Include="Scripts\jquery.ui.slider.js" />
|
||||||
|
<Content Include="Scripts\jquery.ui.slider.min.js" />
|
||||||
|
<Content Include="Scripts\jquery.ui.sortable.js" />
|
||||||
|
<Content Include="Scripts\jquery.ui.sortable.min.js" />
|
||||||
<Content Include="Scripts\jquery.ui.tabs.js" />
|
<Content Include="Scripts\jquery.ui.tabs.js" />
|
||||||
<Content Include="Scripts\jquery.ui.tabs.min.js" />
|
<Content Include="Scripts\jquery.ui.tabs.min.js" />
|
||||||
<Content Include="Scripts\jquery.ui.widget.js" />
|
<Content Include="Scripts\jquery.ui.widget.js" />
|
||||||
|
<Content Include="Scripts\jquery.ui.widget.min.js" />
|
||||||
<Content Include="Scripts\jquery.utils.js" />
|
<Content Include="Scripts\jquery.utils.js" />
|
||||||
<Content Include="Scripts\ui.timepickr.js" />
|
<Content Include="Scripts\ui.timepickr.js" />
|
||||||
<Content Include="Styles\images\ui-bg_flat_0_aaaaaa_40x100.png" />
|
<Content Include="Styles\images\ui-bg_flat_0_aaaaaa_40x100.png" />
|
||||||
|
@@ -18,6 +18,7 @@
|
|||||||
<OldToolsVersion>3.5</OldToolsVersion>
|
<OldToolsVersion>3.5</OldToolsVersion>
|
||||||
<UpgradeBackupLocation />
|
<UpgradeBackupLocation />
|
||||||
<TargetFrameworkProfile />
|
<TargetFrameworkProfile />
|
||||||
|
<UseIISExpress>true</UseIISExpress>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||||
<DebugSymbols>true</DebugSymbols>
|
<DebugSymbols>true</DebugSymbols>
|
||||||
@@ -199,8 +200,7 @@
|
|||||||
<AutoAssignPort>False</AutoAssignPort>
|
<AutoAssignPort>False</AutoAssignPort>
|
||||||
<DevelopmentServerPort>30320</DevelopmentServerPort>
|
<DevelopmentServerPort>30320</DevelopmentServerPort>
|
||||||
<DevelopmentServerVPath>/OrchardLocal</DevelopmentServerVPath>
|
<DevelopmentServerVPath>/OrchardLocal</DevelopmentServerVPath>
|
||||||
<IISUrl>
|
<IISUrl>http://localhost:30320/OrchardLocal</IISUrl>
|
||||||
</IISUrl>
|
|
||||||
<NTLMAuthentication>False</NTLMAuthentication>
|
<NTLMAuthentication>False</NTLMAuthentication>
|
||||||
<UseCustomServer>False</UseCustomServer>
|
<UseCustomServer>False</UseCustomServer>
|
||||||
<CustomServerUrl>
|
<CustomServerUrl>
|
||||||
|
@@ -112,6 +112,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Orchard.Recipes", "Orchard.
|
|||||||
EndProject
|
EndProject
|
||||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Orchard.ImportExport", "Orchard.Web\Modules\Orchard.ImportExport\Orchard.ImportExport.csproj", "{FE5C5947-D2D5-42C5-992A-13D672946135}"
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Orchard.ImportExport", "Orchard.Web\Modules\Orchard.ImportExport\Orchard.ImportExport.csproj", "{FE5C5947-D2D5-42C5-992A-13D672946135}"
|
||||||
EndProject
|
EndProject
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Orchard.Warmup", "Orchard.Web\Modules\Orchard.WarmUp\Orchard.Warmup.csproj", "{9CD5C81F-5828-4384-8474-2E2BE71D5EDD}"
|
||||||
|
EndProject
|
||||||
Global
|
Global
|
||||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
CodeCoverage|Any CPU = CodeCoverage|Any CPU
|
CodeCoverage|Any CPU = CodeCoverage|Any CPU
|
||||||
@@ -595,6 +597,13 @@ Global
|
|||||||
{FE5C5947-D2D5-42C5-992A-13D672946135}.FxCop|Any CPU.Build.0 = Release|Any CPU
|
{FE5C5947-D2D5-42C5-992A-13D672946135}.FxCop|Any CPU.Build.0 = Release|Any CPU
|
||||||
{FE5C5947-D2D5-42C5-992A-13D672946135}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
{FE5C5947-D2D5-42C5-992A-13D672946135}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
{FE5C5947-D2D5-42C5-992A-13D672946135}.Release|Any CPU.Build.0 = Release|Any CPU
|
{FE5C5947-D2D5-42C5-992A-13D672946135}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{9CD5C81F-5828-4384-8474-2E2BE71D5EDD}.CodeCoverage|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{9CD5C81F-5828-4384-8474-2E2BE71D5EDD}.Coverage|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{9CD5C81F-5828-4384-8474-2E2BE71D5EDD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{9CD5C81F-5828-4384-8474-2E2BE71D5EDD}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{9CD5C81F-5828-4384-8474-2E2BE71D5EDD}.FxCop|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{9CD5C81F-5828-4384-8474-2E2BE71D5EDD}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{9CD5C81F-5828-4384-8474-2E2BE71D5EDD}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
GlobalSection(SolutionProperties) = preSolution
|
GlobalSection(SolutionProperties) = preSolution
|
||||||
HideSolutionNode = FALSE
|
HideSolutionNode = FALSE
|
||||||
@@ -634,6 +643,7 @@ Global
|
|||||||
{43D0EC0B-1955-4566-8D31-7B9102DA1703} = {E9C9F120-07BA-4DFB-B9C3-3AFB9D44C9D5}
|
{43D0EC0B-1955-4566-8D31-7B9102DA1703} = {E9C9F120-07BA-4DFB-B9C3-3AFB9D44C9D5}
|
||||||
{FC1D74E8-7A4D-48F4-83DE-95C6173780C4} = {E9C9F120-07BA-4DFB-B9C3-3AFB9D44C9D5}
|
{FC1D74E8-7A4D-48F4-83DE-95C6173780C4} = {E9C9F120-07BA-4DFB-B9C3-3AFB9D44C9D5}
|
||||||
{FE5C5947-D2D5-42C5-992A-13D672946135} = {E9C9F120-07BA-4DFB-B9C3-3AFB9D44C9D5}
|
{FE5C5947-D2D5-42C5-992A-13D672946135} = {E9C9F120-07BA-4DFB-B9C3-3AFB9D44C9D5}
|
||||||
|
{9CD5C81F-5828-4384-8474-2E2BE71D5EDD} = {E9C9F120-07BA-4DFB-B9C3-3AFB9D44C9D5}
|
||||||
{ABC826D4-2FA1-4F2F-87DE-E6095F653810} = {74E681ED-FECC-4034-B9BD-01B0BB1BDECA}
|
{ABC826D4-2FA1-4F2F-87DE-E6095F653810} = {74E681ED-FECC-4034-B9BD-01B0BB1BDECA}
|
||||||
{F112851D-B023-4746-B6B1-8D2E5AD8F7AA} = {74E681ED-FECC-4034-B9BD-01B0BB1BDECA}
|
{F112851D-B023-4746-B6B1-8D2E5AD8F7AA} = {74E681ED-FECC-4034-B9BD-01B0BB1BDECA}
|
||||||
{6CB3EB30-F725-45C0-9742-42599BA8E8D2} = {74E681ED-FECC-4034-B9BD-01B0BB1BDECA}
|
{6CB3EB30-F725-45C0-9742-42599BA8E8D2} = {74E681ED-FECC-4034-B9BD-01B0BB1BDECA}
|
||||||
|
8
src/Orchard/Environment/Warmup/StartupResult.cs
Normal file
8
src/Orchard/Environment/Warmup/StartupResult.cs
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
using System;
|
||||||
|
|
||||||
|
namespace Orchard.Environment.Warmup {
|
||||||
|
public class StartupResult {
|
||||||
|
public IOrchardHost Host { get; set; }
|
||||||
|
public Exception Error { get; set; }
|
||||||
|
}
|
||||||
|
}
|
19
src/Orchard/Environment/Warmup/WarmupUtility.cs
Normal file
19
src/Orchard/Environment/Warmup/WarmupUtility.cs
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
using System;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Text.RegularExpressions;
|
||||||
|
|
||||||
|
namespace Orchard.Environment.Warmup {
|
||||||
|
public static class WarmupUtility {
|
||||||
|
private const string EncodingPattern = "[^a-z0-9]";
|
||||||
|
|
||||||
|
public static string EncodeUrl(string url) {
|
||||||
|
if(String.IsNullOrWhiteSpace(url)) {
|
||||||
|
throw new ArgumentException("url can't be empty");
|
||||||
|
}
|
||||||
|
|
||||||
|
return Regex.Replace(url.ToLower(), EncodingPattern, m => "_" + Encoding.UTF8.GetBytes(m.Value).Select(b => b.ToString("X")).Aggregate((a, b) => a + b));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
@@ -58,8 +58,7 @@ namespace Orchard.FileSystems.LockFile {
|
|||||||
var content = _appDataFolder.ReadFile(path);
|
var content = _appDataFolder.ReadFile(path);
|
||||||
|
|
||||||
DateTime creationUtc;
|
DateTime creationUtc;
|
||||||
if (DateTime.TryParse(content, out creationUtc))
|
if (DateTime.TryParse(content, out creationUtc)) {
|
||||||
{
|
|
||||||
// if expired the file is not removed
|
// if expired the file is not removed
|
||||||
// it should be automatically as there is a finalizer in LockFile
|
// it should be automatically as there is a finalizer in LockFile
|
||||||
// or the next taker can do it, unless it also fails, again
|
// or the next taker can do it, unless it also fails, again
|
||||||
|
@@ -183,6 +183,8 @@
|
|||||||
<Compile Include="Environment\IAssemblyLoader.cs" />
|
<Compile Include="Environment\IAssemblyLoader.cs" />
|
||||||
<Compile Include="Environment\HostComponentsConfigModule.cs" />
|
<Compile Include="Environment\HostComponentsConfigModule.cs" />
|
||||||
<Compile Include="Environment\ViewsBackgroundCompilation.cs" />
|
<Compile Include="Environment\ViewsBackgroundCompilation.cs" />
|
||||||
|
<Compile Include="Environment\Warmup\StartupResult.cs" />
|
||||||
|
<Compile Include="Environment\Warmup\WarmupUtility.cs" />
|
||||||
<Compile Include="Environment\WorkContextImplementation.cs" />
|
<Compile Include="Environment\WorkContextImplementation.cs" />
|
||||||
<Compile Include="Environment\WorkContextModule.cs" />
|
<Compile Include="Environment\WorkContextModule.cs" />
|
||||||
<Compile Include="Environment\WorkContextProperty.cs" />
|
<Compile Include="Environment\WorkContextProperty.cs" />
|
||||||
|
@@ -13,5 +13,6 @@ namespace Orchard.Settings {
|
|||||||
string SiteCulture { get; set; }
|
string SiteCulture { get; set; }
|
||||||
ResourceDebugMode ResourceDebugMode { get; set; }
|
ResourceDebugMode ResourceDebugMode { get; set; }
|
||||||
int PageSize { get; set; }
|
int PageSize { get; set; }
|
||||||
|
string BaseUrl { get; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user