Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added caching option #7

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -44,4 +44,8 @@
<PerplexRecaptchConfig>
<ErrorMessage />
</PerplexRecaptchConfig>
<PerplexCacheConfig>
<EnableCache>true</EnableCache>
<CacheDurationInMinutes>10</CacheDurationInMinutes>
</PerplexCacheConfig>
</PerplexUmbracoFormsConfig>
14 changes: 14 additions & 0 deletions Perplex.Umbraco.Forms/Code/Configuration/PerplexCacheConfig.cs
Original file line number Diff line number Diff line change
@@ -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; }
}
}
Original file line number Diff line number Diff line change
@@ -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
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -139,6 +139,12 @@ private static string GetFilePath()
PerplexRecaptchaConfig = new PerplexRecaptchaConfig
{
ErrorMessage = ""
},

PerplexCacheConfig = new PerplexCacheConfig
{
EnableCache = false,
CacheDurationInMinutes = 10
}
};
}
Expand Down
32 changes: 26 additions & 6 deletions Perplex.Umbraco.Forms/Code/UmbracoEvents.cs
Original file line number Diff line number Diff line change
@@ -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
{
Expand Down Expand Up @@ -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)
Expand All @@ -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);
}
}
}
}
}
185 changes: 107 additions & 78 deletions Perplex.Umbraco.Forms/Controllers/PerplexFormTreeController.cs
Original file line number Diff line number Diff line change
@@ -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
{
Expand All @@ -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<PerplexFolder>())
{
// 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/<GUID>, 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/<GUID>, 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;
Expand All @@ -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())
{
Expand All @@ -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();
Expand Down Expand Up @@ -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");
Expand Down Expand Up @@ -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;
Expand Down
1 change: 1 addition & 0 deletions Perplex.Umbraco.Forms/Perplex.Umbraco.Forms.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -230,6 +230,7 @@
</Reference>
</ItemGroup>
<ItemGroup>
<Compile Include="Code\Configuration\PerplexCacheConfig.cs" />
<Compile Include="Code\Configuration\ExtensionConfig.cs" />
<Compile Include="Code\Configuration\FieldTypeConfig.cs" />
<Compile Include="Code\Configuration\PerplexBaseFileConfig.cs" />
Expand Down