diff --git a/src/Umbraco.Core/CoreBootManager.cs b/src/Umbraco.Core/CoreBootManager.cs index f59b6e37ab..16ffc828a9 100644 --- a/src/Umbraco.Core/CoreBootManager.cs +++ b/src/Umbraco.Core/CoreBootManager.cs @@ -1,10 +1,12 @@ using System; using System.Collections.Generic; +using System.IO; using System.Linq; using System.Web; using AutoMapper; using Umbraco.Core.Cache; using Umbraco.Core.Configuration; +using Umbraco.Core.IO; using Umbraco.Core.Logging; using Umbraco.Core.Models.Mapping; using Umbraco.Core.Models.PublishedContent; @@ -21,6 +23,7 @@ using Umbraco.Core.PropertyEditors; using Umbraco.Core.PropertyEditors.ValueConverters; using Umbraco.Core.Publishing; using Umbraco.Core.Macros; +using Umbraco.Core.Manifest; using Umbraco.Core.Services; using Umbraco.Core.Sync; using Umbraco.Core.Strings; @@ -259,8 +262,12 @@ namespace Umbraco.Core /// protected virtual void InitializeResolvers() { - PropertyEditorResolver.Current = new PropertyEditorResolver(() => PluginManager.Current.ResolvePropertyEditors()); - ParameterEditorResolver.Current = new ParameterEditorResolver(() => PluginManager.Current.ResolveParameterEditors()); + var builder = new ManifestBuilder( + ApplicationCache.RuntimeCache, + new ManifestParser(new DirectoryInfo(IOHelper.MapPath("~/App_Plugins")), ApplicationCache.RuntimeCache)); + + PropertyEditorResolver.Current = new PropertyEditorResolver(() => PluginManager.Current.ResolvePropertyEditors(), builder); + ParameterEditorResolver.Current = new ParameterEditorResolver(() => PluginManager.Current.ResolveParameterEditors(), builder); //setup the validators resolver with our predefined validators ValidatorsResolver.Current = new ValidatorsResolver(new[] diff --git a/src/Umbraco.Core/Manifest/ManifestBuilder.cs b/src/Umbraco.Core/Manifest/ManifestBuilder.cs index d62c215c8d..5f95deea22 100644 --- a/src/Umbraco.Core/Manifest/ManifestBuilder.cs +++ b/src/Umbraco.Core/Manifest/ManifestBuilder.cs @@ -1,6 +1,8 @@ -using System.Collections.Concurrent; +using System; +using System.Collections.Concurrent; using System.Collections.Generic; using System.IO; +using Umbraco.Core.Cache; using Umbraco.Core.IO; using Umbraco.Core.PropertyEditors; @@ -11,73 +13,92 @@ namespace Umbraco.Core.Manifest /// internal class ManifestBuilder { + private readonly IRuntimeCacheProvider _cache; + private readonly ManifestParser _parser; - private static readonly ConcurrentDictionary StaticCache = new ConcurrentDictionary(); - - private const string ManifestKey = "manifests"; + public ManifestBuilder(IRuntimeCacheProvider cache, ManifestParser parser) + { + _cache = cache; + _parser = parser; + } + + private const string GridEditorsKey = "grideditors"; private const string PropertyEditorsKey = "propertyeditors"; private const string ParameterEditorsKey = "parametereditors"; /// - /// Returns all property editors found in the manfifests + /// Returns all grid editors found in the manfifests /// - internal static IEnumerable PropertyEditors + internal IEnumerable GridEditors { get { - return (IEnumerable) StaticCache.GetOrAdd( - PropertyEditorsKey, - s => + return _cache.GetCacheItem>( + typeof (ManifestBuilder) + GridEditorsKey, + () => + { + var editors = new List(); + foreach (var manifest in _parser.GetManifests()) { - var editors = new List(); - foreach (var manifest in GetManifests()) + if (manifest.GridEditors != null) { - if (manifest.PropertyEditors != null) - { - editors.AddRange(ManifestParser.GetPropertyEditors(manifest.PropertyEditors)); - } - + editors.AddRange(ManifestParser.GetGridEditors(manifest.GridEditors)); } - return editors; - }); + + } + return editors; + }, new TimeSpan(0, 10, 0)); + } + } + + /// + /// Returns all property editors found in the manfifests + /// + internal IEnumerable PropertyEditors + { + get + { + return _cache.GetCacheItem>( + typeof(ManifestBuilder) + PropertyEditorsKey, + () => + { + var editors = new List(); + foreach (var manifest in _parser.GetManifests()) + { + if (manifest.PropertyEditors != null) + { + editors.AddRange(ManifestParser.GetPropertyEditors(manifest.PropertyEditors)); + } + + } + return editors; + }, new TimeSpan(0, 10, 0)); } } /// /// Returns all parameter editors found in the manfifests and all property editors that are flagged to be parameter editors /// - internal static IEnumerable ParameterEditors + internal IEnumerable ParameterEditors { get { - return (IEnumerable)StaticCache.GetOrAdd( - ParameterEditorsKey, - s => + return _cache.GetCacheItem>( + typeof (ManifestBuilder) + ParameterEditorsKey, + () => { var editors = new List(); - foreach (var manifest in GetManifests()) + foreach (var manifest in _parser.GetManifests()) { if (manifest.ParameterEditors != null) { - editors.AddRange(ManifestParser.GetParameterEditors(manifest.ParameterEditors)); + editors.AddRange(ManifestParser.GetParameterEditors(manifest.ParameterEditors)); } } return editors; - }); + }, new TimeSpan(0, 10, 0)); } } - - /// - /// Ensures the manifests are found and loaded into memory - /// - private static IEnumerable GetManifests() - { - return (IEnumerable) StaticCache.GetOrAdd(ManifestKey, s => - { - var parser = new ManifestParser(new DirectoryInfo(IOHelper.MapPath("~/App_Plugins"))); - return parser.GetManifests(); - }); - } - + } } \ No newline at end of file diff --git a/src/Umbraco.Core/Manifest/ManifestParser.cs b/src/Umbraco.Core/Manifest/ManifestParser.cs index 6794377001..7847eccf6e 100644 --- a/src/Umbraco.Core/Manifest/ManifestParser.cs +++ b/src/Umbraco.Core/Manifest/ManifestParser.cs @@ -5,6 +5,7 @@ using System.Linq; using System.Text.RegularExpressions; using Newtonsoft.Json; using Newtonsoft.Json.Linq; +using Umbraco.Core.Cache; using Umbraco.Core.IO; using Umbraco.Core.Logging; using Umbraco.Core.PropertyEditors; @@ -17,15 +18,28 @@ namespace Umbraco.Core.Manifest internal class ManifestParser { private readonly DirectoryInfo _pluginsDir; - + private readonly IRuntimeCacheProvider _cache; + //used to strip comments private static readonly Regex CommentsSurround = new Regex(@"/\*([^*]|[\r\n]|(\*+([^*/]|[\r\n])))*\*+/", RegexOptions.Compiled); private static readonly Regex CommentsLine = new Regex(@"^\s*//.*?$", RegexOptions.Compiled | RegexOptions.Multiline); - public ManifestParser(DirectoryInfo pluginsDir) + public ManifestParser(DirectoryInfo pluginsDir, IRuntimeCacheProvider cache) { if (pluginsDir == null) throw new ArgumentNullException("pluginsDir"); _pluginsDir = pluginsDir; + _cache = cache; + } + + /// + /// Parse the grid editors from the json array + /// + /// + /// + internal static IEnumerable GetGridEditors(JArray jsonEditors) + { + return JsonConvert.DeserializeObject>( + jsonEditors.ToString()); } /// @@ -57,11 +71,17 @@ namespace Umbraco.Core.Manifest /// Get all registered manifests /// /// + /// + /// This ensures that we only build and look for all manifests once per Web app (based on the IRuntimeCache) + /// public IEnumerable GetManifests() { - //get all Manifest.js files in the appropriate folders - var manifestFileContents = GetAllManifestFileContents(_pluginsDir); - return CreateManifests(manifestFileContents.ToArray()); + return _cache.GetCacheItem>(typeof (ManifestParser) + "GetManifests", () => + { + //get all Manifest.js files in the appropriate folders + var manifestFileContents = GetAllManifestFileContents(_pluginsDir); + return CreateManifests(manifestFileContents.ToArray()); + }, new TimeSpan(0, 10, 0)); } /// @@ -154,6 +174,20 @@ namespace Umbraco.Core.Manifest throw new FormatException("The manifest is not formatted correctly contains more than one 'propertyEditors' element"); } + //validate the parameterEditors section + var paramEditors = deserialized.Properties().Where(x => x.Name == "parameterEditors").ToArray(); + if (paramEditors.Length > 1) + { + throw new FormatException("The manifest is not formatted correctly contains more than one 'parameterEditors' element"); + } + + //validate the gridEditors section + var gridEditors = deserialized.Properties().Where(x => x.Name == "gridEditors").ToArray(); + if (gridEditors.Length > 1) + { + throw new FormatException("The manifest is not formatted correctly contains more than one 'gridEditors' element"); + } + var jConfig = init.Any() ? (JArray)deserialized["javascript"] : new JArray(); ReplaceVirtualPaths(jConfig); @@ -181,7 +215,8 @@ namespace Umbraco.Core.Manifest JavaScriptInitialize = jConfig, StylesheetInitialize = cssConfig, PropertyEditors = propEditors.Any() ? (JArray)deserialized["propertyEditors"] : new JArray(), - ParameterEditors = propEditors.Any() ? (JArray)deserialized["parameterEditors"] : new JArray() + ParameterEditors = propEditors.Any() ? (JArray)deserialized["parameterEditors"] : new JArray(), + GridEditors = propEditors.Any() ? (JArray)deserialized["gridEditors"] : new JArray() }; result.Add(manifest); } diff --git a/src/Umbraco.Core/Manifest/PackageManifest.cs b/src/Umbraco.Core/Manifest/PackageManifest.cs index 3475a60312..dea1eb9877 100644 --- a/src/Umbraco.Core/Manifest/PackageManifest.cs +++ b/src/Umbraco.Core/Manifest/PackageManifest.cs @@ -26,5 +26,10 @@ namespace Umbraco.Core.Manifest /// The json array of parameter editors /// public JArray ParameterEditors { get; set; } + + /// + /// The json array of grid editors + /// + public JArray GridEditors { get; set; } } } \ No newline at end of file diff --git a/src/Umbraco.Core/PropertyEditors/GridEditor.cs b/src/Umbraco.Core/PropertyEditors/GridEditor.cs new file mode 100644 index 0000000000..2fd24a2e99 --- /dev/null +++ b/src/Umbraco.Core/PropertyEditors/GridEditor.cs @@ -0,0 +1,62 @@ +using System.Collections.Generic; +using Newtonsoft.Json; + +namespace Umbraco.Core.PropertyEditors +{ + internal class GridEditor + { + public GridEditor() + { + Config = new Dictionary(); + } + + [JsonProperty("name", Required = Required.Always)] + public string Name { get; set; } + + [JsonProperty("alias", Required = Required.Always)] + public string Alias { get; set; } + + [JsonProperty("view", Required = Required.Always)] + public string View { get; set; } + + [JsonProperty("render")] + public string Render { get; set; } + + [JsonProperty("icon", Required = Required.Always)] + public string Icon { get; set; } + + [JsonProperty("config")] + public IDictionary Config { get; set; } + + protected bool Equals(GridEditor other) + { + return string.Equals(Alias, other.Alias); + } + + /// + /// Determines whether the specified is equal to the current . + /// + /// + /// true if the specified object is equal to the current object; otherwise, false. + /// + /// The object to compare with the current object. + public override bool Equals(object obj) + { + if (ReferenceEquals(null, obj)) return false; + if (ReferenceEquals(this, obj)) return true; + if (obj.GetType() != this.GetType()) return false; + return Equals((GridEditor) obj); + } + + /// + /// Serves as a hash function for a particular type. + /// + /// + /// A hash code for the current . + /// + public override int GetHashCode() + { + return Alias.GetHashCode(); + } + } +} \ No newline at end of file diff --git a/src/Umbraco.Core/PropertyEditors/ParameterEditorResolver.cs b/src/Umbraco.Core/PropertyEditors/ParameterEditorResolver.cs index ef01b3a891..2130476485 100644 --- a/src/Umbraco.Core/PropertyEditors/ParameterEditorResolver.cs +++ b/src/Umbraco.Core/PropertyEditors/ParameterEditorResolver.cs @@ -15,11 +15,14 @@ namespace Umbraco.Core.PropertyEditors /// internal class ParameterEditorResolver : LazyManyObjectsResolverBase { - public ParameterEditorResolver(Func> typeListProducerList) + private readonly ManifestBuilder _builder; + + public ParameterEditorResolver(Func> typeListProducerList, ManifestBuilder builder) : base(typeListProducerList, ObjectLifetimeScope.Application) { + _builder = builder; } - + /// /// Returns the parameter editors /// @@ -38,9 +41,9 @@ namespace Umbraco.Core.PropertyEditors //exclude the non parameter editor c# property editors .Except(filtered) //include the manifest parameter editors - .Union(ManifestBuilder.ParameterEditors) + .Union(_builder.ParameterEditors) //include the manifest prop editors that are parameter editors - .Union(ManifestBuilder.PropertyEditors.Where(x => x.IsParameterEditor)); + .Union(_builder.PropertyEditors.Where(x => x.IsParameterEditor)); } } diff --git a/src/Umbraco.Core/PropertyEditors/PropertyEditorResolver.cs b/src/Umbraco.Core/PropertyEditors/PropertyEditorResolver.cs index 90c6e184aa..4e17915712 100644 --- a/src/Umbraco.Core/PropertyEditors/PropertyEditorResolver.cs +++ b/src/Umbraco.Core/PropertyEditors/PropertyEditorResolver.cs @@ -1,6 +1,8 @@ using System; using System.Collections.Generic; +using System.IO; using System.Linq; +using Umbraco.Core.IO; using Umbraco.Core.Manifest; using Umbraco.Core.ObjectResolution; @@ -17,7 +19,17 @@ namespace Umbraco.Core.PropertyEditors public PropertyEditorResolver(Func> typeListProducerList) : base(typeListProducerList, ObjectLifetimeScope.Application) { - _unioned = new Lazy>(() => Values.Union(ManifestBuilder.PropertyEditors).ToList()); + var builder = new ManifestBuilder( + ApplicationContext.Current.ApplicationCache.RuntimeCache, + new ManifestParser(new DirectoryInfo(IOHelper.MapPath("~/App_Plugins")), ApplicationContext.Current.ApplicationCache.RuntimeCache)); + + _unioned = new Lazy>(() => Values.Union(builder.PropertyEditors).ToList()); + } + + internal PropertyEditorResolver(Func> typeListProducerList, ManifestBuilder builder) + : base(typeListProducerList, ObjectLifetimeScope.Application) + { + _unioned = new Lazy>(() => Values.Union(builder.PropertyEditors).ToList()); } private readonly Lazy> _unioned; diff --git a/src/Umbraco.Core/Umbraco.Core.csproj b/src/Umbraco.Core/Umbraco.Core.csproj index d9d37cb436..dec8a9c1fc 100644 --- a/src/Umbraco.Core/Umbraco.Core.csproj +++ b/src/Umbraco.Core/Umbraco.Core.csproj @@ -347,6 +347,7 @@ + diff --git a/src/Umbraco.Tests/Manifest/ManifestParserTests.cs b/src/Umbraco.Tests/Manifest/ManifestParserTests.cs index b51d5b84c9..603ff56838 100644 --- a/src/Umbraco.Tests/Manifest/ManifestParserTests.cs +++ b/src/Umbraco.Tests/Manifest/ManifestParserTests.cs @@ -63,6 +63,61 @@ namespace Umbraco.Tests.Manifest Assert.AreEqual(0, parser.ElementAt(0).PreValueEditor.Fields.ElementAt(1).Validators.Count()); } + [Test] + public void Parse_Grid_Editors() + { + var a = JsonConvert.DeserializeObject(@"[ + { + alias: 'Test.Test1', + name: 'Test 1', + view: 'blah', + icon: 'hello' + }, + { + alias: 'Test.Test2', + name: 'Test 2', + config: { key1: 'some default val' }, + view: '/hello/world.cshtml', + icon: 'helloworld' + }, + { + alias: 'Test.Test3', + name: 'Test 3', + config: { key1: 'some default val' }, + view: '/hello/world.html', + render: '/hello/world.cshtml', + icon: 'helloworld' + } +]"); + var parser = ManifestParser.GetGridEditors(a).ToArray(); + + Assert.AreEqual(3, parser.Count()); + + Assert.AreEqual("Test.Test1", parser.ElementAt(0).Alias); + Assert.AreEqual("Test 1", parser.ElementAt(0).Name); + Assert.AreEqual("blah", parser.ElementAt(0).View); + Assert.AreEqual("hello", parser.ElementAt(0).Icon); + Assert.IsNull(parser.ElementAt(0).Render); + Assert.AreEqual(0, parser.ElementAt(0).Config.Count); + + Assert.AreEqual("Test.Test2", parser.ElementAt(1).Alias); + Assert.AreEqual("Test 2", parser.ElementAt(1).Name); + Assert.AreEqual("/hello/world.cshtml", parser.ElementAt(1).View); + Assert.AreEqual("helloworld", parser.ElementAt(1).Icon); + Assert.IsNull(parser.ElementAt(1).Render); + Assert.AreEqual(1, parser.ElementAt(1).Config.Count); + Assert.AreEqual("some default val", parser.ElementAt(1).Config["key1"]); + + Assert.AreEqual("Test.Test3", parser.ElementAt(2).Alias); + Assert.AreEqual("Test 3", parser.ElementAt(2).Name); + Assert.AreEqual("/hello/world.html", parser.ElementAt(2).View); + Assert.AreEqual("helloworld", parser.ElementAt(2).Icon); + Assert.AreEqual("/hello/world.cshtml", parser.ElementAt(2).Render); + Assert.AreEqual(1, parser.ElementAt(2).Config.Count); + Assert.AreEqual("some default val", parser.ElementAt(2).Config["key1"]); + + } + [Test] public void Parse_Property_Editors() { diff --git a/src/Umbraco.Web.UI.Client/src/common/services/grid.service.js b/src/Umbraco.Web.UI.Client/src/common/services/grid.service.js index f64701b8d8..898d1293fe 100644 --- a/src/Umbraco.Web.UI.Client/src/common/services/grid.service.js +++ b/src/Umbraco.Web.UI.Client/src/common/services/grid.service.js @@ -1,7 +1,7 @@ angular.module('umbraco.services') .factory('gridService', function ($http, $q){ - var configPath = "../config/grid.editors.config.js"; + var configPath = Umbraco.Sys.ServerVariables.umbracoUrls.gridConfig; var service = { getGridEditors: function () { return $http.get(configPath); diff --git a/src/Umbraco.Web/Editors/BackOfficeController.cs b/src/Umbraco.Web/Editors/BackOfficeController.cs index 003104de63..37fbd32e22 100644 --- a/src/Umbraco.Web/Editors/BackOfficeController.cs +++ b/src/Umbraco.Web/Editors/BackOfficeController.cs @@ -14,7 +14,10 @@ using Umbraco.Core.Configuration; using Umbraco.Core.IO; using Umbraco.Core.Manifest; using Umbraco.Core; +using Umbraco.Core.Cache; +using Umbraco.Core.Logging; using Umbraco.Core.Models; +using Umbraco.Core.PropertyEditors; using Umbraco.Core.Security; using Umbraco.Web.Models.ContentEditing; using Umbraco.Web.Mvc; @@ -31,6 +34,7 @@ namespace Umbraco.Web.Editors /// A controller to render out the default back office view and JS results /// [UmbracoUseHttps] + [DisableClientCache] public class BackOfficeController : UmbracoController { /// @@ -81,11 +85,11 @@ namespace Umbraco.Web.Editors /// /// [MinifyJavaScriptResult(Order = 0)] - [OutputCache(Order = 1, VaryByParam = "none", Location = OutputCacheLocation.Any, Duration = 5000)] + [OutputCache(Order = 1, VaryByParam = "none", Location = OutputCacheLocation.Server, Duration = 5000)] public JavaScriptResult Application() { var plugins = new DirectoryInfo(Server.MapPath("~/App_Plugins")); - var parser = new ManifestParser(plugins); + var parser = new ManifestParser(plugins, ApplicationContext.ApplicationCache.RuntimeCache); var initJs = new JsInitialization(parser); var initCss = new CssInitialization(parser); @@ -106,16 +110,75 @@ namespace Umbraco.Web.Editors [HttpGet] public JsonNetResult GetManifestAssetList() { - var plugins = new DirectoryInfo(Server.MapPath("~/App_Plugins")); - var parser = new ManifestParser(plugins); - var initJs = new JsInitialization(parser); - var initCss = new CssInitialization(parser); - var jsResult = initJs.GetJavascriptInitializationArray(HttpContext, new JArray()); - var cssResult = initCss.GetStylesheetInitializationArray(HttpContext); + Func getResult = () => + { + var plugins = new DirectoryInfo(Server.MapPath("~/App_Plugins")); + var parser = new ManifestParser(plugins, ApplicationContext.ApplicationCache.RuntimeCache); + var initJs = new JsInitialization(parser); + var initCss = new CssInitialization(parser); + var jsResult = initJs.GetJavascriptInitializationArray(HttpContext, new JArray()); + var cssResult = initCss.GetStylesheetInitializationArray(HttpContext); + ManifestParser.MergeJArrays(jsResult, cssResult); + return jsResult; + }; - ManifestParser.MergeJArrays(jsResult, cssResult); + //cache the result if debugging is disabled + var result = HttpContext.IsDebuggingEnabled + ? getResult() + : ApplicationContext.ApplicationCache.RuntimeCache.GetCacheItem( + typeof (BackOfficeController) + "GetManifestAssetList", + () => getResult(), + new TimeSpan(0, 10, 0)); - return new JsonNetResult {Data = jsResult, Formatting = Formatting.Indented}; + return new JsonNetResult { Data = result, Formatting = Formatting.Indented }; + } + + [UmbracoAuthorize(Order = 0)] + [HttpGet] + public JsonNetResult GetGridConfig() + { + Func> getResult = () => + { + var editors = new List(); + var gridConfig = Server.MapPath("~/Config/grid.editors.config.js"); + if (System.IO.File.Exists(gridConfig)) + { + try + { + var arr = JArray.Parse(System.IO.File.ReadAllText(gridConfig)); + //ensure the contents parse correctly to objects + var parsed = ManifestParser.GetGridEditors(arr); + editors.AddRange(parsed); + } + catch (Exception ex) + { + LogHelper.Error("Could not parse the contents of grid.editors.config.js into a JSON array", ex); + } + } + + var plugins = new DirectoryInfo(Server.MapPath("~/App_Plugins")); + var parser = new ManifestParser(plugins, ApplicationContext.ApplicationCache.RuntimeCache); + var builder = new ManifestBuilder(ApplicationContext.ApplicationCache.RuntimeCache, parser); + foreach (var gridEditor in builder.GridEditors) + { + //no duplicates! (based on alias) + if (editors.Contains(gridEditor) == false) + { + editors.Add(gridEditor); + } + } + return editors; + }; + + //cache the result if debugging is disabled + var result = HttpContext.IsDebuggingEnabled + ? getResult() + : ApplicationContext.ApplicationCache.RuntimeCache.GetCacheItem>( + typeof(BackOfficeController) + "GetGridConfig", + () => getResult(), + new TimeSpan(0, 10, 0)); + + return new JsonNetResult { Data = result, Formatting = Formatting.Indented }; } /// @@ -124,168 +187,172 @@ namespace Umbraco.Web.Editors /// [UmbracoAuthorize(Order = 0)] [MinifyJavaScriptResult(Order = 1)] - [OutputCache(Order = 2, VaryByParam = "none", Location = OutputCacheLocation.Any, Duration = 5000)] public JavaScriptResult ServerVariables() { - //authenticationApiBaseUrl - - //now we need to build up the variables - var d = new Dictionary + Func> getResult = () => new Dictionary + { { + "umbracoUrls", new Dictionary { - "umbracoUrls", new Dictionary - { - {"legacyTreeJs", Url.Action("LegacyTreeJs", "BackOffice")}, - {"manifestAssetList", Url.Action("GetManifestAssetList", "BackOffice")}, - {"serverVarsJs", Url.Action("Application", "BackOffice")}, - //API URLs - { - "embedApiBaseUrl", Url.GetUmbracoApiServiceBaseUrl( - controller => controller.GetEmbed("",0,0)) - }, - { - "userApiBaseUrl", Url.GetUmbracoApiServiceBaseUrl( - controller => controller.PostDisableUser(0)) - }, - { - "contentApiBaseUrl", Url.GetUmbracoApiServiceBaseUrl( - controller => controller.PostSave(null)) - }, - { - "mediaApiBaseUrl", Url.GetUmbracoApiServiceBaseUrl( - controller => controller.GetRootMedia()) - }, - { - "imagesApiBaseUrl", Url.GetUmbracoApiServiceBaseUrl( - controller => controller.GetBigThumbnail(0)) - }, - { - "sectionApiBaseUrl", Url.GetUmbracoApiServiceBaseUrl( - controller => controller.GetSections()) - }, - { - "treeApplicationApiBaseUrl", Url.GetUmbracoApiServiceBaseUrl( - controller => controller.GetApplicationTrees(null, null, null)) - }, - { - "contentTypeApiBaseUrl", Url.GetUmbracoApiServiceBaseUrl( - controller => controller.GetAllowedChildren(0)) - }, - { - "mediaTypeApiBaseUrl", Url.GetUmbracoApiServiceBaseUrl( - controller => controller.GetAllowedChildren(0)) - }, - { - "macroApiBaseUrl", Url.GetUmbracoApiServiceBaseUrl( - controller => controller.GetMacroParameters(0)) - }, - { - "authenticationApiBaseUrl", Url.GetUmbracoApiServiceBaseUrl( - controller => controller.PostLogin(null)) - }, - { - "currentUserApiBaseUrl", Url.GetUmbracoApiServiceBaseUrl( - controller => controller.GetMembershipProviderConfig()) - }, - { - "legacyApiBaseUrl", Url.GetUmbracoApiServiceBaseUrl( - controller => controller.DeleteLegacyItem(null, null, null)) - }, - { - "entityApiBaseUrl", Url.GetUmbracoApiServiceBaseUrl( - controller => controller.GetById(0, UmbracoEntityTypes.Media)) - }, - { - "dataTypeApiBaseUrl", Url.GetUmbracoApiServiceBaseUrl( - controller => controller.GetById(0)) - }, - { - "dashboardApiBaseUrl", Url.GetUmbracoApiServiceBaseUrl( - controller => controller.GetDashboard(null)) - }, - { - "logApiBaseUrl", Url.GetUmbracoApiServiceBaseUrl( - controller => controller.GetEntityLog(0)) - }, - { - "memberApiBaseUrl", Url.GetUmbracoApiServiceBaseUrl( - controller => controller.GetByKey(Guid.Empty)) - }, - { - "packageInstallApiBaseUrl", Url.GetUmbracoApiServiceBaseUrl( - controller => controller.Fetch(string.Empty)) - }, - { - "rteApiBaseUrl", Url.GetUmbracoApiServiceBaseUrl( - controller => controller.GetConfiguration()) - }, - { - "stylesheetApiBaseUrl", Url.GetUmbracoApiServiceBaseUrl( - controller => controller.GetAll()) - }, - { - "memberTypeApiBaseUrl", Url.GetUmbracoApiServiceBaseUrl( - controller => controller.GetAllTypes()) - }, - { - "updateCheckApiBaseUrl", Url.GetUmbracoApiServiceBaseUrl( - controller => controller.GetCheck()) - }, - { - "tagApiBaseUrl", Url.GetUmbracoApiServiceBaseUrl( - controller => controller.GetAllTags(null)) - }, - { - "memberTreeBaseUrl", Url.GetUmbracoApiServiceBaseUrl( - controller => controller.GetNodes("-1", null)) - }, - { - "mediaTreeBaseUrl", Url.GetUmbracoApiServiceBaseUrl( - controller => controller.GetNodes("-1", null)) - }, - { - "contentTreeBaseUrl", Url.GetUmbracoApiServiceBaseUrl( - controller => controller.GetNodes("-1", null)) - }, - { - "tagsDataBaseUrl", Url.GetUmbracoApiServiceBaseUrl( - controller => controller.GetTags("")) - }, - { - "examineMgmtBaseUrl", Url.GetUmbracoApiServiceBaseUrl( - controller => controller.GetIndexerDetails()) - }, - { - "xmlDataIntegrityBaseUrl", Url.GetUmbracoApiServiceBaseUrl( - controller => controller.CheckContentXmlTable()) - } - } - }, + {"legacyTreeJs", Url.Action("LegacyTreeJs", "BackOffice")}, + {"manifestAssetList", Url.Action("GetManifestAssetList", "BackOffice")}, + {"gridConfig", Url.Action("GetGridConfig", "BackOffice")}, + {"serverVarsJs", Url.Action("Application", "BackOffice")}, + //API URLs + { + "embedApiBaseUrl", Url.GetUmbracoApiServiceBaseUrl( + controller => controller.GetEmbed("", 0, 0)) + }, + { + "userApiBaseUrl", Url.GetUmbracoApiServiceBaseUrl( + controller => controller.PostDisableUser(0)) + }, + { + "contentApiBaseUrl", Url.GetUmbracoApiServiceBaseUrl( + controller => controller.PostSave(null)) + }, + { + "mediaApiBaseUrl", Url.GetUmbracoApiServiceBaseUrl( + controller => controller.GetRootMedia()) + }, + { + "imagesApiBaseUrl", Url.GetUmbracoApiServiceBaseUrl( + controller => controller.GetBigThumbnail(0)) + }, + { + "sectionApiBaseUrl", Url.GetUmbracoApiServiceBaseUrl( + controller => controller.GetSections()) + }, + { + "treeApplicationApiBaseUrl", Url.GetUmbracoApiServiceBaseUrl( + controller => controller.GetApplicationTrees(null, null, null)) + }, + { + "contentTypeApiBaseUrl", Url.GetUmbracoApiServiceBaseUrl( + controller => controller.GetAllowedChildren(0)) + }, + { + "mediaTypeApiBaseUrl", Url.GetUmbracoApiServiceBaseUrl( + controller => controller.GetAllowedChildren(0)) + }, + { + "macroApiBaseUrl", Url.GetUmbracoApiServiceBaseUrl( + controller => controller.GetMacroParameters(0)) + }, + { + "authenticationApiBaseUrl", Url.GetUmbracoApiServiceBaseUrl( + controller => controller.PostLogin(null)) + }, + { + "currentUserApiBaseUrl", Url.GetUmbracoApiServiceBaseUrl( + controller => controller.GetMembershipProviderConfig()) + }, + { + "legacyApiBaseUrl", Url.GetUmbracoApiServiceBaseUrl( + controller => controller.DeleteLegacyItem(null, null, null)) + }, + { + "entityApiBaseUrl", Url.GetUmbracoApiServiceBaseUrl( + controller => controller.GetById(0, UmbracoEntityTypes.Media)) + }, + { + "dataTypeApiBaseUrl", Url.GetUmbracoApiServiceBaseUrl( + controller => controller.GetById(0)) + }, + { + "dashboardApiBaseUrl", Url.GetUmbracoApiServiceBaseUrl( + controller => controller.GetDashboard(null)) + }, + { + "logApiBaseUrl", Url.GetUmbracoApiServiceBaseUrl( + controller => controller.GetEntityLog(0)) + }, + { + "memberApiBaseUrl", Url.GetUmbracoApiServiceBaseUrl( + controller => controller.GetByKey(Guid.Empty)) + }, + { + "packageInstallApiBaseUrl", Url.GetUmbracoApiServiceBaseUrl( + controller => controller.Fetch(string.Empty)) + }, + { + "rteApiBaseUrl", Url.GetUmbracoApiServiceBaseUrl( + controller => controller.GetConfiguration()) + }, + { + "stylesheetApiBaseUrl", Url.GetUmbracoApiServiceBaseUrl( + controller => controller.GetAll()) + }, + { + "memberTypeApiBaseUrl", Url.GetUmbracoApiServiceBaseUrl( + controller => controller.GetAllTypes()) + }, + { + "updateCheckApiBaseUrl", Url.GetUmbracoApiServiceBaseUrl( + controller => controller.GetCheck()) + }, + { + "tagApiBaseUrl", Url.GetUmbracoApiServiceBaseUrl( + controller => controller.GetAllTags(null)) + }, + { + "memberTreeBaseUrl", Url.GetUmbracoApiServiceBaseUrl( + controller => controller.GetNodes("-1", null)) + }, + { + "mediaTreeBaseUrl", Url.GetUmbracoApiServiceBaseUrl( + controller => controller.GetNodes("-1", null)) + }, + { + "contentTreeBaseUrl", Url.GetUmbracoApiServiceBaseUrl( + controller => controller.GetNodes("-1", null)) + }, + { + "tagsDataBaseUrl", Url.GetUmbracoApiServiceBaseUrl( + controller => controller.GetTags("")) + }, + { + "examineMgmtBaseUrl", Url.GetUmbracoApiServiceBaseUrl( + controller => controller.GetIndexerDetails()) + }, + { + "xmlDataIntegrityBaseUrl", Url.GetUmbracoApiServiceBaseUrl( + controller => controller.CheckContentXmlTable()) + } + } + }, + { + "umbracoSettings", new Dictionary { - "umbracoSettings", new Dictionary - { - {"umbracoPath", GlobalSettings.Path}, - {"mediaPath", IOHelper.ResolveUrl(SystemDirectories.Media).TrimEnd('/')}, - {"appPluginsPath", IOHelper.ResolveUrl(SystemDirectories.AppPlugins).TrimEnd('/')}, - { - "imageFileTypes", - string.Join(",", UmbracoConfig.For.UmbracoSettings().Content.ImageFileTypes) - }, - {"keepUserLoggedIn", UmbracoConfig.For.UmbracoSettings().Security.KeepUserLoggedIn}, - } - }, + {"umbracoPath", GlobalSettings.Path}, + {"mediaPath", IOHelper.ResolveUrl(SystemDirectories.Media).TrimEnd('/')}, + {"appPluginsPath", IOHelper.ResolveUrl(SystemDirectories.AppPlugins).TrimEnd('/')}, + { + "imageFileTypes", + string.Join(",", UmbracoConfig.For.UmbracoSettings().Content.ImageFileTypes) + }, + {"keepUserLoggedIn", UmbracoConfig.For.UmbracoSettings().Security.KeepUserLoggedIn}, + } + }, + { + "umbracoPlugins", new Dictionary { - "umbracoPlugins", new Dictionary - { - {"trees", GetTreePluginsMetaData()} - } - }, - {"isDebuggingEnabled", HttpContext.IsDebuggingEnabled}, - {"application", GetApplicationState()} - }; + {"trees", GetTreePluginsMetaData()} + } + }, + {"isDebuggingEnabled", HttpContext.IsDebuggingEnabled}, + {"application", GetApplicationState()} + }; + //cache the result if debugging is disabled + var result = HttpContext.IsDebuggingEnabled + ? getResult() + : ApplicationContext.ApplicationCache.RuntimeCache.GetCacheItem>( + typeof(BackOfficeController) + "ServerVariables", + () => getResult(), + new TimeSpan(0, 10, 0)); - return JavaScript(ServerVariablesParser.Parse(d)); + return JavaScript(ServerVariablesParser.Parse(result)); } /// @@ -346,18 +413,30 @@ namespace Umbraco.Web.Editors /// [UmbracoAuthorize(Order = 0)] [MinifyJavaScriptResult(Order = 1)] - [OutputCache(Order = 2, VaryByParam = "none", Location = OutputCacheLocation.Any, Duration = 5000)] public JavaScriptResult LegacyTreeJs() - { - var javascript = new StringBuilder(); - javascript.AppendLine(LegacyTreeJavascript.GetLegacyTreeJavascript()); - javascript.AppendLine(LegacyTreeJavascript.GetLegacyIActionJavascript()); - //add all of the menu blocks - foreach (var file in GetLegacyActionJs(LegacyJsActionType.JsBlock)) + { + Func getResult = () => { - javascript.AppendLine(file); - } - return JavaScript(javascript.ToString()); + var javascript = new StringBuilder(); + javascript.AppendLine(LegacyTreeJavascript.GetLegacyTreeJavascript()); + javascript.AppendLine(LegacyTreeJavascript.GetLegacyIActionJavascript()); + //add all of the menu blocks + foreach (var file in GetLegacyActionJs(LegacyJsActionType.JsBlock)) + { + javascript.AppendLine(file); + } + return javascript.ToString(); + }; + + //cache the result if debugging is disabled + var result = HttpContext.IsDebuggingEnabled + ? getResult() + : ApplicationContext.ApplicationCache.RuntimeCache.GetCacheItem( + typeof(BackOfficeController) + "LegacyTreeJs", + () => getResult(), + new TimeSpan(0, 10, 0)); + + return JavaScript(result); } /// diff --git a/src/Umbraco.Web/Mvc/DisableClientCacheAttribute.cs b/src/Umbraco.Web/Mvc/DisableClientCacheAttribute.cs new file mode 100644 index 0000000000..448e04222c --- /dev/null +++ b/src/Umbraco.Web/Mvc/DisableClientCacheAttribute.cs @@ -0,0 +1,25 @@ +using System; +using System.Web; +using System.Web.Mvc; + +namespace Umbraco.Web.Mvc +{ + /// + /// Will ensure that client-side cache does not occur by sending the correct response headers + /// + public class DisableClientCacheAttribute : ActionFilterAttribute + { + public override void OnResultExecuting(ResultExecutingContext filterContext) + { + if (filterContext.IsChildAction) base.OnResultExecuting(filterContext); + + filterContext.HttpContext.Response.Cache.SetExpires(DateTime.UtcNow.AddDays(-1)); + filterContext.HttpContext.Response.Cache.SetValidUntilExpires(false); + filterContext.HttpContext.Response.Cache.SetRevalidation(HttpCacheRevalidation.AllCaches); + filterContext.HttpContext.Response.Cache.SetCacheability(HttpCacheability.NoCache); + filterContext.HttpContext.Response.Cache.SetNoStore(); + + base.OnResultExecuting(filterContext); + } + } +} \ No newline at end of file diff --git a/src/Umbraco.Web/Mvc/MinifyJavaScriptResultAttribute.cs b/src/Umbraco.Web/Mvc/MinifyJavaScriptResultAttribute.cs index 498c6acc76..8281af7412 100644 --- a/src/Umbraco.Web/Mvc/MinifyJavaScriptResultAttribute.cs +++ b/src/Umbraco.Web/Mvc/MinifyJavaScriptResultAttribute.cs @@ -1,5 +1,4 @@ -using System.Web; -using System.Web.Mvc; +using System.Web.Mvc; using System.Web.UI; using ClientDependency.Core; using ClientDependency.Core.CompositeFiles; @@ -7,7 +6,7 @@ using ClientDependency.Core.CompositeFiles; namespace Umbraco.Web.Mvc { /// - /// Minifies and caches the result for the JavaScriptResult + /// Minifies the result for the JavaScriptResult /// /// /// Only minifies in release mode