From 380775a006cbf34442504245c999663bac336889 Mon Sep 17 00:00:00 2001 From: james kim Date: Mon, 27 Aug 2018 16:43:13 +1000 Subject: [PATCH] Added caching option --- .../PerplexUmbracoForms.config | 4 + .../Code/Configuration/PerplexCacheConfig.cs | 14 ++ .../PerplexUmbracoFormsConfig.cs | 12 +- Perplex.Umbraco.Forms/Code/UmbracoEvents.cs | 32 ++- .../Controllers/PerplexFormTreeController.cs | 185 ++++++++++-------- .../Perplex.Umbraco.Forms.csproj | 1 + 6 files changed, 161 insertions(+), 87 deletions(-) create mode 100644 Perplex.Umbraco.Forms/Code/Configuration/PerplexCacheConfig.cs diff --git a/Perplex.Umbraco.Forms/App_Plugins/PerplexUmbracoForms/PerplexUmbracoForms.config b/Perplex.Umbraco.Forms/App_Plugins/PerplexUmbracoForms/PerplexUmbracoForms.config index 0e560b1..7b24969 100644 --- a/Perplex.Umbraco.Forms/App_Plugins/PerplexUmbracoForms/PerplexUmbracoForms.config +++ b/Perplex.Umbraco.Forms/App_Plugins/PerplexUmbracoForms/PerplexUmbracoForms.config @@ -44,4 +44,8 @@ + + true + 10 + \ No newline at end of file diff --git a/Perplex.Umbraco.Forms/Code/Configuration/PerplexCacheConfig.cs b/Perplex.Umbraco.Forms/Code/Configuration/PerplexCacheConfig.cs new file mode 100644 index 0000000..0d2a328 --- /dev/null +++ b/Perplex.Umbraco.Forms/Code/Configuration/PerplexCacheConfig.cs @@ -0,0 +1,14 @@ +using System.Xml.Serialization; + +namespace PerplexUmbraco.Forms.Code.Configuration +{ + [XmlType("PerplexCacheConfig")] + public class PerplexCacheConfig + { + [XmlElement("CacheDurationInMinutes")] + public int CacheDurationInMinutes { get; set; } + + [XmlElement("EnableCache")] + public bool EnableCache { get; set; } + } +} diff --git a/Perplex.Umbraco.Forms/Code/Configuration/PerplexUmbracoFormsConfig.cs b/Perplex.Umbraco.Forms/Code/Configuration/PerplexUmbracoFormsConfig.cs index 6c876da..27fc3ce 100644 --- a/Perplex.Umbraco.Forms/Code/Configuration/PerplexUmbracoFormsConfig.cs +++ b/Perplex.Umbraco.Forms/Code/Configuration/PerplexUmbracoFormsConfig.cs @@ -1,11 +1,9 @@ using System; using System.Collections.Generic; using System.IO; -using System.Linq; -using System.Text; -using System.Threading.Tasks; using System.Web.Hosting; using System.Xml.Serialization; + using static PerplexUmbraco.Forms.Code.Constants; namespace PerplexUmbraco.Forms.Code.Configuration @@ -73,6 +71,8 @@ public static void CreateIfNotExists() public PerplexRecaptchaConfig PerplexRecaptchaConfig { get; set; } + public PerplexCacheConfig PerplexCacheConfig { get; set; } + private static string GetFilePath() { return HostingEnvironment.MapPath(Constants.CONFIGURATION_FILE_PATH); @@ -139,6 +139,12 @@ private static string GetFilePath() PerplexRecaptchaConfig = new PerplexRecaptchaConfig { ErrorMessage = "" + }, + + PerplexCacheConfig = new PerplexCacheConfig + { + EnableCache = false, + CacheDurationInMinutes = 10 } }; } diff --git a/Perplex.Umbraco.Forms/Code/UmbracoEvents.cs b/Perplex.Umbraco.Forms/Code/UmbracoEvents.cs index 69214d2..c223061 100644 --- a/Perplex.Umbraco.Forms/Code/UmbracoEvents.cs +++ b/Perplex.Umbraco.Forms/Code/UmbracoEvents.cs @@ -1,12 +1,14 @@ -using System.Linq; +using System; +using System.Linq; using System.Web; + +using PerplexUmbraco.Forms.Code.Configuration; + using Umbraco.Core; using Umbraco.Core.Logging; -using PerplexUmbraco.Forms.Code.Configuration; -using Umbraco.Web; -using Umbraco.Forms.Data.Storage; -using System; using Umbraco.Forms.Core; +using Umbraco.Forms.Data.Storage; +using Umbraco.Web; namespace PerplexUmbraco.Forms.Code { @@ -64,6 +66,8 @@ void FormStorage_Created(object sender, FormEventArgs e) folder.Forms.Add(form.Id.ToString()); PerplexFolder.SaveAll(); + + ClearFormsCache(folderId.ToString()); } void FormStorage_Deleted(object sender, FormEventArgs e) @@ -75,7 +79,23 @@ void FormStorage_Deleted(object sender, FormEventArgs e) { folder.Forms.Remove(form.Id.ToString()); PerplexFolder.SaveAll(); + + ClearFormsCache(folder.Id); + } + } + + void ClearFormsCache(string folderId) + { + var cacheConfig = PerplexUmbracoFormsConfig.Get.PerplexCacheConfig; + + if (cacheConfig.EnableCache) + { + var cacheKey = $"PerplexFormTreeController_GetTreeNodes_id:{folderId}"; + var rtCache = ApplicationContext.Current.ApplicationCache.RuntimeCache; + + if (rtCache.GetCacheItemsByKeySearch(cacheKey).Any()) + rtCache.ClearCacheByKeySearch(cacheKey); } } } -} \ No newline at end of file +} diff --git a/Perplex.Umbraco.Forms/Controllers/PerplexFormTreeController.cs b/Perplex.Umbraco.Forms/Controllers/PerplexFormTreeController.cs index 7ee2956..23e5149 100644 --- a/Perplex.Umbraco.Forms/Controllers/PerplexFormTreeController.cs +++ b/Perplex.Umbraco.Forms/Controllers/PerplexFormTreeController.cs @@ -1,23 +1,18 @@ -using Newtonsoft.Json; -using PerplexUmbraco.Forms.Code; -using System; -using System.Collections.Generic; -using System.IO; +using System; using System.Linq; using System.Net.Http.Formatting; -using System.Text; -using System.Threading.Tasks; using System.Web; -using System.Web.Hosting; -using System.Web.Http; -using Umbraco.Forms.Data; + +using PerplexUmbraco.Forms.Code; +using PerplexUmbraco.Forms.Code.Configuration; + +using umbraco; +using umbraco.BusinessLogic.Actions; using Umbraco.Forms.Web.Trees; +using Umbraco.Web; using Umbraco.Web.Models.Trees; using Umbraco.Web.Mvc; using Umbraco.Web.Trees; -using Umbraco.Web; -using umbraco.BusinessLogic.Actions; -using umbraco; namespace PerplexUmbraco.Forms.Controllers { @@ -41,92 +36,126 @@ public class PerplexFormTreeController : FormTreeController { // We load our custom menu actions from our own folder private const string VIEWS_ROOT = "/App_Plugins/PerplexUmbracoForms/views/"; + private readonly PerplexCacheConfig _cacheConfig; - public PerplexFormTreeController() { } + public PerplexFormTreeController() + { + _cacheConfig = PerplexUmbracoFormsConfig.Get.PerplexCacheConfig; + } - protected override Umbraco.Web.Models.Trees.TreeNodeCollection GetTreeNodes(string id, System.Net.Http.Formatting.FormDataCollection queryStrings) + protected override TreeNodeCollection GetTreeNodes(string id, FormDataCollection queryStrings) { + var cacheKey = $"PerplexFormTreeController_GetTreeNodes_id:{queryStrings["id"]}"; + var rtCache = ApplicationContext.ApplicationCache.RuntimeCache; + // If this is a form, use Umbraco's default behavior var folder = PerplexFolder.Get(id); if (folder == null) { - return base.GetTreeNodes(id, queryStrings); - } + var treeNodeCollection = _cacheConfig.EnableCache ? (TreeNodeCollection)rtCache.GetCacheItem(cacheKey) : (TreeNodeCollection)null; - // This is a folder + if (treeNodeCollection == null) + { + treeNodeCollection = base.GetTreeNodes(id, queryStrings); - // We require all forms, and apply filtering based on folders later - var baseTreeNodes = base.GetTreeNodes("-1", queryStrings); + if (_cacheConfig.EnableCache) + { + if (rtCache.GetCacheItemsByKeySearch(cacheKey).Any()) + rtCache.ClearCacheByKeySearch(cacheKey); - // Sanity check; make sure there are no orphan forms around - // (forms not contained within any folder). If so, move them to the root folder - var orphans = baseTreeNodes.Where(n => PerplexFolder.Get(f => f.Forms.Any(formId => formId == n.Id.ToString())) == null).ToList(); - if(orphans.Count > 0) - { - foreach (var orphan in orphans) - { - PerplexFolder.GetRootFolder().Forms.Add(orphan.Id.ToString()); + rtCache.InsertCacheItem(cacheKey, () => treeNodeCollection, new TimeSpan(0, _cacheConfig.CacheDurationInMinutes, 0), true); + } } - PerplexFolder.SaveAll(); + return treeNodeCollection; } - // Hide all Forms that are not contained within this folder - // If this folder itself is disabled (due to the user not having access), - // we also hide all its forms - baseTreeNodes.RemoveAll(n => - !folder.Forms.Contains(n.Id.ToString()) || - (folder.Disabled && folder.Forms.Contains(n.Id.ToString())) - ); + // This is a folder + var baseTreeNodes = _cacheConfig.EnableCache ? (TreeNodeCollection)rtCache.GetCacheItem(cacheKey) : (TreeNodeCollection)null; - // Sort the forms of this folder in the order as defined by the folder - baseTreeNodes.Sort((x, y) => + if (baseTreeNodes == null) { - int idxX, idxY; + // We require all forms, and apply filtering based on folders later + baseTreeNodes = base.GetTreeNodes("-1", queryStrings); - idxX = folder.Forms.IndexOf(x.Id.ToString()); - idxY = folder.Forms.IndexOf(y.Id.ToString()); + // Sanity check; make sure there are no orphan forms around + // (forms not contained within any folder). If so, move them to the root folder + var orphans = baseTreeNodes.Where(n => PerplexFolder.Get(f => f.Forms.Any(formId => formId == n.Id.ToString())) == null).ToList(); + if (orphans.Count > 0) + { + foreach (var orphan in orphans) + { + PerplexFolder.GetRootFolder().Forms.Add(orphan.Id.ToString()); + } - return idxX.CompareTo(idxY); - }); + PerplexFolder.SaveAll(); + } - // Add any subfolders of this node - // We loop through the list in reverse as we add every folder at the start of the list (before forms) - foreach (var subFolder in folder.Folders.Reverse()) - { - // If this subfolder is disabled, and it is not on a path towards - // a folder that is NOT disabled, it should not be listed at all. - // When multiple start nodes are defined, it is possible for a disabled - // folder to be displayed in the tree, when one of its descendant folders is enabled. - if (subFolder.Disabled) + // Hide all Forms that are not contained within this folder + // If this folder itself is disabled (due to the user not having access), + // we also hide all its forms + baseTreeNodes.RemoveAll(n => + !folder.Forms.Contains(n.Id.ToString()) || + (folder.Disabled && folder.Forms.Contains(n.Id.ToString())) + ); + + // Sort the forms of this folder in the order as defined by the folder + baseTreeNodes.Sort((x, y) => + { + int idxX, idxY; + + idxX = folder.Forms.IndexOf(x.Id.ToString()); + idxY = folder.Forms.IndexOf(y.Id.ToString()); + + return idxX.CompareTo(idxY); + }); + + // Add any subfolders of this node + // We loop through the list in reverse as we add every folder at the start of the list (before forms) + foreach (var subFolder in folder.Folders.Reverse()) { - var startFolders = PerplexFolder.GetStartFoldersForCurrentUser(); + // If this subfolder is disabled, and it is not on a path towards + // a folder that is NOT disabled, it should not be listed at all. + // When multiple start nodes are defined, it is possible for a disabled + // folder to be displayed in the tree, when one of its descendant folders is enabled. + if (subFolder.Disabled) + { + var startFolders = PerplexFolder.GetStartFoldersForCurrentUser(); + + bool isOnPathTowardsStartFolder = startFolders.Any(sf => sf.Path.Any(fid => fid == subFolder.Id)); + if (!isOnPathTowardsStartFolder) + { + continue; + } + } - bool isOnPathTowardsStartFolder = startFolders.Any(sf => sf.Path.Any(fid => fid == subFolder.Id)); - if (!isOnPathTowardsStartFolder) + var treeNode = CreateTreeNode(subFolder.Id, id, queryStrings, subFolder.Name); + + // Clicking this folder will show the folder overview + // By default all nodes go to /forms/form/edit/, but this + // is only valid for forms. We direct to our custom folder view + treeNode.RoutePath = "forms/perplexForms/folder/" + treeNode.Id; + if (subFolder.Disabled) { - continue; + treeNode.CssClasses.Add("disabled"); } - } - var treeNode = CreateTreeNode(subFolder.Id, id, queryStrings, subFolder.Name); + // Folder has children if it has either forms or folders. + // If it is disabled, this is only true when it has subfolders + // since we do not show its forms. + treeNode.HasChildren = (subFolder.Disabled && subFolder.Folders.Any()) || (!subFolder.Disabled && (subFolder.Forms.Any() || subFolder.Folders.Any())); - // Clicking this folder will show the folder overview - // By default all nodes go to /forms/form/edit/, but this - // is only valid for forms. We direct to our custom folder view - treeNode.RoutePath = "forms/perplexForms/folder/" + treeNode.Id; - if (subFolder.Disabled) - { - treeNode.CssClasses.Add("disabled"); + // Folders are added at the top of the list, before forms + baseTreeNodes.Insert(0, treeNode); } - // Folder has children if it has either forms or folders. - // If it is disabled, this is only true when it has subfolders - // since we do not show its forms. - treeNode.HasChildren = (subFolder.Disabled && subFolder.Folders.Any()) || (!subFolder.Disabled && (subFolder.Forms.Any() || subFolder.Folders.Any())); + if (_cacheConfig.EnableCache) + { + if (rtCache.GetCacheItemsByKeySearch(cacheKey).Any()) + rtCache.ClearCacheByKeySearch(cacheKey); - // Folders are added at the top of the list, before forms - baseTreeNodes.Insert(0, treeNode); + rtCache.InsertCacheItem(cacheKey, () => baseTreeNodes, new TimeSpan(0, _cacheConfig.CacheDurationInMinutes, 0), true); + } } return baseTreeNodes; @@ -139,7 +168,7 @@ protected override TreeNode CreateRootNode(FormDataCollection queryStrings) // If none are set, all folders are allowed so we just use default behavior // Likewise if the common ancestors of all allowed folders is the root. - PerplexFolder commonAncestor = PerplexFolder.GetCommonAncestor(startFolders); + PerplexFolder commonAncestor = PerplexFolder.GetCommonAncestor(startFolders); if (!startFolders.Any() || commonAncestor == PerplexFolder.GetRootFolder()) { @@ -157,11 +186,11 @@ protected override TreeNode CreateRootNode(FormDataCollection queryStrings) // if this is the common ancestor of this user's start nodes but not // a start node itself. In that case it should also show as disabled in // the UI, and we hide its URL. - rootNode.RoutePath = "forms/perplexForms/folder/" + commonAncestor.Id; - if(commonAncestor.Disabled) + rootNode.RoutePath = "forms/perplexForms/folder/" + commonAncestor.Id; + if (commonAncestor.Disabled) { rootNode.CssClasses.Add("disabled"); - } + } // Folder has children if it has either forms or folders rootNode.HasChildren = commonAncestor.Forms.Any() || commonAncestor.Folders.Any(); @@ -197,10 +226,10 @@ protected override MenuItemCollection GetMenuForNode(string id, FormDataCollecti { menu.Items.RemoveAll(m => m.Alias != ActionRefresh.Instance.Alias); return menu; - } + } // Create Form (default Umbraco view, hence alias) - AddMenuItem(menu, "Create Form", alias: "create", icon: "icon icon-add"); + AddMenuItem(menu, "Create Form", alias: "create", icon: "icon icon-add"); // Create Folder AddMenuItem(menu, "Create Folder", view: "createFolder", icon: "icon icon-folder"); @@ -239,7 +268,7 @@ protected override MenuItemCollection GetMenuForNode(string id, FormDataCollecti var root = PerplexFolder.GetRootFolder(); // If the root folder is disabled, remove all menu actions except Reload - if(root.Disabled) + if (root.Disabled) { menu.Items.RemoveAll(m => m.Alias != ActionRefresh.Instance.Alias); return menu; diff --git a/Perplex.Umbraco.Forms/Perplex.Umbraco.Forms.csproj b/Perplex.Umbraco.Forms/Perplex.Umbraco.Forms.csproj index 23d0741..1409be6 100644 --- a/Perplex.Umbraco.Forms/Perplex.Umbraco.Forms.csproj +++ b/Perplex.Umbraco.Forms/Perplex.Umbraco.Forms.csproj @@ -230,6 +230,7 @@ +