From 2dda95fbf4ae4da9368c226939e2d1638470fb6e Mon Sep 17 00:00:00 2001 From: Sebastien Ros Date: Fri, 3 Jun 2011 17:43:20 -0700 Subject: [PATCH 1/2] #17828: Fixing background covering links in admin menu Work Items: 17828 --HG-- branch : 1.x --- src/Orchard.Web/Themes/TheAdmin/Styles/site.css | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Orchard.Web/Themes/TheAdmin/Styles/site.css b/src/Orchard.Web/Themes/TheAdmin/Styles/site.css index 3fda91fa6..7c832c20d 100644 --- a/src/Orchard.Web/Themes/TheAdmin/Styles/site.css +++ b/src/Orchard.Web/Themes/TheAdmin/Styles/site.css @@ -184,7 +184,6 @@ hr {border:0; height:1px; color:#e4e5e6; background-color:#e4e5e6;} #footer { clear:both; height:70px; - position:relative; margin-top:-70px; /*Top margin set negative px of footer height*/ background:url(images/vinesBackgroundBottom.gif) no-repeat bottom left; } From a605420d1152fbd5c75b8e8b7bc4d5313c415bbd Mon Sep 17 00:00:00 2001 From: Sebastien Ros Date: Fri, 3 Jun 2011 17:44:46 -0700 Subject: [PATCH 2/2] Updating Warmup UI --HG-- branch : 1.x --- .../Warmup/WarmupUpdaterTests.cs | 120 ++++++++++++-- .../Parts.Settings.SiteSettingsPart.cshtml | 12 +- .../Modules/Orchard.Warmup/AdminMenu.cs | 5 +- .../Content/Admin/images/offline.gif | Bin 0 -> 293 bytes .../Content/Admin/images/online.gif | Bin 0 -> 405 bytes .../Modules/Orchard.Warmup/Content/Web.config | 21 +++ .../Controllers/AdminController.cs | 52 +++--- .../Orchard.Warmup/Models/ReportEntry.cs | 10 ++ .../Orchard.Warmup/Orchard.Warmup.csproj | 12 +- .../Services/IWarmupReportManager.cs | 9 + .../Services/WarmupReportManager.cs | 58 +++++++ .../Orchard.Warmup/Services/WarmupUpdater.cs | 155 ++++++++++++------ .../Styles/orchard-warmup-admin.css | 8 + .../ViewModels/WarmupViewModel.cs | 9 + .../Orchard.Warmup/Views/Admin/Index.cshtml | 60 +++++-- .../Parts.Warmup.SiteSettings.cshtml | 28 ---- 16 files changed, 416 insertions(+), 143 deletions(-) create mode 100644 src/Orchard.Web/Modules/Orchard.Warmup/Content/Admin/images/offline.gif create mode 100644 src/Orchard.Web/Modules/Orchard.Warmup/Content/Admin/images/online.gif create mode 100644 src/Orchard.Web/Modules/Orchard.Warmup/Content/Web.config create mode 100644 src/Orchard.Web/Modules/Orchard.Warmup/Models/ReportEntry.cs create mode 100644 src/Orchard.Web/Modules/Orchard.Warmup/Services/IWarmupReportManager.cs create mode 100644 src/Orchard.Web/Modules/Orchard.Warmup/Services/WarmupReportManager.cs create mode 100644 src/Orchard.Web/Modules/Orchard.Warmup/Styles/orchard-warmup-admin.css create mode 100644 src/Orchard.Web/Modules/Orchard.Warmup/ViewModels/WarmupViewModel.cs delete mode 100644 src/Orchard.Web/Modules/Orchard.Warmup/Views/EditorTemplates/Parts.Warmup.SiteSettings.cshtml diff --git a/src/Orchard.Tests.Modules/Warmup/WarmupUpdaterTests.cs b/src/Orchard.Tests.Modules/Warmup/WarmupUpdaterTests.cs index 017ff114f..1984cd2bf 100644 --- a/src/Orchard.Tests.Modules/Warmup/WarmupUpdaterTests.cs +++ b/src/Orchard.Tests.Modules/Warmup/WarmupUpdaterTests.cs @@ -6,6 +6,7 @@ using System.Xml; using Autofac; using Moq; using NUnit.Framework; +using Orchard.Environment.Configuration; using Orchard.Environment.Warmup; using Orchard.FileSystems.AppData; using Orchard.FileSystems.LockFile; @@ -26,11 +27,13 @@ namespace Orchard.Tests.Modules.Warmup { private Mock _webDownloader; private IOrchardServices _orchardServices; private WarmupSettingsPart _settings; + private IWarmupReportManager _reportManager; private readonly string _basePath = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName()); private string _warmupFilename, _lockFilename; private const string WarmupFolder = "Warmup"; + private const string TenantFolder = "Sites/Default"; [TestFixtureTearDown] public void Clean() { @@ -60,15 +63,18 @@ namespace Orchard.Tests.Modules.Warmup { builder.RegisterType().As(); builder.RegisterType().As(); builder.RegisterType().As(); + builder.RegisterType().As(); + builder.RegisterInstance(new ShellSettings { Name = "Default" }).As(); builder.RegisterInstance(_clock = new StubClock()).As(); builder.RegisterInstance(_webDownloader.Object).As(); _container = builder.Build(); _lockFileManager = _container.Resolve(); _warmupUpdater = _container.Resolve(); + _reportManager = _container.Resolve(); - _warmupFilename = _appDataFolder.Combine(WarmupFolder, "warmup.txt"); - _lockFilename = _appDataFolder.Combine(WarmupFolder, "warmup.txt.lock"); + _warmupFilename = _appDataFolder.Combine(TenantFolder, "warmup.txt"); + _lockFilename = _appDataFolder.Combine(TenantFolder, "warmup.txt.lock"); } [Test] @@ -91,8 +97,7 @@ namespace Orchard.Tests.Modules.Warmup { _lockFileManager.TryAcquireLock(_lockFilename, ref lockFile); using(lockFile) { _warmupUpdater.Generate(); - // warmup file + lock file - Assert.That(_appDataFolder.ListFiles(WarmupFolder).Count(), Is.EqualTo(2)); + Assert.That(_appDataFolder.ListFiles(WarmupFolder).Count(), Is.EqualTo(0)); } _warmupUpdater.Generate(); @@ -114,11 +119,14 @@ namespace Orchard.Tests.Modules.Warmup { _warmupUpdater.Generate(); var files = _appDataFolder.ListFiles(WarmupFolder).ToList(); - // warmup + content files - Assert.That(files, Has.Some.Matches(x => x == _appDataFolder.Combine(WarmupFolder, "warmup.txt"))); Assert.That(files, Has.Some.Matches(x => x == _appDataFolder.Combine(WarmupFolder, WarmupUtility.EncodeUrl("http://orchardproject.net")))); Assert.That(files, Has.Some.Matches(x => x == _appDataFolder.Combine(WarmupFolder, WarmupUtility.EncodeUrl("http://orchardproject.net/About")))); + files = _appDataFolder.ListFiles(TenantFolder).ToList(); + + Assert.That(files, Has.Some.Matches(x => x == _appDataFolder.Combine(TenantFolder, "warmup.txt"))); + Assert.That(files, Has.Some.Matches(x => x == _appDataFolder.Combine(TenantFolder, "warmup.xml"))); + 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"))); @@ -141,8 +149,6 @@ namespace Orchard.Tests.Modules.Warmup { _warmupUpdater.Generate(); var files = _appDataFolder.ListFiles(WarmupFolder).ToList(); - // warmup + content file - Assert.That(files, Has.Some.Matches(x => x == _appDataFolder.Combine(WarmupFolder, "warmup.txt"))); Assert.That(files, Has.Some.Matches(x => x == _appDataFolder.Combine(WarmupFolder, WarmupUtility.EncodeUrl("http://orchardproject.net")))); Assert.That(files, Has.None.Matches(x => x == _appDataFolder.Combine(WarmupFolder, WarmupUtility.EncodeUrl("http://orchardproject.net/About")))); } @@ -158,9 +164,7 @@ namespace Orchard.Tests.Modules.Warmup { _warmupUpdater.Generate(); var files = _appDataFolder.ListFiles(WarmupFolder).ToList(); - // warmup + content file - Assert.That(files.Count, Is.EqualTo(2)); - Assert.That(files, Has.Some.Matches(x => x == _appDataFolder.Combine(WarmupFolder, "warmup.txt"))); + Assert.That(files.Count, Is.EqualTo(1)); Assert.That(files, Has.Some.Matches(x => x == _appDataFolder.Combine(WarmupFolder, WarmupUtility.EncodeUrl("http://orchardproject.net")))); } @@ -258,8 +262,6 @@ namespace Orchard.Tests.Modules.Warmup { _warmupUpdater.Generate(); var files = _appDataFolder.ListFiles(WarmupFolder).ToList(); - // warmup + content files - Assert.That(files, Has.Some.Matches(x => x == _appDataFolder.Combine(WarmupFolder, "warmup.txt"))); Assert.That(files, Has.Some.Matches(x => x == _appDataFolder.Combine(WarmupFolder, WarmupUtility.EncodeUrl("http://www.orchardproject.net")))); Assert.That(files, Has.Some.Matches(x => x == _appDataFolder.Combine(WarmupFolder, WarmupUtility.EncodeUrl("http://www.orchardproject.net/About")))); Assert.That(files, Has.Some.Matches(x => x == _appDataFolder.Combine(WarmupFolder, WarmupUtility.EncodeUrl("http://orchardproject.net")))); @@ -276,6 +278,96 @@ namespace Orchard.Tests.Modules.Warmup { Assert.That(aboutcontent, Is.EqualTo("Bar")); Assert.That(wwwaboutcontent, Is.EqualTo("Bar")); } - + + [Test] + public void ReportIsCreated() { + _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(); + + Assert.That(files, Has.Some.Matches(x => x == _appDataFolder.Combine(WarmupFolder, WarmupUtility.EncodeUrl("http://www.orchardproject.net")))); + Assert.That(files, Has.Some.Matches(x => x == _appDataFolder.Combine(WarmupFolder, WarmupUtility.EncodeUrl("http://www.orchardproject.net/About")))); + Assert.That(files, Has.Some.Matches(x => x == _appDataFolder.Combine(WarmupFolder, WarmupUtility.EncodeUrl("http://orchardproject.net")))); + Assert.That(files, Has.Some.Matches(x => x == _appDataFolder.Combine(WarmupFolder, WarmupUtility.EncodeUrl("http://orchardproject.net/About")))); + + var report = _reportManager.Read().ToList(); + + Assert.That(report.Count(), Is.EqualTo(2)); + Assert.That(report, Has.Some.Matches(x => x.RelativeUrl == "/")); + Assert.That(report, Has.Some.Matches(x => x.RelativeUrl == "/About")); + } + + [Test] + public void ShouldNotDeleteOtherFiles() { + _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 }); + + // Create a static file in the warmup folder + _appDataFolder.CreateFile(_appDataFolder.Combine(WarmupFolder, "foo.txt"), "Foo"); + + _warmupUpdater.Generate(); + var files = _appDataFolder.ListFiles(WarmupFolder).ToList(); + + Assert.That(files, Has.Some.Matches(x => x == _appDataFolder.Combine(WarmupFolder, "foo.txt"))); + + Assert.That(files, Has.Some.Matches(x => x == _appDataFolder.Combine(WarmupFolder, WarmupUtility.EncodeUrl("http://www.orchardproject.net")))); + Assert.That(files, Has.Some.Matches(x => x == _appDataFolder.Combine(WarmupFolder, WarmupUtility.EncodeUrl("http://www.orchardproject.net/About")))); + Assert.That(files, Has.Some.Matches(x => x == _appDataFolder.Combine(WarmupFolder, WarmupUtility.EncodeUrl("http://orchardproject.net")))); + Assert.That(files, Has.Some.Matches(x => x == _appDataFolder.Combine(WarmupFolder, WarmupUtility.EncodeUrl("http://orchardproject.net/About")))); + } + + + [Test] + public void ClearingUrlsShouldDeleteContent() { + _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(); + + Assert.That(files, Has.Some.Matches(x => x == _appDataFolder.Combine(WarmupFolder, WarmupUtility.EncodeUrl("http://www.orchardproject.net")))); + Assert.That(files, Has.Some.Matches(x => x == _appDataFolder.Combine(WarmupFolder, WarmupUtility.EncodeUrl("http://www.orchardproject.net/About")))); + Assert.That(files, Has.Some.Matches(x => x == _appDataFolder.Combine(WarmupFolder, WarmupUtility.EncodeUrl("http://orchardproject.net")))); + Assert.That(files, Has.Some.Matches(x => x == _appDataFolder.Combine(WarmupFolder, WarmupUtility.EncodeUrl("http://orchardproject.net/About")))); + + _settings.Urls = @""; + + _warmupUpdater.Generate(); + files = _appDataFolder.ListFiles(WarmupFolder).ToList(); + + Assert.That(files.Count, Is.EqualTo(0)); + } } } diff --git a/src/Orchard.Web/Core/Settings/Views/EditorTemplates/Parts.Settings.SiteSettingsPart.cshtml b/src/Orchard.Web/Core/Settings/Views/EditorTemplates/Parts.Settings.SiteSettingsPart.cshtml index d41a5e723..be3daaadb 100644 --- a/src/Orchard.Web/Core/Settings/Views/EditorTemplates/Parts.Settings.SiteSettingsPart.cshtml +++ b/src/Orchard.Web/Core/Settings/Views/EditorTemplates/Parts.Settings.SiteSettingsPart.cshtml @@ -14,6 +14,12 @@ @Html.EditorFor(m => m.SiteName) @Html.ValidationMessage("SiteName", "*") +
+ + @Html.TextBoxFor(m => m.BaseUrl, new { @class = "textMedium" }) + @T("Enter the fully qualified base url of your website.") + @T("e.g., http://localhost:30320/orchardlocal, http://www.yourdomain.com") +
@Html.DropDownList("SiteCulture", new SelectList(Model.SiteCultures, Model.SiteCulture)) @@ -41,10 +47,4 @@ @Html.TextBoxFor(m => m.PageSize, new { @class = "text-small" }) @T("Determines the default number of items that are shown per page.")
-
- - @Html.TextBoxFor(m => m.BaseUrl, new { @class = "textMedium" }) - @T("Enter the fully qualified base url of your website.") - @T("e.g., http://localhost:30320/orchardlocal, http://www.yourdomain.com") -
\ No newline at end of file diff --git a/src/Orchard.Web/Modules/Orchard.Warmup/AdminMenu.cs b/src/Orchard.Web/Modules/Orchard.Warmup/AdminMenu.cs index 006cb26e0..457138c5f 100644 --- a/src/Orchard.Web/Modules/Orchard.Warmup/AdminMenu.cs +++ b/src/Orchard.Web/Modules/Orchard.Warmup/AdminMenu.cs @@ -10,8 +10,9 @@ namespace Orchard.Warmup { 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)) - ); + .Add(T("Performance"), "10.0", subMenu => subMenu.Action("Index", "Admin", new { area = "Orchard.Warmup" }).Permission(StandardPermissions.SiteOwner) + .Add(T("Warmup"), "10.0", item => item.Action("Index", "Admin", new { area = "Orchard.Warmup" }).Permission(StandardPermissions.SiteOwner).LocalNav()) + )); } } } diff --git a/src/Orchard.Web/Modules/Orchard.Warmup/Content/Admin/images/offline.gif b/src/Orchard.Web/Modules/Orchard.Warmup/Content/Admin/images/offline.gif new file mode 100644 index 0000000000000000000000000000000000000000..95a2792acdd0bd1cdfc7af069d61f8dd523938ea GIT binary patch literal 293 zcmZ?wbhEHb6k-r!I3mXI|Ns9B{&vqId~OE2e1HDrb!o!$XulV+0k4nkeN+^Adq(H0 zhj=7m- z5eJh%PrIXl0V_)zyR%!7iWt)-%Y7ae3`^V|Cg@Jzz|P8B!5}IkSf|dwC@3P@QdQGg z&)nFYo6i)M#>`Y&u9A>6E0u9>W_AlhP{@+y(XkaO&aUpB-oE|;3}zNqHg*o1cE{`L S8yah>be%kP?!1yBgEaskzF;H( literal 0 HcmV?d00001 diff --git a/src/Orchard.Web/Modules/Orchard.Warmup/Content/Admin/images/online.gif b/src/Orchard.Web/Modules/Orchard.Warmup/Content/Admin/images/online.gif new file mode 100644 index 0000000000000000000000000000000000000000..67a22969ae541437cf464e02f693d3183d41b369 GIT binary patch literal 405 zcmZ?wbhEHb6k-r!xT?nR|NsBQ<=&6pJZf6k=-J`sG{Gu)rEm4h%3YUsrmqi7-x`{; zGdy&ONAhaF$Q9oDRa#kFL$ymal2-Z@?2Y>K_xH>TJ^LPSd-mn=+wZTpKiu&5-`@jI zcFsK8_2t*6Yws>iJKHhsbld*NJ6eyHe*F2N{bc3MH&=i*GEfgF{$ycfU{GVw0T~JM z69d~OhshUMG^BbDFA*$MaN%HQDT)x`;o>{$@=$|^#dXz#gApoHliZRHRy4F8Wn@m` zxbc>aBUyk=fs4DeyiAOZn>EOVosYA*g+ZK?kDbAVg^RaOnn6UAmy2a8gP^bwqof?8 z;v7*%Az{Hq(`PC(NX(Hho4>HDhfzhAK}w-~(iE5aMn)x7new)dMGQqHeU;UTF3G9s knc2Dd1q@*kQ894|Q$t*R`~re*db+r~fBN?QlOuyQ0GsEB2><{9 literal 0 HcmV?d00001 diff --git a/src/Orchard.Web/Modules/Orchard.Warmup/Content/Web.config b/src/Orchard.Web/Modules/Orchard.Warmup/Content/Web.config new file mode 100644 index 000000000..0dc62ece6 --- /dev/null +++ b/src/Orchard.Web/Modules/Orchard.Warmup/Content/Web.config @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + + + diff --git a/src/Orchard.Web/Modules/Orchard.Warmup/Controllers/AdminController.cs b/src/Orchard.Web/Modules/Orchard.Warmup/Controllers/AdminController.cs index 07e23012c..89b6d2c95 100644 --- a/src/Orchard.Web/Modules/Orchard.Warmup/Controllers/AdminController.cs +++ b/src/Orchard.Web/Modules/Orchard.Warmup/Controllers/AdminController.cs @@ -1,25 +1,28 @@ using System; using System.IO; +using System.Linq; using System.Web.Mvc; using Orchard.ContentManagement; using Orchard.Core.Contents.Controllers; -using Orchard.FileSystems.AppData; using Orchard.Localization; using Orchard.Security; using Orchard.Warmup.Models; using Orchard.UI.Notify; using Orchard.Warmup.Services; +using Orchard.Warmup.ViewModels; namespace Orchard.Warmup.Controllers { [ValidateInput(false)] public class AdminController : Controller, IUpdateModel { - private readonly IWarmupScheduler _warmupScheduler; + private readonly IWarmupUpdater _warmupUpdater; + private readonly IWarmupReportManager _reportManager; public AdminController( IOrchardServices services, - IWarmupScheduler warmupScheduler, - IAppDataFolder appDataFolder) { - _warmupScheduler = warmupScheduler; + IWarmupUpdater warmupUpdater, + IWarmupReportManager reportManager) { + _warmupUpdater = warmupUpdater; + _reportManager = reportManager; Services = services; T = NullLocalizer.Instance; @@ -33,7 +36,13 @@ namespace Orchard.Warmup.Controllers { return new HttpUnauthorizedResult(); var warmupPart = Services.WorkContext.CurrentSite.As(); - return View(warmupPart); + + var viewModel = new WarmupViewModel { + Settings = warmupPart, + ReportEntries = _reportManager.Read() + }; + + return View(viewModel); } [FormValueRequired("submit")] @@ -42,11 +51,14 @@ namespace Orchard.Warmup.Controllers { if (!Services.Authorizer.Authorize(StandardPermissions.SiteOwner, T("Not authorized to manage settings"))) return new HttpUnauthorizedResult(); - var warmupPart = Services.WorkContext.CurrentSite.As(); + var viewModel = new WarmupViewModel { + Settings = Services.WorkContext.CurrentSite.As(), + ReportEntries = Enumerable.Empty() + }; - if(TryUpdateModel(warmupPart)) { - if (!String.IsNullOrEmpty(warmupPart.Urls)) { - using (var urlReader = new StringReader(warmupPart.Urls)) { + if (TryUpdateModel(viewModel)) { + if (!String.IsNullOrEmpty(viewModel.Settings.Urls)) { + using (var urlReader = new StringReader(viewModel.Settings.Urls)) { string relativeUrl; while (null != (relativeUrl = urlReader.ReadLine())) { if (!Uri.IsWellFormedUriString(relativeUrl, UriKind.Relative) || !(relativeUrl.StartsWith("/"))) { @@ -57,30 +69,20 @@ namespace Orchard.Warmup.Controllers { } } - if (warmupPart.Scheduled) { - if (warmupPart.Delay <= 0) { + if (viewModel.Settings.Scheduled) { + if (viewModel.Settings.Delay <= 0) { AddModelError("Delay", T("Delay must be greater than zero.")); } } if (ModelState.IsValid) { Services.Notifier.Information(T("Warmup updated successfully.")); - } - - return View(warmupPart); - } - - [FormValueRequired("submit.Generate")] - [HttpPost, ActionName("Index")] - public ActionResult IndexPostGenerate() { - var result = IndexPost(); - + } if (ModelState.IsValid) { - _warmupScheduler.Schedule(true); - Services.Notifier.Information(T("Static pages are currently being generated.")); + _warmupUpdater.Generate(); } - return result; + return RedirectToAction("Index"); } bool IUpdateModel.TryUpdateModel(TModel model, string prefix, string[] includeProperties, string[] excludeProperties) { diff --git a/src/Orchard.Web/Modules/Orchard.Warmup/Models/ReportEntry.cs b/src/Orchard.Web/Modules/Orchard.Warmup/Models/ReportEntry.cs new file mode 100644 index 000000000..c5ae4ab2d --- /dev/null +++ b/src/Orchard.Web/Modules/Orchard.Warmup/Models/ReportEntry.cs @@ -0,0 +1,10 @@ +using System; + +namespace Orchard.Warmup.Models { + public class ReportEntry { + public string RelativeUrl { get; set; } + public string Filename { get; set; } + public int StatusCode { get; set; } + public DateTime CreatedUtc { get; set; } + } +} \ No newline at end of file diff --git a/src/Orchard.Web/Modules/Orchard.Warmup/Orchard.Warmup.csproj b/src/Orchard.Web/Modules/Orchard.Warmup/Orchard.Warmup.csproj index 1f958bf1a..431a0cd3b 100644 --- a/src/Orchard.Web/Modules/Orchard.Warmup/Orchard.Warmup.csproj +++ b/src/Orchard.Web/Modules/Orchard.Warmup/Orchard.Warmup.csproj @@ -59,6 +59,9 @@ + + + @@ -83,8 +86,11 @@ + + + @@ -93,10 +99,12 @@ + - - + + Designer + diff --git a/src/Orchard.Web/Modules/Orchard.Warmup/Services/IWarmupReportManager.cs b/src/Orchard.Web/Modules/Orchard.Warmup/Services/IWarmupReportManager.cs new file mode 100644 index 000000000..9a0c397a4 --- /dev/null +++ b/src/Orchard.Web/Modules/Orchard.Warmup/Services/IWarmupReportManager.cs @@ -0,0 +1,9 @@ +using System.Collections.Generic; +using Orchard.Warmup.Models; + +namespace Orchard.Warmup.Services { + public interface IWarmupReportManager : IDependency { + IEnumerable Read(); + void Create(IEnumerable reportEntries); + } +} \ No newline at end of file diff --git a/src/Orchard.Web/Modules/Orchard.Warmup/Services/WarmupReportManager.cs b/src/Orchard.Web/Modules/Orchard.Warmup/Services/WarmupReportManager.cs new file mode 100644 index 000000000..658554546 --- /dev/null +++ b/src/Orchard.Web/Modules/Orchard.Warmup/Services/WarmupReportManager.cs @@ -0,0 +1,58 @@ +using System; +using System.Collections.Generic; +using System.Xml; +using System.Xml.Linq; +using Orchard.Environment.Configuration; +using Orchard.FileSystems.AppData; +using Orchard.Warmup.Models; + +namespace Orchard.Warmup.Services { + public class WarmupReportManager : IWarmupReportManager { + private readonly IAppDataFolder _appDataFolder; + private const string WarmupReportFilename = "warmup.xml"; + private readonly string _warmupReportPath; + + public WarmupReportManager( + ShellSettings shellSettings, + IAppDataFolder appDataFolder) { + _appDataFolder = appDataFolder; + + _warmupReportPath = _appDataFolder.Combine("Sites", _appDataFolder.Combine(shellSettings.Name, WarmupReportFilename)); + } + + public IEnumerable Read() { + if(!_appDataFolder.FileExists(_warmupReportPath)) { + yield break; + } + + var warmupReportContent = _appDataFolder.ReadFile(_warmupReportPath); + + var doc = XDocument.Parse(warmupReportContent); + foreach (var entryNode in doc.Root.Descendants("ReportEntry")) { + yield return new ReportEntry { + CreatedUtc = XmlConvert.ToDateTime(entryNode.Attribute("CreatedUtc").Value, XmlDateTimeSerializationMode.Utc), + Filename = entryNode.Attribute("Filename").Value, + RelativeUrl = entryNode.Attribute("RelativeUrl").Value, + StatusCode = Int32.Parse(entryNode.Attribute("StatusCode").Value) + }; + } + } + + public void Create(IEnumerable reportEntries) { + var report = new XDocument(new XElement("WarmupReport")); + + foreach (var reportEntry in reportEntries) { + report.Root.Add( + new XElement("ReportEntry", + new XAttribute("RelativeUrl", reportEntry.RelativeUrl), + new XAttribute("Filename", reportEntry.Filename), + new XAttribute("StatusCode", reportEntry.StatusCode), + new XAttribute("CreatedUtc", XmlConvert.ToString(reportEntry.CreatedUtc, XmlDateTimeSerializationMode.Utc)) + ) + ); + } + + _appDataFolder.CreateFile(_warmupReportPath, report.ToString()); + } + } +} \ No newline at end of file diff --git a/src/Orchard.Web/Modules/Orchard.Warmup/Services/WarmupUpdater.cs b/src/Orchard.Web/Modules/Orchard.Warmup/Services/WarmupUpdater.cs index 9ebb7afdc..486abe97e 100644 --- a/src/Orchard.Web/Modules/Orchard.Warmup/Services/WarmupUpdater.cs +++ b/src/Orchard.Web/Modules/Orchard.Warmup/Services/WarmupUpdater.cs @@ -1,9 +1,11 @@ using System; +using System.Collections.Generic; using System.IO; using System.Net; using System.Web; using System.Xml; using Orchard.ContentManagement; +using Orchard.Environment.Configuration; using Orchard.Environment.Warmup; using Orchard.FileSystems.AppData; using Orchard.FileSystems.LockFile; @@ -18,8 +20,11 @@ namespace Orchard.Warmup.Services { private readonly IClock _clock; private readonly IAppDataFolder _appDataFolder; private readonly IWebDownloader _webDownloader; + private readonly IWarmupReportManager _reportManager; private const string BaseFolder = "Warmup"; private const string WarmupFilename = "warmup.txt"; + + private readonly string _warmupPath; private readonly string _lockFilename; public WarmupUpdater( @@ -27,13 +32,18 @@ namespace Orchard.Warmup.Services { ILockFileManager lockFileManager, IClock clock, IAppDataFolder appDataFolder, - IWebDownloader webDownloader) { + IWebDownloader webDownloader, + IWarmupReportManager reportManager, + ShellSettings shellSettings) { _orchardServices = orchardServices; _lockFileManager = lockFileManager; _clock = clock; _appDataFolder = appDataFolder; _webDownloader = webDownloader; - _lockFilename = _appDataFolder.Combine(BaseFolder, WarmupFilename + ".lock"); + _reportManager = reportManager; + + _lockFilename = _appDataFolder.Combine("Sites", _appDataFolder.Combine(shellSettings.Name, WarmupFilename + ".lock")); + _warmupPath = _appDataFolder.Combine("Sites", _appDataFolder.Combine(shellSettings.Name, WarmupFilename)); Logger = NullLogger.Instance; } @@ -44,8 +54,8 @@ namespace Orchard.Warmup.Services { var baseUrl = _orchardServices.WorkContext.CurrentSite.BaseUrl; var part = _orchardServices.WorkContext.CurrentSite.As(); - // 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)) { + // do nothing while the base url setting is not defined + if (String.IsNullOrWhiteSpace(baseUrl)) { return; } @@ -60,10 +70,9 @@ namespace Orchard.Warmup.Services { // 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)) { + if (_appDataFolder.FileExists(_warmupPath)) { try { - var warmupContent = _appDataFolder.ReadFile(warmupPath); + var warmupContent = _appDataFolder.ReadFile(_warmupPath); var expired = XmlConvert.ToDateTimeOffset(warmupContent).AddMinutes(part.Delay); if (expired > _clock.UtcNow) { return; @@ -71,63 +80,104 @@ namespace Orchard.Warmup.Services { } catch { // invalid file, delete continue processing - _appDataFolder.DeleteFile(warmupPath); + _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(); + // delete peviously generated pages, by reading the Warmup Report file + try { + var encodedPrefix = WarmupUtility.EncodeUrl("http://www."); + foreach (var reportEntry in _reportManager.Read()) { try { - url = VirtualPathUtility.RemoveTrailingSlash(baseUrl) + relativeUrl; - var download = _webDownloader.Download(url); - - if (download != null && download.StatusCode == HttpStatusCode.OK) { - var filename = WarmupUtility.EncodeUrl(url.TrimEnd('/')); - 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.TrimEnd('/')); - path = _appDataFolder.Combine(BaseFolder, filename); - _appDataFolder.CreateFile(path, download.Content); - } + // use FileName as the SiteBaseUrl could have changed in the meantime + var path = _appDataFolder.Combine(BaseFolder, reportEntry.Filename); + _appDataFolder.DeleteFile(path); + // delete the www-less version too if it's available + if (reportEntry.Filename.StartsWith(encodedPrefix, StringComparison.OrdinalIgnoreCase)) { + var filename = WarmupUtility.EncodeUrl("http://") + reportEntry.Filename.Substring(encodedPrefix.Length); + path = _appDataFolder.Combine(BaseFolder, filename); + _appDataFolder.DeleteFile(path); } } catch (Exception e) { - Logger.Error(e, "Could not extract warmup page content for: ", url); + Logger.Error(e, "Could not delete specific warmup file: ", reportEntry.Filename); + } + } + } + catch(Exception e) { + Logger.Error(e, "Could not read warmup report file"); + } + + var reportEntries = new List(); + + if (!String.IsNullOrEmpty(part.Urls)) { + // 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 filename = WarmupUtility.EncodeUrl(url.TrimEnd('/')); + var path = _appDataFolder.Combine(BaseFolder, filename); + + var download = _webDownloader.Download(url); + + if (download != null) { + if (download.StatusCode == HttpStatusCode.OK) { + // success + _appDataFolder.CreateFile(path, download.Content); + + reportEntries.Add(new ReportEntry { + RelativeUrl = relativeUrl, + Filename = filename, + StatusCode = (int) download.StatusCode, + CreatedUtc = _clock.UtcNow + }); + + // 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.TrimEnd('/')); + path = _appDataFolder.Combine(BaseFolder, filename); + _appDataFolder.CreateFile(path, download.Content); + } + } + else { + reportEntries.Add(new ReportEntry { + RelativeUrl = relativeUrl, + Filename = filename, + StatusCode = (int) download.StatusCode, + CreatedUtc = _clock.UtcNow + }); + } + } + else { + // download failed + reportEntries.Add(new ReportEntry { + RelativeUrl = relativeUrl, + Filename = filename, + StatusCode = 0, + CreatedUtc = _clock.UtcNow + }); + } + } + catch (Exception e) { + Logger.Error(e, "Could not extract warmup page content for: ", url); + } } } } + _reportManager.Create(reportEntries); + // finally write the time the generation has been executed - _appDataFolder.CreateFile(warmupPath, XmlConvert.ToString(_clock.UtcNow, XmlDateTimeSerializationMode.Utc)); + _appDataFolder.CreateFile(_warmupPath, XmlConvert.ToString(_clock.UtcNow, XmlDateTimeSerializationMode.Utc)); } } @@ -139,9 +189,8 @@ namespace Orchard.Warmup.Services { } using (lockFile) { - var warmupPath = _appDataFolder.Combine(BaseFolder, WarmupFilename); - if (_appDataFolder.FileExists(warmupPath)) { - _appDataFolder.DeleteFile(warmupPath); + if (_appDataFolder.FileExists(_warmupPath)) { + _appDataFolder.DeleteFile(_warmupPath); } } diff --git a/src/Orchard.Web/Modules/Orchard.Warmup/Styles/orchard-warmup-admin.css b/src/Orchard.Web/Modules/Orchard.Warmup/Styles/orchard-warmup-admin.css new file mode 100644 index 000000000..4847a44f8 --- /dev/null +++ b/src/Orchard.Web/Modules/Orchard.Warmup/Styles/orchard-warmup-admin.css @@ -0,0 +1,8 @@ + +td.status-error { + background: transparent url(../Content/Admin/images/offline.gif) no-repeat right; +} + +td.status-ok { + background: transparent url(../Content/Admin/images/online.gif) no-repeat right; +} diff --git a/src/Orchard.Web/Modules/Orchard.Warmup/ViewModels/WarmupViewModel.cs b/src/Orchard.Web/Modules/Orchard.Warmup/ViewModels/WarmupViewModel.cs new file mode 100644 index 000000000..8b2dc20ba --- /dev/null +++ b/src/Orchard.Web/Modules/Orchard.Warmup/ViewModels/WarmupViewModel.cs @@ -0,0 +1,9 @@ +using System.Collections.Generic; +using Orchard.Warmup.Models; + +namespace Orchard.Warmup.ViewModels { + public class WarmupViewModel { + public WarmupSettingsPart Settings { get; set; } + public IEnumerable ReportEntries { get; set; } + } +} \ No newline at end of file diff --git a/src/Orchard.Web/Modules/Orchard.Warmup/Views/Admin/Index.cshtml b/src/Orchard.Web/Modules/Orchard.Warmup/Views/Admin/Index.cshtml index c46c14fd6..9b6c8d2f9 100644 --- a/src/Orchard.Web/Modules/Orchard.Warmup/Views/Admin/Index.cshtml +++ b/src/Orchard.Web/Modules/Orchard.Warmup/Views/Admin/Index.cshtml @@ -1,38 +1,72 @@ -@model Orchard.Warmup.Models.WarmupSettingsPart +@model Orchard.Warmup.ViewModels.WarmupViewModel @using Orchard.Utility.Extensions; @using Orchard.Warmup.Models; -@{ Layout.Title = T("Settings").ToString(); } +@{ + Style.Include("orchard-warmup-admin.css"); + Layout.Title = T("Performance").ToString(); +} + +

The urls below will be requested using @Html.Link(WorkContext.CurrentSite.BaseUrl, WorkContext.CurrentSite.BaseUrl) as a base url. You can change it on the @Html.ActionLink(T("General Settings page").Text, "Index", new { controller = "Admin", area = "Settings" }).

@using (Html.BeginFormAntiForgeryPost()) { @Html.ValidationSummary() -
- @T("Warmup")
- - @Html.TextAreaFor(m => m.Urls, new { @class = "textMedium" }) + @Html.TextAreaFor(m => m.Settings.Urls, new { @class = "textMedium" }) @T("This must be a set of relative paths, e.g., /, /About")
- @Html.EditorFor(m => m.Scheduled) - + @Html.EditorFor(m => m.Settings.Scheduled) +
-
+
@T("Every") - @Html.TextBoxFor(m => m.Delay, new { @class = "text-small" }) + @Html.TextBoxFor(m => m.Settings.Delay, new { @class = "text-small" }) @T("minutes") @Html.ValidationMessage("Delay", "*")
- @Html.EditorFor(m => m.OnPublish) - + @Html.EditorFor(m => m.Settings.OnPublish) +
-
} + +
+ + + + + + + + + + + + + + + + + @foreach (var reportEntry in Model.ReportEntries) { + + + + + + } +
@T("Url")@T("Status")@T("Date")
+ @Html.Link(Html.Encode(reportEntry.RelativeUrl), reportEntry.RelativeUrl, new { target = "_blank" }) + + @reportEntry.StatusCode + + @Display.DateTimeRelative(dateTimeUtc: reportEntry.CreatedUtc).ToString() +
+
diff --git a/src/Orchard.Web/Modules/Orchard.Warmup/Views/EditorTemplates/Parts.Warmup.SiteSettings.cshtml b/src/Orchard.Web/Modules/Orchard.Warmup/Views/EditorTemplates/Parts.Warmup.SiteSettings.cshtml deleted file mode 100644 index 6fb7c7a40..000000000 --- a/src/Orchard.Web/Modules/Orchard.Warmup/Views/EditorTemplates/Parts.Warmup.SiteSettings.cshtml +++ /dev/null @@ -1,28 +0,0 @@ -@model Orchard.Warmup.Models.WarmupSettingsPartRecord -@using Orchard.Utility.Extensions; -@using Orchard.Warmup.Models; - -
- @T("Warmup") - -
- - @Html.TextAreaFor(m => m.Urls, new { @class = "textMedium" }) - @Html.ValidationMessage("Urls", "*") - @T("This must be a set of virtual paths, e.g., ~/, ~/About") -
- -
- @Html.EditorFor(m => m.Scheduled) - -
-
m.Urls)">@T("Delay to generate pages") - @Html.TextBoxFor(m => m.Delay, new { @class = "" }) @T("minutes") - @Html.ValidationMessage("Delay", "*") -
-
- @Html.EditorFor(m => m.OnPublish) - -
-
\ No newline at end of file