Created
October 23, 2013 10:28
-
-
Save feanz/7116203 to your computer and use it in GitHub Desktop.
Sitecore deployment manageer
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
public partial class DeploymentController : Controller | |
{ | |
private DeploymentManager _manager; | |
protected DeploymentManager Manager | |
{ | |
get { return _manager ?? (_manager = new DeploymentManager()); } | |
} | |
public virtual ActionResult Delete() | |
{ | |
Manager.Logger = new HtmlLogger(); | |
Manager.DeleteAll(); | |
ViewBag.LogInfo = Manager.Logger.ToString(); | |
CheckForErrors(); | |
return View(); | |
} | |
public string DeleteCatalogues() | |
{ | |
Manager.DeleteCatalogues(); | |
return string.Format("Delete completed at {0}.", DateTime.Now.ToString("s")); | |
} | |
/// <summary> | |
/// Deserialize what already is there (manual unzip/web deploy etc) | |
/// </summary> | |
/// <returns></returns> | |
public virtual ActionResult DeserializeAll(bool cleanUp = false) | |
{ | |
Manager.Logger = new HtmlLogger(); | |
Manager.DeserializeAll(); | |
if (cleanUp) | |
{ | |
Manager.DeleteAll(); | |
} | |
ViewBag.LogInfo = Manager.Logger.ToString(); | |
CheckForErrors(); | |
return View(); | |
} | |
/// <summary> | |
/// Deserialize what already is there (manual unzip/web deploy etc), Force full update ignoring local changes | |
/// </summary> | |
/// <returns></returns> | |
public virtual ActionResult DeserializeAllForceUpdate() | |
{ | |
Manager.Logger = new HtmlLogger(); | |
Manager.Options = new LoadOptions {DisableEvents = true, ForceUpdate = true}; | |
Manager.DeserializeAll(); | |
ViewBag.LogInfo = Manager.Logger.ToString(); | |
CheckForErrors(); | |
return View(); | |
} | |
/// <summary> | |
/// Present the form for uploading a file | |
/// </summary> | |
/// <returns></returns> | |
public virtual ActionResult DeserializeFile() | |
{ | |
ViewBag.SerializationFolder = Manager.SerializationRootFolder; | |
CheckForErrors(); | |
return View(); | |
} | |
/// <summary> | |
/// Handle the uploaded file | |
/// </summary> | |
/// <param name="theFile"></param> | |
/// <returns></returns> | |
[HttpPost] | |
public virtual ActionResult DeserializeFile(HttpPostedFileBase theFile) | |
{ | |
if (theFile == null || theFile.ContentLength == 0 || !theFile.ContentType.Contains("zip")) | |
{ | |
return RedirectToAction("DeserializeFile"); | |
} | |
var rootPath = Server.MapPath("~/temp/deploy/"); | |
var upload = Path.Combine(rootPath, "content.zip"); | |
try | |
{ | |
if (!Directory.Exists(rootPath)) | |
Directory.CreateDirectory(rootPath); | |
if (System.IO.File.Exists(upload)) | |
System.IO.File.Delete(upload); | |
} | |
catch (Exception e) | |
{ | |
Log.Error("Failed to remove previous uploaded file", e, this); | |
} | |
theFile.SaveAs(upload); | |
Manager.DeserializeFromZipFile(upload); | |
ViewBag.LogInfo = Manager.Logger.ToString(); | |
CheckForErrors(); | |
return View("DeserializeAll"); | |
} | |
/// <summary> | |
/// Deserialize what already is there (manual unzip/web deploy etc) | |
/// </summary> | |
/// <returns></returns> | |
public string Publish() | |
{ | |
Manager.Publish(); | |
return string.Format("Publish completed at {0}.", DateTime.Now.ToString("s")); | |
} | |
public virtual ActionResult SerializeAll() | |
{ | |
ViewBag.File = SerializeAndZip(); | |
ViewBag.LogInfo = Manager.Logger.ToString(); | |
return View("SerializeAll"); | |
} | |
/// <summary> | |
/// Serializ(s)es all content and returns a zip file of it all. | |
/// </summary> | |
/// <returns></returns> | |
public virtual FileResult SerializeAllFile() | |
{ | |
var file = SerializeAndZip(); | |
ViewBag.File = file; | |
var cd = new ContentDisposition | |
{ | |
FileName = Path.GetFileName(file), | |
Inline = false, | |
}; | |
Response.AppendHeader("content-disposition", cd.ToString()); | |
return File(System.IO.File.OpenRead(file), "application/zip"); | |
} | |
/// <summary> | |
/// Deserialise and pubish site | |
/// </summary> | |
/// <returns></returns> | |
public virtual ActionResult Update() | |
{ | |
Manager.Logger = new HtmlLogger(); | |
Manager.DeserializeAll(); | |
Manager.Publish(); | |
Manager.Logger.Info("Smart Publish site content"); | |
ViewBag.LogInfo = Manager.Logger.ToString(); | |
return View(); | |
} | |
private void CheckForErrors() | |
{ | |
if (Manager.Logger.As<HtmlLogger>().ContainsErrors()) | |
{ | |
Response.StatusCode = 500; | |
} | |
} | |
private string SerializeAndZip() | |
{ | |
Manager.Logger = new HtmlLogger(); | |
Manager.SerializeAll(); | |
return Manager.CreateZipFile(); | |
} | |
} |
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
using Sitecore.Data.Managers; | |
using Sitecore.Data.Serialization; | |
using Sitecore.Diagnostics; | |
using Sitecore.Exceptions; | |
using Sitecore.Publishing; | |
using Sitecore.SecurityModel; | |
using System; | |
using System.Collections.Generic; | |
using System.Diagnostics; | |
using System.IO; | |
using System.Linq; | |
using Think.UKB.Deployment.Logging; | |
namespace Think.UKB.Deployment | |
{ | |
public class DeploymentManager | |
{ | |
/// <summary> | |
/// Lock to prevent serialization and deserialization to be running at same time. | |
/// re-entrant locks will prevent a deadlock in the case of this called by the DeserializeFromZip and then DeserializeAll (etc) | |
/// </summary> | |
private static readonly object LockAll = new object(); | |
#region Properties | |
/// <summary> | |
/// Get the folder to read serialized content from. Defaults to SerializationFolder from Sitecore settings (web.config) | |
/// For deserializing it must be SerializationFolder from Sitecore settings (web.config) | |
/// </summary> | |
public string SerializationRootFolder { get; protected set; } | |
public ILogger Log { get; set; } | |
private static LoadOptions _options; | |
protected LoadOptions Options { get { return _options ?? (_options = new LoadOptions { DisableEvents = true }); } } | |
#endregion Properties | |
#region Constants | |
protected const string WebDb = "web"; | |
protected const string MasterDb = "master"; | |
protected const string CoreDb = "core"; | |
protected const string RootItem = "sitecore"; | |
protected const string ContentRoot = RootItem + "\\content"; | |
protected const string LayoutRoot = RootItem + "\\layout"; | |
protected const string RenderingsRoot = LayoutRoot + "\\renderings"; | |
protected const string SublayoutsRoot = LayoutRoot + "\\sublayouts"; | |
protected const string SystemRoot = RootItem + "\\system"; | |
protected const string MediaRoot = RootItem + "\\media library"; | |
protected const string TemplateRoot = RootItem + "\\templates"; | |
#endregion Constants | |
#region Constructor | |
public DeploymentManager() | |
{ | |
SerializationRootFolder = System.Web.HttpContext.Current.Server.MapPath(Sitecore.Configuration.Settings.SerializationFolder); | |
Log = new HtmlLogger(); | |
} | |
/// <summary> | |
/// Constructor | |
/// </summary> | |
/// <param name="log">change the default logger</param> | |
/// <param name="serializationRoot">use a different root for serialization - warning Sitecore will fail if deserializing if it's not the SerializationFolder</param> | |
public DeploymentManager(ILogger log, string serializationRoot) | |
{ | |
SerializationRootFolder = serializationRoot; | |
Log = log; | |
} | |
#endregion Constructor | |
#region Deserialize | |
/// <summary> | |
/// Given a zip file, will unzip and load the serialized content - will clear all existing serialized content from the disk. | |
/// </summary> | |
/// <param name="zipFilePath"></param> | |
public void DeserializeFromZipFile(string zipFilePath) | |
{ | |
lock (LockAll) | |
{ | |
var timer = GetTimer(); | |
var zipReader = new Sitecore.Zip.ZipReader(zipFilePath); | |
//The serialization has to start from the web.config settings folder, otherwsie | |
//the deserialize methods will throw exceptions about the folder location. | |
var unzipPath = SerializationRootFolder; | |
try | |
{ | |
if (Directory.Exists(unzipPath)) | |
Directory.Delete(unzipPath, true); | |
} | |
catch (Exception e) | |
{ | |
Log.Error("Could not remove previous unzipped content. " + e.Message); | |
Log.Info("Derializing halted"); | |
return; | |
} | |
foreach (var entry in zipReader.Entries) | |
{ | |
if (entry.IsDirectory) | |
{ | |
Directory.CreateDirectory(Path.Combine(unzipPath, entry.Name)); | |
continue; | |
} | |
var path = Path.GetDirectoryName(Path.Combine(unzipPath, entry.Name)); | |
if (string.IsNullOrEmpty(path)) | |
throw new Exception("Error getting path location during unzip."); | |
try | |
{ | |
if (!Directory.Exists(path)) | |
{ | |
//Log.Info("Creating folder " + path); | |
Directory.CreateDirectory(path); | |
} | |
} | |
catch (Exception e) | |
{ | |
Log.Error("Couldn't create path " + path + e.Message); | |
} | |
try | |
{ | |
using (var stream = entry.GetStream()) | |
using (var output = File.OpenWrite(Path.Combine(unzipPath, entry.Name))) | |
{ | |
byte[] buffer = new byte[0x1000]; | |
int read; | |
while ((read = stream.Read(buffer, 0, buffer.Length)) > 0) | |
output.Write(buffer, 0, read); | |
} | |
} | |
catch (Exception ex) | |
{ | |
Log.Error("Error while unzipping content" + ex.Message); | |
return; | |
} | |
} | |
timer.Stop(); | |
Log.Info(string.Format("Unzip took {0} seconds", timer.Elapsed.TotalSeconds)); | |
DeserializeAll(); | |
} | |
} | |
/// <summary> | |
/// Update everything from the file system. | |
/// </summary> | |
public void DeserializeAll() | |
{ | |
lock (LockAll) | |
{ | |
Log.Info("Deserialize All"); | |
var timer = GetTimer(); | |
try | |
{ | |
//disable the automatic serialization (in all threads) while this is running | |
ItemHandler.Disabled = true; | |
DeserializeMedia(); | |
DeserialiseTemplates(); | |
DeserializeLayouts(); | |
DeserializeCoreDb(); | |
DeserializeContent(); | |
} | |
finally | |
{ | |
ItemHandler.Disabled = false; | |
} | |
timer.Stop(); | |
Log.Info(string.Format("Deserialize all took : {0} seconds", timer.Elapsed.TotalSeconds)); | |
} | |
} | |
/// <summary> | |
/// Updates everything in core/sitecore | |
/// </summary> | |
public void DeserializeCoreDb() | |
{ | |
lock (LockAll) | |
{ | |
var contentPath = Path.Combine(new[] { SerializationRootFolder, CoreDb, RootItem }); | |
DeserialiseTree(contentPath); | |
} | |
} | |
/// <summary> | |
/// Updates master/sitecore/system recursively | |
/// </summary> | |
public void DeserializeSystemSettings() | |
{ | |
lock (LockAll) | |
{ | |
var contentPath = Path.Combine(new[] { SerializationRootFolder, MasterDb, SystemRoot }); | |
DeserialiseTree(contentPath); | |
} | |
} | |
/// <summary> | |
/// Updates master sitecore/layout recursively | |
/// </summary> | |
public void DeserializeLayouts() | |
{ | |
lock (LockAll) | |
{ | |
var contentPath = Path.Combine(new[] { SerializationRootFolder, MasterDb, LayoutRoot }); | |
DeserialiseTree(contentPath); | |
} | |
} | |
/// <summary> | |
/// Updates master/sitecore/content recursively | |
/// </summary> | |
public void DeserializeContent() | |
{ | |
lock (LockAll) | |
{ | |
var contentPath = Path.Combine(new[] { SerializationRootFolder, MasterDb, ContentRoot }); | |
DeserialiseTree(contentPath); | |
} | |
} | |
/// <summary> | |
/// Updates master/sitecore/layout/sublayouts | |
/// </summary> | |
public void DeserialiseSubLayouts() | |
{ | |
lock (LockAll) | |
{ | |
var sublayoutsPath = Path.Combine(new[] { SerializationRootFolder, MasterDb, SublayoutsRoot }); | |
DeserialiseTree(sublayoutsPath); | |
} | |
} | |
/// <summary> | |
/// Updates master/sitecore/layout/renderings recursively | |
/// </summary> | |
public void DeserialiseRenderings() | |
{ | |
lock (LockAll) | |
{ | |
var renderingPath = Path.Combine(new[] { SerializationRootFolder, MasterDb, RenderingsRoot }); | |
DeserialiseTree(renderingPath); | |
} | |
} | |
/// <summary> | |
/// Updates master/sitecore/media library recursively | |
/// </summary> | |
public void DeserializeMedia() | |
{ | |
lock (LockAll) | |
{ | |
var mediaItemsPath = Path.Combine(new[] { SerializationRootFolder, MasterDb, MediaRoot }); | |
DeserialiseTree(mediaItemsPath); | |
} | |
} | |
/// <summary> | |
/// Updates master/sitecore/Templates recursively | |
/// </summary> | |
public void DeserialiseTemplates() | |
{ | |
lock (LockAll) | |
{ | |
var templatesPath = Path.Combine(new[] { SerializationRootFolder, MasterDb, TemplateRoot }); | |
DeserialiseTree(templatesPath); | |
} | |
} | |
/// <summary> | |
/// Calls the sitecore load to deserialize and and create/update the tree with the items found | |
/// </summary> | |
/// <param name="contentPath"></param> | |
private void DeserialiseTree(string contentPath) | |
{ | |
Log.Debug("Getting all from " + contentPath); | |
try | |
{ | |
using (new SecurityDisabler()) | |
{ | |
Manager.LoadTree(contentPath, Options); | |
} | |
} | |
catch (Exception e) | |
{ | |
Log.Error(e); | |
} | |
} | |
#endregion Deserialize | |
#region Serialize | |
/// <summary> | |
/// Serialize All the things | |
/// </summary> | |
/// <returns></returns> | |
public void SerializeAll() | |
{ | |
lock (LockAll) | |
{ | |
var timer = GetTimer(); | |
Log.Info("Serialize All"); | |
Manager.CleanupPath(SerializationRootFolder, true); | |
SerializeContent(); | |
SerializeLayouts(); | |
SerializeMedia(); | |
SerializeTemplates(); | |
//SerializeCore(); | |
timer.Stop(); | |
Log.Info(string.Format("Serialize All took {0} seconds", timer.Elapsed.TotalSeconds)); | |
} | |
} | |
public void SerializeCore() | |
{ | |
lock (LockAll) | |
{ | |
Log.Info("Serialize Core"); | |
DumpTree(ContentRoot, CoreDb); | |
} | |
} | |
/// <summary> | |
/// Serialize the tree from /sitecore/content | |
/// </summary> | |
public void SerializeContent() | |
{ | |
lock (LockAll) | |
{ | |
Log.Info("Serialize Content"); | |
DumpTree(ContentRoot); | |
} | |
} | |
/// <summary> | |
/// Serailize layouts. | |
/// </summary> | |
public void SerializeLayouts() | |
{ | |
lock (LockAll) | |
{ | |
Log.Info("Serialize Layouts"); | |
DumpTree(LayoutRoot); | |
} | |
} | |
/// <summary> | |
/// Serialize Media items | |
/// </summary> | |
public void SerializeMedia() | |
{ | |
lock (LockAll) | |
{ | |
Log.Info("Serialize Media"); | |
DumpTree(MediaRoot); | |
} | |
} | |
/// <summary> | |
/// Serializes Templates section | |
/// </summary> | |
public void SerializeTemplates() | |
{ | |
lock (LockAll) | |
{ | |
Log.Info("Serialize Templates"); | |
DumpTree(TemplateRoot); | |
} | |
} | |
/// <summary> | |
/// Serialize the tree from the given (sitecore) path. Assumes master DB if name not supplied. | |
/// </summary> | |
/// <param name="path"></param> | |
/// <param name="database"></param> | |
protected void DumpTree(string path, string database = MasterDb) | |
{ | |
if (string.IsNullOrEmpty(database)) | |
throw new InvalidValueException("Invalid database name"); | |
var db = Sitecore.Data.Database.GetDatabase(database); | |
if (db == null) | |
throw new InvalidValueException(string.Format("{0} in not a vaild database", database)); | |
var itemPath = ConvertToSitecorePath(path); | |
//Need security disabled to allow access to core/other sections that may be restricted for anon access. | |
using (new SecurityDisabler()) | |
{ | |
var item = db.GetItem(itemPath); | |
if (item == null) | |
{ | |
Log.Warn(string.Format("Path '{0}' didn't return an item for db '{1}'", itemPath, database)); | |
return; | |
} | |
Manager.DumpTree(item); | |
} | |
} | |
/// <summary> | |
/// Convert folder path to sitecore path (swap '\' with '/') | |
/// </summary> | |
/// <param name="path"></param> | |
/// <returns></returns> | |
private static string ConvertToSitecorePath(string path) | |
{ | |
var itemPath = "/" + path.Replace("\\", "/"); | |
return itemPath; | |
} | |
private static Stopwatch GetTimer() | |
{ | |
var timer = new Stopwatch(); | |
timer.Start(); | |
return timer; | |
} | |
/// <summary> | |
/// Creates a new zip file of all currently serialized content. | |
/// </summary> | |
/// <returns>Full path to the zip file</returns> | |
public string CreateZipFile() | |
{ | |
lock (LockAll) | |
{ | |
var timer = GetTimer(); | |
var zipFile = Path.Combine(SerializationRootFolder, "content.zip"); | |
try | |
{ | |
Log.Info("Remove previous zip file"); | |
if (File.Exists(zipFile)) | |
{ | |
File.Delete(zipFile); | |
} | |
} | |
catch | |
{ | |
Log.Warn("Unable to delete previous zip file."); | |
} | |
using (var fileWriter = new Sitecore.Zip.ZipWriter(zipFile)) | |
{ | |
var files = GetFiles(SerializationRootFolder, "*.item|*.user|*.role", | |
SearchOption.AllDirectories); | |
Log.Info(string.Format("Adding files ({0})", files.Count())); | |
var length = SerializationRootFolder.Length; | |
if (!SerializationRootFolder.EndsWith("\\")) | |
length += 1; | |
foreach (var file in files) | |
{ | |
fileWriter.AddEntry(file.Remove(0, length), file); | |
} | |
} | |
timer.Stop(); | |
Log.Info(string.Format("Zip took {0} seconds", timer.Elapsed.TotalSeconds)); | |
return zipFile; | |
} | |
} | |
/// <summary> | |
/// Get all files based on search string supplied. | |
/// </summary> | |
/// <param name="path"></param> | |
/// <param name="searchPattern"></param> | |
/// <param name="searchOption"></param> | |
/// <returns></returns> | |
protected static string[] GetFiles(string path, string searchPattern, SearchOption searchOption) | |
{ | |
string[] searchPatterns = searchPattern.Split('|'); | |
List<string> files = new List<string>(); | |
foreach (string sp in searchPatterns) | |
files.AddRange(Directory.GetFiles(path, sp, searchOption)); | |
files.Sort(); | |
return files.ToArray(); | |
} | |
#endregion Serialize | |
#region Publish | |
/// <summary> | |
/// Perform incremental publish of all master -> web. | |
/// </summary> | |
/// <param name="targetDb">db name - eg "web", "web_cd" - default is "Web"</param> | |
public void PublishIncremental(string targetDb = WebDb) | |
{ | |
if (string.IsNullOrEmpty(targetDb)) throw new ArgumentNullException("targetDb"); | |
lock (LockAll) | |
{ | |
Sitecore.Context.SetActiveSite("shell"); | |
using (new SecurityDisabler()) | |
{ | |
Sitecore.Data.Database master = Sitecore.Configuration.Factory.GetDatabase(MasterDb); | |
Sitecore.Data.Database target = Sitecore.Configuration.Factory.GetDatabase(targetDb); | |
Assert.IsNotNull(master, "Could not find master database"); | |
Assert.IsNotNull(target, string.Format("Could not find '{0}' database", targetDb)); | |
PublishManager.PublishIncremental(master, new[] { target }, | |
LanguageManager.GetLanguages(master).ToArray()); | |
} | |
} | |
} | |
#endregion | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment