WORK IN PROGRESS, GET THE STABLE SOURCE FROM THE DOWNLOADS TAB
Merging into main 4.1.0 branch: Resolves a number of open issues, see associated WorkItems - Fix WP crash on invalid connection string due to generation of XML cache on a separate thread - Added option to generate xml cache file on local filesystem (in ASP.NET Temporary Files / CodeGenDir location) by adding <add key="umbracoContentXMLUseLocalTemp" value="true" /> to web.config file. This allows you to run from a SAN on a load-balanced environment whilst allowing each web node to have isolated copies of the xml cache file to avoid WP crashes due to multiple file locks - Others in WorkItems :) [TFS Changeset #64002]
This commit is contained in:
@@ -181,26 +181,40 @@ namespace umbraco.BasePages {
|
||||
/// </summary>
|
||||
/// <value>The umbraco user context ID.</value>
|
||||
public static string umbracoUserContextID {
|
||||
get {
|
||||
if (System.Web.HttpContext.Current.Request.Cookies.Get("UserContext") != null)
|
||||
return System.Web.HttpContext.Current.Request.Cookies.Get("UserContext").Value;
|
||||
else
|
||||
return "";
|
||||
get
|
||||
{
|
||||
if (HttpContext.Current != null)
|
||||
if (HttpContext.Current.Request != null)
|
||||
if (HttpContext.Current.Request.Cookies != null)
|
||||
{
|
||||
HttpCookie userContext = HttpContext.Current.Request.Cookies.Get("UserContext");
|
||||
if (userContext != null)
|
||||
return userContext.Value;
|
||||
}
|
||||
return "";
|
||||
}
|
||||
set {
|
||||
// Clearing all old cookies before setting a new one.
|
||||
try {
|
||||
if (System.Web.HttpContext.Current.Request.Cookies["UserContext"] != null) {
|
||||
System.Web.HttpContext.Current.Response.Cookies.Clear();
|
||||
if (HttpContext.Current != null)
|
||||
{
|
||||
// Clearing all old cookies before setting a new one.
|
||||
try
|
||||
{
|
||||
if (HttpContext.Current.Request != null)
|
||||
if (HttpContext.Current.Request.Cookies["UserContext"] != null)
|
||||
{
|
||||
HttpContext.Current.Response.Cookies.Clear();
|
||||
}
|
||||
}
|
||||
} catch {
|
||||
catch
|
||||
{
|
||||
}
|
||||
// Create new cookie.
|
||||
var c = new HttpCookie("UserContext");
|
||||
c.Name = "UserContext";
|
||||
c.Value = value;
|
||||
c.Expires = DateTime.Now.AddDays(1);
|
||||
HttpContext.Current.Response.Cookies.Add(c);
|
||||
}
|
||||
// Create new cookie.
|
||||
System.Web.HttpCookie c = new System.Web.HttpCookie("UserContext");
|
||||
c.Name = "UserContext";
|
||||
c.Value = value;
|
||||
c.Expires = DateTime.Now.AddDays(1);
|
||||
System.Web.HttpContext.Current.Response.Cookies.Add(c);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -46,6 +46,10 @@ namespace umbraco.IO
|
||||
|
||||
public static string MapPath(string path, bool useHttpContext)
|
||||
{
|
||||
// Check if the path is already mapped
|
||||
if (path.Length >= 2 && path[1] == Path.VolumeSeparatorChar)
|
||||
return path;
|
||||
|
||||
if (useHttpContext)
|
||||
{
|
||||
//string retval;
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Configuration;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Web;
|
||||
|
||||
namespace umbraco.IO
|
||||
{
|
||||
@@ -76,8 +79,27 @@ namespace umbraco.IO
|
||||
{
|
||||
get
|
||||
{
|
||||
if (ContentCacheXmlIsEphemeral)
|
||||
{
|
||||
return Path.Combine(HttpRuntime.CodegenDir, @"UmbracoData\umbraco.config");
|
||||
}
|
||||
return IOHelper.returnPath("umbracoContentXML", "~/data/umbraco.config");
|
||||
}
|
||||
}
|
||||
|
||||
public static bool ContentCacheXmlIsEphemeral
|
||||
{
|
||||
get
|
||||
{
|
||||
bool returnValue = false;
|
||||
string configSetting = ConfigurationManager.AppSettings["umbracoContentXMLUseLocalTemp"];
|
||||
|
||||
if (!string.IsNullOrEmpty(configSetting))
|
||||
if(bool.TryParse(configSetting, out returnValue))
|
||||
return returnValue;
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -320,7 +320,10 @@ namespace umbraco.cms.businesslogic.web
|
||||
public static string GenerateDtd()
|
||||
{
|
||||
StringBuilder dtd = new StringBuilder();
|
||||
dtd.AppendLine("<!DOCTYPE umbraco [ ");
|
||||
// Renamed 'umbraco' to 'root' since the top level of the DOCTYPE should specify the name of the root node for it to be valid;
|
||||
// there's no mention of 'umbraco' anywhere in the schema that this DOCTYPE governs
|
||||
// (Alex N 20100212)
|
||||
dtd.AppendLine("<!DOCTYPE root [ ");
|
||||
|
||||
if (UmbracoSettings.UseLegacyXmlSchema)
|
||||
{
|
||||
@@ -328,13 +331,33 @@ namespace umbraco.cms.businesslogic.web
|
||||
}
|
||||
else
|
||||
{
|
||||
List<DocumentType> dts = GetAllAsList();
|
||||
foreach (DocumentType dt in dts)
|
||||
// TEMPORARY: Added Try-Catch to this call since trying to generate a DTD against a corrupt db
|
||||
// or a broken connection string is not handled yet
|
||||
// (Alex N 20100212)
|
||||
try
|
||||
{
|
||||
string safeAlias = helpers.Casing.SafeAlias(dt.Alias);
|
||||
dtd.AppendLine(String.Format("<!ELEMENT {0} ANY>", safeAlias));
|
||||
dtd.AppendLine(String.Format("<!ATTLIST {0} id ID #REQUIRED>", safeAlias));
|
||||
StringBuilder strictSchemaBuilder = new StringBuilder();
|
||||
|
||||
List<DocumentType> dts = GetAllAsList();
|
||||
foreach (DocumentType dt in dts)
|
||||
{
|
||||
string safeAlias = helpers.Casing.SafeAlias(dt.Alias);
|
||||
if (safeAlias != null)
|
||||
{
|
||||
strictSchemaBuilder.AppendLine(String.Format("<!ELEMENT {0} ANY>", safeAlias));
|
||||
strictSchemaBuilder.AppendLine(String.Format("<!ATTLIST {0} id ID #REQUIRED>", safeAlias));
|
||||
}
|
||||
}
|
||||
|
||||
// Only commit the strong schema to the container if we didn't generate an error building it
|
||||
dtd.Append(strictSchemaBuilder);
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
// Note, Log.Add quietly swallows the exception if it can't write to the database
|
||||
Log.Add(LogTypes.System, -1, string.Format("{0} while trying to build DTD for Xml schema; is Umbraco installed correctly and the connection string configured?", exception.Message));
|
||||
}
|
||||
|
||||
}
|
||||
dtd.AppendLine("]>");
|
||||
|
||||
|
||||
@@ -29,7 +29,25 @@ namespace umbraco
|
||||
{
|
||||
#region Declarations
|
||||
|
||||
private readonly string UmbracoXmlDiskCacheFileName = IOHelper.MapPath(SystemFiles.ContentCacheXml, false);
|
||||
private string _umbracoXmlDiskCacheFileName = IOHelper.MapPath(SystemFiles.ContentCacheXml, false);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the path of the umbraco XML disk cache file.
|
||||
/// </summary>
|
||||
/// <value>The name of the umbraco XML disk cache file.</value>
|
||||
public string UmbracoXmlDiskCacheFileName
|
||||
{
|
||||
get
|
||||
{
|
||||
return _umbracoXmlDiskCacheFileName;
|
||||
}
|
||||
set
|
||||
{
|
||||
_umbracoXmlDiskCacheFileName = value;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
HttpRuntime.AppDomainAppPath + '\\' +
|
||||
SystemFiles.ContentCacheXml.Replace('/', '\\').TrimStart('\\');
|
||||
@@ -60,13 +78,16 @@ namespace umbraco
|
||||
|
||||
static content()
|
||||
{
|
||||
Trace.Write("Initializing content");
|
||||
ThreadPool.QueueUserWorkItem(
|
||||
delegate
|
||||
{
|
||||
XmlDocument xmlDoc = Instance.XmlContentInternal;
|
||||
Trace.WriteLine("Content initialized");
|
||||
});
|
||||
//Trace.Write("Initializing content");
|
||||
//ThreadPool.QueueUserWorkItem(
|
||||
// delegate
|
||||
// {
|
||||
// XmlDocument xmlDoc = Instance.XmlContentInternal;
|
||||
// Trace.WriteLine("Content initialized");
|
||||
// });
|
||||
|
||||
Trace.WriteLine("Checking for xml content initialisation...");
|
||||
Instance.CheckXmlContentPopulation();
|
||||
}
|
||||
|
||||
#endregion
|
||||
@@ -124,18 +145,7 @@ namespace umbraco
|
||||
{
|
||||
get
|
||||
{
|
||||
if (isInitializing)
|
||||
{
|
||||
lock (_xmlContentInternalSyncLock)
|
||||
{
|
||||
if (isInitializing)
|
||||
{
|
||||
_xmlContent = LoadContent();
|
||||
if (!UmbracoSettings.isXmlContentCacheDisabled && !IsValidDiskCachePresent())
|
||||
SaveContentToDiskAsync(_xmlContent);
|
||||
}
|
||||
}
|
||||
}
|
||||
CheckXmlContentPopulation();
|
||||
|
||||
return _xmlContent;
|
||||
}
|
||||
@@ -157,6 +167,36 @@ namespace umbraco
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Triggers the XML content population if necessary.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
private bool CheckXmlContentPopulation()
|
||||
{
|
||||
if (isInitializing)
|
||||
{
|
||||
lock (_xmlContentInternalSyncLock)
|
||||
{
|
||||
if (isInitializing)
|
||||
{
|
||||
Trace.WriteLine(string.Format("Initializing content on thread '{0}' (Threadpool? {1})", Thread.CurrentThread.Name, Thread.CurrentThread.IsThreadPoolThread.ToString()));
|
||||
_xmlContent = LoadContent();
|
||||
Trace.WriteLine("Content initialized (loaded)");
|
||||
|
||||
// Only save new XML cache to disk if we just repopulated it
|
||||
// TODO: Re-architect this so that a call to this method doesn't invoke a new thread for saving disk cache
|
||||
if (!UmbracoSettings.isXmlContentCacheDisabled && !IsValidDiskCachePresent())
|
||||
{
|
||||
SaveContentToDiskAsync(_xmlContent);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
Trace.WriteLine("Content initialized (was already in context)");
|
||||
return false;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
protected static ISqlHelper SqlHelper
|
||||
@@ -164,6 +204,8 @@ namespace umbraco
|
||||
get { return Application.SqlHelper; }
|
||||
}
|
||||
|
||||
|
||||
|
||||
#region Public Methods
|
||||
|
||||
/// <summary>
|
||||
@@ -760,15 +802,30 @@ namespace umbraco
|
||||
private XmlDocument LoadContentFromDatabase()
|
||||
{
|
||||
XmlDocument xmlDoc = new XmlDocument();
|
||||
InitContentDocumentBase(xmlDoc);
|
||||
|
||||
// Moved User to a local variable - why are we causing user 0 to load from the DB though?
|
||||
// Alex N 20100212
|
||||
User staticUser = null;
|
||||
try
|
||||
{
|
||||
staticUser = User.GetCurrent(); //User.GetUser(0);
|
||||
}
|
||||
catch
|
||||
{
|
||||
/* We don't care later if the staticUser is null */
|
||||
}
|
||||
|
||||
Log.Add(LogTypes.System, User.GetUser(0), -1, "Loading content from database...");
|
||||
// Try to log to the DB
|
||||
Log.Add(LogTypes.System, staticUser, -1, "Loading content from database...");
|
||||
|
||||
// Moved this to after the logging since the 2010 schema accesses the DB just to generate the DTD
|
||||
InitContentDocumentBase(xmlDoc);
|
||||
|
||||
Hashtable nodes = new Hashtable();
|
||||
Hashtable parents = new Hashtable();
|
||||
try
|
||||
{
|
||||
Log.Add(LogTypes.Debug, User.GetUser(0), -1, "Republishing starting");
|
||||
Log.Add(LogTypes.Debug, staticUser, -1, "Republishing starting");
|
||||
|
||||
// Esben Carlsen: At some point we really need to put all data access into to a tier of its own.
|
||||
string sql =
|
||||
@@ -800,8 +857,9 @@ order by umbracoNode.level, umbracoNode.sortOrder";
|
||||
}
|
||||
}
|
||||
|
||||
Log.Add(LogTypes.Debug, User.GetUser(0), -1, "Xml Pages loaded");
|
||||
Log.Add(LogTypes.Debug, staticUser, -1, "Xml Pages loaded");
|
||||
|
||||
// TODO: Why is the following line here, it should have already been generated? Alex N 20100212
|
||||
// Reset
|
||||
InitContentDocumentBase(xmlDoc);
|
||||
|
||||
@@ -811,23 +869,23 @@ order by umbracoNode.level, umbracoNode.sortOrder";
|
||||
}
|
||||
catch (Exception ee)
|
||||
{
|
||||
Log.Add(LogTypes.Error, User.GetUser(0), -1,
|
||||
Log.Add(LogTypes.Error, staticUser, -1,
|
||||
string.Format("Error while generating XmlDocument from database: {0}", ee));
|
||||
}
|
||||
}
|
||||
catch (OutOfMemoryException)
|
||||
{
|
||||
Log.Add(LogTypes.Error, User.GetUser(0), -1,
|
||||
Log.Add(LogTypes.Error, staticUser, -1,
|
||||
string.Format("Error Republishing: Out Of Memory. Parents: {0}, Nodes: {1}",
|
||||
parents.Count, nodes.Count));
|
||||
}
|
||||
catch (Exception ee)
|
||||
{
|
||||
Log.Add(LogTypes.Error, User.GetUser(0), -1, string.Format("Error Republishing: {0}", ee));
|
||||
Log.Add(LogTypes.Error, staticUser, -1, string.Format("Error Republishing: {0}", ee));
|
||||
}
|
||||
finally
|
||||
{
|
||||
Log.Add(LogTypes.Debug, User.GetUser(0), -1, "Done republishing Xml Index");
|
||||
Log.Add(LogTypes.Debug, staticUser, -1, "Done republishing Xml Index");
|
||||
}
|
||||
|
||||
return xmlDoc;
|
||||
@@ -874,20 +932,42 @@ order by umbracoNode.level, umbracoNode.sortOrder";
|
||||
{
|
||||
lock (_readerWriterSyncLock)
|
||||
{
|
||||
Trace.Write(string.Format("Saving content to disk on thread '{0}' (Threadpool? {1})", Thread.CurrentThread.Name, Thread.CurrentThread.IsThreadPoolThread.ToString()));
|
||||
|
||||
// Moved the user into a variable and avoided it throwing an error if one can't be loaded (e.g. empty / corrupt db on initial install)
|
||||
User staticUser = null;
|
||||
try
|
||||
{
|
||||
staticUser = User.GetCurrent();
|
||||
}
|
||||
catch
|
||||
{}
|
||||
|
||||
try
|
||||
{
|
||||
Stopwatch stopWatch = Stopwatch.StartNew();
|
||||
|
||||
ClearDiskCache();
|
||||
|
||||
// Try to create directory for cache path if it doesn't yet exist
|
||||
if (!File.Exists(UmbracoXmlDiskCacheFileName) && !Directory.Exists(Path.GetDirectoryName(UmbracoXmlDiskCacheFileName)))
|
||||
{
|
||||
// We're already in a try-catch and saving will fail if this does, so don't need another
|
||||
Directory.CreateDirectory(UmbracoXmlDiskCacheFileName);
|
||||
}
|
||||
|
||||
xmlDoc.Save(UmbracoXmlDiskCacheFileName);
|
||||
|
||||
Log.Add(LogTypes.Debug, User.GetUser(0), -1, string.Format("Xml saved in {0}", stopWatch.Elapsed));
|
||||
Trace.Write(string.Format("Saved content on thread '{0}' in {1} (Threadpool? {2})", Thread.CurrentThread.Name, stopWatch.Elapsed, Thread.CurrentThread.IsThreadPoolThread.ToString()));
|
||||
Log.Add(LogTypes.Debug, staticUser, -1, string.Format("Xml saved in {0}", stopWatch.Elapsed));
|
||||
}
|
||||
catch (Exception ee)
|
||||
{
|
||||
// If for whatever reason something goes wrong here, invalidate disk cache
|
||||
ClearDiskCache();
|
||||
Log.Add(LogTypes.Error, User.GetUser(0), -1, string.Format("Xml wasn't saved: {0}", ee));
|
||||
|
||||
Trace.Write(string.Format("Error saving content on thread '{0}' due to '{1}' (Threadpool? {2})", Thread.CurrentThread.Name, ee.Message, Thread.CurrentThread.IsThreadPoolThread.ToString()));
|
||||
Log.Add(LogTypes.Error, staticUser, -1, string.Format("Xml wasn't saved: {0}", ee));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -83,6 +83,8 @@ namespace umbraco.presentation.install.steps
|
||||
DbConnectionStringBuilder connectionStringBuilder = new DbConnectionStringBuilder();
|
||||
connectionStringBuilder.ConnectionString = GlobalSettings.DbDSN;
|
||||
|
||||
// "Data Source=.\\SQLEXPRESS;Initial Catalog=BB_Umbraco_Sandbox1;integrated security=false;user id=umbraco;pwd=umbraco"
|
||||
|
||||
// Prepare the fields
|
||||
|
||||
// Prepare data layer type
|
||||
@@ -97,9 +99,12 @@ namespace umbraco.presentation.install.steps
|
||||
|
||||
// Prepare other fields
|
||||
DatabaseServer.Text = GetConnectionStringValue(connectionStringBuilder, "server");
|
||||
if (string.IsNullOrEmpty(DatabaseServer.Text)) DatabaseServer.Text = GetConnectionStringValue(connectionStringBuilder, "Data Source");
|
||||
DatabaseName.Text = GetConnectionStringValue(connectionStringBuilder, "database");
|
||||
if (string.IsNullOrEmpty(DatabaseName.Text)) DatabaseName.Text = GetConnectionStringValue(connectionStringBuilder, "Initial Catalog");
|
||||
DatabaseUsername.Text = GetConnectionStringValue(connectionStringBuilder, "user id");
|
||||
DatabasePassword.Text = GetConnectionStringValue(connectionStringBuilder, "password");
|
||||
if (string.IsNullOrEmpty(DatabasePassword.Text)) DatabasePassword.Text = GetConnectionStringValue(connectionStringBuilder, "pwd");
|
||||
}
|
||||
|
||||
// Make sure ASP.Net displays the password text
|
||||
|
||||
@@ -232,7 +232,18 @@ namespace umbraco.presentation
|
||||
else
|
||||
error = "No Context available -> "
|
||||
+ mApp.Context.Server.GetLastError().InnerException;
|
||||
Log.Add(LogTypes.Error, User.GetUser(0), -1, error);
|
||||
|
||||
// Hide error if getting the user throws an error (e.g. corrupt / blank db)
|
||||
User staticUser = null;
|
||||
try
|
||||
{
|
||||
User.GetCurrent();
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
|
||||
Log.Add(LogTypes.Error, staticUser, -1, error);
|
||||
Trace.TraceError(error);
|
||||
lock (unhandledErrors)
|
||||
{
|
||||
|
||||
@@ -17,6 +17,10 @@ h2 {
|
||||
font-size: 14pt;
|
||||
}
|
||||
|
||||
h3 {
|
||||
font-size: 12pt;
|
||||
}
|
||||
|
||||
p.rel {
|
||||
padding-left: 2em;
|
||||
text-indent: -2em;
|
||||
|
||||
@@ -47,6 +47,7 @@
|
||||
<add key="umbracoProfileUrl" value="profiler" />
|
||||
<add key="umbracoUseSSL" value="false" />
|
||||
<add key="umbracoUseMediumTrust" value="false" />
|
||||
<add key="umbracoContentXMLUseLocalTemp" value="false"/> <!-- Set to true to write this to the local CodeGenDir instead, in SAN / NAS situations -->
|
||||
</appSettings>
|
||||
<system.net>
|
||||
<mailSettings>
|
||||
@@ -226,4 +227,4 @@
|
||||
</assemblyBinding>
|
||||
</runtime>
|
||||
|
||||
</configuration>
|
||||
</configuration>
|
||||
|
||||
@@ -28,7 +28,7 @@
|
||||
<UmbracoExamine configSource="config\ExamineSettings.config" />
|
||||
<ExamineLuceneIndexSets configSource="config\ExamineIndex.config" />
|
||||
<appSettings>
|
||||
<add key="umbracoDbDSN" value="server=.\sqlexpress;database=UmbracoTest2;user id=sa;password=test" />
|
||||
<add key="umbracoDbDSN" value="Data Source=.\SQLEXPRESS;Initial Catalog=BB_Umbraco_Sandbox1;integrated security=false;user id=umbraco;pwd=umbraco" />
|
||||
<add key="umbracoConfigurationStatus" value="4.1.0.beta" />
|
||||
<add key="umbracoReservedUrls" value="~/config/splashes/booting.aspx,~/install/default.aspx,~/config/splashes/noNodes.aspx" />
|
||||
<add key="umbracoReservedPaths" value="~/umbraco,~/install/" />
|
||||
@@ -47,6 +47,8 @@
|
||||
<add key="umbracoProfileUrl" value="profiler" />
|
||||
<add key="umbracoUseSSL" value="false" />
|
||||
<add key="umbracoUseMediumTrust" value="false" />
|
||||
<add key="umbracoContentXMLUseLocalTemp" value="false" />
|
||||
<!-- Set to true to write this to the local CodeGenDir instead, in SAN / NAS situations -->
|
||||
</appSettings>
|
||||
<system.net>
|
||||
<mailSettings>
|
||||
|
||||
Reference in New Issue
Block a user