Last active
August 23, 2016 10:41
Persists and restores umbraco form files to and from the media IFileSystem storage (Amazon S3 or Azure Blob Storage)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
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; | |
} | |
} | |
} | |
} |
Agree with nullydragon, pure gold
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Mate this is pure gold. Cheers.