2012-08-01 22:06:15 +06:00
using System ;
2012-08-07 21:40:34 +06:00
using System.Collections.Generic ;
2012-09-26 11:04:07 +07:00
using System.Linq ;
2013-03-11 23:46:47 +06:00
using System.Web ;
2013-02-26 03:45:39 +06:00
using System.Web.Http ;
2012-08-07 21:40:34 +06:00
using System.Web.Mvc ;
using System.Web.Routing ;
2013-05-12 19:05:49 -10:00
using StackExchange.Profiling.MVCHelpers ;
2012-08-01 22:06:15 +06:00
using Umbraco.Core ;
2012-09-25 13:09:59 +07:00
using Umbraco.Core.Configuration ;
2012-08-15 23:20:37 +06:00
using Umbraco.Core.Dictionary ;
2013-02-12 03:46:27 +06:00
using Umbraco.Core.Logging ;
2013-01-29 09:45:12 +06:00
using Umbraco.Core.ObjectResolution ;
2013-05-12 15:25:46 -10:00
using Umbraco.Core.Profiling ;
2012-09-08 07:13:03 +07:00
using Umbraco.Core.PropertyEditors ;
2013-09-23 16:30:24 +02:00
using Umbraco.Core.PropertyEditors.ValueConverters ;
2013-02-12 03:46:27 +06:00
using Umbraco.Core.Sync ;
2012-08-15 23:20:37 +06:00
using Umbraco.Web.Dictionary ;
2012-11-21 07:14:40 -01:00
using Umbraco.Web.Media ;
2012-08-04 06:20:06 +06:00
using Umbraco.Web.Media.ThumbnailProviders ;
2012-10-24 09:59:23 +05:00
using Umbraco.Web.Models ;
2012-08-07 21:40:34 +06:00
using Umbraco.Web.Mvc ;
2012-09-08 07:13:03 +07:00
using Umbraco.Web.PropertyEditors ;
2013-09-23 16:30:24 +02:00
using Umbraco.Web.PropertyEditors.ValueConverters ;
2013-02-05 06:31:13 -01:00
using Umbraco.Web.PublishedCache ;
2012-08-01 22:06:15 +06:00
using Umbraco.Web.Routing ;
2013-02-26 22:14:32 +06:00
using Umbraco.Web.WebApi ;
2013-02-12 03:46:27 +06:00
using umbraco.BusinessLogic ;
2013-02-06 09:53:13 +06:00
using umbraco.presentation.cache ;
2013-10-02 17:48:44 +02:00
using ProfilingViewEngine = Umbraco . Core . Profiling . ProfilingViewEngine ;
2012-08-01 22:06:15 +06:00
2012-09-25 13:09:59 +07:00
2012-08-01 22:06:15 +06:00
namespace Umbraco.Web
{
2013-01-18 08:47:38 -01:00
/// <summary>
/// A bootstrapper for the Umbraco application which initializes all objects including the Web portion of the application
/// </summary>
2013-01-27 12:59:41 -01:00
public class WebBootManager : CoreBootManager
2013-01-18 08:47:38 -01:00
{
private readonly bool _isForTesting ;
2012-08-14 12:03:34 +06:00
2013-06-18 14:59:00 +10:00
public WebBootManager ( UmbracoApplicationBase umbracoApplication )
2013-01-18 08:47:38 -01:00
: this ( umbracoApplication , false )
{
2013-06-18 14:59:00 +10:00
2013-01-18 08:47:38 -01:00
}
2012-10-02 02:33:19 +05:00
2013-01-18 08:47:38 -01:00
/// <summary>
/// Constructor for unit tests, ensures some resolvers are not initialized
/// </summary>
/// <param name="umbracoApplication"></param>
/// <param name="isForTesting"></param>
2013-06-18 14:59:00 +10:00
internal WebBootManager ( UmbracoApplicationBase umbracoApplication , bool isForTesting )
2013-01-29 09:45:12 +06:00
: base ( umbracoApplication )
2013-01-18 08:47:38 -01:00
{
2013-06-18 14:59:00 +10:00
_isForTesting = isForTesting ;
2013-01-18 08:47:38 -01:00
}
2012-08-01 22:06:15 +06:00
2013-01-18 08:47:38 -01:00
/// <summary>
/// Initialize objects before anything during the boot cycle happens
/// </summary>
/// <returns></returns>
public override IBootManager Initialize ()
2013-06-18 14:59:00 +10:00
{
2013-01-18 08:47:38 -01:00
base . Initialize ();
2012-08-01 22:06:15 +06:00
2013-01-18 08:47:38 -01:00
// Backwards compatibility - set the path and URL type for ClientDependency 1.5.1 [LK]
ClientDependency . Core . CompositeFiles . Providers . XmlFileMapper . FileMapVirtualFolder = "~/App_Data/TEMP/ClientDependency" ;
ClientDependency . Core . CompositeFiles . Providers . BaseCompositeFileProcessingProvider . UrlTypeDefault = ClientDependency . Core . CompositeFiles . Providers . CompositeUrlType . Base64QueryStrings ;
2012-08-07 21:40:34 +06:00
2013-01-18 08:47:38 -01:00
//set master controller factory
ControllerBuilder . Current . SetControllerFactory (
new MasterControllerFactory ( FilteredControllerFactoriesResolver . Current ));
2012-08-07 21:40:34 +06:00
2013-01-18 08:47:38 -01:00
//set the render view engine
2013-10-02 17:48:44 +02:00
ViewEngines . Engines . Add ( new RenderViewEngine ());
2013-01-18 08:47:38 -01:00
//set the plugin view engine
2013-10-02 17:48:44 +02:00
ViewEngines . Engines . Add ( new PluginViewEngine ());
2013-01-18 08:47:38 -01:00
//set model binder
ModelBinders . Binders . Add ( new KeyValuePair < Type , IModelBinder >( typeof ( RenderModel ), new RenderModelBinder ()));
2012-08-07 21:40:34 +06:00
2013-05-12 19:05:49 -10:00
//add the profiling action filter
GlobalFilters . Filters . Add ( new ProfilingActionFilter ());
2013-01-18 08:47:38 -01:00
return this ;
}
2012-08-14 12:03:34 +06:00
2013-03-11 23:46:47 +06:00
/// <summary>
/// Override this method in order to ensure that the UmbracoContext is also created, this can only be
/// created after resolution is frozen!
/// </summary>
protected override void FreezeResolution ()
{
base . FreezeResolution ();
//before we do anything, we'll ensure the umbraco context
//see: http://issues.umbraco.org/issue/U4-1717
UmbracoContext . EnsureContext ( new HttpContextWrapper ( UmbracoApplication . Context ), ApplicationContext );
}
2013-05-13 21:11:03 -10:00
/// <summary>
/// Ensure the current profiler is the web profiler
/// </summary>
protected override void InitializeProfilerResolver ()
{
base . InitializeProfilerResolver ();
//Set the profiler to be the web profiler
ProfilerResolver . Current . SetProfiler ( new WebProfiler ());
}
2013-01-18 08:47:38 -01:00
/// <summary>
2013-01-29 09:45:12 +06:00
/// Adds custom types to the ApplicationEventsResolver
2013-01-18 08:47:38 -01:00
/// </summary>
2013-01-29 09:45:12 +06:00
protected override void InitializeApplicationEventsResolver ()
2013-01-18 08:47:38 -01:00
{
2013-01-29 09:45:12 +06:00
base . InitializeApplicationEventsResolver ();
ApplicationEventsResolver . Current . AddType < CacheHelperExtensions . CacheHelperApplicationEventListener >();
ApplicationEventsResolver . Current . AddType < LegacyScheduledTasks >();
2013-02-06 09:53:13 +06:00
//We need to remove these types because we've obsoleted them and we don't want them executing:
ApplicationEventsResolver . Current . RemoveType < global :: umbraco . LibraryCacheRefresher >();
2013-01-18 08:47:38 -01:00
}
2012-08-14 12:03:34 +06:00
2013-01-18 08:47:38 -01:00
/// <summary>
/// Ensure that the OnApplicationStarted methods of the IApplicationEvents are called
/// </summary>
/// <param name="afterComplete"></param>
/// <returns></returns>
public override IBootManager Complete ( Action < ApplicationContext > afterComplete )
{
2013-10-02 17:48:44 +02:00
//Wrap viewengines in the profiling engine
IViewEngine [] engines = ViewEngines . Engines . Select ( e => e ). ToArray ();
ViewEngines . Engines . Clear ();
foreach ( var engine in engines )
{
2013-10-02 18:30:59 +02:00
var wrappedEngine = engine is ProfilingViewEngine ? engine : new ProfilingViewEngine ( engine );
ViewEngines . Engines . Add ( wrappedEngine );
2013-10-02 17:48:44 +02:00
}
//set routes
2013-01-18 08:47:38 -01:00
CreateRoutes ();
2012-09-25 13:09:59 +07:00
2013-01-18 08:47:38 -01:00
base . Complete ( afterComplete );
2012-09-29 07:20:23 +07:00
2013-02-14 23:26:29 +06:00
//Now, startup all of our legacy startup handler
2013-04-16 04:27:03 -02:00
ApplicationEventsResolver . Current . InstantiateLegacyStartupHandlers ();
2013-02-14 23:26:29 +06:00
2013-01-18 08:47:38 -01:00
return this ;
}
2012-08-01 22:06:15 +06:00
2013-01-18 08:47:38 -01:00
/// <summary>
/// Creates the routes
/// </summary>
protected internal void CreateRoutes ()
{
var umbracoPath = GlobalSettings . UmbracoMvcArea ;
2012-09-25 13:09:59 +07:00
2013-01-18 08:47:38 -01:00
//Create the front-end route
var defaultRoute = RouteTable . Routes . MapRoute (
"Umbraco_default" ,
2013-02-19 06:18:51 +06:00
umbracoPath + "/RenderMvc/{action}/{id}" ,
2013-01-18 08:47:38 -01:00
new { controller = "RenderMvc" , action = "Index" , id = UrlParameter . Optional }
);
defaultRoute . RouteHandler = new RenderRouteHandler ( ControllerBuilder . Current . GetControllerFactory ());
2012-09-25 13:09:59 +07:00
2013-06-18 14:59:00 +10:00
//register all back office routes
RouteBackOfficeControllers ();
2012-12-30 03:11:21 +03:00
2013-06-18 14:59:00 +10:00
//plugin controllers must come first because the next route will catch many things
2013-02-26 03:45:39 +06:00
RoutePluginControllers ();
}
2012-12-30 03:11:21 +03:00
2013-06-18 14:59:00 +10:00
private void RouteBackOfficeControllers ()
{
var backOfficeArea = new BackOfficeArea ();
RouteTable . Routes . RegisterArea ( backOfficeArea );
}
2013-02-26 03:45:39 +06:00
private void RoutePluginControllers ()
{
var umbracoPath = GlobalSettings . UmbracoMvcArea ;
2012-09-26 11:04:07 +07:00
2013-02-26 03:45:39 +06:00
//we need to find the plugin controllers and route them
var pluginControllers =
SurfaceControllerResolver . Current . RegisteredSurfaceControllers . Concat (
UmbracoApiControllerResolver . Current . RegisteredUmbracoApiControllers ). ToArray ();
//local controllers do not contain the attribute
var localControllers = pluginControllers . Where ( x => PluginController . GetMetadata ( x ). AreaName . IsNullOrWhiteSpace ());
foreach ( var s in localControllers )
2013-01-18 08:47:38 -01:00
{
2013-02-26 03:45:39 +06:00
if ( TypeHelper . IsTypeAssignableFrom < SurfaceController >( s ))
{
RouteLocalSurfaceController ( s , umbracoPath );
}
else if ( TypeHelper . IsTypeAssignableFrom < UmbracoApiController >( s ))
{
RouteLocalApiController ( s , umbracoPath );
}
2013-01-18 08:47:38 -01:00
}
2012-08-14 23:35:34 +06:00
2013-01-18 08:47:38 -01:00
//need to get the plugin controllers that are unique to each area (group by)
2013-02-26 03:45:39 +06:00
var pluginSurfaceControlleres = pluginControllers . Where ( x => ! PluginController . GetMetadata ( x ). AreaName . IsNullOrWhiteSpace ());
2013-01-18 08:47:38 -01:00
var groupedAreas = pluginSurfaceControlleres . GroupBy ( controller => PluginController . GetMetadata ( controller ). AreaName );
//loop through each area defined amongst the controllers
foreach ( var g in groupedAreas )
{
//create an area for the controllers (this will throw an exception if all controllers are not in the same area)
var pluginControllerArea = new PluginControllerArea ( g . Select ( PluginController . GetMetadata ));
//register it
RouteTable . Routes . RegisterArea ( pluginControllerArea );
}
}
2012-09-25 13:09:59 +07:00
2013-02-26 03:45:39 +06:00
private void RouteLocalApiController ( Type controller , string umbracoPath )
{
var meta = PluginController . GetMetadata ( controller );
var route = RouteTable . Routes . MapHttpRoute (
string . Format ( "umbraco-{0}-{1}" , "api" , meta . ControllerName ),
2013-06-18 15:46:36 +10:00
umbracoPath + "/Api/" + meta . ControllerName + "/{action}/{id}" , //url to match
new { controller = meta . ControllerName , id = UrlParameter . Optional });
2013-02-26 03:45:39 +06:00
//web api routes don't set the data tokens object
if ( route . DataTokens == null )
{
route . DataTokens = new RouteValueDictionary ();
}
2013-04-15 21:44:26 +06:00
route . DataTokens . Add ( "Namespaces" , new [] { meta . ControllerNamespace }); //look in this namespace to create the controller
2013-06-18 15:24:44 +10:00
route . DataTokens . Add ( "UseNamespaceFallback" , false ); //Don't look anywhere else except this namespace!
2013-02-26 03:45:39 +06:00
route . DataTokens . Add ( "umbraco" , "api" ); //ensure the umbraco token is set
}
private void RouteLocalSurfaceController ( Type controller , string umbracoPath )
{
var meta = PluginController . GetMetadata ( controller );
var route = RouteTable . Routes . MapRoute (
string . Format ( "umbraco-{0}-{1}" , "surface" , meta . ControllerName ),
umbracoPath + "/Surface/" + meta . ControllerName + "/{action}/{id}" , //url to match
new { controller = meta . ControllerName , action = "Index" , id = UrlParameter . Optional },
new [] { meta . ControllerNamespace }); //look in this namespace to create the controller
route . DataTokens . Add ( "umbraco" , "surface" ); //ensure the umbraco token is set
2013-06-18 15:24:44 +10:00
route . DataTokens . Add ( "UseNamespaceFallback" , false ); //Don't look anywhere else except this namespace!
2013-02-26 03:45:39 +06:00
//make it use our custom/special SurfaceMvcHandler
route . RouteHandler = new SurfaceRouteHandler ();
}
2012-11-12 10:50:23 +05:00
2013-01-18 08:47:38 -01:00
/// <summary>
/// Initializes all web based and core resolves
/// </summary>
protected override void InitializeResolvers ()
{
base . InitializeResolvers ();
2013-02-06 09:53:13 +06:00
2013-04-28 16:09:24 -10:00
//set the default RenderMvcController
DefaultRenderMvcControllerResolver . Current = new DefaultRenderMvcControllerResolver ( typeof ( RenderMvcController ));
2013-03-16 01:37:05 +06:00
//Override the ServerMessengerResolver to set a username/password for the distributed calls
ServerMessengerResolver . Current . SetServerMessenger ( new DefaultServerMessenger (() =>
2013-02-12 03:46:27 +06:00
{
2013-03-16 01:37:05 +06:00
//we should not proceed to change this if the app/database is not configured since there will
// be no user, plus we don't need to have server messages sent if this is the case.
if ( ApplicationContext . IsConfigured && ApplicationContext . DatabaseContext . IsDatabaseConfigured )
{
try
{
var user = User . GetUser ( UmbracoSettings . DistributedCallUser );
return new System . Tuple < string , string >( user . LoginName , user . GetPassword ());
}
catch ( Exception e )
{
LogHelper . Error < WebBootManager >( "An error occurred trying to set the IServerMessenger during application startup" , e );
return null ;
}
2013-02-12 03:46:27 +06:00
}
2013-03-16 01:37:05 +06:00
LogHelper . Warn < WebBootManager >( "Could not initialize the DefaultServerMessenger, the application is not configured or the database is not configured" );
return null ;
}));
2013-02-12 03:46:27 +06:00
2013-02-06 09:53:13 +06:00
//We are going to manually remove a few cache refreshers here because we've obsoleted them and we don't want them
// to be registered more than once
CacheRefreshersResolver . Current . RemoveType < pageRefresher >();
CacheRefreshersResolver . Current . RemoveType < global :: umbraco . presentation . cache . MediaLibraryRefreshers >();
CacheRefreshersResolver . Current . RemoveType < global :: umbraco . presentation . cache . MemberLibraryRefreshers >();
CacheRefreshersResolver . Current . RemoveType < global :: umbraco . templateCacheRefresh >();
CacheRefreshersResolver . Current . RemoveType < global :: umbraco . macroCacheRefresh >();
2013-02-02 07:06:27 +06:00
2013-01-18 08:47:38 -01:00
SurfaceControllerResolver . Current = new SurfaceControllerResolver (
PluginManager . Current . ResolveSurfaceControllers ());
2012-08-10 13:38:02 +06:00
2013-02-26 03:45:39 +06:00
UmbracoApiControllerResolver . Current = new UmbracoApiControllerResolver (
PluginManager . Current . ResolveUmbracoApiControllers ());
2013-09-23 16:30:24 +02:00
// both TinyMceValueConverter (in Core) and RteMacroRenderingValueConverter (in Web) will be
// discovered when CoreBootManager configures the converters. We HAVE to remove one of them
// here because there cannot be two converters for one property editor - and we want the full
// RteMacroRenderingValueConverter that converts macros, etc. So remove TinyMceValueConverter.
// (why it exists in in the first place, I'm not sure to understand)
2013-09-05 17:47:13 +02:00
PropertyValueConvertersResolver . Current . RemoveType < TinyMceValueConverter >();
2013-01-18 08:47:38 -01:00
2013-03-31 18:40:55 -02:00
PublishedCachesResolver . Current = new PublishedCachesResolver ( new PublishedCaches (
new PublishedCache . XmlPublishedCache . PublishedContentCache (),
new PublishedCache . XmlPublishedCache . PublishedMediaCache ()));
2013-01-18 08:47:38 -01:00
FilteredControllerFactoriesResolver . Current = new FilteredControllerFactoriesResolver (
2013-01-31 10:06:25 -01:00
// add all known factories, devs can then modify this list on application
// startup either by binding to events or in their own global.asax
2013-01-18 08:47:38 -01:00
new []
2012-08-07 21:40:34 +06:00
{
typeof ( RenderControllerFactory )
});
2013-01-31 10:06:25 -01:00
UrlProviderResolver . Current = new UrlProviderResolver (
2013-02-19 06:37:25 -01:00
//typeof(AliasUrlProvider), // not enabled by default
2013-01-31 10:06:25 -01:00
typeof ( DefaultUrlProvider )
);
2013-08-26 15:47:48 +02:00
ContentLastChanceFinderResolver . Current = new ContentLastChanceFinderResolver (
// handled by ContentLastChanceFinderByNotFoundHandlers for the time being
// soon as we get rid of INotFoundHandler support, we must enable this
//new ContentFinderByLegacy404()
// implement INotFoundHandler support... remove once we get rid of it
new ContentLastChanceFinderByNotFoundHandlers ());
2012-08-01 22:06:15 +06:00
2013-02-02 10:22:19 -01:00
ContentFinderResolver . Current = new ContentFinderResolver (
2013-08-26 15:47:48 +02:00
// all built-in finders in the correct order, devs can then modify this list
// on application startup via an application event handler.
typeof ( ContentFinderByPageIdQuery ),
typeof ( ContentFinderByNiceUrl ),
typeof ( ContentFinderByIdPath ),
// these will be handled by ContentFinderByNotFoundHandlers so they can be enabled/disabled
// via the config file... soon as we get rid of INotFoundHandler support, we must enable
// them here.
//typeof (ContentFinderByNiceUrlAndTemplate),
//typeof (ContentFinderByProfile),
//typeof (ContentFinderByUrlAlias),
// implement INotFoundHandler support... remove once we get rid of it
typeof ( ContentFinderByNotFoundHandlers )
);
2012-08-04 06:20:06 +06:00
2013-02-19 06:37:25 -01:00
SiteDomainHelperResolver . Current = new SiteDomainHelperResolver ( new SiteDomainHelper ());
2013-03-31 18:40:55 -02:00
// ain't that a bit dirty?
PublishedCache . XmlPublishedCache . PublishedContentCache . UnitTesting = _isForTesting ;
2012-08-04 06:20:06 +06:00
2013-01-18 08:47:38 -01:00
ThumbnailProvidersResolver . Current = new ThumbnailProvidersResolver (
PluginManager . Current . ResolveThumbnailProviders ());
2012-08-15 23:20:37 +06:00
2012-11-21 07:14:40 -01:00
ImageUrlProviderResolver . Current = new ImageUrlProviderResolver (
PluginManager . Current . ResolveImageUrlProviders ());
2013-01-18 08:47:38 -01:00
CultureDictionaryFactoryResolver . Current = new CultureDictionaryFactoryResolver (
new DefaultCultureDictionaryFactory ());
}
2012-08-01 22:06:15 +06:00
2013-01-18 08:47:38 -01:00
}
2012-08-01 22:06:15 +06:00
}