diff --git a/src/Umbraco.Core/Models/IApplication.cs b/src/Umbraco.Core/Models/IApplication.cs
deleted file mode 100644
index cac8e5dd48..0000000000
--- a/src/Umbraco.Core/Models/IApplication.cs
+++ /dev/null
@@ -1,8 +0,0 @@
-namespace Umbraco.Core.Models
-{
- ///
- /// Interface for created applications in the umbraco backoffice
- ///
- public interface IApplication
- { }
-}
\ No newline at end of file
diff --git a/src/Umbraco.Core/Services/IApplicationTreeService.cs b/src/Umbraco.Core/Services/IApplicationTreeService.cs
index 03d1793c18..9721cd9a56 100644
--- a/src/Umbraco.Core/Services/IApplicationTreeService.cs
+++ b/src/Umbraco.Core/Services/IApplicationTreeService.cs
@@ -5,18 +5,6 @@ namespace Umbraco.Core.Services
{
public interface IApplicationTreeService
{
- ///
- /// Initializes the service with any trees found in plugins
- ///
- ///
- /// A collection of all available tree found in assemblies in the application
- ///
- ///
- /// This will update the trees.config with the found tree plugins that are not currently listed in the file when the first
- /// access is made to resolve the tree collection
- ///
- void Intitialize(IEnumerable allAvailableTrees);
-
///
/// Creates a new application tree.
///
@@ -68,4 +56,82 @@ namespace Umbraco.Core.Services
/// Returns a ApplicationTree Array
IEnumerable GetApplicationTrees(string applicationAlias, bool onlyInitialized);
}
+
+ ///
+ /// Purely used to allow a service context to create the default services
+ ///
+ internal class EmptyApplicationTreeService : IApplicationTreeService
+ {
+ ///
+ /// Creates a new application tree.
+ ///
+ /// if set to true [initialize].
+ /// The sort order.
+ /// The application alias.
+ /// The alias.
+ /// The title.
+ /// The icon closed.
+ /// The icon opened.
+ /// The type.
+ public void MakeNew(bool initialize, int sortOrder, string applicationAlias, string alias, string title, string iconClosed, string iconOpened, string type)
+ {
+ throw new System.NotImplementedException();
+ }
+
+ ///
+ /// Saves this instance.
+ ///
+ public void SaveTree(ApplicationTree tree)
+ {
+ throw new System.NotImplementedException();
+ }
+
+ ///
+ /// Deletes this instance.
+ ///
+ public void DeleteTree(ApplicationTree tree)
+ {
+ throw new System.NotImplementedException();
+ }
+
+ ///
+ /// Gets an ApplicationTree by it's tree alias.
+ ///
+ /// The tree alias.
+ /// An ApplicationTree instance
+ public ApplicationTree GetByAlias(string treeAlias)
+ {
+ throw new System.NotImplementedException();
+ }
+
+ ///
+ /// Gets all applicationTrees registered in umbraco from the umbracoAppTree table..
+ ///
+ /// Returns a ApplicationTree Array
+ public IEnumerable GetAll()
+ {
+ throw new System.NotImplementedException();
+ }
+
+ ///
+ /// Gets the application tree for the applcation with the specified alias
+ ///
+ /// The application alias.
+ /// Returns a ApplicationTree Array
+ public IEnumerable GetApplicationTrees(string applicationAlias)
+ {
+ throw new System.NotImplementedException();
+ }
+
+ ///
+ /// Gets the application tree for the applcation with the specified alias
+ ///
+ /// The application alias.
+ ///
+ /// Returns a ApplicationTree Array
+ public IEnumerable GetApplicationTrees(string applicationAlias, bool onlyInitialized)
+ {
+ throw new System.NotImplementedException();
+ }
+ }
}
\ No newline at end of file
diff --git a/src/Umbraco.Core/Services/ISectionService.cs b/src/Umbraco.Core/Services/ISectionService.cs
index c6a56e5f4a..52c86093c7 100644
--- a/src/Umbraco.Core/Services/ISectionService.cs
+++ b/src/Umbraco.Core/Services/ISectionService.cs
@@ -46,4 +46,69 @@ namespace Umbraco.Core.Services
///
void DeleteSection(Section section);
}
+
+ ///
+ /// Purely used to allow a service context to create the default services
+ ///
+ internal class EmptySectionService : ISectionService
+ {
+ ///
+ /// The cache storage for all applications
+ ///
+ public IEnumerable GetSections()
+ {
+ throw new System.NotImplementedException();
+ }
+
+ ///
+ /// Get the user's allowed sections
+ ///
+ ///
+ ///
+ public IEnumerable GetAllowedSections(int userId)
+ {
+ throw new System.NotImplementedException();
+ }
+
+ ///
+ /// Gets the application by its alias.
+ ///
+ /// The application alias.
+ ///
+ public Section GetByAlias(string appAlias)
+ {
+ throw new System.NotImplementedException();
+ }
+
+ ///
+ /// 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.
+ public void MakeNew(string name, string alias, string icon)
+ {
+ throw new System.NotImplementedException();
+ }
+
+ ///
+ /// Makes the new.
+ ///
+ /// The name.
+ /// The alias.
+ /// The icon.
+ /// The sort order.
+ public void MakeNew(string name, string alias, string icon, int sortOrder)
+ {
+ throw new System.NotImplementedException();
+ }
+
+ ///
+ /// Deletes the section
+ ///
+ public void DeleteSection(Section section)
+ {
+ throw new System.NotImplementedException();
+ }
+ }
}
\ No newline at end of file
diff --git a/src/Umbraco.Core/Services/ServiceContext.cs b/src/Umbraco.Core/Services/ServiceContext.cs
index 600a8d120d..c28c4e19e5 100644
--- a/src/Umbraco.Core/Services/ServiceContext.cs
+++ b/src/Umbraco.Core/Services/ServiceContext.cs
@@ -279,10 +279,10 @@ namespace Umbraco.Core.Services
_relationService = new Lazy(() => new RelationService(provider, repositoryFactory, logger, eventMessagesFactory, _entityService.Value));
if (_treeService == null)
- _treeService = new Lazy(() => new ApplicationTreeService(logger, cache));
+ _treeService = new Lazy(() => new EmptyApplicationTreeService());
if (_sectionService == null)
- _sectionService = new Lazy(() => new SectionService(_userService.Value, _treeService.Value, provider, cache));
+ _sectionService = new Lazy(() => new EmptySectionService());
if (_macroService == null)
_macroService = new Lazy(() => new MacroService(provider, repositoryFactory, logger, eventMessagesFactory));
diff --git a/src/Umbraco.Core/Umbraco.Core.csproj b/src/Umbraco.Core/Umbraco.Core.csproj
index 379ea863b2..b10b9ae2e8 100644
--- a/src/Umbraco.Core/Umbraco.Core.csproj
+++ b/src/Umbraco.Core/Umbraco.Core.csproj
@@ -342,13 +342,11 @@
+
-
-
-
@@ -1095,7 +1093,6 @@
-
@@ -1262,7 +1259,6 @@
-
@@ -1313,7 +1309,6 @@
-
diff --git a/src/Umbraco.Tests/Plugins/TypeFinderTests.cs b/src/Umbraco.Tests/Plugins/TypeFinderTests.cs
index 10d8e43771..414221bd5b 100644
--- a/src/Umbraco.Tests/Plugins/TypeFinderTests.cs
+++ b/src/Umbraco.Tests/Plugins/TypeFinderTests.cs
@@ -20,6 +20,8 @@ using Umbraco.Core.IO;
using umbraco.DataLayer;
using umbraco.interfaces;
using umbraco.uicontrols;
+using Umbraco.Web.Models.Trees;
+using Umbraco.Web.Trees;
namespace Umbraco.Tests.Plugins
{
@@ -80,7 +82,7 @@ namespace Umbraco.Tests.Plugins
[Test]
public void Find_Classes_With_Attribute()
{
- var typesFound = TypeFinder.FindClassesWithAttribute(_assemblies);
+ var typesFound = TypeFinder.FindClassesWithAttribute(_assemblies);
//TODO: Fix this with the correct count
Assert.AreEqual(1, typesFound.Count());
}
diff --git a/src/Umbraco.Tests/TreesAndSections/ApplicationTreeTest.cs b/src/Umbraco.Tests/TreesAndSections/ApplicationTreeTest.cs
index 42144864c2..f31cd1a7a1 100644
--- a/src/Umbraco.Tests/TreesAndSections/ApplicationTreeTest.cs
+++ b/src/Umbraco.Tests/TreesAndSections/ApplicationTreeTest.cs
@@ -4,6 +4,7 @@ using Umbraco.Core.Services;
using Umbraco.Tests.TestHelpers;
using System;
using System.Linq;
+using Umbraco.Web.Services;
namespace Umbraco.Tests.TreesAndSections
{
diff --git a/src/Umbraco.Tests/TreesAndSections/SectionTests.cs b/src/Umbraco.Tests/TreesAndSections/SectionTests.cs
index 67ef50af38..c18b9ca7c0 100644
--- a/src/Umbraco.Tests/TreesAndSections/SectionTests.cs
+++ b/src/Umbraco.Tests/TreesAndSections/SectionTests.cs
@@ -6,6 +6,7 @@ using umbraco.BusinessLogic;
using System;
using System.Linq;
using Umbraco.Core;
+using Umbraco.Web.Services;
namespace Umbraco.Tests.TreesAndSections
{
diff --git a/src/Umbraco.Web/Cache/CacheRefresherEventHandler.cs b/src/Umbraco.Web/Cache/CacheRefresherEventHandler.cs
index 4249276302..f5465dcca6 100644
--- a/src/Umbraco.Web/Cache/CacheRefresherEventHandler.cs
+++ b/src/Umbraco.Web/Cache/CacheRefresherEventHandler.cs
@@ -12,6 +12,7 @@ using System.Linq;
using umbraco.cms.businesslogic.web;
using Umbraco.Core.Logging;
using Umbraco.Core.Publishing;
+using Umbraco.Web.Services;
using Content = Umbraco.Core.Models.Content;
using ApplicationTree = Umbraco.Core.Models.ApplicationTree;
using DeleteEventArgs = umbraco.cms.businesslogic.DeleteEventArgs;
diff --git a/src/Umbraco.Core/Models/ApplicationAttribute.cs b/src/Umbraco.Web/Models/Trees/ApplicationAttribute.cs
similarity index 96%
rename from src/Umbraco.Core/Models/ApplicationAttribute.cs
rename to src/Umbraco.Web/Models/Trees/ApplicationAttribute.cs
index 8ef78f411e..9eba9b31fd 100644
--- a/src/Umbraco.Core/Models/ApplicationAttribute.cs
+++ b/src/Umbraco.Web/Models/Trees/ApplicationAttribute.cs
@@ -1,6 +1,6 @@
using System;
-namespace Umbraco.Core.Models
+namespace Umbraco.Web.Models.Trees
{
///
/// Identifies an application tree
diff --git a/src/Umbraco.Core/Models/ApplicationDefinitions.cs b/src/Umbraco.Web/Models/Trees/ApplicationDefinitions.cs
similarity index 94%
rename from src/Umbraco.Core/Models/ApplicationDefinitions.cs
rename to src/Umbraco.Web/Models/Trees/ApplicationDefinitions.cs
index 9c0a69df66..002b96bc64 100644
--- a/src/Umbraco.Core/Models/ApplicationDefinitions.cs
+++ b/src/Umbraco.Web/Models/Trees/ApplicationDefinitions.cs
@@ -1,4 +1,7 @@
-namespace Umbraco.Core.Models
+using Umbraco.Core;
+using Umbraco.Core.Models;
+
+namespace Umbraco.Web.Models.Trees
{
// The application definitions are intended as a means to auto populate
// the application.config. On app startup, Umbraco will look for any
diff --git a/src/Umbraco.Web/PluginManagerExtensions.cs b/src/Umbraco.Web/PluginManagerExtensions.cs
index 1f269ac638..0f4150c59d 100644
--- a/src/Umbraco.Web/PluginManagerExtensions.cs
+++ b/src/Umbraco.Web/PluginManagerExtensions.cs
@@ -9,6 +9,7 @@ using Umbraco.Web.Trees;
using Umbraco.Web.WebApi;
using umbraco;
using umbraco.interfaces;
+using Umbraco.Web.Models.Trees;
namespace Umbraco.Web
{
@@ -28,7 +29,7 @@ namespace Umbraco.Web
return resolver.ResolveTypesWithAttribute();
}
- internal static IEnumerable ResolveSurfaceControllers(this PluginManager resolver)
+ internal static IEnumerable ResolveSurfaceControllers(this PluginManager resolver)
{
return resolver.ResolveTypes();
}
diff --git a/src/Umbraco.Web/Properties/AssemblyInfo.cs b/src/Umbraco.Web/Properties/AssemblyInfo.cs
index c881ba448a..c187b754c3 100644
--- a/src/Umbraco.Web/Properties/AssemblyInfo.cs
+++ b/src/Umbraco.Web/Properties/AssemblyInfo.cs
@@ -21,12 +21,6 @@ using System.Security;
// The following GUID is for the ID of the typelib if this project is exposed to COM
[assembly: Guid("ce9d3539-299e-40d3-b605-42ac423e24fa")]
-//This is required so that Medium trust works and this is because of this class:
-// umbraco.presentation.templateControls.ItemDesigner since this class cannot inherit from
-// the System.Web.UI.Design.ControlDesigner in partial trust (or something along those lines)
-// if we remove this class then we won't need to do this.
-[assembly: System.Security.SecurityRules(System.Security.SecurityRuleSet.Level1)]
-
[assembly: InternalsVisibleTo("Umbraco.Tests")]
[assembly: InternalsVisibleTo("umbraco.MacroEngines")]
[assembly: InternalsVisibleTo("Umbraco.Web.UI")]
diff --git a/src/Umbraco.Core/Services/ApplicationTreeService.cs b/src/Umbraco.Web/Services/ApplicationTreeService.cs
similarity index 72%
rename from src/Umbraco.Core/Services/ApplicationTreeService.cs
rename to src/Umbraco.Web/Services/ApplicationTreeService.cs
index 763176b141..4d3c767a79 100644
--- a/src/Umbraco.Core/Services/ApplicationTreeService.cs
+++ b/src/Umbraco.Web/Services/ApplicationTreeService.cs
@@ -1,400 +1,423 @@
-using System;
-using System.Collections.Generic;
-using System.IO;
-using System.Linq;
-using System.Xml.Linq;
-using Umbraco.Core.Cache;
-using Umbraco.Core.Events;
-using Umbraco.Core.IO;
-using Umbraco.Core.Logging;
-using Umbraco.Core.Models;
-using Umbraco.Core.Cache;
-using File = System.IO.File;
-
-namespace Umbraco.Core.Services
-{
- internal class ApplicationTreeService : IApplicationTreeService
- {
- private readonly ILogger _logger;
- private readonly CacheHelper _cache;
- private IEnumerable _allAvailableTrees;
- private volatile bool _isInitialized = false;
- internal const string TreeConfigFileName = "trees.config";
- private static string _treeConfig;
- private static readonly object Locker = new object();
-
- public ApplicationTreeService(ILogger logger, CacheHelper cache)
- {
- _logger = logger;
- _cache = cache;
- }
-
-
- ///
- /// gets/sets the trees.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 TreeConfigFilePath
- {
- get
- {
- if (string.IsNullOrWhiteSpace(_treeConfig))
- {
- _treeConfig = IOHelper.MapPath(SystemDirectories.Config + "/" + TreeConfigFileName);
- }
- return _treeConfig;
- }
- set { _treeConfig = value; }
- }
-
- ///
- /// The main entry point to get application trees
- ///
- ///
- /// This lazily on first access will scan for plugin trees and ensure the trees.config is up-to-date with the plugins. If plugins
- /// haven't changed on disk then the file will not be saved. The trees are all then loaded from this config file into cache and returned.
- ///
- private List GetAppTrees()
- {
- return _cache.RuntimeCache.GetCacheItem>(
- CacheKeys.ApplicationTreeCacheKey,
- () =>
- {
- var list = ReadFromXmlAndSort();
-
- //On first access we need to do some initialization
- if (_isInitialized == false)
- {
- lock (Locker)
- {
- if (_isInitialized == false)
- {
- //now we can check the non-volatile flag
- if (_allAvailableTrees != null)
- {
- var hasChanges = false;
-
- LoadXml(doc =>
- {
- //Now, load in the xml structure and update it with anything that is not declared there and save the file.
-
- //NOTE: On the first iteration here, it will lazily scan all trees, etc... this is because this ienumerable is lazy
- // based on the ApplicationTreeRegistrar - and as noted there this is not an ideal way to do things but were stuck like this
- // currently because of the legacy assemblies and types not in the Core.
-
- //Get all the trees not registered in the config
- var unregistered = _allAvailableTrees
- .Where(x => list.Any(l => l.Alias == x.Alias) == false)
- .ToArray();
-
- hasChanges = unregistered.Any();
-
- if (hasChanges == false) return false;
-
- //add the unregistered ones to the list and re-save the file if any changes were found
- var count = 0;
- foreach (var tree in unregistered)
- {
- doc.Root.Add(new XElement("add",
- new XAttribute("initialize", tree.Initialize),
- new XAttribute("sortOrder", tree.SortOrder),
- new XAttribute("alias", tree.Alias),
- new XAttribute("application", tree.ApplicationAlias),
- new XAttribute("title", tree.Title),
- new XAttribute("iconClosed", tree.IconClosed),
- new XAttribute("iconOpen", tree.IconOpened),
- new XAttribute("type", tree.Type)));
- count++;
- }
-
- //don't save if there's no changes
- return count > 0;
- }, true);
-
- if (hasChanges)
- {
- //If there were changes, we need to re-read the structures from the XML
- list = ReadFromXmlAndSort();
- }
- }
-
- _isInitialized = true;
- }
- }
- }
-
-
- return list;
-
-
- }, new TimeSpan(0, 10, 0));
- }
-
- ///
- /// Initializes the service with any trees found in plugins
- ///
- ///
- /// A collection of all available tree found in assemblies in the application
- ///
- ///
- /// This will update the trees.config with the found tree plugins that are not currently listed in the file when the first
- /// access is made to resolve the tree collection
- ///
- public void Intitialize(IEnumerable allAvailableTrees)
- {
- _allAvailableTrees = allAvailableTrees;
- }
-
- ///
- /// Creates a new application tree.
- ///
- /// if set to true [initialize].
- /// The sort order.
- /// The application alias.
- /// The alias.
- /// The title.
- /// The icon closed.
- /// The icon opened.
- /// The type.
- public void MakeNew(bool initialize, int sortOrder, string applicationAlias, string alias, string title, string iconClosed, string iconOpened, string type)
- {
- LoadXml(doc =>
- {
- var el = doc.Root.Elements("add").SingleOrDefault(x => x.Attribute("alias").Value == alias && x.Attribute("application").Value == applicationAlias);
-
- if (el == null)
- {
- doc.Root.Add(new XElement("add",
- new XAttribute("initialize", initialize),
- new XAttribute("sortOrder", sortOrder),
- new XAttribute("alias", alias),
- new XAttribute("application", applicationAlias),
- new XAttribute("title", title),
- new XAttribute("iconClosed", iconClosed),
- new XAttribute("iconOpen", iconOpened),
- new XAttribute("type", type)));
- }
-
- return true;
-
- }, true);
-
- OnNew(new ApplicationTree(initialize, sortOrder, applicationAlias, alias, title, iconClosed, iconOpened, type), new EventArgs());
- }
-
- ///
- /// Saves this instance.
- ///
- public void SaveTree(ApplicationTree tree)
- {
- LoadXml(doc =>
- {
- var el = doc.Root.Elements("add").SingleOrDefault(x => x.Attribute("alias").Value == tree.Alias && x.Attribute("application").Value == tree.ApplicationAlias);
-
- if (el != null)
- {
- el.RemoveAttributes();
-
- el.Add(new XAttribute("initialize", tree.Initialize));
- el.Add(new XAttribute("sortOrder", tree.SortOrder));
- el.Add(new XAttribute("alias", tree.Alias));
- el.Add(new XAttribute("application", tree.ApplicationAlias));
- el.Add(new XAttribute("title", tree.Title));
- el.Add(new XAttribute("iconClosed", tree.IconClosed));
- el.Add(new XAttribute("iconOpen", tree.IconOpened));
- el.Add(new XAttribute("type", tree.Type));
- }
-
- return true;
-
- }, true);
-
- OnUpdated(tree, new EventArgs());
- }
-
- ///
- /// Deletes this instance.
- ///
- public void DeleteTree(ApplicationTree tree)
- {
- LoadXml(doc =>
- {
- doc.Root.Elements("add")
- .Where(x => x.Attribute("application") != null
- && x.Attribute("application").Value == tree.ApplicationAlias
- && x.Attribute("alias") != null && x.Attribute("alias").Value == tree.Alias).Remove();
-
- return true;
-
- }, true);
-
- OnDeleted(tree, new EventArgs());
- }
-
- ///
- /// Gets an ApplicationTree by it's tree alias.
- ///
- /// The tree alias.
- /// An ApplicationTree instance
- public ApplicationTree GetByAlias(string treeAlias)
- {
- return GetAppTrees().Find(t => (t.Alias == treeAlias));
-
- }
-
- ///
- /// Gets all applicationTrees registered in umbraco from the umbracoAppTree table..
- ///
- /// Returns a ApplicationTree Array
- public IEnumerable GetAll()
- {
- return GetAppTrees().OrderBy(x => x.SortOrder);
- }
-
- ///
- /// Gets the application tree for the applcation with the specified alias
- ///
- /// The application alias.
- /// Returns a ApplicationTree Array
- public IEnumerable GetApplicationTrees(string applicationAlias)
- {
- return GetApplicationTrees(applicationAlias, false);
- }
-
- ///
- /// Gets the application tree for the applcation with the specified alias
- ///
- /// The application alias.
- ///
- /// Returns a ApplicationTree Array
- public IEnumerable GetApplicationTrees(string applicationAlias, bool onlyInitialized)
- {
- var list = GetAppTrees().FindAll(
- t =>
- {
- if (onlyInitialized)
- return (t.ApplicationAlias == applicationAlias && t.Initialize);
- return (t.ApplicationAlias == applicationAlias);
- }
- );
-
- return list.OrderBy(x => x.SortOrder).ToArray();
- }
-
- ///
- /// Loads in the xml structure from disk if one is found, otherwise loads in an empty xml structure, calls the
- /// callback with the xml document and saves the structure back to disk if saveAfterCallback is true.
- ///
- ///
- ///
- internal void LoadXml(Func callback, bool saveAfterCallbackIfChanges)
- {
- lock (Locker)
- {
- var doc = File.Exists(TreeConfigFilePath)
- ? XDocument.Load(TreeConfigFilePath)
- : XDocument.Parse("");
-
- if (doc.Root != null)
- {
- var hasChanges = callback.Invoke(doc);
-
- if (saveAfterCallbackIfChanges && hasChanges
- //Don't save it if it is empty, in some very rare cases if the app domain get's killed in the middle of this process
- // in some insane way the file saved will be empty. I'm pretty sure it's not actually anything to do with the xml doc and
- // more about the IO trying to save the XML doc, but it doesn't hurt to check.
- && doc.Root != null && doc.Root.Elements().Any())
- {
- //ensures the folder exists
- Directory.CreateDirectory(Path.GetDirectoryName(TreeConfigFilePath));
-
- //saves it
- doc.Save(TreeConfigFilePath);
-
- //remove the cache now that it has changed 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.
- _cache.ClearCacheItem(CacheKeys.ApplicationTreeCacheKey);
- }
- }
- }
- }
-
- private List ReadFromXmlAndSort()
- {
- var list = new List();
-
- //read in the xml file containing trees and convert them all to ApplicationTree instances
- 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 applicationAlias = (string)addElement.Attribute("application");
- var type = (string)addElement.Attribute("type");
- var assembly = (string)addElement.Attribute("assembly");
-
- var clrType = Type.GetType(type);
- if (clrType == null)
- {
- _logger.Warn("The tree definition: " + addElement.ToString() + " could not be resolved to a .Net object type");
- continue;
- }
-
- //check if the tree definition (applicationAlias + type + assembly) is already in the list
-
- if (list.Any(tree => tree.ApplicationAlias.InvariantEquals(applicationAlias) && tree.GetRuntimeType() == clrType) == false)
- {
- list.Add(new ApplicationTree(
- addElement.Attribute("initialize") == null || Convert.ToBoolean(addElement.Attribute("initialize").Value),
- addElement.Attribute("sortOrder") != null ? Convert.ToByte(addElement.Attribute("sortOrder").Value) : (byte)0,
- addElement.Attribute("application").Value,
- addElement.Attribute("alias").Value,
- addElement.Attribute("title").Value,
- addElement.Attribute("iconClosed").Value,
- addElement.Attribute("iconOpen").Value,
- addElement.Attribute("type").Value));
- }
- }
-
- return false;
-
- }, false);
-
- return list;
- }
-
-
- internal static event TypedEventHandler Deleted;
- private static void OnDeleted(ApplicationTree app, EventArgs args)
- {
- if (Deleted != null)
- {
- Deleted(app, args);
- }
- }
-
- internal static event TypedEventHandler New;
- private static void OnNew(ApplicationTree app, EventArgs args)
- {
- if (New != null)
- {
- New(app, args);
- }
- }
-
- internal static event TypedEventHandler Updated;
- private static void OnUpdated(ApplicationTree app, EventArgs args)
- {
- if (Updated != null)
- {
- Updated(app, args);
- }
- }
-
- }
-}
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Xml.Linq;
+using Umbraco.Core;
+using Umbraco.Core.Cache;
+using Umbraco.Core.Events;
+using Umbraco.Core.IO;
+using Umbraco.Core.Logging;
+using Umbraco.Core.Models;
+using Umbraco.Core.Services;
+using Umbraco.Web.Trees;
+
+namespace Umbraco.Web.Services
+{
+ internal class ApplicationTreeService : IApplicationTreeService
+ {
+ private readonly ILogger _logger;
+ private readonly CacheHelper _cache;
+ private Lazy> _allAvailableTrees;
+ internal const string TreeConfigFileName = "trees.config";
+ private static string _treeConfig;
+ private static readonly object Locker = new object();
+
+ public ApplicationTreeService(ILogger logger, CacheHelper cache)
+ {
+ _logger = logger;
+ _cache = cache;
+ }
+
+ ///
+ /// gets/sets the trees.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 TreeConfigFilePath
+ {
+ get
+ {
+ if (string.IsNullOrWhiteSpace(_treeConfig))
+ {
+ _treeConfig = IOHelper.MapPath(SystemDirectories.Config + "/" + TreeConfigFileName);
+ }
+ return _treeConfig;
+ }
+ set { _treeConfig = value; }
+ }
+
+ ///
+ /// The main entry point to get application trees
+ ///
+ ///
+ /// This lazily on first access will scan for plugin trees and ensure the trees.config is up-to-date with the plugins. If plugins
+ /// haven't changed on disk then the file will not be saved. The trees are all then loaded from this config file into cache and returned.
+ ///
+ private List GetAppTrees()
+ {
+ return _cache.RuntimeCache.GetCacheItem>(
+ CacheKeys.ApplicationTreeCacheKey,
+ () =>
+ {
+ var list = ReadFromXmlAndSort();
+
+ //now we can check the non-volatile flag
+ if (_allAvailableTrees != null)
+ {
+ var hasChanges = false;
+
+ LoadXml(doc =>
+ {
+ //Now, load in the xml structure and update it with anything that is not declared there and save the file.
+
+ //NOTE: On the first iteration here, it will lazily scan all trees, etc... this is because this ienumerable is lazy
+ // based on the ApplicationTreeRegistrar - and as noted there this is not an ideal way to do things but were stuck like this
+ // currently because of the legacy assemblies and types not in the Core.
+
+ //Get all the trees not registered in the config
+ var unregistered = _allAvailableTrees.Value
+ .Where(x => list.Any(l => l.Alias == x.Alias) == false)
+ .ToArray();
+
+ hasChanges = unregistered.Any();
+
+ if (hasChanges == false) return false;
+
+ //add the unregistered ones to the list and re-save the file if any changes were found
+ var count = 0;
+ foreach (var tree in unregistered)
+ {
+ doc.Root.Add(new XElement("add",
+ new XAttribute("initialize", tree.Initialize),
+ new XAttribute("sortOrder", tree.SortOrder),
+ new XAttribute("alias", tree.Alias),
+ new XAttribute("application", tree.ApplicationAlias),
+ new XAttribute("title", tree.Title),
+ new XAttribute("iconClosed", tree.IconClosed),
+ new XAttribute("iconOpen", tree.IconOpened),
+ new XAttribute("type", tree.Type)));
+ count++;
+ }
+
+ //don't save if there's no changes
+ return count > 0;
+ }, true);
+
+ if (hasChanges)
+ {
+ //If there were changes, we need to re-read the structures from the XML
+ list = ReadFromXmlAndSort();
+ }
+ }
+
+
+ return list;
+
+
+ }, new TimeSpan(0, 10, 0));
+ }
+
+ ///
+ /// Creates a new application tree.
+ ///
+ /// if set to true [initialize].
+ /// The sort order.
+ /// The application alias.
+ /// The alias.
+ /// The title.
+ /// The icon closed.
+ /// The icon opened.
+ /// The type.
+ public void MakeNew(bool initialize, int sortOrder, string applicationAlias, string alias, string title, string iconClosed, string iconOpened, string type)
+ {
+ LoadXml(doc =>
+ {
+ var el = doc.Root.Elements("add").SingleOrDefault(x => x.Attribute("alias").Value == alias && x.Attribute("application").Value == applicationAlias);
+
+ if (el == null)
+ {
+ doc.Root.Add(new XElement("add",
+ new XAttribute("initialize", initialize),
+ new XAttribute("sortOrder", sortOrder),
+ new XAttribute("alias", alias),
+ new XAttribute("application", applicationAlias),
+ new XAttribute("title", title),
+ new XAttribute("iconClosed", iconClosed),
+ new XAttribute("iconOpen", iconOpened),
+ new XAttribute("type", type)));
+ }
+
+ return true;
+
+ }, true);
+
+ OnNew(new ApplicationTree(initialize, sortOrder, applicationAlias, alias, title, iconClosed, iconOpened, type), new EventArgs());
+ }
+
+ ///
+ /// Saves this instance.
+ ///
+ public void SaveTree(ApplicationTree tree)
+ {
+ LoadXml(doc =>
+ {
+ var el = doc.Root.Elements("add").SingleOrDefault(x => x.Attribute("alias").Value == tree.Alias && x.Attribute("application").Value == tree.ApplicationAlias);
+
+ if (el != null)
+ {
+ el.RemoveAttributes();
+
+ el.Add(new XAttribute("initialize", tree.Initialize));
+ el.Add(new XAttribute("sortOrder", tree.SortOrder));
+ el.Add(new XAttribute("alias", tree.Alias));
+ el.Add(new XAttribute("application", tree.ApplicationAlias));
+ el.Add(new XAttribute("title", tree.Title));
+ el.Add(new XAttribute("iconClosed", tree.IconClosed));
+ el.Add(new XAttribute("iconOpen", tree.IconOpened));
+ el.Add(new XAttribute("type", tree.Type));
+ }
+
+ return true;
+
+ }, true);
+
+ OnUpdated(tree, new EventArgs());
+ }
+
+ ///
+ /// Deletes this instance.
+ ///
+ public void DeleteTree(ApplicationTree tree)
+ {
+ LoadXml(doc =>
+ {
+ doc.Root.Elements("add")
+ .Where(x => x.Attribute("application") != null
+ && x.Attribute("application").Value == tree.ApplicationAlias
+ && x.Attribute("alias") != null && x.Attribute("alias").Value == tree.Alias).Remove();
+
+ return true;
+
+ }, true);
+
+ OnDeleted(tree, new EventArgs());
+ }
+
+ ///
+ /// Gets an ApplicationTree by it's tree alias.
+ ///
+ /// The tree alias.
+ /// An ApplicationTree instance
+ public ApplicationTree GetByAlias(string treeAlias)
+ {
+ return GetAppTrees().Find(t => (t.Alias == treeAlias));
+
+ }
+
+ ///
+ /// Gets all applicationTrees registered in umbraco from the umbracoAppTree table..
+ ///
+ /// Returns a ApplicationTree Array
+ public IEnumerable GetAll()
+ {
+ return GetAppTrees().OrderBy(x => x.SortOrder);
+ }
+
+ ///
+ /// Gets the application tree for the applcation with the specified alias
+ ///
+ /// The application alias.
+ /// Returns a ApplicationTree Array
+ public IEnumerable GetApplicationTrees(string applicationAlias)
+ {
+ return GetApplicationTrees(applicationAlias, false);
+ }
+
+ ///
+ /// Gets the application tree for the applcation with the specified alias
+ ///
+ /// The application alias.
+ ///
+ /// Returns a ApplicationTree Array
+ public IEnumerable GetApplicationTrees(string applicationAlias, bool onlyInitialized)
+ {
+ var list = GetAppTrees().FindAll(
+ t =>
+ {
+ if (onlyInitialized)
+ return (t.ApplicationAlias == applicationAlias && t.Initialize);
+ return (t.ApplicationAlias == applicationAlias);
+ }
+ );
+
+ return list.OrderBy(x => x.SortOrder).ToArray();
+ }
+
+ ///
+ /// Loads in the xml structure from disk if one is found, otherwise loads in an empty xml structure, calls the
+ /// callback with the xml document and saves the structure back to disk if saveAfterCallback is true.
+ ///
+ ///
+ ///
+ internal void LoadXml(Func callback, bool saveAfterCallbackIfChanges)
+ {
+ lock (Locker)
+ {
+ var doc = System.IO.File.Exists(TreeConfigFilePath)
+ ? XDocument.Load(TreeConfigFilePath)
+ : XDocument.Parse("");
+
+ if (doc.Root != null)
+ {
+ var hasChanges = callback.Invoke(doc);
+
+ if (saveAfterCallbackIfChanges && hasChanges
+ //Don't save it if it is empty, in some very rare cases if the app domain get's killed in the middle of this process
+ // in some insane way the file saved will be empty. I'm pretty sure it's not actually anything to do with the xml doc and
+ // more about the IO trying to save the XML doc, but it doesn't hurt to check.
+ && doc.Root != null && doc.Root.Elements().Any())
+ {
+ //ensures the folder exists
+ Directory.CreateDirectory(Path.GetDirectoryName(TreeConfigFilePath));
+
+ //saves it
+ doc.Save(TreeConfigFilePath);
+
+ //remove the cache now that it has changed 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.
+ _cache.ClearCacheItem(CacheKeys.ApplicationTreeCacheKey);
+ }
+ }
+ }
+ }
+
+ private List ReadFromXmlAndSort()
+ {
+ var list = new List();
+
+ //read in the xml file containing trees and convert them all to ApplicationTree instances
+ 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 applicationAlias = (string)addElement.Attribute("application");
+ var type = (string)addElement.Attribute("type");
+ var assembly = (string)addElement.Attribute("assembly");
+
+ var clrType = Type.GetType(type);
+ if (clrType == null)
+ {
+ _logger.Warn("The tree definition: " + addElement.ToString() + " could not be resolved to a .Net object type");
+ continue;
+ }
+
+ //check if the tree definition (applicationAlias + type + assembly) is already in the list
+
+ if (list.Any(tree => tree.ApplicationAlias.InvariantEquals(applicationAlias) && tree.GetRuntimeType() == clrType) == false)
+ {
+ list.Add(new ApplicationTree(
+ addElement.Attribute("initialize") == null || Convert.ToBoolean(addElement.Attribute("initialize").Value),
+ addElement.Attribute("sortOrder") != null ? Convert.ToByte(addElement.Attribute("sortOrder").Value) : (byte)0,
+ addElement.Attribute("application").Value,
+ addElement.Attribute("alias").Value,
+ addElement.Attribute("title").Value,
+ addElement.Attribute("iconClosed").Value,
+ addElement.Attribute("iconOpen").Value,
+ addElement.Attribute("type").Value));
+ }
+ }
+
+ return false;
+
+ }, false);
+
+ return list;
+ }
+
+
+ internal static event TypedEventHandler Deleted;
+ private static void OnDeleted(ApplicationTree app, EventArgs args)
+ {
+ if (Deleted != null)
+ {
+ Deleted(app, args);
+ }
+ }
+
+ internal static event TypedEventHandler New;
+ private static void OnNew(ApplicationTree app, EventArgs args)
+ {
+ if (New != null)
+ {
+ New(app, args);
+ }
+ }
+
+ internal static event TypedEventHandler Updated;
+ private static void OnUpdated(ApplicationTree app, EventArgs args)
+ {
+ if (Updated != null)
+ {
+ Updated(app, args);
+ }
+ }
+
+ ///
+ /// This class is here so that we can provide lazy access to tree scanning for when it is needed
+ ///
+ private class LazyEnumerableTrees : IEnumerable
+ {
+ public LazyEnumerableTrees()
+ {
+ _lazyTrees = new Lazy>(() =>
+ {
+ var added = new List();
+
+ // Load all Controller Trees by attribute
+ var types = PluginManager.Current.ResolveTypesWithAttribute();
+ //convert them to ApplicationTree instances
+ var items = types
+ .Select(x =>
+ new Tuple(x, x.GetCustomAttributes(false).Single()))
+ .Select(x => new ApplicationTree(x.Item2.Initialize, x.Item2.SortOrder, x.Item2.ApplicationAlias, x.Item2.Alias, x.Item2.Title, x.Item2.IconClosed, x.Item2.IconOpen, x.Item1.GetFullNameWithAssembly()))
+ .ToArray();
+
+ added.AddRange(items.Select(x => x.Alias));
+
+ return items.ToArray();
+ });
+ }
+
+ private readonly Lazy> _lazyTrees;
+
+ ///
+ /// Returns an enumerator that iterates through the collection.
+ ///
+ ///
+ /// A that can be used to iterate through the collection.
+ ///
+ public IEnumerator GetEnumerator()
+ {
+ return _lazyTrees.Value.GetEnumerator();
+ }
+
+ ///
+ /// Returns an enumerator that iterates through a collection.
+ ///
+ ///
+ /// An object that can be used to iterate through the collection.
+ ///
+ IEnumerator IEnumerable.GetEnumerator()
+ {
+ return GetEnumerator();
+ }
+ }
+ }
+}
diff --git a/src/Umbraco.Core/Services/SectionService.cs b/src/Umbraco.Web/Services/SectionService.cs
similarity index 95%
rename from src/Umbraco.Core/Services/SectionService.cs
rename to src/Umbraco.Web/Services/SectionService.cs
index 376c41471e..07da567636 100644
--- a/src/Umbraco.Core/Services/SectionService.cs
+++ b/src/Umbraco.Web/Services/SectionService.cs
@@ -1,329 +1,330 @@
-using System;
-using System.Collections;
-using System.Collections.Generic;
-using System.IO;
-using System.Linq;
-using System.Runtime.CompilerServices;
-using System.Xml.Linq;
-using Umbraco.Core.Cache;
-using Umbraco.Core.Events;
-using Umbraco.Core.IO;
-using Umbraco.Core.Models;
-using Umbraco.Core.Persistence;
-using Umbraco.Core.Persistence.UnitOfWork;
-using File = System.IO.File;
-
-namespace Umbraco.Core.Services
-{
- internal class SectionService : ISectionService
- {
- private readonly IUserService _userService;
- private readonly Lazy> _allAvailableSections;
- private readonly IApplicationTreeService _applicationTreeService;
- private readonly IDatabaseUnitOfWorkProvider _uowProvider;
- private readonly CacheHelper _cache;
- internal const string AppConfigFileName = "applications.config";
- private static string _appConfig;
- private static readonly object Locker = new object();
-
- public SectionService(
- IUserService userService,
- IApplicationTreeService applicationTreeService,
- IDatabaseUnitOfWorkProvider uowProvider,
- CacheHelper cache)
- {
- if (applicationTreeService == null) throw new ArgumentNullException("applicationTreeService");
- if (cache == null) throw new ArgumentNullException("cache");
-
- _userService = userService;
- _applicationTreeService = applicationTreeService;
- _uowProvider = uowProvider;
- _cache = cache;
- _allAvailableSections = new Lazy>(() => new LazyEnumerableSections());
- }
-
-
- ///
- /// 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
- ///
- public IEnumerable GetSections()
- {
- return _cache.RuntimeCache.GetCacheItem>(
- CacheKeys.ApplicationsCacheKey,
- () =>
- {
- var list = ReadFromXmlAndSort();
- var hasChanges = false;
- var localCopyList = list;
-
- LoadXml(doc =>
- {
- //Now, load in the xml structure and update it with anything that is not declared there and save the file.
- //NOTE: On the first iteration here, it will lazily scan all apps, etc... this is because this ienumerable is lazy
- //Get all the trees not registered in the config
-
- var unregistered = _allAvailableSections.Value
- .Where(x => localCopyList.Any(l => l.Alias == x.Alias) == false)
- .ToArray();
-
- hasChanges = unregistered.Any();
-
- var count = 0;
- foreach (var attr in unregistered)
- {
- 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)));
- count++;
- }
-
- //don't save if there's no changes
- return count > 0;
- }, true);
-
- if (hasChanges)
- {
- //If there were changes, we need to re-read the structures from the XML
- list = ReadFromXmlAndSort();
- }
-
- return list;
-
- }, new TimeSpan(0, 10, 0));
- }
-
- internal void LoadXml(Func callback, bool saveAfterCallbackIfChanged)
- {
- lock (Locker)
- {
- var doc = File.Exists(AppConfigFilePath)
- ? XDocument.Load(AppConfigFilePath)
- : XDocument.Parse("");
-
- if (doc.Root != null)
- {
- var changed = callback.Invoke(doc);
-
- if (saveAfterCallbackIfChanged && changed)
- {
- //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.
- _cache.ClearCacheItem(CacheKeys.ApplicationsCacheKey);
- }
- }
- }
- }
-
- ///
- /// Get the user's allowed sections
- ///
- ///
- ///
- public IEnumerable GetAllowedSections(int userId)
- {
-
- var user = _userService.GetUserById(userId);
- if (user == null)
- {
- throw new InvalidOperationException("No user found with id " + userId);
- }
-
- return GetSections().Where(x => user.AllowedSections.Contains(x.Alias));
- }
-
- ///
- /// Gets the application by its alias.
- ///
- /// The application alias.
- ///
- public Section GetByAlias(string appAlias)
- {
- return GetSections().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.
- public void MakeNew(string name, string alias, string icon)
- {
- MakeNew(name, alias, icon, GetSections().Max(x => x.SortOrder) + 1);
- }
-
- ///
- /// Makes the new.
- ///
- /// The name.
- /// The alias.
- /// The icon.
- /// The sort order.
- public void MakeNew(string name, string alias, string icon, int sortOrder)
- {
- if (GetSections().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)));
- return true;
- }, true);
-
- //raise event
- OnNew(new Section(name, alias, icon, sortOrder), new EventArgs());
- }
- }
-
- ///
- /// Deletes the section
- ///
- public void DeleteSection(Section section)
- {
- lock (Locker)
- {
- //delete the assigned applications
- _uowProvider.GetUnitOfWork().Database.Execute(
- "delete from umbracoUser2App where app = @appAlias",
- new { appAlias = section.Alias });
-
- //delete the assigned trees
- var trees = _applicationTreeService.GetApplicationTrees(section.Alias);
- foreach (var t in trees)
- {
- _applicationTreeService.DeleteTree(t);
- }
-
- LoadXml(doc =>
- {
- doc.Root.Elements("add").Where(x => x.Attribute("alias") != null && x.Attribute("alias").Value == section.Alias)
- .Remove();
-
- return true;
- }, true);
-
- //raise event
- OnDeleted(section, new EventArgs());
- }
- }
-
- private List ReadFromXmlAndSort()
- {
- 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));
- }
- return false;
- }, false);
-
- return tmp;
- }
-
- 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);
- }
- }
-
- ///
- /// This class is here so that we can provide lazy access to tree scanning for when it is needed
- ///
- private class LazyEnumerableSections : IEnumerable
- {
- public LazyEnumerableSections()
- {
- _lazySections = new Lazy>(() =>
- {
- // Load all Applications by attribute and add them to the XML config
-
- //don't cache the result of this because it is only used once during app startup, caching will just add a bit more mem overhead for no reason
- var types = PluginManager.Current.ResolveTypesWithAttribute(cacheResult: false);
-
- //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());
- return attrs.Select(x => new Section(x.Name, x.Alias, x.Icon, x.SortOrder)).ToArray();
- });
- }
-
- private readonly Lazy> _lazySections;
-
- ///
- /// Returns an enumerator that iterates through the collection.
- ///
- ///
- /// A that can be used to iterate through the collection.
- ///
- public IEnumerator GetEnumerator()
- {
- return _lazySections.Value.GetEnumerator();
- }
-
- ///
- /// Returns an enumerator that iterates through a collection.
- ///
- ///
- /// An object that can be used to iterate through the collection.
- ///
- IEnumerator IEnumerable.GetEnumerator()
- {
- return GetEnumerator();
- }
- }
-
- }
-}
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Xml.Linq;
+using Umbraco.Core;
+using Umbraco.Core.Cache;
+using Umbraco.Core.Events;
+using Umbraco.Core.IO;
+using Umbraco.Core.Models;
+using Umbraco.Core.Persistence.UnitOfWork;
+using Umbraco.Core.Services;
+using Umbraco.Web.Models.Trees;
+using File = System.IO.File;
+
+namespace Umbraco.Web.Services
+{
+ internal class SectionService : ISectionService
+ {
+ private readonly IUserService _userService;
+ private readonly Lazy> _allAvailableSections;
+ private readonly IApplicationTreeService _applicationTreeService;
+ private readonly IDatabaseUnitOfWorkProvider _uowProvider;
+ private readonly CacheHelper _cache;
+ internal const string AppConfigFileName = "applications.config";
+ private static string _appConfig;
+ private static readonly object Locker = new object();
+
+ public SectionService(
+ IUserService userService,
+ IApplicationTreeService applicationTreeService,
+ IDatabaseUnitOfWorkProvider uowProvider,
+ CacheHelper cache)
+ {
+ if (applicationTreeService == null) throw new ArgumentNullException("applicationTreeService");
+ if (cache == null) throw new ArgumentNullException("cache");
+
+ _userService = userService;
+ _applicationTreeService = applicationTreeService;
+ _uowProvider = uowProvider;
+ _cache = cache;
+ _allAvailableSections = new Lazy>(() => new LazyEnumerableSections());
+ }
+
+
+ ///
+ /// 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
+ ///
+ public IEnumerable GetSections()
+ {
+ return _cache.RuntimeCache.GetCacheItem>(
+ CacheKeys.ApplicationsCacheKey,
+ () =>
+ {
+ var list = ReadFromXmlAndSort();
+ var hasChanges = false;
+ var localCopyList = list;
+
+ LoadXml(doc =>
+ {
+ //Now, load in the xml structure and update it with anything that is not declared there and save the file.
+ //NOTE: On the first iteration here, it will lazily scan all apps, etc... this is because this ienumerable is lazy
+ //Get all the trees not registered in the config
+
+ var unregistered = _allAvailableSections.Value
+ .Where(x => localCopyList.Any(l => l.Alias == x.Alias) == false)
+ .ToArray();
+
+ hasChanges = unregistered.Any();
+
+ var count = 0;
+ foreach (var attr in unregistered)
+ {
+ 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)));
+ count++;
+ }
+
+ //don't save if there's no changes
+ return count > 0;
+ }, true);
+
+ if (hasChanges)
+ {
+ //If there were changes, we need to re-read the structures from the XML
+ list = ReadFromXmlAndSort();
+ }
+
+ return list;
+
+ }, new TimeSpan(0, 10, 0));
+ }
+
+ internal void LoadXml(Func callback, bool saveAfterCallbackIfChanged)
+ {
+ lock (Locker)
+ {
+ var doc = File.Exists(AppConfigFilePath)
+ ? XDocument.Load(AppConfigFilePath)
+ : XDocument.Parse("");
+
+ if (doc.Root != null)
+ {
+ var changed = callback.Invoke(doc);
+
+ if (saveAfterCallbackIfChanged && changed)
+ {
+ //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.
+ _cache.ClearCacheItem(CacheKeys.ApplicationsCacheKey);
+ }
+ }
+ }
+ }
+
+ ///
+ /// Get the user's allowed sections
+ ///
+ ///
+ ///
+ public IEnumerable GetAllowedSections(int userId)
+ {
+
+ var user = _userService.GetUserById(userId);
+ if (user == null)
+ {
+ throw new InvalidOperationException("No user found with id " + userId);
+ }
+
+ return GetSections().Where(x => user.AllowedSections.Contains(x.Alias));
+ }
+
+ ///
+ /// Gets the application by its alias.
+ ///
+ /// The application alias.
+ ///
+ public Section GetByAlias(string appAlias)
+ {
+ return GetSections().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.
+ public void MakeNew(string name, string alias, string icon)
+ {
+ MakeNew(name, alias, icon, GetSections().Max(x => x.SortOrder) + 1);
+ }
+
+ ///
+ /// Makes the new.
+ ///
+ /// The name.
+ /// The alias.
+ /// The icon.
+ /// The sort order.
+ public void MakeNew(string name, string alias, string icon, int sortOrder)
+ {
+ if (GetSections().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)));
+ return true;
+ }, true);
+
+ //raise event
+ OnNew(new Section(name, alias, icon, sortOrder), new EventArgs());
+ }
+ }
+
+ ///
+ /// Deletes the section
+ ///
+ public void DeleteSection(Section section)
+ {
+ lock (Locker)
+ {
+ //delete the assigned applications
+ _uowProvider.GetUnitOfWork().Database.Execute(
+ "delete from umbracoUser2App where app = @appAlias",
+ new { appAlias = section.Alias });
+
+ //delete the assigned trees
+ var trees = _applicationTreeService.GetApplicationTrees(section.Alias);
+ foreach (var t in trees)
+ {
+ _applicationTreeService.DeleteTree(t);
+ }
+
+ LoadXml(doc =>
+ {
+ doc.Root.Elements("add").Where(x => x.Attribute("alias") != null && x.Attribute("alias").Value == section.Alias)
+ .Remove();
+
+ return true;
+ }, true);
+
+ //raise event
+ OnDeleted(section, new EventArgs());
+ }
+ }
+
+ private List ReadFromXmlAndSort()
+ {
+ 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));
+ }
+ return false;
+ }, false);
+
+ return tmp;
+ }
+
+ 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);
+ }
+ }
+
+ ///
+ /// This class is here so that we can provide lazy access to tree scanning for when it is needed
+ ///
+ private class LazyEnumerableSections : IEnumerable
+ {
+ public LazyEnumerableSections()
+ {
+ _lazySections = new Lazy>(() =>
+ {
+ // Load all Applications by attribute and add them to the XML config
+
+ //don't cache the result of this because it is only used once during app startup, caching will just add a bit more mem overhead for no reason
+ var types = PluginManager.Current.ResolveTypesWithAttribute(cacheResult: false);
+
+ //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());
+ return Enumerable.ToArray(attrs.Select(x => new Section(x.Name, x.Alias, x.Icon, x.SortOrder)));
+ });
+ }
+
+ private readonly Lazy> _lazySections;
+
+ ///
+ /// Returns an enumerator that iterates through the collection.
+ ///
+ ///
+ /// A that can be used to iterate through the collection.
+ ///
+ public IEnumerator GetEnumerator()
+ {
+ return _lazySections.Value.GetEnumerator();
+ }
+
+ ///
+ /// Returns an enumerator that iterates through a collection.
+ ///
+ ///
+ /// An object that can be used to iterate through the collection.
+ ///
+ IEnumerator IEnumerable.GetEnumerator()
+ {
+ return GetEnumerator();
+ }
+ }
+
+ }
+}
diff --git a/src/Umbraco.Web/Trees/ApplicationTreeRegistrar.cs b/src/Umbraco.Web/Trees/ApplicationTreeRegistrar.cs
deleted file mode 100644
index 314cc9142e..0000000000
--- a/src/Umbraco.Web/Trees/ApplicationTreeRegistrar.cs
+++ /dev/null
@@ -1,80 +0,0 @@
-using System;
-using System.Collections;
-using System.Collections.Generic;
-using System.Linq;
-using System.Xml.Linq;
-using Umbraco.Core;
-using Umbraco.Core.Models;
-using umbraco.businesslogic;
-using Umbraco.Core.Services;
-
-namespace Umbraco.Web.Trees
-{
- ///
- /// A startup handler for putting the tree config in the config file based on attributes found
- ///
- ///
- /// TODO: This is really not a very ideal process but the code is found here because tree plugins are in the Web project or the legacy business logic project.
- /// Moving forward we can put the base tree plugin classes in the core and then this can all just be taken care of normally within the service.
- ///
- public sealed class ApplicationTreeRegistrar : ApplicationEventHandler
- {
- protected override void ApplicationStarted(UmbracoApplicationBase umbracoApplication, ApplicationContext applicationContext)
- {
- //Call initialize on the tree service with the lazy enumerable class below, when the tree service needs to resolve,
- // it will lazily do the scanning and comparing so it's not actually done on app start.
- applicationContext.Services.ApplicationTreeService.Intitialize(new LazyEnumerableTrees());
- }
-
- ///
- /// This class is here so that we can provide lazy access to tree scanning for when it is needed
- ///
- private class LazyEnumerableTrees : IEnumerable
- {
- public LazyEnumerableTrees()
- {
- _lazyTrees = new Lazy>(() =>
- {
- var added = new List();
-
- // Load all Controller Trees by attribute
- var types = PluginManager.Current.ResolveAttributedTreeControllers();
- //convert them to ApplicationTree instances
- var items = types
- .Select(x =>
- new Tuple(x, x.GetCustomAttributes(false).Single()))
- .Select(x => new ApplicationTree(x.Item2.Initialize, x.Item2.SortOrder, x.Item2.ApplicationAlias, x.Item2.Alias, x.Item2.Title, x.Item2.IconClosed, x.Item2.IconOpen, x.Item1.GetFullNameWithAssembly()))
- .ToArray();
-
- added.AddRange(items.Select(x => x.Alias));
-
- return items.ToArray();
- });
- }
-
- private readonly Lazy> _lazyTrees;
-
- ///
- /// Returns an enumerator that iterates through the collection.
- ///
- ///
- /// A that can be used to iterate through the collection.
- ///
- public IEnumerator GetEnumerator()
- {
- return _lazyTrees.Value.GetEnumerator();
- }
-
- ///
- /// Returns an enumerator that iterates through a collection.
- ///
- ///
- /// An object that can be used to iterate through the collection.
- ///
- IEnumerator IEnumerable.GetEnumerator()
- {
- return GetEnumerator();
- }
- }
- }
-}
diff --git a/src/Umbraco.Web/Umbraco.Web.csproj b/src/Umbraco.Web/Umbraco.Web.csproj
index f581747494..c2bad0a5ca 100644
--- a/src/Umbraco.Web/Umbraco.Web.csproj
+++ b/src/Umbraco.Web/Umbraco.Web.csproj
@@ -348,6 +348,13 @@
+
+
+
+
+
+
+
@@ -378,6 +385,7 @@
+
@@ -590,7 +598,6 @@
-
@@ -777,7 +784,6 @@
-
@@ -791,8 +797,6 @@
-
-