mirror of
https://github.com/OrchardCMS/Orchard.git
synced 2025-10-15 19:54:57 +08:00
Merge
--HG-- branch : dev
This commit is contained in:
@@ -293,6 +293,8 @@
|
|||||||
DestinationFolder="$(MsDeployFolder)\Orchard\%(RecursiveDir)"/>
|
DestinationFolder="$(MsDeployFolder)\Orchard\%(RecursiveDir)"/>
|
||||||
<Copy SourceFiles="@(MsDeploy-Parameters)"
|
<Copy SourceFiles="@(MsDeploy-Parameters)"
|
||||||
DestinationFolder="$(MsDeployFolder)"/>
|
DestinationFolder="$(MsDeployFolder)"/>
|
||||||
|
|
||||||
|
<MakeDir Directories="$(MsDeployFolder)\Orchard\bin\HostRestart"/>
|
||||||
</Target>
|
</Target>
|
||||||
|
|
||||||
<Target Name="Package-Zip">
|
<Target Name="Package-Zip">
|
||||||
|
@@ -2,5 +2,6 @@
|
|||||||
<iisapp path="Orchard" managedRuntimeVersion="v4.0" />
|
<iisapp path="Orchard" managedRuntimeVersion="v4.0" />
|
||||||
<setAcl path="Orchard/App_Data" setAclAccess="Modify" />
|
<setAcl path="Orchard/App_Data" setAclAccess="Modify" />
|
||||||
<setAcl path="Orchard/Media" setAclAccess="Modify" />
|
<setAcl path="Orchard/Media" setAclAccess="Modify" />
|
||||||
|
<setAcl path="Orchard/bin/HostRestart" setAclAccess="Modify" />
|
||||||
<dbFullSql path="install.sql" />
|
<dbFullSql path="install.sql" />
|
||||||
</MSDeploy.iisApp>
|
</MSDeploy.iisApp>
|
||||||
|
@@ -13,6 +13,10 @@
|
|||||||
<parameterEntry type="ProviderPath" scope="setAcl" match="Orchard/Media" />
|
<parameterEntry type="ProviderPath" scope="setAcl" match="Orchard/Media" />
|
||||||
</parameter>
|
</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 -->
|
<!-- 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 name="Database Server" description="Location of your database server (i.e. server name, IP address, or server\instance)" defaultValue=".\SQLEXPRESS" tags="SQL, dbServer">
|
||||||
</parameter>
|
</parameter>
|
||||||
|
@@ -4,7 +4,7 @@ using Orchard.Environment.Warmup;
|
|||||||
|
|
||||||
namespace Orchard.Tests.Environment.Warmup {
|
namespace Orchard.Tests.Environment.Warmup {
|
||||||
[TestFixture]
|
[TestFixture]
|
||||||
public class WarmUpUtilityTests {
|
public class WarmupUtilityTests {
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void EmptyStringsAreNotAllowed() {
|
public void EmptyStringsAreNotAllowed() {
|
||||||
|
@@ -240,7 +240,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="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" />
|
||||||
|
@@ -1,22 +1,18 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.IO;
|
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Web;
|
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 StartupResult _startupResult;
|
private static IOrchardHost _host;
|
||||||
private static EventWaitHandle _waitHandle;
|
private static Exception _error;
|
||||||
|
|
||||||
public static void RegisterRoutes(RouteCollection routes) {
|
public static void RegisterRoutes(RouteCollection routes) {
|
||||||
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
|
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
|
||||||
@@ -26,77 +22,30 @@ namespace Orchard.Web {
|
|||||||
LaunchStartupThread();
|
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() {
|
protected void Application_BeginRequest() {
|
||||||
// Host is still starting up?
|
if (_error != null) {
|
||||||
if (_startupResult.Host == null && _startupResult.Error == null) {
|
// Host startup resulted in an error
|
||||||
|
|
||||||
// use the url as it was requested by the client
|
// Throw error once, and restart launch; machine state may have changed
|
||||||
// the real url might be different if it has been translated (proxy, load balancing, ...)
|
// so we need to simulate a "restart".
|
||||||
var url = Request.ToUrlString();
|
var error = _error;
|
||||||
var virtualFileCopy = "~/App_Data/WarmUp/" + WarmupUtility.EncodeUrl(url.Trim('/'));
|
LaunchStartupThread();
|
||||||
var localCopy = HostingEnvironment.MapPath(virtualFileCopy);
|
throw error;
|
||||||
|
|
||||||
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();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else {
|
|
||||||
if (_startupResult.Error != null) {
|
|
||||||
// Host startup resulted in an error
|
|
||||||
|
|
||||||
// Throw error once, and restart launch (machine state may have changed
|
// Only notify if the host has started up
|
||||||
// so we need to simulate a "restart".
|
if (_host == null) {
|
||||||
var error = _startupResult.Error;
|
return;
|
||||||
LaunchStartupThread();
|
|
||||||
throw error;
|
|
||||||
}
|
|
||||||
|
|
||||||
Context.Items["originalHttpContext"] = Context;
|
|
||||||
_startupResult.Host.BeginRequest();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Context.Items["originalHttpContext"] = Context;
|
||||||
|
_host.BeginRequest();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void Application_EndRequest() {
|
protected void Application_EndRequest() {
|
||||||
// Only notify if the host has started up
|
// Only notify if the host has started up
|
||||||
if (_startupResult.Host != null) {
|
if (_host != null) {
|
||||||
_startupResult.Host.EndRequest();
|
_host.EndRequest();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -105,5 +54,31 @@ 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();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <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();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
35
src/Orchard.Web/Modules/Orchard.Experimental/LockLogging.cs
Normal file
35
src/Orchard.Web/Modules/Orchard.Experimental/LockLogging.cs
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -21,3 +21,7 @@ Features:
|
|||||||
Description: Adds some content definitions to help test lists
|
Description: Adds some content definitions to help test lists
|
||||||
Dependencies: Orchard.Lists
|
Dependencies: Orchard.Lists
|
||||||
Category: Developer
|
Category: Developer
|
||||||
|
Orchard.Experimental.LockLogging:
|
||||||
|
Name: Lock Logging
|
||||||
|
Description: Logs extra diagnostic information for Sql CE if a deadlock timeout occurs
|
||||||
|
Category: Developer
|
||||||
|
@@ -39,10 +39,12 @@
|
|||||||
<HintPath>..\..\..\..\lib\autofac\Autofac.dll</HintPath>
|
<HintPath>..\..\..\..\lib\autofac\Autofac.dll</HintPath>
|
||||||
</Reference>
|
</Reference>
|
||||||
<Reference Include="Microsoft.CSharp" />
|
<Reference Include="Microsoft.CSharp" />
|
||||||
|
<Reference Include="NHibernate, Version=2.1.2.4000, Culture=neutral, PublicKeyToken=aa95f207798dfdb4, processorArchitecture=MSIL" />
|
||||||
<Reference Include="System" />
|
<Reference Include="System" />
|
||||||
<Reference Include="System.Core">
|
<Reference Include="System.Core">
|
||||||
<RequiredTargetFramework>3.5</RequiredTargetFramework>
|
<RequiredTargetFramework>3.5</RequiredTargetFramework>
|
||||||
</Reference>
|
</Reference>
|
||||||
|
<Reference Include="System.Data" />
|
||||||
<Reference Include="System.Web.Mvc, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
|
<Reference Include="System.Web.Mvc, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
|
||||||
<SpecificVersion>False</SpecificVersion>
|
<SpecificVersion>False</SpecificVersion>
|
||||||
<HintPath>..\..\..\..\lib\aspnetmvc\System.Web.Mvc.dll</HintPath>
|
<HintPath>..\..\..\..\lib\aspnetmvc\System.Web.Mvc.dll</HintPath>
|
||||||
@@ -62,6 +64,7 @@
|
|||||||
<Compile Include="Controllers\MetadataController.cs" />
|
<Compile Include="Controllers\MetadataController.cs" />
|
||||||
<Compile Include="DebugFilter.cs" />
|
<Compile Include="DebugFilter.cs" />
|
||||||
<Compile Include="Handlers\DebugLinkHandler.cs" />
|
<Compile Include="Handlers\DebugLinkHandler.cs" />
|
||||||
|
<Compile Include="LockLogging.cs" />
|
||||||
<Compile Include="Migrations.cs" />
|
<Compile Include="Migrations.cs" />
|
||||||
<Compile Include="Models\ShowDebugLink.cs" />
|
<Compile Include="Models\ShowDebugLink.cs" />
|
||||||
<Compile Include="Models\Simple.cs" />
|
<Compile Include="Models\Simple.cs" />
|
||||||
|
@@ -60,47 +60,26 @@ namespace Lists.Controllers {
|
|||||||
public ActionResult List(ListContentsViewModel model, PagerParameters pagerParameters) {
|
public ActionResult List(ListContentsViewModel model, PagerParameters pagerParameters) {
|
||||||
var pager = new Pager(_siteService.GetSiteSettings(), pagerParameters);
|
var pager = new Pager(_siteService.GetSiteSettings(), pagerParameters);
|
||||||
var container = model.ContainerId.HasValue ? _contentManager.GetLatest((int)model.ContainerId) : null;
|
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();
|
return HttpNotFound();
|
||||||
}
|
}
|
||||||
var restrictedContentType = container == null ? null : container.As<ContainerPart>().Record.ItemContentType;
|
var restrictedContentType = container.As<ContainerPart>().Record.ItemContentType;
|
||||||
var hasRestriction = !string.IsNullOrEmpty(restrictedContentType);
|
var hasRestriction = !string.IsNullOrEmpty(restrictedContentType);
|
||||||
if (hasRestriction) {
|
if (hasRestriction) {
|
||||||
model.FilterByContentType = restrictedContentType;
|
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.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) {
|
if (!hasRestriction) {
|
||||||
model.Options.FilterOptions = GetContainableTypes()
|
model.Options.FilterOptions = GetContainableTypes()
|
||||||
.Select(ctd => new KeyValuePair<string, string>(ctd.Name, ctd.DisplayName))
|
.Select(ctd => new KeyValuePair<string, string>(ctd.Name, ctd.DisplayName))
|
||||||
@@ -120,7 +99,7 @@ namespace Lists.Controllers {
|
|||||||
.Options(model.Options)
|
.Options(model.Options)
|
||||||
.HasRestriction(hasRestriction)
|
.HasRestriction(hasRestriction)
|
||||||
.ContainerDisplayName(model.ContainerDisplayName)
|
.ContainerDisplayName(model.ContainerDisplayName)
|
||||||
.ContainerContentType(container == null ? null : container.ContentType)
|
.ContainerContentType(container.ContentType)
|
||||||
.ContainerItemContentType(hasRestriction ? restrictedContentType : (model.FilterByContentType ?? ""))
|
.ContainerItemContentType(hasRestriction ? restrictedContentType : (model.FilterByContentType ?? ""))
|
||||||
.OtherLists(_contentManager.Query<ContainerPart>(VersionOptions.Latest).List()
|
.OtherLists(_contentManager.Query<ContainerPart>(VersionOptions.Latest).List()
|
||||||
.Select(part => part.ContentItem)
|
.Select(part => part.ContentItem)
|
||||||
@@ -131,6 +110,32 @@ namespace Lists.Controllers {
|
|||||||
return View((object)viewModel);
|
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")]
|
[HttpPost, ActionName("List")]
|
||||||
[FormValueRequired("submit.BulkEdit")]
|
[FormValueRequired("submit.BulkEdit")]
|
||||||
public ActionResult ListPOST(ContentOptions options, IEnumerable<int> itemIds, int? targetContainerId, string returnUrl) {
|
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"));
|
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) {
|
private bool BulkMoveToList(IEnumerable<int> itemIds, int? targetContainerId) {
|
||||||
if (!targetContainerId.HasValue) {
|
if (!targetContainerId.HasValue) {
|
||||||
Services.Notifier.Information(T("Please select the list to move the items to."));
|
Services.Notifier.Information(T("Please select the list to move the items to."));
|
||||||
@@ -248,23 +366,5 @@ namespace Lists.Controllers {
|
|||||||
return true;
|
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -52,6 +52,7 @@
|
|||||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||||
<Compile Include="Routes.cs" />
|
<Compile Include="Routes.cs" />
|
||||||
<Content Include="Scripts\orchard-lists-admin.js" />
|
<Content Include="Scripts\orchard-lists-admin.js" />
|
||||||
|
<Compile Include="ViewModels\ChooseContentsViewModel.cs" />
|
||||||
<Compile Include="ViewModels\ListContentsViewModel.cs" />
|
<Compile Include="ViewModels\ListContentsViewModel.cs" />
|
||||||
<Content Include="Module.txt" />
|
<Content Include="Module.txt" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
@@ -98,6 +99,9 @@
|
|||||||
<SubType>Designer</SubType>
|
<SubType>Designer</SubType>
|
||||||
</Content>
|
</Content>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<Content Include="Views\Admin\Choose.cshtml" />
|
||||||
|
</ItemGroup>
|
||||||
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
|
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
|
||||||
<Import Project="$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v10.0\WebApplications\Microsoft.WebApplication.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.
|
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
|
||||||
|
@@ -24,24 +24,7 @@ namespace Orchard.Lists {
|
|||||||
{"filterByContentType", ""}
|
{"filterByContentType", ""}
|
||||||
},
|
},
|
||||||
new RouteValueDictionary{
|
new RouteValueDictionary{
|
||||||
{"filterByContentType", @"\w+"},
|
{"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 {
|
|
||||||
{"containerId", @"\d+"}
|
{"containerId", @"\d+"}
|
||||||
},
|
},
|
||||||
new RouteValueDictionary {
|
new RouteValueDictionary {
|
||||||
@@ -53,15 +36,17 @@ namespace Orchard.Lists {
|
|||||||
new RouteDescriptor {
|
new RouteDescriptor {
|
||||||
Priority = 5,
|
Priority = 5,
|
||||||
Route = new Route(
|
Route = new Route(
|
||||||
"Admin/Lists/Orphaned/{filterByContentType}",
|
"Admin/Lists/Choose/From/Orphaned/To/{targetContainerId}/{filterByContentType}",
|
||||||
new RouteValueDictionary {
|
new RouteValueDictionary {
|
||||||
{"area", "Orchard.Lists"},
|
{"area", "Orchard.Lists"},
|
||||||
{"controller", "Admin"},
|
{"controller", "Admin"},
|
||||||
{"action", "List"},
|
{"action", "Choose"},
|
||||||
{"filterByContentType", ""},
|
{"filterByContentType", ""},
|
||||||
|
{"sourceContainerId", "0"}
|
||||||
},
|
},
|
||||||
new RouteValueDictionary{
|
new RouteValueDictionary{
|
||||||
{"filterByContentType", @"\w+"},
|
{"filterByContentType", @"\w*"},
|
||||||
|
{"targetContainerId", @"\d+"},
|
||||||
},
|
},
|
||||||
new RouteValueDictionary {
|
new RouteValueDictionary {
|
||||||
{"area", "Orchard.Lists"}
|
{"area", "Orchard.Lists"}
|
||||||
@@ -71,19 +56,23 @@ namespace Orchard.Lists {
|
|||||||
new RouteDescriptor {
|
new RouteDescriptor {
|
||||||
Priority = 5,
|
Priority = 5,
|
||||||
Route = new Route(
|
Route = new Route(
|
||||||
"Admin/Lists/Orphaned",
|
"Admin/Lists/Choose/From/{sourceContainerId}/To/{targetContainerId}/{filterByContentType}",
|
||||||
new RouteValueDictionary {
|
new RouteValueDictionary {
|
||||||
{"area", "Orchard.Lists"},
|
{"area", "Orchard.Lists"},
|
||||||
{"controller", "Admin"},
|
{"controller", "Admin"},
|
||||||
{"action", "List"},
|
{"action", "Choose"},
|
||||||
|
{"filterByContentType", ""}
|
||||||
|
},
|
||||||
|
new RouteValueDictionary{
|
||||||
|
{"filterByContentType", @"\w*"},
|
||||||
|
{"sourceContainerId", @"\d+"},
|
||||||
|
{"targetContainerId", @"\d+"},
|
||||||
},
|
},
|
||||||
null,
|
|
||||||
new RouteValueDictionary {
|
new RouteValueDictionary {
|
||||||
{"area", "Orchard.Lists"}
|
{"area", "Orchard.Lists"}
|
||||||
},
|
},
|
||||||
new MvcRouteHandler())
|
new MvcRouteHandler())
|
||||||
},
|
},
|
||||||
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -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; }
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@@ -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)
|
||||||
|
}
|
@@ -3,11 +3,11 @@
|
|||||||
@{
|
@{
|
||||||
Script.Include("orchard-lists-admin.js");
|
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();
|
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(
|
var lists = ((IEnumerable<ContentItem>)Model.OtherLists).Select(
|
||||||
contentItem => new SelectListItem {
|
contentItem => new SelectListItem {
|
||||||
@@ -24,22 +24,19 @@
|
|||||||
|
|
||||||
<div> </div>
|
<div> </div>
|
||||||
@Display.Parts_Container_Manage(ContainerDisplayName: Model.ContainerDisplayName, ContainerContentType: Model.ContainerContentType, ContainerId: containerId)
|
@Display.Parts_Container_Manage(ContainerDisplayName: Model.ContainerDisplayName, ContainerContentType: Model.ContainerContentType, ContainerId: containerId)
|
||||||
@if (containerId.HasValue) {
|
|
||||||
<div class="manage">
|
<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>
|
</div>
|
||||||
}
|
|
||||||
@using (Html.BeginFormAntiForgeryPost()) {
|
@using (Html.BeginFormAntiForgeryPost()) {
|
||||||
<fieldset class="bulk-actions">
|
<fieldset class="bulk-actions">
|
||||||
<label for="publishActions">@T("Actions:")</label>
|
<label for="publishActions">@T("Actions:")</label>
|
||||||
<select id="publishActions" name="Options.BulkAction">
|
<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.None, T("Choose action...").ToString())
|
||||||
@Html.SelectOption((ContentsBulkAction)Model.Options.BulkAction, ContentsBulkAction.PublishNow, T("Publish Now").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.Unpublish, T("Unpublish").ToString())
|
||||||
@Html.SelectOption((ContentsBulkAction)Model.Options.BulkAction, ContentsBulkAction.Remove, T("Delete").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.RemoveFromList, T("Remove from List").ToString())
|
||||||
}
|
|
||||||
@Html.SelectOption((ContentsBulkAction)Model.Options.BulkAction, ContentsBulkAction.MoveToList, T("Move to List...").ToString())
|
@Html.SelectOption((ContentsBulkAction)Model.Options.BulkAction, ContentsBulkAction.MoveToList, T("Move to List...").ToString())
|
||||||
</select>
|
</select>
|
||||||
@Html.DropDownList("TargetContainerId", lists, new { id = "TargetContainerId" })
|
@Html.DropDownList("TargetContainerId", lists, new { id = "TargetContainerId" })
|
||||||
|
@@ -1,10 +1,7 @@
|
|||||||
<div class="item-properties actions">
|
<div class="item-properties actions">
|
||||||
<p>
|
<p>
|
||||||
@Html.ActionLink(T("Show Other Lists").ToString(), "List", new { Area = "Contents", Id = Model.ContainerContentType }) |
|
@Html.ActionLink(T("Choose Items").ToString(), "Choose", new { TargetContainerId = (int)Model.ContainerId, Area = "Orchard.Lists" })
|
||||||
@Html.ActionLink(T("Show Orphaned Content Items").ToString(), "Orphaned", new { Area = "Orchard.Lists" })
|
<text>|</text>
|
||||||
@if (((int?)Model.ContainerId).HasValue) {
|
@Html.ActionLink(T("{0} Properties", (string)Model.ContainerContentType).ToString(), "Edit", new { Area = "Contents", Id = (int)Model.ContainerId, ReturnUrl = Html.ViewContext.HttpContext.Request.RawUrl })
|
||||||
<text>|</text>
|
|
||||||
@Html.ActionLink(T("{0} Properties", (string)Model.ContainerContentType).ToString(), "Edit", new { Area = "Contents", Id = (int)Model.ContainerId })
|
|
||||||
}
|
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
@@ -127,6 +127,7 @@
|
|||||||
<Compile Include="Global.asax.cs">
|
<Compile Include="Global.asax.cs">
|
||||||
<DependentUpon>Global.asax</DependentUpon>
|
<DependentUpon>Global.asax</DependentUpon>
|
||||||
</Compile>
|
</Compile>
|
||||||
|
<Compile Include="WarmupHttpModule.cs" />
|
||||||
<Content Include="Config\log4net.config" />
|
<Content Include="Config\log4net.config" />
|
||||||
<Content Include="Config\Sample.HostComponents.config">
|
<Content Include="Config\Sample.HostComponents.config">
|
||||||
<SubType>Designer</SubType>
|
<SubType>Designer</SubType>
|
||||||
|
@@ -243,6 +243,7 @@ form.link button:hover, button.link:hover {
|
|||||||
***************************************************************/
|
***************************************************************/
|
||||||
#header {
|
#header {
|
||||||
height:50px;
|
height:50px;
|
||||||
|
margin:16px 0 0 0;
|
||||||
background:#f3f4f5;
|
background:#f3f4f5;
|
||||||
}
|
}
|
||||||
#branding {
|
#branding {
|
||||||
|
170
src/Orchard.Web/WarmupHttpModule.cs
Normal file
170
src/Orchard.Web/WarmupHttpModule.cs
Normal 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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -120,6 +120,10 @@
|
|||||||
<add path="*" verb="*" type="System.Web.HttpNotFoundHandler"/>
|
<add path="*" verb="*" type="System.Web.HttpNotFoundHandler"/>
|
||||||
|
|
||||||
</httpHandlers>
|
</httpHandlers>
|
||||||
|
|
||||||
|
<httpModules>
|
||||||
|
<add name="WarmupHttpModule" type="Orchard.Web.WarmupHttpModule, Orchard.Web"/>
|
||||||
|
</httpModules>
|
||||||
</system.web>
|
</system.web>
|
||||||
<!--
|
<!--
|
||||||
The system.webServer section is required for running ASP.NET AJAX under Internet
|
The system.webServer section is required for running ASP.NET AJAX under Internet
|
||||||
@@ -128,7 +132,10 @@
|
|||||||
<system.webServer>
|
<system.webServer>
|
||||||
|
|
||||||
<validation validateIntegratedModeConfiguration="false" />
|
<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">
|
<handlers accessPolicy="Script">
|
||||||
<!-- clear all handlers, prevents executing code file extensions, prevents returning any file contents -->
|
<!-- clear all handlers, prevents executing code file extensions, prevents returning any file contents -->
|
||||||
<clear/>
|
<clear/>
|
||||||
|
@@ -23,7 +23,11 @@ namespace Orchard.Data {
|
|||||||
void ITransactionManager.Demand() {
|
void ITransactionManager.Demand() {
|
||||||
if (_scope == null) {
|
if (_scope == null) {
|
||||||
Logger.Debug("Creating transaction on Demand");
|
Logger.Debug("Creating transaction on Demand");
|
||||||
_scope = new TransactionScope(TransactionScopeOption.Required);
|
_scope = new TransactionScope(
|
||||||
|
TransactionScopeOption.Required,
|
||||||
|
new TransactionOptions {
|
||||||
|
IsolationLevel = IsolationLevel.ReadCommitted
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -1,8 +0,0 @@
|
|||||||
using System;
|
|
||||||
|
|
||||||
namespace Orchard.Environment.Warmup {
|
|
||||||
public class StartupResult {
|
|
||||||
public IOrchardHost Host { get; set; }
|
|
||||||
public Exception Error { get; set; }
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,19 +1,29 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Linq;
|
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Text.RegularExpressions;
|
|
||||||
|
|
||||||
namespace Orchard.Environment.Warmup {
|
namespace Orchard.Environment.Warmup {
|
||||||
public static class WarmupUtility {
|
public static class WarmupUtility {
|
||||||
private const string EncodingPattern = "[^a-z0-9]";
|
|
||||||
|
|
||||||
public static string EncodeUrl(string url) {
|
public static string EncodeUrl(string url) {
|
||||||
if(String.IsNullOrWhiteSpace(url)) {
|
if(String.IsNullOrWhiteSpace(url)) {
|
||||||
throw new ArgumentException("url can't be empty");
|
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();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -24,9 +24,7 @@ namespace Orchard.FileSystems.Dependencies {
|
|||||||
public Localizer T { get; set; }
|
public Localizer T { get; set; }
|
||||||
|
|
||||||
private string PersistencePath {
|
private string PersistencePath {
|
||||||
get {
|
get { return _appDataFolder.Combine(BasePath, FileName); }
|
||||||
return _appDataFolder.Combine(BasePath, FileName);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public DependencyDescriptor GetDescriptor(string moduleName) {
|
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) {
|
private IEnumerable<DependencyDescriptor> ReadDependencies(string persistancePath) {
|
||||||
Func<string, XName> ns = (name => XName.Get(name));
|
Func<string, XName> ns = (name => XName.Get(name));
|
||||||
Func<XElement, string, string> elem = (e, name) => e.Element(ns(name)).Value;
|
Func<XElement, string, string> elem = (e, name) => e.Element(ns(name)).Value;
|
||||||
|
@@ -23,6 +23,5 @@ namespace Orchard.FileSystems.Dependencies {
|
|||||||
DependencyDescriptor GetDescriptor(string moduleName);
|
DependencyDescriptor GetDescriptor(string moduleName);
|
||||||
IEnumerable<DependencyDescriptor> LoadDescriptors();
|
IEnumerable<DependencyDescriptor> LoadDescriptors();
|
||||||
void StoreDescriptors(IEnumerable<DependencyDescriptor> dependencyDescriptors);
|
void StoreDescriptors(IEnumerable<DependencyDescriptor> dependencyDescriptors);
|
||||||
IEnumerable<string> GetViewCompilationDependencies();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -84,14 +84,11 @@ namespace Orchard.Mvc.ViewEngines.Razor {
|
|||||||
provider.AssemblyBuilder.AddAssemblyReference(assembly);
|
provider.AssemblyBuilder.AddAssemblyReference(assembly);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach (var virtualDependency in entry.dependencies) {
|
foreach (var virtualDependency in entry.dependencies) {
|
||||||
provider.AddVirtualPathDependency(virtualDependency);
|
provider.AddVirtualPathDependency(virtualDependency);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach (var virtualDependency in _dependenciesFolder.GetViewCompilationDependencies()) {
|
|
||||||
provider.AddVirtualPathDependency(virtualDependency);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private DependencyDescriptor GetModuleDependencyDescriptor(string virtualPath) {
|
private DependencyDescriptor GetModuleDependencyDescriptor(string virtualPath) {
|
||||||
|
@@ -187,7 +187,6 @@
|
|||||||
<Compile Include="Environment\HostComponentsConfigModule.cs" />
|
<Compile Include="Environment\HostComponentsConfigModule.cs" />
|
||||||
<Compile Include="Environment\IOrchardFrameworkAssemblies.cs" />
|
<Compile Include="Environment\IOrchardFrameworkAssemblies.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\Warmup\WarmupUtility.cs" />
|
||||||
<Compile Include="Environment\WorkContextImplementation.cs" />
|
<Compile Include="Environment\WorkContextImplementation.cs" />
|
||||||
<Compile Include="Environment\WorkContextModule.cs" />
|
<Compile Include="Environment\WorkContextModule.cs" />
|
||||||
|
Reference in New Issue
Block a user