--HG--
branch : dev
This commit is contained in:
Nathan Heskew
2011-03-23 11:05:40 -07:00
27 changed files with 557 additions and 189 deletions

View File

@@ -293,6 +293,8 @@
DestinationFolder="$(MsDeployFolder)\Orchard\%(RecursiveDir)"/>
<Copy SourceFiles="@(MsDeploy-Parameters)"
DestinationFolder="$(MsDeployFolder)"/>
<MakeDir Directories="$(MsDeployFolder)\Orchard\bin\HostRestart"/>
</Target>
<Target Name="Package-Zip">

View File

@@ -2,5 +2,6 @@
<iisapp path="Orchard" managedRuntimeVersion="v4.0" />
<setAcl path="Orchard/App_Data" setAclAccess="Modify" />
<setAcl path="Orchard/Media" setAclAccess="Modify" />
<setAcl path="Orchard/bin/HostRestart" setAclAccess="Modify" />
<dbFullSql path="install.sql" />
</MSDeploy.iisApp>

View File

@@ -13,6 +13,10 @@
<parameterEntry type="ProviderPath" scope="setAcl" match="Orchard/Media" />
</parameter>
<parameter name="SetAclParameter3" description="Hidden - automatically sets write access for the app" defaultValue="{Application Path}/bin/HostRestart" tags="Hidden">
<parameterEntry type="ProviderPath" scope="setAcl" match="Orchard/bin/HostRestart" />
</parameter>
<!-- Prompts for database server name, this is used in the connection string parameter later -->
<parameter name="Database Server" description="Location of your database server (i.e. server name, IP address, or server\instance)" defaultValue=".\SQLEXPRESS" tags="SQL, dbServer">
</parameter>

View File

@@ -4,7 +4,7 @@ using Orchard.Environment.Warmup;
namespace Orchard.Tests.Environment.Warmup {
[TestFixture]
public class WarmUpUtilityTests {
public class WarmupUtilityTests {
[Test]
public void EmptyStringsAreNotAllowed() {

View File

@@ -240,7 +240,7 @@
<Compile Include="Environment\RunningShellTableTests.cs" />
<Compile Include="Environment\StubHostEnvironment.cs" />
<Compile Include="Environment\Utility\Build.cs" />
<Compile Include="Environment\WarmUp\WarmUpUtilityTests.cs" />
<Compile Include="Environment\Warmup\WarmupUtilityTests.cs" />
<Compile Include="FileSystems\AppData\AppDataFolderTests.cs" />
<Compile Include="Environment\Configuration\DefaultTenantManagerTests.cs" />
<Compile Include="Environment\DefaultCompositionStrategyTests.cs" />

View File

@@ -1,22 +1,18 @@
using System;
using System.IO;
using System.Threading;
using System.Web;
using System.Web.Hosting;
using System.Web.Mvc;
using System.Web.Routing;
using Autofac;
using Orchard.Environment;
using Orchard.Environment.Warmup;
using Orchard.Utility.Extensions;
namespace Orchard.Web {
// Note: For instructions on enabling IIS6 or IIS7 classic mode,
// visit http://go.microsoft.com/?LinkId=9394801
public class MvcApplication : HttpApplication {
private static StartupResult _startupResult;
private static EventWaitHandle _waitHandle;
private static IOrchardHost _host;
private static Exception _error;
public static void RegisterRoutes(RouteCollection routes) {
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
@@ -26,77 +22,30 @@ namespace Orchard.Web {
LaunchStartupThread();
}
/// <summary>
/// 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() {
// Host is still starting up?
if (_startupResult.Host == null && _startupResult.Error == null) {
if (_error != null) {
// Host startup resulted in an error
// 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.WriteFile(localCopy);
Context.Response.End();
}
else if(!File.Exists(Request.PhysicalPath)) {
// there is no local copy and the host is not running
// wait for the host to initialize
_waitHandle.WaitOne();
}
// Throw error once, and restart launch; machine state may have changed
// so we need to simulate a "restart".
var error = _error;
LaunchStartupThread();
throw error;
}
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();
// Only notify if the host has started up
if (_host == null) {
return;
}
Context.Items["originalHttpContext"] = Context;
_host.BeginRequest();
}
protected void Application_EndRequest() {
// Only notify if the host has started up
if (_startupResult.Host != null) {
_startupResult.Host.EndRequest();
if (_host != null) {
_host.EndRequest();
}
}
@@ -105,5 +54,31 @@ namespace Orchard.Web {
builder.Register(ctx => ModelBinders.Binders).SingleInstance();
builder.Register(ctx => ViewEngines.Engines).SingleInstance();
}
/// <summary>
/// Initializes Orchard's Host in a separate thread
/// </summary>
private static void LaunchStartupThread() {
RegisterRoutes(RouteTable.Routes);
_host = null;
_error = null;
ThreadPool.QueueUserWorkItem(
state => {
try {
var host = OrchardStarter.CreateHost(MvcSingletons);
host.Initialize();
_host = host;
}
catch (Exception e) {
_error = e;
}
finally {
// Execute pending actions as the host is available
WarmupHttpModule.Signal();
}
});
}
}
}

View File

@@ -0,0 +1,35 @@
using System.Linq;
using System.Web.Mvc;
using Orchard.Data;
using Orchard.Environment;
using Orchard.Environment.Extensions;
using Orchard.Logging;
using Orchard.Mvc.Filters;
namespace Orchard.Experimental {
[OrchardFeature("Orchard.Experimental.LockLogging")]
public class LockLogging : FilterProvider, IExceptionFilter {
private readonly Work<ISessionLocator> _sessionLocator;
private readonly ITransactionManager _transactionManager;
public LockLogging(Work<ISessionLocator> sessionLocator) {
_sessionLocator = sessionLocator;
Logger = NullLogger.Instance;
}
public ILogger Logger { get; set; }
public void OnException(ExceptionContext filterContext) {
var connection = _sessionLocator.Value.For(null).Connection;
var command = connection.CreateCommand();
command.CommandText = "SELECT * FROM sys.lock_information";
var reader = command.ExecuteReader();
while (reader.Read()) {
var fields = Enumerable.Range(0, reader.FieldCount)
.Select(i => new { Key = reader.GetName(i), Value = reader.GetValue(i) });
var message = fields.Aggregate("sys.lock_information", (sz, kv) => sz + " " + kv.Key + ":" + kv.Value);
Logger.Debug(message);
}
}
}
}

View File

@@ -21,3 +21,7 @@ Features:
Description: Adds some content definitions to help test lists
Dependencies: Orchard.Lists
Category: Developer
Orchard.Experimental.LockLogging:
Name: Lock Logging
Description: Logs extra diagnostic information for Sql CE if a deadlock timeout occurs
Category: Developer

View File

@@ -39,10 +39,12 @@
<HintPath>..\..\..\..\lib\autofac\Autofac.dll</HintPath>
</Reference>
<Reference Include="Microsoft.CSharp" />
<Reference Include="NHibernate, Version=2.1.2.4000, Culture=neutral, PublicKeyToken=aa95f207798dfdb4, processorArchitecture=MSIL" />
<Reference Include="System" />
<Reference Include="System.Core">
<RequiredTargetFramework>3.5</RequiredTargetFramework>
</Reference>
<Reference Include="System.Data" />
<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>
@@ -62,6 +64,7 @@
<Compile Include="Controllers\MetadataController.cs" />
<Compile Include="DebugFilter.cs" />
<Compile Include="Handlers\DebugLinkHandler.cs" />
<Compile Include="LockLogging.cs" />
<Compile Include="Migrations.cs" />
<Compile Include="Models\ShowDebugLink.cs" />
<Compile Include="Models\Simple.cs" />

View File

@@ -60,47 +60,26 @@ namespace Lists.Controllers {
public ActionResult List(ListContentsViewModel model, PagerParameters pagerParameters) {
var pager = new Pager(_siteService.GetSiteSettings(), pagerParameters);
var container = model.ContainerId.HasValue ? _contentManager.GetLatest((int)model.ContainerId) : null;
if (container == null && model.ContainerId.HasValue) {
if (container == null || !container.Has<ContainerPart>()) {
return HttpNotFound();
}
var restrictedContentType = container == null ? null : container.As<ContainerPart>().Record.ItemContentType;
var restrictedContentType = container.As<ContainerPart>().Record.ItemContentType;
var hasRestriction = !string.IsNullOrEmpty(restrictedContentType);
if (hasRestriction) {
model.FilterByContentType = restrictedContentType;
}
if (container != null) {
var metadata = container.ContentManager.GetItemMetadata(container);
model.ContainerDisplayName = metadata.DisplayText;
if (string.IsNullOrEmpty(model.ContainerDisplayName)) {
model.ContainerDisplayName = container.ContentType;
}
}
var query = _contentManager.Query<ContainablePart>(VersionOptions.Latest);
if (!string.IsNullOrEmpty(model.FilterByContentType)) {
var contentTypeDefinition = _contentDefinitionManager.GetTypeDefinition(model.FilterByContentType);
if (contentTypeDefinition == null)
return HttpNotFound();
query = query.ForType(model.FilterByContentType);
}
query = query.Join<CommonPartRecord>().Where(cr => cr.Container.Id == model.ContainerId);
switch (model.Options.OrderBy) {
case ContentsOrder.Modified:
query = query.OrderByDescending<CommonPartRecord, DateTime?>(cr => cr.ModifiedUtc);
break;
case ContentsOrder.Published:
query = query.OrderByDescending<CommonPartRecord, DateTime?>(cr => cr.PublishedUtc);
break;
case ContentsOrder.Created:
query = query.OrderByDescending<CommonPartRecord, DateTime?>(cr => cr.CreatedUtc);
break;
}
model.Options.SelectedFilter = model.FilterByContentType;
model.ContainerDisplayName = container.ContentManager.GetItemMetadata(container).DisplayText;
if (string.IsNullOrEmpty(model.ContainerDisplayName)) {
model.ContainerDisplayName = container.ContentType;
}
var query = GetListContentItemQuery(model);
if (query == null) {
return new HttpNotFoundResult();
}
if (!hasRestriction) {
model.Options.FilterOptions = GetContainableTypes()
.Select(ctd => new KeyValuePair<string, string>(ctd.Name, ctd.DisplayName))
@@ -120,7 +99,7 @@ namespace Lists.Controllers {
.Options(model.Options)
.HasRestriction(hasRestriction)
.ContainerDisplayName(model.ContainerDisplayName)
.ContainerContentType(container == null ? null : container.ContentType)
.ContainerContentType(container.ContentType)
.ContainerItemContentType(hasRestriction ? restrictedContentType : (model.FilterByContentType ?? ""))
.OtherLists(_contentManager.Query<ContainerPart>(VersionOptions.Latest).List()
.Select(part => part.ContentItem)
@@ -131,6 +110,32 @@ namespace Lists.Controllers {
return View((object)viewModel);
}
private IContentQuery<ContainablePart> GetListContentItemQuery(ListContentsViewModel model) {
var query = _contentManager.Query<ContainablePart>(VersionOptions.Latest);
if (!string.IsNullOrEmpty(model.FilterByContentType)) {
var contentTypeDefinition = _contentDefinitionManager.GetTypeDefinition(model.FilterByContentType);
if (contentTypeDefinition == null) {
return null;
}
query = query.ForType(model.FilterByContentType);
}
query = query.Join<CommonPartRecord>().Where(cr => cr.Container.Id == model.ContainerId);
switch (model.Options.OrderBy) {
case ContentsOrder.Modified:
query = query.OrderByDescending<CommonPartRecord, DateTime?>(cr => cr.ModifiedUtc);
break;
case ContentsOrder.Published:
query = query.OrderByDescending<CommonPartRecord, DateTime?>(cr => cr.PublishedUtc);
break;
case ContentsOrder.Created:
query = query.OrderByDescending<CommonPartRecord, DateTime?>(cr => cr.CreatedUtc);
break;
}
return query;
}
[HttpPost, ActionName("List")]
[FormValueRequired("submit.BulkEdit")]
public ActionResult ListPOST(ContentOptions options, IEnumerable<int> itemIds, int? targetContainerId, string returnUrl) {
@@ -172,6 +177,119 @@ namespace Lists.Controllers {
return this.RedirectLocal(returnUrl, () => RedirectToAction("List"));
}
[HttpPost, ActionName("List")]
[FormValueRequired("submit.Filter")]
public ActionResult ListFilterPOST(ContentOptions options) {
var routeValues = ControllerContext.RouteData.Values;
if (options != null) {
routeValues["Options.OrderBy"] = options.OrderBy;
if (GetContainableTypes().Any(ctd => string.Equals(ctd.Name, options.SelectedFilter, StringComparison.OrdinalIgnoreCase))) {
routeValues["filterByContentType"] = options.SelectedFilter;
}
else {
routeValues.Remove("filterByContentType");
}
}
return RedirectToAction("List", routeValues);
}
public ActionResult Choose(ChooseContentsViewModel model, PagerParameters pagerParameters) {
var pager = new Pager(_siteService.GetSiteSettings(), pagerParameters);
var container = model.SourceContainerId == 0 ? null : _contentManager.GetLatest(model.SourceContainerId);
if (container == null && model.SourceContainerId != 0) {
return HttpNotFound();
}
var query = _contentManager.Query<ContainablePart>(VersionOptions.Latest);
if (!string.IsNullOrEmpty(model.FilterByContentType)) {
var contentTypeDefinition = _contentDefinitionManager.GetTypeDefinition(model.FilterByContentType);
if (contentTypeDefinition == null) {
return HttpNotFound();
}
query = query.ForType(model.FilterByContentType);
}
if (model.SourceContainerId == 0) {
query = query.Join<CommonPartRecord>().Where(cr => cr.Container == null);
}
else {
query = query.Join<CommonPartRecord>().Where(cr => cr.Container.Id == model.SourceContainerId);
}
switch (model.OrderBy) {
case ContentsOrder.Modified:
query = query.OrderByDescending<CommonPartRecord, DateTime?>(cr => cr.ModifiedUtc);
break;
case ContentsOrder.Published:
query = query.OrderByDescending<CommonPartRecord, DateTime?>(cr => cr.PublishedUtc);
break;
case ContentsOrder.Created:
query = query.OrderByDescending<CommonPartRecord, DateTime?>(cr => cr.CreatedUtc);
break;
}
model.SelectedFilter = model.FilterByContentType;
model.FilterOptions = GetContainableTypes()
.Select(ctd => new KeyValuePair<string, string>(ctd.Name, ctd.DisplayName))
.ToList().OrderBy(kvp => kvp.Key);
var pagerShape = Shape.Pager(pager).TotalItemCount(query.Count());
var pageOfContentItems = query.Slice(pager.GetStartIndex(), pager.PageSize).ToList();
var list = Shape.List();
list.AddRange(pageOfContentItems.Select(ci => _contentManager.BuildDisplay(ci, "SummaryAdmin")));
dynamic viewModel = Shape.ViewModel()
.ContentItems(list)
.Pager(pagerShape)
.SourceContainerId(model.SourceContainerId)
.TargetContainerId(model.TargetContainerId)
.SelectedFilter(model.SelectedFilter)
.FilterOptions(model.FilterOptions)
.OrderBy(model.OrderBy)
.Containers(_contentManager.Query<ContainerPart>(VersionOptions.Latest).List()
.Select(part => part.ContentItem)
.OrderBy(item => item.As<CommonPart>().VersionPublishedUtc));
// Casting to avoid invalid (under medium trust) reflection over the protected View method and force a static invocation.
return View((object)viewModel);
}
[HttpPost, ActionName("Choose")]
[FormValueRequired("submit.MoveTo")]
public ActionResult ChoosePOST(IEnumerable<int> itemIds, int targetContainerId, string returnUrl) {
if (itemIds != null && !BulkMoveToList(itemIds, targetContainerId)) {
return new HttpUnauthorizedResult();
}
return this.RedirectLocal(returnUrl, () => RedirectToAction("List", new { ContainerId = targetContainerId }));
}
[HttpPost, ActionName("Choose")]
[FormValueRequired("submit.Filter")]
public ActionResult ChooseFilterPOST(ChooseContentsViewModel model) {
var routeValues = ControllerContext.RouteData.Values;
if (GetContainableTypes().Any(ctd => string.Equals(ctd.Name, model.SelectedFilter, StringComparison.OrdinalIgnoreCase))) {
routeValues["filterByContentType"] = model.SelectedFilter;
}
else {
routeValues.Remove("filterByContentType");
}
if (model.SourceContainerId == 0) {
routeValues.Remove("SourceContainerId");
}
else {
routeValues["SourceContainerId"] = model.SourceContainerId;
}
routeValues["OrderBy"] = model.OrderBy;
routeValues["TargetContainerId"] = model.TargetContainerId;
return RedirectToAction("Choose", routeValues);
}
private bool BulkMoveToList(IEnumerable<int> itemIds, int? targetContainerId) {
if (!targetContainerId.HasValue) {
Services.Notifier.Information(T("Please select the list to move the items to."));
@@ -248,23 +366,5 @@ namespace Lists.Controllers {
return true;
}
[HttpPost, ActionName("List")]
[FormValueRequired("submit.Filter")]
public ActionResult ListFilterPOST(ContentOptions options) {
var routeValues = ControllerContext.RouteData.Values;
if (options != null) {
routeValues["Options.OrderBy"] = options.OrderBy;
if (GetContainableTypes().Any(ctd => string.Equals(ctd.Name, options.SelectedFilter, StringComparison.OrdinalIgnoreCase))) {
routeValues["filterByContentType"] = options.SelectedFilter;
}
else {
routeValues.Remove("filterByContentType");
}
}
return RedirectToAction("List", routeValues);
}
}
}

View File

@@ -52,6 +52,7 @@
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="Routes.cs" />
<Content Include="Scripts\orchard-lists-admin.js" />
<Compile Include="ViewModels\ChooseContentsViewModel.cs" />
<Compile Include="ViewModels\ListContentsViewModel.cs" />
<Content Include="Module.txt" />
</ItemGroup>
@@ -98,6 +99,9 @@
<SubType>Designer</SubType>
</Content>
</ItemGroup>
<ItemGroup>
<Content Include="Views\Admin\Choose.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.

View File

@@ -24,24 +24,7 @@ namespace Orchard.Lists {
{"filterByContentType", ""}
},
new RouteValueDictionary{
{"filterByContentType", @"\w+"},
{"containerId", @"\d+"}
},
new RouteValueDictionary {
{"area", "Orchard.Lists"}
},
new MvcRouteHandler())
},
new RouteDescriptor {
Priority = 5,
Route = new Route(
"Admin/Lists/{containerId}",
new RouteValueDictionary {
{"area", "Orchard.Lists"},
{"controller", "Admin"},
{"action", "List"}
},
new RouteValueDictionary {
{"filterByContentType", @"\w*"},
{"containerId", @"\d+"}
},
new RouteValueDictionary {
@@ -53,15 +36,17 @@ namespace Orchard.Lists {
new RouteDescriptor {
Priority = 5,
Route = new Route(
"Admin/Lists/Orphaned/{filterByContentType}",
"Admin/Lists/Choose/From/Orphaned/To/{targetContainerId}/{filterByContentType}",
new RouteValueDictionary {
{"area", "Orchard.Lists"},
{"controller", "Admin"},
{"action", "List"},
{"action", "Choose"},
{"filterByContentType", ""},
{"sourceContainerId", "0"}
},
new RouteValueDictionary{
{"filterByContentType", @"\w+"},
{"filterByContentType", @"\w*"},
{"targetContainerId", @"\d+"},
},
new RouteValueDictionary {
{"area", "Orchard.Lists"}
@@ -71,19 +56,23 @@ namespace Orchard.Lists {
new RouteDescriptor {
Priority = 5,
Route = new Route(
"Admin/Lists/Orphaned",
"Admin/Lists/Choose/From/{sourceContainerId}/To/{targetContainerId}/{filterByContentType}",
new RouteValueDictionary {
{"area", "Orchard.Lists"},
{"controller", "Admin"},
{"action", "List"},
{"action", "Choose"},
{"filterByContentType", ""}
},
new RouteValueDictionary{
{"filterByContentType", @"\w*"},
{"sourceContainerId", @"\d+"},
{"targetContainerId", @"\d+"},
},
null,
new RouteValueDictionary {
{"area", "Orchard.Lists"}
},
new MvcRouteHandler())
},
};
}
}

View File

@@ -0,0 +1,21 @@
using System.Collections.Generic;
using Orchard.ContentManagement;
namespace Orchard.Lists.ViewModels {
public class ChooseContentsViewModel {
public ChooseContentsViewModel() {
OrderBy = ContentsOrder.Modified;
}
public string FilterByContentType { get; set; }
public int SourceContainerId { get; set; }
public int TargetContainerId { get; set; }
public int? Page { get; set; }
public string SelectedFilter { get; set; }
public IEnumerable<KeyValuePair<string, string>> FilterOptions { get; set; }
public ContentsOrder OrderBy { get; set; }
}
}

View File

@@ -0,0 +1,62 @@
@using Orchard.Lists.ViewModels;
@using Orchard.ContentManagement;
@{
var sourceContainerId = (int)Model.SourceContainerId;
var targetContainerId = (int)Model.TargetContainerId;
Layout.Title = T("Choose Items");
var targetContainers = ((IEnumerable<ContentItem>)Model.Containers).Select(
contentItem => new SelectListItem {
Text = T("Move to {0}", contentItem.ContentManager.GetItemMetadata(contentItem).DisplayText).ToString(),
Value = contentItem.Id.ToString(System.Globalization.CultureInfo.InvariantCulture),
Selected = contentItem.Id == targetContainerId
}).ToList();
var sourceContainers = ((IEnumerable<ContentItem>)Model.Containers).Select(
contentItem => new SelectListItem {
Text = contentItem.ContentManager.GetItemMetadata(contentItem).DisplayText,
Value = contentItem.Id.ToString(System.Globalization.CultureInfo.InvariantCulture),
Selected = contentItem.Id == sourceContainerId
}).ToList();
sourceContainers.Insert(0, new SelectListItem { Text = T("Orphan items").ToString(), Value = "" });
}
@using (Html.BeginFormAntiForgeryPost()) {
<fieldset class="bulk-actions">
<label for="filterResults">@T("Show")</label>
<select id="filterResults" name="SelectedFilter">
@Html.SelectOption((string)Model.SelectedFilter, "", T("any (show all)").ToString())
@foreach(var filterOption in Model.FilterOptions) {
@Html.SelectOption((string)Model.SelectedFilter, (string)filterOption.Key, (string)filterOption.Value)
}
</select>
<label for="SourceContainerId">@T("from")</label>
@Html.DropDownList("SourceContainerId", sourceContainers, new { id = "SourceContainerId" })
<label for="orderResults" class="bulk-order">@T("Ordered by")</label>
<select id="orderResults" name="OrderBy">
@Html.SelectOption((ContentsOrder)Model.OrderBy, ContentsOrder.Created, T("recently created").ToString())
@Html.SelectOption((ContentsOrder)Model.OrderBy, ContentsOrder.Modified, T("recently modified").ToString())
@Html.SelectOption((ContentsOrder)Model.OrderBy, ContentsOrder.Published, T("recently published").ToString())
</select>
<button type="submit" name="submit.Filter" value="yes">@T("Apply")</button>
</fieldset>
<div class="manage">
@Html.DropDownList("TargetContainerId", targetContainers)
<button type="submit" name="submit.MoveTo" value="yes">@T("Apply")</button>
</div>
<fieldset class="contentItems bulk-items">
@if ((int)Model.ContentItems.Items.Count == 0) {
@T("There are no content items.")
}
else {
@Display(Model.ContentItems)
}
</fieldset>
@Display(Model.Pager)
}

View File

@@ -3,11 +3,11 @@
@{
Script.Include("orchard-lists-admin.js");
int? containerId = (int?)Model.ContainerId;
int containerId = ((int?)Model.ContainerId).GetValueOrDefault();
string createLinkText = string.IsNullOrEmpty(Model.ContainerItemContentType) ? T("Create New Content").ToString() : T("Create New {0}", Model.ContainerItemContentType).ToString();
Layout.Title = containerId.HasValue ? T("Manage Content for {0}", Model.ContainerDisplayName) : T("Orphaned Content Items");
Layout.Title = T("Manage Content for {0}", Model.ContainerDisplayName);
var lists = ((IEnumerable<ContentItem>)Model.OtherLists).Select(
contentItem => new SelectListItem {
@@ -24,22 +24,19 @@
<div>&nbsp;</div>
@Display.Parts_Container_Manage(ContainerDisplayName: Model.ContainerDisplayName, ContainerContentType: Model.ContainerContentType, ContainerId: containerId)
@if (containerId.HasValue) {
<div class="manage">
@Html.ActionLink(createLinkText, "Create", new { Area = "Contents", Id = (string)Model.Options.SelectedFilter, ContainerId = (int)containerId, ReturnUrl = Url.Action("List", "Admin", new { Area = "Orchard.Lists", ContainerId = (int)containerId }) }, new { @class = "button primaryAction" })
@Html.ActionLink(createLinkText, "Create", new { Area = "Contents", Id = (string)Model.Options.SelectedFilter, ContainerId = containerId, ReturnUrl = Url.Action("List", "Admin", new { Area = "Orchard.Lists", ContainerId = containerId }) }, new { @class = "button primaryAction" })
</div>
}
@using (Html.BeginFormAntiForgeryPost()) {
<fieldset class="bulk-actions">
<label for="publishActions">@T("Actions:")</label>
<select id="publishActions" name="Options.BulkAction">
@if (containerId.HasValue) {
@Html.SelectOption((ContentsBulkAction)Model.Options.BulkAction, ContentsBulkAction.None, T("Choose action...").ToString())
@Html.SelectOption((ContentsBulkAction)Model.Options.BulkAction, ContentsBulkAction.PublishNow, T("Publish Now").ToString())
@Html.SelectOption((ContentsBulkAction)Model.Options.BulkAction, ContentsBulkAction.Unpublish, T("Unpublish").ToString())
@Html.SelectOption((ContentsBulkAction)Model.Options.BulkAction, ContentsBulkAction.Remove, T("Delete").ToString())
@Html.SelectOption((ContentsBulkAction)Model.Options.BulkAction, ContentsBulkAction.RemoveFromList, T("Remove from List").ToString())
}
@Html.SelectOption((ContentsBulkAction)Model.Options.BulkAction, ContentsBulkAction.MoveToList, T("Move to List...").ToString())
</select>
@Html.DropDownList("TargetContainerId", lists, new { id = "TargetContainerId" })

View File

@@ -1,10 +1,7 @@
<div class="item-properties actions">
<p>
@Html.ActionLink(T("Show Other Lists").ToString(), "List", new { Area = "Contents", Id = Model.ContainerContentType }) |
@Html.ActionLink(T("Show Orphaned Content Items").ToString(), "Orphaned", new { Area = "Orchard.Lists" })
@if (((int?)Model.ContainerId).HasValue) {
<text>|</text>
@Html.ActionLink(T("{0} Properties", (string)Model.ContainerContentType).ToString(), "Edit", new { Area = "Contents", Id = (int)Model.ContainerId })
}
@Html.ActionLink(T("Choose Items").ToString(), "Choose", new { TargetContainerId = (int)Model.ContainerId, Area = "Orchard.Lists" })
<text>|</text>
@Html.ActionLink(T("{0} Properties", (string)Model.ContainerContentType).ToString(), "Edit", new { Area = "Contents", Id = (int)Model.ContainerId, ReturnUrl = Html.ViewContext.HttpContext.Request.RawUrl })
</p>
</div>

View File

@@ -127,6 +127,7 @@
<Compile Include="Global.asax.cs">
<DependentUpon>Global.asax</DependentUpon>
</Compile>
<Compile Include="WarmupHttpModule.cs" />
<Content Include="Config\log4net.config" />
<Content Include="Config\Sample.HostComponents.config">
<SubType>Designer</SubType>

View File

@@ -243,6 +243,7 @@ form.link button:hover, button.link:hover {
***************************************************************/
#header {
height:50px;
margin:16px 0 0 0;
background:#f3f4f5;
}
#branding {

View File

@@ -0,0 +1,170 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Threading;
using System.Web;
using System.Web.Hosting;
namespace Orchard.Web {
public class WarmupHttpModule : IHttpModule {
private const string WarmupFilesPath = "~/App_Data/Warmup/";
private HttpApplication _context;
public void Init(HttpApplication context) {
_context = context;
context.AddOnBeginRequestAsync(BeginBeginRequest, EndBeginRequest, null);
}
static IList<Action> _awaiting = new List<Action>();
public static bool InWarmup() {
if (_awaiting == null) return false;
lock (_awaiting) {
return _awaiting != null;
}
}
public static void Signal() {
lock(typeof(WarmupHttpModule)) {
var awaiting = _awaiting;
_awaiting = null;
foreach (var action in awaiting) {
action();
}
}
}
public static void Await(Action action) {
if (_awaiting == null) {
action();
return;
}
lock(typeof(WarmupHttpModule)) {
if (_awaiting == null) {
action();
return;
}
_awaiting.Add(action);
}
}
private IAsyncResult BeginBeginRequest(object sender, EventArgs e, AsyncCallback cb, object extradata) {
var asyncResult = new WarmupAsyncResult(cb);
// host is available, process every requests, or file is processed
if (!InWarmup() || DoBeginRequest()) {
asyncResult.Done();
}
else {
// this is the "on hold" execution path
Await(asyncResult.Done);
}
return asyncResult;
}
private void EndBeginRequest(IAsyncResult ar) {
((WarmupAsyncResult)ar).Wait();
}
public void Dispose() {
}
/// <summary>
/// return true to put request on hold (until call to Signal()) - return false to allow pipeline to execute immediately
/// </summary>
/// <returns></returns>
private bool DoBeginRequest() {
// 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 = ToUrlString(_context.Request);
var virtualFileCopy = WarmupFilesPath + EncodeUrl(url.Trim('/'));
var localCopy = HostingEnvironment.MapPath(virtualFileCopy);
if (localCopy != null && 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.WriteFile(localCopy);
_context.Response.End();
return true;
}
// there is no local copy and the file exists
// serve the static file
if (File.Exists(_context.Request.PhysicalPath)) {
return true;
}
return false;
}
public static string EncodeUrl(string url) {
if(String.IsNullOrWhiteSpace(url)) {
throw new ArgumentException("url can't be empty");
}
var sb = new StringBuilder();
foreach(var c in url.ToLowerInvariant()) {
// only accept alphanumeric chars
if((c >= 'a' && c <= 'z') || (c >= '0' && c <= '9')) {
sb.Append(c);
}
// otherwise encode them in UTF8
else {
sb.Append("_");
foreach(var b in Encoding.UTF8.GetBytes(new [] {c})) {
sb.Append(b.ToString("X"));
}
}
}
return sb.ToString();
}
public static string ToUrlString(HttpRequest request) {
return string.Format("{0}://{1}{2}", request.Url.Scheme, request.Headers["Host"], request.RawUrl);
}
private class WarmupAsyncResult : IAsyncResult {
readonly EventWaitHandle _eventWaitHandle = new AutoResetEvent(false);
private readonly AsyncCallback _cb;
public WarmupAsyncResult(AsyncCallback cb) {
_cb = cb;
IsCompleted = false;
}
public void Done() {
IsCompleted = true;
_eventWaitHandle.Set();
_cb(this);
}
public object AsyncState {
get { return null; }
}
public WaitHandle AsyncWaitHandle {
get { throw new NotImplementedException(); }
}
public bool CompletedSynchronously {
get { return true; }
}
public bool IsCompleted { get; private set; }
public void Wait() {
_eventWaitHandle.WaitOne();
}
}
}
}

View File

@@ -120,6 +120,10 @@
<add path="*" verb="*" type="System.Web.HttpNotFoundHandler"/>
</httpHandlers>
<httpModules>
<add name="WarmupHttpModule" type="Orchard.Web.WarmupHttpModule, Orchard.Web"/>
</httpModules>
</system.web>
<!--
The system.webServer section is required for running ASP.NET AJAX under Internet
@@ -128,7 +132,10 @@
<system.webServer>
<validation validateIntegratedModeConfiguration="false" />
<modules runAllManagedModulesForAllRequests="true" />
<modules runAllManagedModulesForAllRequests="true">
<remove name="WarmupHttpModule" />
<add name="WarmupHttpModule" type="Orchard.Web.WarmupHttpModule, Orchard.Web"/>
</modules>
<handlers accessPolicy="Script">
<!-- clear all handlers, prevents executing code file extensions, prevents returning any file contents -->
<clear/>

View File

@@ -23,7 +23,11 @@ namespace Orchard.Data {
void ITransactionManager.Demand() {
if (_scope == null) {
Logger.Debug("Creating transaction on Demand");
_scope = new TransactionScope(TransactionScopeOption.Required);
_scope = new TransactionScope(
TransactionScopeOption.Required,
new TransactionOptions {
IsolationLevel = IsolationLevel.ReadCommitted
});
}
}

View File

@@ -1,8 +0,0 @@
using System;
namespace Orchard.Environment.Warmup {
public class StartupResult {
public IOrchardHost Host { get; set; }
public Exception Error { get; set; }
}
}

View File

@@ -1,19 +1,29 @@
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));
}
var sb = new StringBuilder();
foreach (var c in url.ToLowerInvariant()) {
// only accept alphanumeric chars
if ((c >= 'a' && c <= 'z') || (c >= '0' && c <= '9')) {
sb.Append(c);
}
// otherwise encode them in UTF8
else {
sb.Append("_");
foreach(var b in Encoding.UTF8.GetBytes(new [] {c})) {
sb.Append(b.ToString("X"));
}
}
}
return sb.ToString();
}
}
}

View File

@@ -24,9 +24,7 @@ namespace Orchard.FileSystems.Dependencies {
public Localizer T { get; set; }
private string PersistencePath {
get {
return _appDataFolder.Combine(BasePath, FileName);
}
get { return _appDataFolder.Combine(BasePath, FileName); }
}
public DependencyDescriptor GetDescriptor(string moduleName) {
@@ -55,10 +53,6 @@ namespace Orchard.FileSystems.Dependencies {
}
}
public IEnumerable<string> GetViewCompilationDependencies() {
yield return _appDataFolder.GetVirtualPath(this.PersistencePath);
}
private IEnumerable<DependencyDescriptor> ReadDependencies(string persistancePath) {
Func<string, XName> ns = (name => XName.Get(name));
Func<XElement, string, string> elem = (e, name) => e.Element(ns(name)).Value;

View File

@@ -23,6 +23,5 @@ namespace Orchard.FileSystems.Dependencies {
DependencyDescriptor GetDescriptor(string moduleName);
IEnumerable<DependencyDescriptor> LoadDescriptors();
void StoreDescriptors(IEnumerable<DependencyDescriptor> dependencyDescriptors);
IEnumerable<string> GetViewCompilationDependencies();
}
}

View File

@@ -84,14 +84,11 @@ namespace Orchard.Mvc.ViewEngines.Razor {
provider.AssemblyBuilder.AddAssemblyReference(assembly);
}
}
foreach (var virtualDependency in entry.dependencies) {
provider.AddVirtualPathDependency(virtualDependency);
}
}
foreach (var virtualDependency in _dependenciesFolder.GetViewCompilationDependencies()) {
provider.AddVirtualPathDependency(virtualDependency);
}
}
private DependencyDescriptor GetModuleDependencyDescriptor(string virtualPath) {

View File

@@ -187,7 +187,6 @@
<Compile Include="Environment\HostComponentsConfigModule.cs" />
<Compile Include="Environment\IOrchardFrameworkAssemblies.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\WorkContextModule.cs" />