Skip to content

Instantly share code, notes, and snippets.

@CameronWills
Last active August 23, 2016 10:41
Show Gist options
  • Save CameronWills/53be0f00b873bf25bc8bbe9a8097d333 to your computer and use it in GitHub Desktop.
Save CameronWills/53be0f00b873bf25bc8bbe9a8097d333 to your computer and use it in GitHub Desktop.
Persists and restores umbraco form files to and from the media IFileSystem storage (Amazon S3 or Azure Blob Storage)
/*
IMPORTANT:
To enable the persist operation, you need to add an AppSetting in your web.config file:
<add key="persistFormsOnChange" value="true" />
This should only be set to true on the Admin/BackOffice server.
*/
using System;
using System.Configuration;
using System.IO;
using System.IO.Compression;
using System.Threading;
using Umbraco.Core;
using Umbraco.Core.IO;
using Umbraco.Forms.Data.Storage;
using System.Web.Hosting;
namespace UmbracoHelpers
{
/// <summary>
/// Persists and restores form files to and from the media IFileSystem storage (Amazon S3 or Azure Blob Storage)
/// </summary>
public class FormsPersistence : ApplicationEventHandler
{
private const string packageFile = "forms-data.zip";
private static string formsPath = HostingEnvironment.MapPath("~/App_Plugins/UmbracoForms/Data");
private static string tempPath = HostingEnvironment.MapPath("~/App_Data/TEMP");
private static object persistLock = new object();
private static object restoreLock = new object();
protected override void ApplicationStarted(UmbracoApplicationBase umbracoApplication, ApplicationContext applicationContext)
{
bool enablePersist;
bool.TryParse(ConfigurationManager.AppSettings["persistFormsOnChange"], out enablePersist);
if (enablePersist)
{
FormStorage.Saved += OnFormsChange;
FormStorage.Created += OnFormsChange;
FormStorage.Deleted += OnFormsChange;
DataSourceStorage.Saved += OnFormsChange;
DataSourceStorage.Created += OnFormsChange;
DataSourceStorage.Deleted += OnFormsChange;
PrevalueSourceStorage.Saved += OnFormsChange;
PrevalueSourceStorage.Created += OnFormsChange;
PrevalueSourceStorage.Deleted += OnFormsChange;
WorkflowStorage.Saved += OnFormsChange;
WorkflowStorage.Created += OnFormsChange;
WorkflowStorage.Deleted += OnFormsChange;
}
RestoreFormFiles();
}
private void OnFormsChange(object sender, EventArgs args)
{
PersistFormsFiles();
}
/// <summary>
/// Persist form files to IFileSystem storage (Amazon S3)
/// </summary>
public static void PersistFormsFiles()
{
using (var tryLock = new TryLock(persistLock))
{
if (tryLock.HasLock)
{
try
{
var destination = Path.Combine(tempPath, packageFile);
if (File.Exists(destination))
File.Delete(destination);
ZipFile.CreateFromDirectory(formsPath, destination);
var fileSystem = FileSystemProviderManager.Current.GetFileSystemProvider<MediaFileSystem>();
fileSystem.AddFile(packageFile, File.Open(destination, FileMode.Open, FileAccess.Read), true);
}
catch(Exception)
{ }
}
}
}
/// <summary>
/// Restore form files from persistent IFileSystem storage (Amazon S3)
/// </summary>
public static void RestoreFormFiles()
{
using (var tryLock = new TryLock(restoreLock))
{
if (tryLock.HasLock)
{
lock(persistLock)
{
var destination = Path.Combine(tempPath, packageFile);
var fileSystem = FileSystemProviderManager.Current.GetFileSystemProvider<MediaFileSystem>();
if(fileSystem.FileExists(packageFile))
{
using (var fileStream = File.Create(destination))
{
fileSystem.OpenFile(packageFile).CopyTo(fileStream);
}
ExtractToDirectory(destination, formsPath);
}
}
}
}
}
/// <summary>
/// Extract zip archive to destination folder, defaults to skipping existing files, doesn't throw an error.
/// </summary>
private static void ExtractToDirectory(string sourceArchiveFileName, string destinationDirectory, bool overwrite = false)
{
using (var archive = ZipFile.OpenRead(sourceArchiveFileName))
{
foreach (var entry in archive.Entries)
{
var targetPath = Path.Combine(destinationDirectory, entry.FullName);
if(!(File.Exists(targetPath) || Directory.Exists(targetPath)) || overwrite)
{
var directory = Path.GetDirectoryName(targetPath);
if (!(Directory.Exists(directory)))
Directory.CreateDirectory(directory);
var fileName = Path.GetFileName(targetPath);
if (!string.IsNullOrEmpty(fileName))
entry.ExtractToFile(targetPath, true);
}
}
}
}
}
/// <summary>
/// Try and obtain a lock on an object
/// </summary>
public class TryLock : IDisposable
{
private object locked;
public bool HasLock { get; private set; }
public TryLock(object obj)
{
if (Monitor.TryEnter(obj))
{
HasLock = true;
locked = obj;
}
}
public void Dispose()
{
if (HasLock)
{
Monitor.Exit(locked);
locked = null;
HasLock = false;
}
}
}
}
@nullydragon
Copy link

Mate this is pure gold. Cheers.

@PaulJohnMaddison
Copy link

Agree with nullydragon, pure gold

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment