diff --git a/src/Umbraco.Core/CoreBootManager.cs b/src/Umbraco.Core/CoreBootManager.cs
index d4aa83e342..8f962ae7c7 100644
--- a/src/Umbraco.Core/CoreBootManager.cs
+++ b/src/Umbraco.Core/CoreBootManager.cs
@@ -1,7 +1,10 @@
using System;
using System.Collections.Generic;
+using System.Linq;
+using AutoMapper;
using Umbraco.Core.Configuration;
using Umbraco.Core.Logging;
+using Umbraco.Core.Models.Mapping;
using Umbraco.Core.ObjectResolution;
using Umbraco.Core.Persistence;
using Umbraco.Core.Persistence.Mappers;
@@ -100,9 +103,19 @@ namespace Umbraco.Core
///
/// This method allows for configuration of model mappers
///
- protected virtual void InitializeModelMappers()
+ ///
+ /// Model mappers MUST be defined on ApplicationEventHandler instances with the interface IMapperConfiguration.
+ /// This allows us to search for less types on startup.
+ ///
+ protected void InitializeModelMappers()
{
- //TODO: There will most likely be AutoMapper configs to put in here, we know they exist in web for now so we'll leave this here for future use
+ Mapper.Initialize(configuration =>
+ {
+ foreach (var m in ApplicationEventsResolver.Current.ApplicationEventHandlers.OfType())
+ {
+ m.ConfigureMappings(configuration);
+ }
+ });
}
///
@@ -135,8 +148,6 @@ namespace Umbraco.Core
{
CanResolveBeforeFrozen = true
};
- //add custom types here that are internal
- ApplicationEventsResolver.Current.AddType();
}
///
@@ -280,10 +291,6 @@ namespace Umbraco.Core
PropertyEditorValueConvertersResolver.Current = new PropertyEditorValueConvertersResolver(
PluginManager.Current.ResolvePropertyEditorValueConverters());
- //add the internal ones, these are not public currently so need to add them manually
- PropertyEditorValueConvertersResolver.Current.AddType();
- PropertyEditorValueConvertersResolver.Current.AddType();
- PropertyEditorValueConvertersResolver.Current.AddType();
// this is how we'd switch over to DefaultShortStringHelper _and_ still use
// UmbracoSettings UrlReplaceCharacters...
diff --git a/src/Umbraco.Core/ModelMapperHelper.cs b/src/Umbraco.Core/ModelMapperHelper.cs
index efd843a059..584ce5b108 100644
--- a/src/Umbraco.Core/ModelMapperHelper.cs
+++ b/src/Umbraco.Core/ModelMapperHelper.cs
@@ -7,9 +7,9 @@ namespace Umbraco.Core
///
internal static class ModelMapperHelper
{
- internal static IMappingExpression SelfMap()
+ internal static IMappingExpression SelfMap(this IConfiguration config)
{
- return Mapper.CreateMap();
+ return config.CreateMap();
}
}
}
\ No newline at end of file
diff --git a/src/Umbraco.Core/Models/Mapping/IMapperConfiguration.cs b/src/Umbraco.Core/Models/Mapping/IMapperConfiguration.cs
new file mode 100644
index 0000000000..0ca76417de
--- /dev/null
+++ b/src/Umbraco.Core/Models/Mapping/IMapperConfiguration.cs
@@ -0,0 +1,22 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using AutoMapper;
+
+namespace Umbraco.Core.Models.Mapping
+{
+ ///
+ /// This is used to explicitly decorate any ApplicationEventHandler class if there are
+ /// AutoMapper configurations defined.
+ ///
+ ///
+ /// All automapper configurations are done during startup
+ /// inside an Automapper Initialize call which is better for performance
+ ///
+ internal interface IMapperConfiguration : IApplicationEventHandler
+ {
+ void ConfigureMappings(IConfiguration config);
+ }
+}
diff --git a/src/Umbraco.Core/Models/Mapping/MapperConfiguration.cs b/src/Umbraco.Core/Models/Mapping/MapperConfiguration.cs
new file mode 100644
index 0000000000..8bde82d13f
--- /dev/null
+++ b/src/Umbraco.Core/Models/Mapping/MapperConfiguration.cs
@@ -0,0 +1,15 @@
+using AutoMapper;
+
+namespace Umbraco.Core.Models.Mapping
+{
+ ///
+ /// Used to declare a mapper configuration
+ ///
+ ///
+ /// Use this class if your mapper configuration isn't also explicitly an ApplicationEventHandler.
+ ///
+ internal abstract class MapperConfiguration : ApplicationEventHandler, IMapperConfiguration
+ {
+ public abstract void ConfigureMappings(IConfiguration config);
+ }
+}
\ No newline at end of file
diff --git a/src/Umbraco.Core/Sections/Section.cs b/src/Umbraco.Core/Sections/Section.cs
new file mode 100644
index 0000000000..b39cbe009d
--- /dev/null
+++ b/src/Umbraco.Core/Sections/Section.cs
@@ -0,0 +1,23 @@
+namespace Umbraco.Core.Sections
+{
+ public class Section
+ {
+ public Section(string name, string @alias, string icon, int sortOrder)
+ {
+ Name = name;
+ Alias = alias;
+ Icon = icon;
+ SortOrder = sortOrder;
+ }
+
+ public Section()
+ {
+
+ }
+
+ public string Name { get; set; }
+ public string Alias { get; set; }
+ public string Icon { get; set; }
+ public int SortOrder { get; set; }
+ }
+}
\ No newline at end of file
diff --git a/src/Umbraco.Core/Sections/SectionCollection.cs b/src/Umbraco.Core/Sections/SectionCollection.cs
new file mode 100644
index 0000000000..8b3f51c49f
--- /dev/null
+++ b/src/Umbraco.Core/Sections/SectionCollection.cs
@@ -0,0 +1,202 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Runtime.CompilerServices;
+using System.Text;
+using System.Threading.Tasks;
+using System.Xml.Linq;
+using Umbraco.Core.Cache;
+using Umbraco.Core.Events;
+using Umbraco.Core.IO;
+using Umbraco.Core.Trees;
+
+namespace Umbraco.Core.Sections
+{
+ public class SectionCollection
+ {
+ internal const string AppConfigFileName = "applications.config";
+ private static string _appConfig;
+ private static readonly object Locker = new object();
+
+ ///
+ /// gets/sets the application.config file path
+ ///
+ ///
+ /// The setter is generally only going to be used in unit tests, otherwise it will attempt to resolve it using the IOHelper.MapPath
+ ///
+ internal static string AppConfigFilePath
+ {
+ get
+ {
+ if (string.IsNullOrWhiteSpace(_appConfig))
+ {
+ _appConfig = IOHelper.MapPath(SystemDirectories.Config + "/" + AppConfigFileName);
+ }
+ return _appConfig;
+ }
+ set { _appConfig = value; }
+ }
+
+ ///
+ /// The cache storage for all applications
+ ///
+ internal static IEnumerable Sections
+ {
+ get
+ {
+ return ApplicationContext.Current.ApplicationCache.GetCacheItem(
+ CacheKeys.ApplicationsCacheKey,
+ () =>
+ {
+ ////used for unit tests
+ //if (_testApps != null)
+ // return _testApps;
+
+ var tmp = new List();
+
+ LoadXml(doc =>
+ {
+ foreach (var addElement in doc.Root.Elements("add").OrderBy(x =>
+ {
+ var sortOrderAttr = x.Attribute("sortOrder");
+ return sortOrderAttr != null ? Convert.ToInt32(sortOrderAttr.Value) : 0;
+ }))
+ {
+ var sortOrderAttr = addElement.Attribute("sortOrder");
+ tmp.Add(new Section(addElement.Attribute("name").Value,
+ addElement.Attribute("alias").Value,
+ addElement.Attribute("icon").Value,
+ sortOrderAttr != null ? Convert.ToInt32(sortOrderAttr.Value) : 0));
+ }
+
+ }, false);
+ return tmp;
+ });
+ }
+ }
+
+ internal static void LoadXml(Action callback, bool saveAfterCallback)
+ {
+ lock (Locker)
+ {
+ var doc = File.Exists(AppConfigFilePath)
+ ? XDocument.Load(AppConfigFilePath)
+ : XDocument.Parse("");
+
+ if (doc.Root != null)
+ {
+ callback.Invoke(doc);
+
+ if (saveAfterCallback)
+ {
+ //ensure the folder is created!
+ Directory.CreateDirectory(Path.GetDirectoryName(AppConfigFilePath));
+
+ doc.Save(AppConfigFilePath);
+
+ //remove the cache so it gets re-read ... SD: I'm leaving this here even though it
+ // is taken care of by events as well, I think unit tests may rely on it being cleared here.
+ ApplicationContext.Current.ApplicationCache.ClearCacheItem(CacheKeys.ApplicationsCacheKey);
+ }
+ }
+ }
+ }
+
+ ///
+ /// Gets the application by its alias.
+ ///
+ /// The application alias.
+ ///
+ public static Section GetByAlias(string appAlias)
+ {
+ return Sections.FirstOrDefault(t => t.Alias == appAlias);
+ }
+
+ ///
+ /// Creates a new applcation if no application with the specified alias is found.
+ ///
+ /// The application name.
+ /// The application alias.
+ /// The application icon, which has to be located in umbraco/images/tray folder.
+ [MethodImpl(MethodImplOptions.Synchronized)]
+ public static void MakeNew(string name, string alias, string icon)
+ {
+ MakeNew(name, alias, icon, Sections.Max(x => x.SortOrder) + 1);
+ }
+
+ ///
+ /// Makes the new.
+ ///
+ /// The name.
+ /// The alias.
+ /// The icon.
+ /// The sort order.
+ [MethodImpl(MethodImplOptions.Synchronized)]
+ public static void MakeNew(string name, string alias, string icon, int sortOrder)
+ {
+ if (Sections.All(x => x.Alias != alias))
+ {
+ LoadXml(doc =>
+ {
+ doc.Root.Add(new XElement("add",
+ new XAttribute("alias", alias),
+ new XAttribute("name", name),
+ new XAttribute("icon", icon),
+ new XAttribute("sortOrder", sortOrder)));
+ }, true);
+
+ //raise event
+ OnNew(new Section(name, alias, icon, sortOrder), new EventArgs());
+ }
+ }
+
+ ///
+ /// Deletes the section
+ ///
+ public static void DeleteSection(Section section)
+ {
+ lock (Locker)
+ {
+ //delete the assigned applications
+ ApplicationContext.Current.DatabaseContext.Database.Execute(
+ "delete from umbracoUser2App where app = @appAlias",
+ new { appAlias = section.Alias });
+
+ //delete the assigned trees
+ var trees = ApplicationTreeCollection.GetApplicationTree(section.Alias);
+ foreach (var t in trees)
+ {
+ ApplicationTreeCollection.DeleteTree(t);
+ }
+
+ LoadXml(doc =>
+ {
+ doc.Root.Elements("add").Where(x => x.Attribute("alias") != null && x.Attribute("alias").Value == section.Alias).Remove();
+ }, true);
+
+ //raise event
+ OnDeleted(section, new EventArgs());
+ }
+ }
+
+ internal static event TypedEventHandler Deleted;
+ private static void OnDeleted(Section app, EventArgs args)
+ {
+ if (Deleted != null)
+ {
+ Deleted(app, args);
+ }
+ }
+
+ internal static event TypedEventHandler New;
+ private static void OnNew(Section app, EventArgs args)
+ {
+ if (New != null)
+ {
+ New(app, args);
+ }
+ }
+
+ }
+}
diff --git a/src/Umbraco.Core/Umbraco.Core.csproj b/src/Umbraco.Core/Umbraco.Core.csproj
index 3581d15a00..e3de933b23 100644
--- a/src/Umbraco.Core/Umbraco.Core.csproj
+++ b/src/Umbraco.Core/Umbraco.Core.csproj
@@ -211,6 +211,8 @@
+
+
@@ -688,6 +690,8 @@
+
+
diff --git a/src/Umbraco.Tests/BusinessLogic/BaseTest.cs b/src/Umbraco.Tests/BusinessLogic/BaseTest.cs
index c6efefc474..1f3173b926 100644
--- a/src/Umbraco.Tests/BusinessLogic/BaseTest.cs
+++ b/src/Umbraco.Tests/BusinessLogic/BaseTest.cs
@@ -1,10 +1,13 @@
using System;
using System.Collections.Generic;
using System.Configuration;
+using AutoMapper;
using NUnit.Framework;
using SqlCE4Umbraco;
using Umbraco.Core;
using Umbraco.Core.IO;
+using Umbraco.Core.Models.Mapping;
+using Umbraco.Core.Sections;
using Umbraco.Tests.TestHelpers;
using umbraco.BusinessLogic;
using umbraco.DataLayer;
@@ -33,12 +36,25 @@ namespace Umbraco.Tests.BusinessLogic
public void Initialize()
{
ApplicationContext.Current = new ApplicationContext(false){IsReady = true};
+ InitializeMappers();
InitializeDatabase();
InitializeApps();
InitializeAppConfigFile();
InitializeTreeConfigFile();
}
+ private void InitializeMappers()
+ {
+ Mapper.Initialize(configuration =>
+ {
+ var mappers = PluginManager.Current.FindAndCreateInstances();
+ foreach (var mapper in mappers)
+ {
+ mapper.ConfigureMappings(configuration);
+ }
+ });
+ }
+
private void ClearDatabase()
{
var databaseSettings = ConfigurationManager.ConnectionStrings[Core.Configuration.GlobalSettings.UmbracoConnectionName];
@@ -71,7 +87,7 @@ namespace Umbraco.Tests.BusinessLogic
private void InitializeApps()
{
- Application.MakeNew("content", "content", "file", 0);
+ SectionCollection.MakeNew("content", "content", "file", 0);
//Application.SetTestApps(new List()
// {
// new Application(Constants.Applications.Content, "content", "content", 0)
@@ -80,7 +96,7 @@ namespace Umbraco.Tests.BusinessLogic
private void InitializeAppConfigFile()
{
- Application.AppConfigFilePath = IOHelper.MapPath(SystemDirectories.Config + "/" + Application.AppConfigFileName, false);
+ SectionCollection.AppConfigFilePath = IOHelper.MapPath(SystemDirectories.Config + "/" + SectionCollection.AppConfigFileName, false);
}
private void InitializeTreeConfigFile()
diff --git a/src/Umbraco.Tests/Umbraco.Tests.csproj b/src/Umbraco.Tests/Umbraco.Tests.csproj
index d9565a9b18..423a8c2410 100644
--- a/src/Umbraco.Tests/Umbraco.Tests.csproj
+++ b/src/Umbraco.Tests/Umbraco.Tests.csproj
@@ -51,6 +51,10 @@
false
+
+ False
+ ..\packages\AutoMapper.2.2.1\lib\net40\AutoMapper.dll
+
False
..\packages\Examine.0.1.51.2941\lib\Examine.dll
@@ -288,8 +292,8 @@
-
-
+
+
diff --git a/src/Umbraco.Tests/packages.config b/src/Umbraco.Tests/packages.config
index 4f69076931..07ac8de73d 100644
--- a/src/Umbraco.Tests/packages.config
+++ b/src/Umbraco.Tests/packages.config
@@ -1,5 +1,6 @@
+
diff --git a/src/Umbraco.Web.UI.Client/src/common/resources/section.resource.js b/src/Umbraco.Web.UI.Client/src/common/resources/section.resource.js
new file mode 100644
index 0000000000..d4f1d19e38
--- /dev/null
+++ b/src/Umbraco.Web.UI.Client/src/common/resources/section.resource.js
@@ -0,0 +1,34 @@
+/**
+ * @ngdoc factory
+ * @name umbraco.resources.section
+ * @description Loads in data for section
+ **/
+function sectionResource($q, $http) {
+
+ /** internal method to get the tree app url */
+ function getSectionsUrl(section) {
+ return Umbraco.Sys.ServerVariables.sectionApiBaseUrl + "GetSections";
+ }
+
+ //the factory object returned
+ return {
+ /** Loads in the data to display the section list */
+ getSections: function (options) {
+
+ var deferred = $q.defer();
+
+ //go and get the tree data
+ $http.get(getSectionsUrl()).
+ success(function (data, status, headers, config) {
+ deferred.resolve(data);
+ }).
+ error(function (data, status, headers, config) {
+ deferred.reject('Failed to retreive data for sections');
+ });
+
+ return deferred.promise;
+ }
+ };
+}
+
+angular.module('umbraco.resources').factory('treeResource', treeResource);
\ No newline at end of file
diff --git a/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj b/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj
index 6ec0aa4b4f..8999407607 100644
--- a/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj
+++ b/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj
@@ -2558,7 +2558,6 @@
-
diff --git a/src/Umbraco.Web/Cache/CacheRefresherEventHandler.cs b/src/Umbraco.Web/Cache/CacheRefresherEventHandler.cs
index 37be376381..cbfb321f55 100644
--- a/src/Umbraco.Web/Cache/CacheRefresherEventHandler.cs
+++ b/src/Umbraco.Web/Cache/CacheRefresherEventHandler.cs
@@ -3,6 +3,7 @@ using Umbraco.Core;
using Umbraco.Core.Cache;
using Umbraco.Core.Models;
using Umbraco.Core.Models.EntityBase;
+using Umbraco.Core.Sections;
using Umbraco.Core.Services;
using Umbraco.Core.Trees;
using umbraco;
@@ -30,8 +31,8 @@ namespace Umbraco.Web.Cache
ApplicationTreeCollection.New += ApplicationTreeNew;
//bind to application events
- Application.Deleted += ApplicationDeleted;
- Application.New += ApplicationNew;
+ SectionCollection.Deleted += ApplicationDeleted;
+ SectionCollection.New += ApplicationNew;
//bind to user type events
UserType.Deleted += UserTypeDeleted;
@@ -145,12 +146,12 @@ namespace Umbraco.Web.Cache
#endregion
#region Application event handlers
- static void ApplicationNew(Application sender, System.EventArgs e)
+ static void ApplicationNew(Section sender, System.EventArgs e)
{
DistributedCache.Instance.RefreshAllApplicationCache();
}
- static void ApplicationDeleted(Application sender, System.EventArgs e)
+ static void ApplicationDeleted(Section sender, System.EventArgs e)
{
DistributedCache.Instance.RefreshAllApplicationCache();
}
diff --git a/src/Umbraco.Web/Editors/BackOfficeController.cs b/src/Umbraco.Web/Editors/BackOfficeController.cs
index ec1486988f..7d4968e429 100644
--- a/src/Umbraco.Web/Editors/BackOfficeController.cs
+++ b/src/Umbraco.Web/Editors/BackOfficeController.cs
@@ -58,7 +58,8 @@ namespace Umbraco.Web.Editors
{"umbracoPath", GlobalSettings.Path},
{"contentApiBaseUrl", Url.GetUmbracoApiService("PostSave").TrimEnd("PostSave")},
{"mediaApiBaseUrl", Url.GetUmbracoApiService("GetRootMedia").TrimEnd("GetRootMedia")},
- {"treeApplicationApiBaseUrl", Url.GetUmbracoApiService("GetTreeData").TrimEnd("GetTreeData")},
+ {"sectionApiBaseUrl", Url.GetUmbracoApiService("GetSections").TrimEnd("GetSections")},
+ {"treeApplicationApiBaseUrl", Url.GetUmbracoApiService("GetApplicationTrees").TrimEnd("GetApplicationTrees")},
{"contentTypeApiBaseUrl", Url.GetUmbracoApiService("GetAllowedChildren").TrimEnd("GetAllowedChildren")},
{"mediaTypeApiBaseUrl", Url.GetUmbracoApiService("GetAllowedChildren").TrimEnd("GetAllowedChildren")},
{"authenticationApiBaseUrl", Url.GetUmbracoApiService("PostLogin").TrimEnd("PostLogin")}
diff --git a/src/Umbraco.Web/Editors/SectionController.cs b/src/Umbraco.Web/Editors/SectionController.cs
new file mode 100644
index 0000000000..f80525c227
--- /dev/null
+++ b/src/Umbraco.Web/Editors/SectionController.cs
@@ -0,0 +1,21 @@
+using System.Collections.Generic;
+using AutoMapper;
+using Umbraco.Web.Models.ContentEditing;
+using Umbraco.Web.Mvc;
+using Umbraco.Web.WebApi;
+using System.Linq;
+
+namespace Umbraco.Web.Editors
+{
+ ///
+ /// The API controller used for using the list of sections
+ ///
+ [PluginController("UmbracoApi")]
+ public class SectionController : UmbracoAuthorizedApiController
+ {
+ public IEnumerable GetSections()
+ {
+ return Core.Sections.SectionCollection.Sections.Select(Mapper.Map);
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Umbraco.Web/Models/ContentEditing/Section.cs b/src/Umbraco.Web/Models/ContentEditing/Section.cs
new file mode 100644
index 0000000000..4ffc7a8b3a
--- /dev/null
+++ b/src/Umbraco.Web/Models/ContentEditing/Section.cs
@@ -0,0 +1,28 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Runtime.Serialization;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Umbraco.Web.Models.ContentEditing
+{
+
+ ///
+ /// Represents a section (application) in the back office
+ ///
+ [DataContract(Name = "section", Namespace = "")]
+ public class Section
+ {
+
+ [DataMember(Name = "name")]
+ public string Name { get; set; }
+
+ [DataMember(Name = "cssclass")]
+ public string CssClass { get; set; }
+
+ [DataMember(Name = "alias")]
+ public string Alias { get; set; }
+
+ }
+}
diff --git a/src/Umbraco.Web/Models/Mapping/SectionModelMapper.cs b/src/Umbraco.Web/Models/Mapping/SectionModelMapper.cs
new file mode 100644
index 0000000000..7453e7487b
--- /dev/null
+++ b/src/Umbraco.Web/Models/Mapping/SectionModelMapper.cs
@@ -0,0 +1,15 @@
+using AutoMapper;
+using Umbraco.Core.Models.Mapping;
+using Umbraco.Web.Models.ContentEditing;
+
+namespace Umbraco.Web.Models.Mapping
+{
+ internal class SectionModelMapper : MapperConfiguration
+ {
+ public override void ConfigureMappings(IConfiguration config)
+ {
+ config.CreateMap()
+ .ReverseMap(); //backwards too!
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Umbraco.Web/Models/Mapping/UserModelMapper.cs b/src/Umbraco.Web/Models/Mapping/UserModelMapper.cs
index bada4d4e12..9ea7f082f4 100644
--- a/src/Umbraco.Web/Models/Mapping/UserModelMapper.cs
+++ b/src/Umbraco.Web/Models/Mapping/UserModelMapper.cs
@@ -1,26 +1,27 @@
using System;
using AutoMapper;
using Umbraco.Core;
+using Umbraco.Core.Models.Mapping;
using Umbraco.Core.Models.Membership;
using Umbraco.Web.Models.ContentEditing;
namespace Umbraco.Web.Models.Mapping
{
- internal class UserModelMapper
+ internal class UserModelMapper : MapperConfiguration
{
- ///
- /// Configures the automapper mappings
- ///
- internal static void Configure()
+
+ #region Mapper config
+ public override void ConfigureMappings(IConfiguration config)
{
- Mapper.CreateMap()
+ config.CreateMap()
.ForMember(detail => detail.UserId, opt => opt.MapFrom(user => GetIntId(user.Id)))
.ForMember(
detail => detail.EmailHash,
opt => opt.MapFrom(user => user.Email.ToLowerInvariant().Trim().ToMd5()));
- Mapper.CreateMap()
+ config.CreateMap()
.ForMember(detail => detail.UserId, opt => opt.MapFrom(profile => GetIntId(profile.Id)));
- }
+ }
+ #endregion
private static int GetIntId(object id)
{
@@ -41,6 +42,6 @@ namespace Umbraco.Web.Models.Mapping
public UserBasic ToUserBasic(IProfile profile)
{
return Mapper.Map(profile);
- }
+ }
}
}
\ No newline at end of file
diff --git a/src/Umbraco.Web/Trees/ApplicationTreeApiController.cs b/src/Umbraco.Web/Trees/ApplicationTreeController.cs
similarity index 78%
rename from src/Umbraco.Web/Trees/ApplicationTreeApiController.cs
rename to src/Umbraco.Web/Trees/ApplicationTreeController.cs
index 005ec172a8..c8dda9b6e0 100644
--- a/src/Umbraco.Web/Trees/ApplicationTreeApiController.cs
+++ b/src/Umbraco.Web/Trees/ApplicationTreeController.cs
@@ -1,123 +1,123 @@
-using System;
-using System.Linq;
-using System.Management.Instrumentation;
-using System.Net.Http.Formatting;
-using System.Web.Mvc;
-using Umbraco.Core;
-using Umbraco.Core.Trees;
-using Umbraco.Web.Mvc;
-using Umbraco.Web.WebApi;
-using Umbraco.Web.WebApi.Filters;
-
-namespace Umbraco.Web.Trees
-{
-
- [PluginController("UmbracoTrees")]
- public class ApplicationTreeApiController : UmbracoAuthorizedApiController
- {
-
- ///
- /// Remove the xml formatter... only support JSON!
- ///
- ///
- protected override void Initialize(global::System.Web.Http.Controllers.HttpControllerContext controllerContext)
- {
- base.Initialize(controllerContext);
- controllerContext.Configuration.Formatters.Remove(controllerContext.Configuration.Formatters.XmlFormatter);
- }
-
- ///
- /// Returns the tree nodes for an application
- ///
- ///
- ///
- ///
- [HttpQueryStringFilter("queryStrings")]
- public TreeNodeCollection GetApplicationTrees(string application, FormDataCollection queryStrings)
- {
- if (application == null) throw new ArgumentNullException("application");
-
- //find all tree definitions that have the current application alias
- var appTrees = ApplicationTreeCollection.GetApplicationTree(application).Where(x => x.Initialize).ToArray();
- if (appTrees.Count() == 1)
- {
- //return the nodes for the one tree assigned
- return GetNodeCollection(appTrees.Single(), "-1", queryStrings);
- }
-
- var collection = new TreeNodeCollection();
- foreach (var tree in appTrees)
- {
- //return the root nodes for each tree in the app
- var rootNode = GetRoot(tree, queryStrings);
- collection.Add(rootNode);
- }
- return collection;
- }
-
- ///
- /// Returns the tree data for a specific tree for the children of the id
- ///
- ///
- ///
- ///
- ///
- [HttpQueryStringFilter("queryStrings")]
- public TreeNodeCollection GetTreeData(string treeType, string id, FormDataCollection queryStrings)
- {
- if (treeType == null) throw new ArgumentNullException("treeType");
-
- //get the configured tree
- var foundConfigTree = ApplicationTreeCollection.GetByAlias(treeType);
- if (foundConfigTree == null)
- throw new InstanceNotFoundException("Could not find tree of type " + treeType + " in the trees.config");
-
- return GetNodeCollection(foundConfigTree, id, queryStrings);
- }
-
- private TreeNode GetRoot(ApplicationTree configTree, FormDataCollection queryStrings)
- {
- if (configTree == null) throw new ArgumentNullException("configTree");
- var byControllerAttempt = configTree.TryGetRootNodeFromControllerTree(queryStrings, ControllerContext, Request);
- if (byControllerAttempt.Success)
- {
- return byControllerAttempt.Result;
- }
- var legacyAttempt = configTree.TryGetRootNodeFromLegacyTree(queryStrings, Url);
- if (legacyAttempt.Success)
- {
- return legacyAttempt.Result;
- }
-
- throw new ApplicationException("Could not get root node for tree type " + configTree.Alias);
- }
-
- ///
- /// Get the node collection for the tree, try loading from new controllers first, then from legacy trees
- ///
- ///
- ///
- ///
- ///
- private TreeNodeCollection GetNodeCollection(ApplicationTree configTree, string id, FormDataCollection queryStrings)
- {
- if (configTree == null) throw new ArgumentNullException("configTree");
- var byControllerAttempt = configTree.TryLoadFromControllerTree(id, queryStrings, ControllerContext, Request);
- if (byControllerAttempt.Success)
- {
- return byControllerAttempt.Result;
- }
- var legacyAttempt = configTree.TryLoadFromLegacyTree(id, queryStrings, Url);
- if (legacyAttempt.Success)
- {
- return legacyAttempt.Result;
- }
-
- throw new ApplicationException("Could not render a tree for type " + configTree.Alias);
- }
-
-
- }
-
-
-}
+using System;
+using System.Linq;
+using System.Management.Instrumentation;
+using System.Net.Http.Formatting;
+using System.Web.Mvc;
+using Umbraco.Core;
+using Umbraco.Core.Trees;
+using Umbraco.Web.Mvc;
+using Umbraco.Web.WebApi;
+using Umbraco.Web.WebApi.Filters;
+
+namespace Umbraco.Web.Trees
+{
+
+ [PluginController("UmbracoTrees")]
+ public class ApplicationTreeController : UmbracoAuthorizedApiController
+ {
+
+ ///
+ /// Remove the xml formatter... only support JSON!
+ ///
+ ///
+ protected override void Initialize(global::System.Web.Http.Controllers.HttpControllerContext controllerContext)
+ {
+ base.Initialize(controllerContext);
+ controllerContext.Configuration.Formatters.Remove(controllerContext.Configuration.Formatters.XmlFormatter);
+ }
+
+ ///
+ /// Returns the tree nodes for an application
+ ///
+ ///
+ ///
+ ///
+ [HttpQueryStringFilter("queryStrings")]
+ public TreeNodeCollection GetApplicationTrees(string application, FormDataCollection queryStrings)
+ {
+ if (application == null) throw new ArgumentNullException("application");
+
+ //find all tree definitions that have the current application alias
+ var appTrees = ApplicationTreeCollection.GetApplicationTree(application).Where(x => x.Initialize).ToArray();
+ if (appTrees.Count() == 1)
+ {
+ //return the nodes for the one tree assigned
+ return GetNodeCollection(appTrees.Single(), "-1", queryStrings);
+ }
+
+ var collection = new TreeNodeCollection();
+ foreach (var tree in appTrees)
+ {
+ //return the root nodes for each tree in the app
+ var rootNode = GetRoot(tree, queryStrings);
+ collection.Add(rootNode);
+ }
+ return collection;
+ }
+
+ /////
+ ///// Returns the tree data for a specific tree for the children of the id
+ /////
+ /////
+ /////
+ /////
+ /////
+ //[HttpQueryStringFilter("queryStrings")]
+ //public TreeNodeCollection GetTreeData(string treeType, string id, FormDataCollection queryStrings)
+ //{
+ // if (treeType == null) throw new ArgumentNullException("treeType");
+
+ // //get the configured tree
+ // var foundConfigTree = ApplicationTreeCollection.GetByAlias(treeType);
+ // if (foundConfigTree == null)
+ // throw new InstanceNotFoundException("Could not find tree of type " + treeType + " in the trees.config");
+
+ // return GetNodeCollection(foundConfigTree, id, queryStrings);
+ //}
+
+ private TreeNode GetRoot(ApplicationTree configTree, FormDataCollection queryStrings)
+ {
+ if (configTree == null) throw new ArgumentNullException("configTree");
+ var byControllerAttempt = configTree.TryGetRootNodeFromControllerTree(queryStrings, ControllerContext, Request);
+ if (byControllerAttempt.Success)
+ {
+ return byControllerAttempt.Result;
+ }
+ var legacyAttempt = configTree.TryGetRootNodeFromLegacyTree(queryStrings, Url);
+ if (legacyAttempt.Success)
+ {
+ return legacyAttempt.Result;
+ }
+
+ throw new ApplicationException("Could not get root node for tree type " + configTree.Alias);
+ }
+
+ ///
+ /// Get the node collection for the tree, try loading from new controllers first, then from legacy trees
+ ///
+ ///
+ ///
+ ///
+ ///
+ private TreeNodeCollection GetNodeCollection(ApplicationTree configTree, string id, FormDataCollection queryStrings)
+ {
+ if (configTree == null) throw new ArgumentNullException("configTree");
+ var byControllerAttempt = configTree.TryLoadFromControllerTree(id, queryStrings, ControllerContext, Request);
+ if (byControllerAttempt.Success)
+ {
+ return byControllerAttempt.Result;
+ }
+ var legacyAttempt = configTree.TryLoadFromLegacyTree(id, queryStrings, Url);
+ if (legacyAttempt.Success)
+ {
+ return legacyAttempt.Result;
+ }
+
+ throw new ApplicationException("Could not render a tree for type " + configTree.Alias);
+ }
+
+
+ }
+
+
+}
diff --git a/src/Umbraco.Web/Umbraco.Web.csproj b/src/Umbraco.Web/Umbraco.Web.csproj
index 16140a2f6b..e481ad5a80 100644
--- a/src/Umbraco.Web/Umbraco.Web.csproj
+++ b/src/Umbraco.Web/Umbraco.Web.csproj
@@ -298,10 +298,12 @@
+
+
@@ -311,6 +313,7 @@
+
@@ -331,7 +334,7 @@
-
+
diff --git a/src/Umbraco.Web/WebBootManager.cs b/src/Umbraco.Web/WebBootManager.cs
index a166b96702..66b5dda7a6 100644
--- a/src/Umbraco.Web/WebBootManager.cs
+++ b/src/Umbraco.Web/WebBootManager.cs
@@ -112,23 +112,12 @@ namespace Umbraco.Web
ProfilerResolver.Current.SetProfiler(new WebProfiler());
}
- ///
- /// Configure the model mappers
- ///
- protected override void InitializeModelMappers()
- {
- base.InitializeModelMappers();
- UserModelMapper.Configure();
- }
-
///
/// Adds custom types to the ApplicationEventsResolver
///
protected override void InitializeApplicationEventsResolver()
{
base.InitializeApplicationEventsResolver();
- ApplicationEventsResolver.Current.AddType();
- ApplicationEventsResolver.Current.AddType();
//We need to remove these types because we've obsoleted them and we don't want them executing:
ApplicationEventsResolver.Current.RemoveType();
}
@@ -291,10 +280,10 @@ namespace Umbraco.Web
UmbracoApiControllerResolver.Current = new UmbracoApiControllerResolver(
PluginManager.Current.ResolveUmbracoApiControllers());
- //the base creates the PropertyEditorValueConvertersResolver but we want to modify it in the web app and replace
- //the TinyMcePropertyEditorValueConverter with the RteMacroRenderingPropertyEditorValueConverter
- PropertyEditorValueConvertersResolver.Current.RemoveType();
- PropertyEditorValueConvertersResolver.Current.AddType();
+ //the base creates the PropertyEditorValueConvertersResolver but we want to modify it in the web app and remove
+ //the TinyMcePropertyEditorValueConverter since when the web app is loaded the RteMacroRenderingPropertyEditorValueConverter
+ //is found and we'll use that instead.
+ PropertyEditorValueConvertersResolver.Current.RemoveType();
PublishedCachesResolver.Current = new PublishedCachesResolver(new PublishedCaches(
new PublishedCache.XmlPublishedCache.PublishedContentCache(),
diff --git a/src/umbraco.businesslogic/Application.cs b/src/umbraco.businesslogic/Application.cs
index efa7131196..d21238b8cb 100644
--- a/src/umbraco.businesslogic/Application.cs
+++ b/src/umbraco.businesslogic/Application.cs
@@ -5,11 +5,13 @@ using System.IO;
using System.Linq;
using System.Web;
using System.Xml.Linq;
+using AutoMapper;
using Umbraco.Core;
using Umbraco.Core.Cache;
using Umbraco.Core.Events;
using Umbraco.Core.IO;
using Umbraco.Core.Logging;
+using Umbraco.Core.Sections;
using umbraco.DataLayer;
using System.Runtime.CompilerServices;
using umbraco.businesslogic;
@@ -19,88 +21,18 @@ namespace umbraco.BusinessLogic
///
/// Class for handling all registered applications in Umbraco.
///
+ [Obsolete("Use Umbraco.Core.Sections.SectionCollection instead")]
public class Application
{
private static ISqlHelper _sqlHelper;
-
- internal const string AppConfigFileName = "applications.config";
- private static string _appConfig;
- private static readonly object Locker = new object();
-
- ///
- /// gets/sets the application.config file path
- ///
- ///
- /// The setter is generally only going to be used in unit tests, otherwise it will attempt to resolve it using the IOHelper.MapPath
- ///
- internal static string AppConfigFilePath
- {
- get
- {
- if (string.IsNullOrWhiteSpace(_appConfig))
- {
- _appConfig = IOHelper.MapPath(SystemDirectories.Config + "/" + AppConfigFileName);
- }
- return _appConfig;
- }
- set { _appConfig = value; }
- }
-
- ///
- /// The cache storage for all applications
- ///
- internal static List Apps
- {
- get
- {
- return ApplicationContext.Current.ApplicationCache.GetCacheItem(
- CacheKeys.ApplicationsCacheKey,
- () =>
- {
- ////used for unit tests
- //if (_testApps != null)
- // return _testApps;
-
- var tmp = new List();
-
- try
- {
- LoadXml(doc =>
- {
- foreach (var addElement in doc.Root.Elements("add").OrderBy(x =>
- {
- var sortOrderAttr = x.Attribute("sortOrder");
- return sortOrderAttr != null ? Convert.ToInt32(sortOrderAttr.Value) : 0;
- }))
- {
- var sortOrderAttr = addElement.Attribute("sortOrder");
- tmp.Add(new Application(addElement.Attribute("name").Value,
- addElement.Attribute("alias").Value,
- addElement.Attribute("icon").Value,
- sortOrderAttr != null ? Convert.ToInt32(sortOrderAttr.Value) : 0));
- }
-
- }, false);
- return tmp;
- }
- catch
- {
- //this is a bit of a hack that just ensures the application doesn't crash when the
- //installer is run and there is no database or connection string defined.
- //the reason this method may get called during the installation is that the
- //SqlHelper of this class is shared amongst everything "Application" wide.
-
- //TODO: Perhaps we should log something here??
- return null;
- }
- });
- }
- }
+
+
///
/// Gets the SQL helper.
///
/// The SQL helper.
+ [Obsolete("Do not use SqlHelper anymore, if database querying needs to be done use the DatabaseContext instead")]
public static ISqlHelper SqlHelper
{
get
@@ -197,10 +129,9 @@ namespace umbraco.BusinessLogic
/// The application name.
/// The application alias.
/// The application icon, which has to be located in umbraco/images/tray folder.
- [MethodImpl(MethodImplOptions.Synchronized)]
public static void MakeNew(string name, string alias, string icon)
{
- MakeNew(name, alias, icon, Apps.Max(x => x.sortOrder) + 1);
+ SectionCollection.MakeNew(name, alias, icon);
}
///
@@ -210,25 +141,9 @@ namespace umbraco.BusinessLogic
/// The alias.
/// The icon.
/// The sort order.
- [MethodImpl(MethodImplOptions.Synchronized)]
public static void MakeNew(string name, string alias, string icon, int sortOrder)
{
- var exist = getAll().Any(x => x.alias == alias);
-
- if (!exist)
- {
- LoadXml(doc =>
- {
- doc.Root.Add(new XElement("add",
- new XAttribute("alias", alias),
- new XAttribute("name", name),
- new XAttribute("icon", icon),
- new XAttribute("sortOrder", sortOrder)));
- }, true);
-
- //raise event
- OnNew(new Application(name, alias, icon, sortOrder), new EventArgs());
- }
+ SectionCollection.MakeNew(name, alias, icon, sortOrder);
}
///
@@ -238,7 +153,8 @@ namespace umbraco.BusinessLogic
///
public static Application getByAlias(string appAlias)
{
- return Apps.Find(t => t.alias == appAlias);
+ return Mapper.Map(
+ SectionCollection.GetByAlias(appAlias));
}
///
@@ -246,23 +162,7 @@ namespace umbraco.BusinessLogic
///
public void Delete()
{
- //delete the assigned applications
- SqlHelper.ExecuteNonQuery("delete from umbracoUser2App where app = @appAlias", SqlHelper.CreateParameter("@appAlias", this.alias));
-
- //delete the assigned trees
- var trees = ApplicationTree.getApplicationTree(this.alias);
- foreach (var t in trees)
- {
- t.Delete();
- }
-
- LoadXml(doc =>
- {
- doc.Root.Elements("add").Where(x => x.Attribute("alias") != null && x.Attribute("alias").Value == this.alias).Remove();
- }, true);
-
- //raise event
- OnDeleted(this, new EventArgs());
+ SectionCollection.DeleteSection(Mapper.Map(this));
}
///
@@ -271,7 +171,7 @@ namespace umbraco.BusinessLogic
/// Returns a Application Array
public static List getAll()
{
- return Apps;
+ return SectionCollection.Sections.Select(Mapper.Map).ToList();
}
///
@@ -283,49 +183,6 @@ namespace umbraco.BusinessLogic
ApplicationStartupHandler.RegisterHandlers();
}
- internal static void LoadXml(Action callback, bool saveAfterCallback)
- {
- lock (Locker)
- {
- var doc = File.Exists(AppConfigFilePath)
- ? XDocument.Load(AppConfigFilePath)
- : XDocument.Parse("");
-
- if (doc.Root != null)
- {
- callback.Invoke(doc);
-
- if (saveAfterCallback)
- {
- //ensure the folder is created!
- Directory.CreateDirectory(Path.GetDirectoryName(AppConfigFilePath));
-
- doc.Save(AppConfigFilePath);
-
- //remove the cache so it gets re-read ... SD: I'm leaving this here even though it
- // is taken care of by events as well, I think unit tests may rely on it being cleared here.
- ApplicationContext.Current.ApplicationCache.ClearCacheItem(CacheKeys.ApplicationsCacheKey);
- }
- }
- }
- }
-
- internal static event TypedEventHandler Deleted;
- private static void OnDeleted(Application app, EventArgs args)
- {
- if (Deleted != null)
- {
- Deleted(app, args);
- }
- }
-
- internal static event TypedEventHandler New;
- private static void OnNew(Application app, EventArgs args)
- {
- if (New != null)
- {
- New(app, args);
- }
- }
+
}
}
diff --git a/src/umbraco.businesslogic/ApplicationRegistrar.cs b/src/umbraco.businesslogic/ApplicationRegistrar.cs
index 8eb695eebb..6cb962bab2 100644
--- a/src/umbraco.businesslogic/ApplicationRegistrar.cs
+++ b/src/umbraco.businesslogic/ApplicationRegistrar.cs
@@ -3,8 +3,11 @@ using System.Configuration;
using System.Data.SqlClient;
using System.Linq;
using System.Xml.Linq;
+using AutoMapper;
using Umbraco.Core;
+using Umbraco.Core.Models.Mapping;
using Umbraco.Core.Persistence;
+using Umbraco.Core.Sections;
using umbraco.BusinessLogic.Utils;
using umbraco.DataLayer;
using umbraco.businesslogic;
@@ -12,75 +15,47 @@ using umbraco.interfaces;
namespace umbraco.BusinessLogic
{
- public class ApplicationRegistrar : IApplicationStartupHandler
+ public class ApplicationRegistrar : ApplicationEventHandler, IMapperConfiguration
{
- private ISqlHelper _sqlHelper;
- protected ISqlHelper SqlHelper
+ protected override void ApplicationStarted(UmbracoApplicationBase umbracoApplication, ApplicationContext applicationContext)
{
- get
+ // Load all Applications by attribute and add them to the XML config
+ var types = PluginManager.Current.ResolveApplications();
+
+ //since applications don't populate their metadata from the attribute and because it is an interface,
+ //we need to interrogate the attributes for the data. Would be better to have a base class that contains
+ //metadata populated by the attribute. Oh well i guess.
+ var attrs = types.Select(x => x.GetCustomAttributes(false).Single())
+ .Where(x => SectionCollection.GetByAlias(x.Alias) == null)
+ .ToArray();
+
+ var allAliases = SectionCollection.Sections.Select(x => x.Alias).Concat(attrs.Select(x => x.Alias));
+
+ SectionCollection.LoadXml(doc =>
{
- if (_sqlHelper == null)
+ foreach (var attr in attrs)
{
- try
- {
- var databaseSettings = ConfigurationManager.ConnectionStrings[Umbraco.Core.Configuration.GlobalSettings.UmbracoConnectionName];
- _sqlHelper = DataLayerHelper.CreateSqlHelper(databaseSettings.ConnectionString, false);
- }
- catch { }
+ doc.Root.Add(new XElement("add",
+ new XAttribute("alias", attr.Alias),
+ new XAttribute("name", attr.Name),
+ new XAttribute("icon", attr.Icon),
+ new XAttribute("sortOrder", attr.SortOrder)));
}
- return _sqlHelper;
- }
+ }, true);
}
- public ApplicationRegistrar()
+ public void ConfigureMappings(IConfiguration config)
{
-
- //don't do anything if the application is not configured!
- if (!ApplicationContext.Current.IsConfigured)
- return;
-
- // Load all Applications by attribute and add them to the XML config
- var types = PluginManager.Current.ResolveApplications();
-
- //since applications don't populate their metadata from the attribute and because it is an interface,
- //we need to interrogate the attributes for the data. Would be better to have a base class that contains
- //metadata populated by the attribute. Oh well i guess.
- var attrs = types.Select(x => x.GetCustomAttributes(false).Single())
- .Where(x => Application.getByAlias(x.Alias) == null);
-
- var allAliases = Application.getAll().Select(x => x.alias).Concat(attrs.Select(x => x.Alias));
- var inString = "'" + string.Join("','", allAliases) + "'";
-
- Application.LoadXml(doc =>
- {
- foreach (var attr in attrs)
- {
- doc.Root.Add(new XElement("add",
- new XAttribute("alias", attr.Alias),
- new XAttribute("name", attr.Name),
- new XAttribute("icon", attr.Icon),
- new XAttribute("sortOrder", attr.SortOrder)));
- }
-
- var db = ApplicationContext.Current.DatabaseContext.Database;
- var exist = db.TableExist("umbracoApp");
- if (exist)
- {
- var dbApps = SqlHelper.ExecuteReader("SELECT * FROM umbracoApp WHERE appAlias NOT IN (" + inString + ")");
- while (dbApps.Read())
- {
- doc.Root.Add(new XElement("add",
- new XAttribute("alias", dbApps.GetString("appAlias")),
- new XAttribute("name", dbApps.GetString("appName")),
- new XAttribute("icon", dbApps.GetString("appIcon")),
- new XAttribute("sortOrder", dbApps.GetByte("sortOrder"))));
- }
- }
-
- }, true);
-
- //TODO Shouldn't this be enabled and then delete the whole table?
- //SqlHelper.ExecuteNonQuery("DELETE FROM umbracoApp");
+ config.CreateMap()
+ .ForMember(x => x.alias, expression => expression.MapFrom(x => x.Alias))
+ .ForMember(x => x.icon, expression => expression.MapFrom(x => x.Icon))
+ .ForMember(x => x.name, expression => expression.MapFrom(x => x.Name))
+ .ForMember(x => x.sortOrder, expression => expression.MapFrom(x => x.SortOrder)).ReverseMap();
+ config.CreateMap()
+ .ForMember(x => x.Alias, expression => expression.MapFrom(x => x.alias))
+ .ForMember(x => x.Icon, expression => expression.MapFrom(x => x.icon))
+ .ForMember(x => x.Name, expression => expression.MapFrom(x => x.name))
+ .ForMember(x => x.SortOrder, expression => expression.MapFrom(x => x.sortOrder));
}
}
}
\ No newline at end of file
diff --git a/src/umbraco.businesslogic/ApplicationTreeRegistrar.cs b/src/umbraco.businesslogic/ApplicationTreeRegistrar.cs
index 341ba12d11..c9ef52410e 100644
--- a/src/umbraco.businesslogic/ApplicationTreeRegistrar.cs
+++ b/src/umbraco.businesslogic/ApplicationTreeRegistrar.cs
@@ -3,6 +3,7 @@ using System.Linq;
using System.Xml.Linq;
using AutoMapper;
using Umbraco.Core;
+using Umbraco.Core.Models.Mapping;
using Umbraco.Core.Models.Rdbms;
using Umbraco.Core.Persistence;
using umbraco.businesslogic;
@@ -17,21 +18,20 @@ namespace umbraco.BusinessLogic
///
/// A startup handler for dealing with trees
///
- public class ApplicationTreeRegistrar : ApplicationEventHandler
+ public class ApplicationTreeRegistrar : ApplicationEventHandler, IMapperConfiguration
{
protected override void ApplicationStarted(UmbracoApplicationBase umbracoApplication, ApplicationContext applicationContext)
{
- ConfigureMappings();
ScanTrees();
}
///
/// Configures automapper model mappings
///
- private static void ConfigureMappings()
+ public void ConfigureMappings(IConfiguration config)
{
- Mapper.CreateMap()
+ config.CreateMap()
.ReverseMap(); //two way
}