Merge remote-tracking branch 'origin/dev-v7' into dev-v7.6
# Conflicts: # src/Umbraco.Core/Configuration/CoreDebug.cs # src/Umbraco.Core/Publishing/PublishingStrategy.cs # src/Umbraco.Core/Umbraco.Core.csproj
This commit is contained in:
@@ -18,10 +18,14 @@ namespace Umbraco.Core.Configuration
|
||||
{
|
||||
var appSettings = System.Configuration.ConfigurationManager.AppSettings;
|
||||
LogUncompletedScopes = string.Equals("true", appSettings["Umbraco.CoreDebug.LogUncompletedScopes"], StringComparison.OrdinalIgnoreCase);
|
||||
DumpOnTimeoutThreadAbort = string.Equals("true", appSettings["Umbraco.CoreDebug.DumpOnTimeoutThreadAbort"], StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
|
||||
// when true, Scope logs the stack trace for any scope that gets disposed without being completed.
|
||||
// this helps troubleshooting rogue scopes that we forget to complete
|
||||
public bool LogUncompletedScopes { get; private set; }
|
||||
// when true, the Logger creates a minidump of w3wp in ~/App_Data/MiniDump whenever it logs
|
||||
// an error due to a ThreadAbortException that is due to a timeout.
|
||||
public bool DumpOnTimeoutThreadAbort { get; private set; }
|
||||
}
|
||||
}
|
||||
|
||||
135
src/Umbraco.Core/Diagnostics/MiniDump.cs
Normal file
135
src/Umbraco.Core/Diagnostics/MiniDump.cs
Normal file
@@ -0,0 +1,135 @@
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Runtime.InteropServices;
|
||||
using Umbraco.Core.IO;
|
||||
|
||||
namespace Umbraco.Core.Diagnostics
|
||||
{
|
||||
// taken from https://blogs.msdn.microsoft.com/dondu/2010/10/24/writing-minidumps-in-c/
|
||||
// and https://blogs.msdn.microsoft.com/dondu/2010/10/31/writing-minidumps-from-exceptions-in-c/
|
||||
// which itself got it from http://blog.kalmbach-software.de/2008/12/13/writing-minidumps-in-c/
|
||||
|
||||
internal static class MiniDump
|
||||
{
|
||||
private static readonly object LockO = new object();
|
||||
|
||||
[Flags]
|
||||
public enum Option : uint
|
||||
{
|
||||
// From dbghelp.h:
|
||||
Normal = 0x00000000,
|
||||
WithDataSegs = 0x00000001,
|
||||
WithFullMemory = 0x00000002,
|
||||
WithHandleData = 0x00000004,
|
||||
FilterMemory = 0x00000008,
|
||||
ScanMemory = 0x00000010,
|
||||
WithUnloadedModules = 0x00000020,
|
||||
WithIndirectlyReferencedMemory = 0x00000040,
|
||||
FilterModulePaths = 0x00000080,
|
||||
WithProcessThreadData = 0x00000100,
|
||||
WithPrivateReadWriteMemory = 0x00000200,
|
||||
WithoutOptionalData = 0x00000400,
|
||||
WithFullMemoryInfo = 0x00000800,
|
||||
WithThreadInfo = 0x00001000,
|
||||
WithCodeSegs = 0x00002000,
|
||||
WithoutAuxiliaryState = 0x00004000,
|
||||
WithFullAuxiliaryState = 0x00008000,
|
||||
WithPrivateWriteCopyMemory = 0x00010000,
|
||||
IgnoreInaccessibleMemory = 0x00020000,
|
||||
ValidTypeFlags = 0x0003ffff,
|
||||
}
|
||||
|
||||
//typedef struct _MINIDUMP_EXCEPTION_INFORMATION {
|
||||
// DWORD ThreadId;
|
||||
// PEXCEPTION_POINTERS ExceptionPointers;
|
||||
// BOOL ClientPointers;
|
||||
//} MINIDUMP_EXCEPTION_INFORMATION, *PMINIDUMP_EXCEPTION_INFORMATION;
|
||||
[StructLayout(LayoutKind.Sequential, Pack = 4)] // Pack=4 is important! So it works also for x64!
|
||||
public struct MiniDumpExceptionInformation
|
||||
{
|
||||
public uint ThreadId;
|
||||
public IntPtr ExceptionPointers;
|
||||
[MarshalAs(UnmanagedType.Bool)]
|
||||
public bool ClientPointers;
|
||||
}
|
||||
|
||||
//BOOL
|
||||
//WINAPI
|
||||
//MiniDumpWriteDump(
|
||||
// __in HANDLE hProcess,
|
||||
// __in DWORD ProcessId,
|
||||
// __in HANDLE hFile,
|
||||
// __in MINIDUMP_TYPE DumpType,
|
||||
// __in_opt PMINIDUMP_EXCEPTION_INFORMATION ExceptionParam,
|
||||
// __in_opt PMINIDUMP_USER_STREAM_INFORMATION UserStreamParam,
|
||||
// __in_opt PMINIDUMP_CALLBACK_INFORMATION CallbackParam
|
||||
// );
|
||||
|
||||
// Overload requiring MiniDumpExceptionInformation
|
||||
[DllImport("dbghelp.dll", EntryPoint = "MiniDumpWriteDump", CallingConvention = CallingConvention.StdCall, CharSet = CharSet.Unicode, ExactSpelling = true, SetLastError = true)]
|
||||
private static extern bool MiniDumpWriteDump(IntPtr hProcess, uint processId, SafeHandle hFile, uint dumpType, ref MiniDumpExceptionInformation expParam, IntPtr userStreamParam, IntPtr callbackParam);
|
||||
|
||||
// Overload supporting MiniDumpExceptionInformation == NULL
|
||||
[DllImport("dbghelp.dll", EntryPoint = "MiniDumpWriteDump", CallingConvention = CallingConvention.StdCall, CharSet = CharSet.Unicode, ExactSpelling = true, SetLastError = true)]
|
||||
private static extern bool MiniDumpWriteDump(IntPtr hProcess, uint processId, SafeHandle hFile, uint dumpType, IntPtr expParam, IntPtr userStreamParam, IntPtr callbackParam);
|
||||
|
||||
[DllImport("kernel32.dll", EntryPoint = "GetCurrentThreadId", ExactSpelling = true)]
|
||||
private static extern uint GetCurrentThreadId();
|
||||
|
||||
private static bool Write(SafeHandle fileHandle, Option options, bool withException = false)
|
||||
{
|
||||
var currentProcess = Process.GetCurrentProcess();
|
||||
var currentProcessHandle = currentProcess.Handle;
|
||||
var currentProcessId = (uint)currentProcess.Id;
|
||||
|
||||
MiniDumpExceptionInformation exp;
|
||||
|
||||
exp.ThreadId = GetCurrentThreadId();
|
||||
exp.ClientPointers = false;
|
||||
exp.ExceptionPointers = IntPtr.Zero;
|
||||
|
||||
if (withException)
|
||||
exp.ExceptionPointers = Marshal.GetExceptionPointers();
|
||||
|
||||
var bRet = exp.ExceptionPointers == IntPtr.Zero
|
||||
? MiniDumpWriteDump(currentProcessHandle, currentProcessId, fileHandle, (uint) options, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero)
|
||||
: MiniDumpWriteDump(currentProcessHandle, currentProcessId, fileHandle, (uint) options, ref exp, IntPtr.Zero, IntPtr.Zero);
|
||||
|
||||
return bRet;
|
||||
}
|
||||
|
||||
public static bool Dump(Option options = Option.WithFullMemory, bool withException = false)
|
||||
{
|
||||
lock (LockO)
|
||||
{
|
||||
// work around "stack trace is not available while minidump debugging",
|
||||
// by making sure a local var (that we can inspect) contains the stack trace.
|
||||
// getting the call stack before it is unwound would require a special exception
|
||||
// filter everywhere in our code = not!
|
||||
var stacktrace = withException ? Environment.StackTrace : string.Empty;
|
||||
|
||||
var filepath = IOHelper.MapPath("~/App_Data/MiniDump");
|
||||
if (Directory.Exists(filepath) == false)
|
||||
Directory.CreateDirectory(filepath);
|
||||
|
||||
var filename = Path.Combine(filepath, string.Format("{0:yyyyMMddTHHmmss}.{1}.dmp", DateTime.UtcNow, Guid.NewGuid().ToString("N").Substring(0, 4)));
|
||||
using (var stream = new FileStream(filename, FileMode.Create, FileAccess.ReadWrite, FileShare.Write))
|
||||
{
|
||||
return Write(stream.SafeFileHandle, options, withException);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static bool OkToDump()
|
||||
{
|
||||
lock (LockO)
|
||||
{
|
||||
var filepath = IOHelper.MapPath("~/App_Data/MiniDump");
|
||||
if (Directory.Exists(filepath) == false) return true;
|
||||
var count = Directory.GetFiles(filepath, "*.dmp").Length;
|
||||
return count < 8;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -7,6 +7,8 @@ using System.Threading;
|
||||
using System.Web;
|
||||
using log4net;
|
||||
using log4net.Config;
|
||||
using Umbraco.Core.Configuration;
|
||||
using Umbraco.Core.Diagnostics;
|
||||
|
||||
namespace Umbraco.Core.Logging
|
||||
{
|
||||
@@ -57,23 +59,55 @@ namespace Umbraco.Core.Logging
|
||||
internal ILog LoggerFor(object getTypeFromInstance)
|
||||
{
|
||||
if (getTypeFromInstance == null) throw new ArgumentNullException("getTypeFromInstance");
|
||||
|
||||
|
||||
return LogManager.GetLogger(getTypeFromInstance.GetType());
|
||||
}
|
||||
|
||||
|
||||
public void Error(Type callingType, string message, Exception exception)
|
||||
{
|
||||
var logger = LogManager.GetLogger(callingType);
|
||||
if (logger == null) return;
|
||||
|
||||
var dump = false;
|
||||
|
||||
if (IsTimeoutThreadAbortException(exception))
|
||||
{
|
||||
message += "\r\nThe thread has been aborted, because the request has timed out.";
|
||||
|
||||
// dump if configured, or if stacktrace contains Monitor.ReliableEnter
|
||||
dump = UmbracoConfig.For.CoreDebug().DumpOnTimeoutThreadAbort || IsMonitorEnterThreadAbortException(exception);
|
||||
|
||||
// dump if it is ok to dump (might have a cap on number of dump...)
|
||||
dump &= MiniDump.OkToDump();
|
||||
}
|
||||
|
||||
logger.Error(message, exception);
|
||||
if (dump)
|
||||
{
|
||||
try
|
||||
{
|
||||
var dumped = MiniDump.Dump(withException: true);
|
||||
message += dumped
|
||||
? "\r\nA minidump was created in App_Data/MiniDump"
|
||||
: "\r\nFailed to create a minidump";
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
message += string.Format("\r\nFailed to create a minidump ({0}: {1})", e.GetType().FullName, e.Message);
|
||||
}
|
||||
}
|
||||
|
||||
logger.Error(message, exception);
|
||||
}
|
||||
|
||||
private static bool IsMonitorEnterThreadAbortException(Exception exception)
|
||||
{
|
||||
var abort = exception as ThreadAbortException;
|
||||
if (abort == null) return false;
|
||||
|
||||
var stacktrace = abort.StackTrace;
|
||||
return stacktrace.Contains("System.Threading.Monitor.ReliableEnter");
|
||||
}
|
||||
|
||||
private static bool IsTimeoutThreadAbortException(Exception exception)
|
||||
{
|
||||
var abort = exception as ThreadAbortException;
|
||||
@@ -105,7 +139,7 @@ namespace Umbraco.Core.Logging
|
||||
if (showHttpTrace && HttpContext.Current != null)
|
||||
{
|
||||
HttpContext.Current.Trace.Warn(callingType.Name, string.Format(message, formatItems.Select(x => x.Invoke()).ToArray()));
|
||||
}
|
||||
}
|
||||
|
||||
var logger = LogManager.GetLogger(callingType);
|
||||
if (logger == null || logger.IsWarnEnabled == false) return;
|
||||
@@ -122,7 +156,7 @@ namespace Umbraco.Core.Logging
|
||||
var logger = LogManager.GetLogger(callingType);
|
||||
if (logger == null || logger.IsWarnEnabled == false) return;
|
||||
var executedParams = formatItems.Select(x => x.Invoke()).ToArray();
|
||||
logger.WarnFormat((message) + ". Exception: " + e, executedParams);
|
||||
logger.WarnFormat((message) + ". Exception: " + e, executedParams);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -179,7 +179,7 @@ namespace Umbraco.Core.Models.PublishedContent
|
||||
}
|
||||
|
||||
if (contentType == null)
|
||||
throw new Exception(string.Format("ContentTypeService failed to find a {0} type with alias \"{1}\".",
|
||||
throw new InvalidOperationException(string.Format("ContentTypeService failed to find a {0} type with alias \"{1}\".",
|
||||
itemType.ToString().ToLower(), alias));
|
||||
|
||||
return new PublishedContentType(contentType);
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Umbraco.Core.Events;
|
||||
using Umbraco.Core.Models;
|
||||
using Umbraco.Core.Services;
|
||||
@@ -12,7 +13,8 @@ namespace Umbraco.Core.Publishing
|
||||
{
|
||||
public PublishStatus(IContent content, PublishStatusType statusType, EventMessages eventMessages)
|
||||
: base(content, statusType, eventMessages)
|
||||
{
|
||||
{
|
||||
InvalidProperties = Enumerable.Empty<Property>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -20,8 +22,7 @@ namespace Umbraco.Core.Publishing
|
||||
/// </summary>
|
||||
public PublishStatus(IContent content, EventMessages eventMessages)
|
||||
: this(content, PublishStatusType.Success, eventMessages)
|
||||
{
|
||||
}
|
||||
{ }
|
||||
|
||||
public IContent ContentItem
|
||||
{
|
||||
|
||||
@@ -51,7 +51,7 @@ namespace Umbraco.Core.Publishing
|
||||
/// </summary>
|
||||
/// <param name="uow"></param>
|
||||
/// <param name="content"><see cref="IContent"/> to publish</param>
|
||||
/// <param name="userId">Id of the User issueing the publish operation</param>
|
||||
/// <param name="userId">Id of the User issueing the publish operation</param>
|
||||
Attempt<PublishStatus> IPublishingStrategy2.Publish(IScopeUnitOfWork uow, IContent content, int userId)
|
||||
{
|
||||
var evtMsgs = _eventMessagesFactory.Get();
|
||||
@@ -124,26 +124,26 @@ namespace Umbraco.Core.Publishing
|
||||
/// By default this is set to true which means that it will publish any content item in the list that is completely unpublished and
|
||||
/// not visible on the front-end. If set to false, this will only publish content that is live on the front-end but has new versions
|
||||
/// that have yet to be published.
|
||||
/// </param>
|
||||
/// </param>
|
||||
/// <returns></returns>
|
||||
/// <remarks>
|
||||
///
|
||||
///
|
||||
/// This method becomes complex once we start to be able to cancel events or stop publishing a content item in any way because if a
|
||||
/// content item is not published then it's children shouldn't be published either. This rule will apply for the following conditions:
|
||||
/// * If a document fails to be published, do not proceed to publish it's children if:
|
||||
/// ** The document does not have a publish version
|
||||
/// ** The document does have a published version but the includeUnpublishedDocuments = false
|
||||
///
|
||||
///
|
||||
/// In order to do this, we will order the content by level and begin by publishing each item at that level, then proceed to the next
|
||||
/// level and so on. If we detect that the above rule applies when the document publishing is cancelled we'll add it to the list of
|
||||
/// level and so on. If we detect that the above rule applies when the document publishing is cancelled we'll add it to the list of
|
||||
/// parentsIdsCancelled so that it's children don't get published.
|
||||
///
|
||||
///
|
||||
/// Its important to note that all 'root' documents included in the list *will* be published regardless of the rules mentioned
|
||||
/// above (unless it is invalid)!! By 'root' documents we are referring to documents in the list with the minimum value for their 'level'.
|
||||
/// In most cases the 'root' documents will only be one document since under normal circumstance we only publish one document and
|
||||
/// above (unless it is invalid)!! By 'root' documents we are referring to documents in the list with the minimum value for their 'level'.
|
||||
/// In most cases the 'root' documents will only be one document since under normal circumstance we only publish one document and
|
||||
/// its children. The reason we have to do this is because if a user is publishing a document and it's children, it is implied that
|
||||
/// the user definitely wants to publish it even if it has never been published before.
|
||||
///
|
||||
///
|
||||
/// </remarks>
|
||||
IEnumerable<Attempt<PublishStatus>> IPublishingStrategy2.PublishWithChildren(IScopeUnitOfWork uow,
|
||||
IEnumerable<IContent> content, int userId, bool includeUnpublishedDocuments)
|
||||
@@ -157,7 +157,7 @@ namespace Umbraco.Core.Publishing
|
||||
|
||||
//group by levels and iterate over the sorted ascending level.
|
||||
//TODO: This will cause all queries to execute, they will not be lazy but I'm not really sure being lazy actually made
|
||||
// much difference because we iterate over them all anyways?? Morten?
|
||||
// much difference because we iterate over them all anyways?? Morten?
|
||||
// Because we're grouping I think this will execute all the queries anyways so need to fetch it all first.
|
||||
var fetchedContent = content.ToArray();
|
||||
|
||||
@@ -174,7 +174,7 @@ namespace Umbraco.Core.Publishing
|
||||
var levelGroups = fetchedContent.GroupBy(x => x.Level);
|
||||
foreach (var level in levelGroups.OrderBy(x => x.Key))
|
||||
{
|
||||
//set the first level flag, used to ensure that all documents at the first level will
|
||||
//set the first level flag, used to ensure that all documents at the first level will
|
||||
//be published regardless of the rules mentioned in the remarks.
|
||||
if (!firstLevel.HasValue)
|
||||
{
|
||||
@@ -224,7 +224,10 @@ namespace Umbraco.Core.Publishing
|
||||
_logger.Info<PublishingStrategy>(
|
||||
string.Format("Content '{0}' with Id '{1}' will not be published because some of it's content is not passing validation rules.",
|
||||
item.Name, item.Id));
|
||||
statuses.Add(Attempt.Fail(new PublishStatus(item, PublishStatusType.FailedContentInvalid, evtMsgs)));
|
||||
statuses.Add(Attempt.Fail(new PublishStatus(item, PublishStatusType.FailedContentInvalid, evtMsgs)
|
||||
{
|
||||
InvalidProperties = ((ContentBase)item).LastInvalidProperties
|
||||
}));
|
||||
|
||||
//Does this document apply to our rule to cancel it's children being published?
|
||||
CheckCancellingOfChildPublishing(item, parentsIdsCancelled, includeUnpublishedDocuments);
|
||||
@@ -296,11 +299,11 @@ namespace Umbraco.Core.Publishing
|
||||
/// <param name="includeUnpublishedDocuments"></param>
|
||||
/// <remarks>
|
||||
/// See remarks on method: PublishWithChildrenInternal
|
||||
/// </remarks>
|
||||
/// </remarks>
|
||||
private void CheckCancellingOfChildPublishing(IContent content, List<int> parentsIdsCancelled, bool includeUnpublishedDocuments)
|
||||
{
|
||||
//Does this document apply to our rule to cancel it's children being published?
|
||||
//TODO: We're going back to the service layer here... not sure how to avoid this? And this will add extra overhead to
|
||||
//TODO: We're going back to the service layer here... not sure how to avoid this? And this will add extra overhead to
|
||||
// any document that fails to publish...
|
||||
var hasPublishedVersion = ApplicationContext.Current.Services.ContentService.HasPublishedVersion(content.Id);
|
||||
|
||||
|
||||
@@ -199,6 +199,7 @@
|
||||
<Compile Include="Configuration\BaseRest\ExtensionElementCollection.cs" />
|
||||
<Compile Include="Configuration\BaseRest\MethodElement.cs" />
|
||||
<Compile Include="Configuration\ContentXmlStorage.cs" />
|
||||
<Compile Include="Configuration\CoreDebug.cs" />
|
||||
<Compile Include="Configuration\Dashboard\AccessElement.cs" />
|
||||
<Compile Include="Configuration\Dashboard\AccessItem.cs" />
|
||||
<Compile Include="Configuration\Dashboard\AccessType.cs" />
|
||||
@@ -219,7 +220,6 @@
|
||||
<Compile Include="Configuration\Dashboard\SectionElement.cs" />
|
||||
<Compile Include="Configuration\Dashboard\TabCollection.cs" />
|
||||
<Compile Include="Configuration\Dashboard\TabElement.cs" />
|
||||
<Compile Include="Configuration\CoreDebug.cs" />
|
||||
<Compile Include="Configuration\FileSystemProviderElement.cs" />
|
||||
<Compile Include="Configuration\FileSystemProviderElementCollection.cs" />
|
||||
<Compile Include="Configuration\FileSystemProvidersSection.cs" />
|
||||
@@ -323,6 +323,7 @@
|
||||
<Compile Include="DateTimeExtensions.cs" />
|
||||
<Compile Include="DecimalExtensions.cs" />
|
||||
<Compile Include="DelegateExtensions.cs" />
|
||||
<Compile Include="Diagnostics\MiniDump.cs" />
|
||||
<Compile Include="Deploy\ArtifactBase.cs" />
|
||||
<Compile Include="Deploy\ArtifactDependency.cs" />
|
||||
<Compile Include="Deploy\ArtifactDependencyCollection.cs" />
|
||||
|
||||
@@ -163,13 +163,13 @@ angular.module('umbraco.directives')
|
||||
}
|
||||
|
||||
// ignore clicks on dialog from old dialog service
|
||||
var oldDialog = $(el).parents("#old-dialog-service");
|
||||
var oldDialog = $(event.target).parents("#old-dialog-service");
|
||||
if (oldDialog.length === 1) {
|
||||
return;
|
||||
}
|
||||
|
||||
// ignore clicks in tinyMCE dropdown(floatpanel)
|
||||
var floatpanel = $(el).parents(".mce-floatpanel");
|
||||
var floatpanel = $(event.target).closest(".mce-floatpanel");
|
||||
if (floatpanel.length === 1) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -80,7 +80,7 @@
|
||||
|
||||
|
||||
<div id="feedbackMsg" data-bind="visible: processStatus() == 'complete'">
|
||||
<div data-bind="css: { success: isSuccessful(), error: !isSuccessful() }">
|
||||
<div data-bind="css: { 'text-success': isSuccessful(), 'text-error': !isSuccessful() }">
|
||||
<span data-bind="text: resultMessage, visible: resultMessages().length == 0"></span>
|
||||
<ul data-bind="foreach: resultMessages, visible: resultMessages().length > 1">
|
||||
<li data-bind="text: message"></li>
|
||||
|
||||
@@ -4,6 +4,7 @@ using System.Linq;
|
||||
using System.Xml;
|
||||
using System.Xml.Serialization;
|
||||
using System.Xml.XPath;
|
||||
using umbraco;
|
||||
using Umbraco.Core;
|
||||
using Umbraco.Core.Configuration;
|
||||
using Umbraco.Core.Models;
|
||||
@@ -315,9 +316,9 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache
|
||||
|
||||
private void InitializeNode()
|
||||
{
|
||||
InitializeNode(_xmlNode, UmbracoConfig.For.UmbracoSettings().Content.UseLegacyXmlSchema, _isPreviewing,
|
||||
out _id, out _key, out _template, out _sortOrder, out _name, out _writerName,
|
||||
out _urlName, out _creatorName, out _creatorId, out _writerId, out _docTypeAlias, out _docTypeId, out _path,
|
||||
InitializeNode(_xmlNode, UmbracoConfig.For.UmbracoSettings().Content.UseLegacyXmlSchema, _isPreviewing,
|
||||
out _id, out _key, out _template, out _sortOrder, out _name, out _writerName,
|
||||
out _urlName, out _creatorName, out _creatorId, out _writerId, out _docTypeAlias, out _docTypeId, out _path,
|
||||
out _version, out _createDate, out _updateDate, out _level, out _isDraft, out _contentType, out _properties,
|
||||
PublishedContentType.Get);
|
||||
|
||||
@@ -345,7 +346,7 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache
|
||||
|
||||
//return if this is null
|
||||
if (xmlNode == null)
|
||||
{
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -407,8 +408,8 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache
|
||||
}
|
||||
|
||||
//dictionary to store the property node data
|
||||
var propertyNodes = new Dictionary<string, XmlNode>();
|
||||
|
||||
var propertyNodes = new Dictionary<string, XmlNode>();
|
||||
|
||||
foreach (XmlNode n in xmlNode.ChildNodes)
|
||||
{
|
||||
var e = n as XmlElement;
|
||||
@@ -432,7 +433,22 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache
|
||||
}
|
||||
|
||||
//lookup the content type and create the properties collection
|
||||
contentType = getPublishedContentType(PublishedItemType.Content, docTypeAlias);
|
||||
try
|
||||
{
|
||||
contentType = getPublishedContentType(PublishedItemType.Content, docTypeAlias);
|
||||
|
||||
}
|
||||
catch (InvalidOperationException e)
|
||||
{
|
||||
content.Instance.RefreshContentFromDatabase();
|
||||
|
||||
|
||||
throw new InvalidOperationException(
|
||||
string.Format("{0}. This usually indicates that the content cache is corrupt; the content cache has been rebuilt in an attempt to self-fix the issue.",
|
||||
//keep the original message but don't use this as an inner exception because we want the above message to be displayed, if we use the inner exception
|
||||
//we can keep the stack trace but the header message will be the original message and the one we really want to display will be hidden below the fold.
|
||||
e.Message));
|
||||
}
|
||||
properties = new Dictionary<string, IPublishedProperty>(StringComparer.OrdinalIgnoreCase);
|
||||
|
||||
//fill in the property collection
|
||||
|
||||
@@ -46,16 +46,16 @@ namespace Umbraco.Web.WebServices
|
||||
/*var result = ((ContentService) Services.ContentService)
|
||||
.PublishWithChildrenInternal(content, UmbracoUser.Id, includeUnpublished)
|
||||
.ToArray();*/
|
||||
var result = doc.PublishWithSubs(UmbracoUser.Id, includeUnpublished);
|
||||
var result = doc.PublishWithSubs(UmbracoUser.Id, includeUnpublished).ToArray();
|
||||
return Json(new
|
||||
{
|
||||
success = result.All(x => x.Success),
|
||||
message = GetMessageForStatuses(result.Select(x => x.Result), content)
|
||||
message = GetMessageForStatuses(result.Select(x => x.Result).ToArray(), content)
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private string GetMessageForStatuses(IEnumerable<PublishStatus> statuses, IContent doc)
|
||||
private string GetMessageForStatuses(PublishStatus[] statuses, IContent doc)
|
||||
{
|
||||
//if all are successful then just say it was successful
|
||||
if (statuses.All(x => ((int) x.StatusType) < 10))
|
||||
@@ -91,12 +91,12 @@ namespace Umbraco.Web.WebServices
|
||||
return "Cannot publish document with a status of " + status.StatusType;
|
||||
case PublishStatusType.FailedCancelledByEvent:
|
||||
return ui.Text("publish", "contentPublishedFailedByEvent",
|
||||
string.Format("{0} ({1})", status.ContentItem.Name, status.ContentItem.Id), UmbracoUser);
|
||||
string.Format("'{0}' ({1})", status.ContentItem.Name, status.ContentItem.Id), UmbracoUser);
|
||||
case PublishStatusType.FailedContentInvalid:
|
||||
return ui.Text("publish", "contentPublishedFailedInvalid",
|
||||
new []{
|
||||
string.Format("{0} ({1})", status.ContentItem.Name, status.ContentItem.Id),
|
||||
string.Join(",", status.InvalidProperties.Select(x => x.Alias))
|
||||
string.Format("'{0}' ({1})", status.ContentItem.Name, status.ContentItem.Id),
|
||||
string.Format("'{0}'", string.Join(", ", status.InvalidProperties.Select(x => x.Alias)))
|
||||
},
|
||||
UmbracoUser);
|
||||
default:
|
||||
|
||||
@@ -99,7 +99,7 @@ namespace umbraco.presentation.preview
|
||||
//Inject preview xml
|
||||
parentId = document.Level == 1 ? -1 : document.ParentId;
|
||||
var previewXml = document.ToPreviewXml(XmlContent);
|
||||
if (document.ContentEntity.Published == false
|
||||
if (document.ContentEntity.Published == false
|
||||
&& ApplicationContext.Current.Services.ContentService.HasPublishedVersion(document.Id))
|
||||
previewXml.Attributes.Append(XmlContent.CreateAttribute("isDraft"));
|
||||
XmlContent = content.GetAddOrUpdateXmlNode(XmlContent, document.Id, document.Level, parentId, previewXml);
|
||||
@@ -187,13 +187,9 @@ namespace umbraco.presentation.preview
|
||||
|
||||
private static void CleanPreviewDirectory(int userId, DirectoryInfo dir)
|
||||
{
|
||||
foreach (FileInfo file in dir.GetFiles(userId + "_*.config"))
|
||||
{
|
||||
DeletePreviewFile(userId, file);
|
||||
}
|
||||
// also delete any files accessed more than 10 minutes ago
|
||||
var now = DateTime.Now;
|
||||
foreach (FileInfo file in dir.GetFiles("*.config"))
|
||||
foreach (var file in dir.GetFiles("*.config"))
|
||||
{
|
||||
if ((now - file.LastAccessTime).TotalMinutes > 10)
|
||||
DeletePreviewFile(userId, file);
|
||||
@@ -206,6 +202,12 @@ namespace umbraco.presentation.preview
|
||||
{
|
||||
file.Delete();
|
||||
}
|
||||
catch (IOException)
|
||||
{
|
||||
// for *some* reason deleting the file can fail,
|
||||
// and it will work later on (long-lasting locks, etc),
|
||||
// so just ignore the exception
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogHelper.Error<PreviewContent>(string.Format("Couldn't delete preview set: {0} - User {1}", file.Name, userId), ex);
|
||||
|
||||
@@ -302,7 +302,11 @@ namespace umbraco.cms.businesslogic.packager
|
||||
XmlHelper.SetAttribute(Source, xmlDef, "enableSkins", package.EnableSkins.ToString());
|
||||
XmlHelper.SetAttribute(Source, xmlDef, "skinRepoGuid", package.SkinRepoGuid.ToString());
|
||||
XmlHelper.SetAttribute(Source, xmlDef, "iconUrl", package.IconUrl);
|
||||
XmlHelper.SetAttribute(Source, xmlDef, "umbVersion", package.UmbracoVersion.ToString(3));
|
||||
if (package.UmbracoVersion != null)
|
||||
{
|
||||
XmlHelper.SetAttribute(Source, xmlDef, "umbVersion", package.UmbracoVersion.ToString(3));
|
||||
}
|
||||
|
||||
|
||||
var licenseNode = xmlDef.SelectSingleNode("license");
|
||||
if (licenseNode == null)
|
||||
|
||||
@@ -874,18 +874,15 @@ namespace umbraco.cms.businesslogic.web
|
||||
[Obsolete("Don't use! Only used internally to support the legacy events", false)]
|
||||
internal IEnumerable<Attempt<PublishStatus>> PublishWithSubs(int userId, bool includeUnpublished)
|
||||
{
|
||||
PublishEventArgs e = new PublishEventArgs();
|
||||
var e = new PublishEventArgs();
|
||||
FireBeforePublish(e);
|
||||
|
||||
IEnumerable<Attempt<PublishStatus>> publishedResults = Enumerable.Empty<Attempt<PublishStatus>>();
|
||||
if (e.Cancel) return Enumerable.Empty<Attempt<PublishStatus>>();
|
||||
|
||||
if (!e.Cancel)
|
||||
{
|
||||
publishedResults = ApplicationContext.Current.Services.ContentService
|
||||
.PublishWithChildrenWithStatus(ContentEntity, userId, includeUnpublished);
|
||||
var publishedResults = ApplicationContext.Current.Services.ContentService
|
||||
.PublishWithChildrenWithStatus(ContentEntity, userId, includeUnpublished);
|
||||
|
||||
FireAfterPublish(e);
|
||||
}
|
||||
FireAfterPublish(e);
|
||||
|
||||
return publishedResults;
|
||||
}
|
||||
@@ -893,7 +890,6 @@ namespace umbraco.cms.businesslogic.web
|
||||
[Obsolete("Don't use! Only used internally to support the legacy events", false)]
|
||||
internal Attempt<PublishStatus> SaveAndPublish(int userId)
|
||||
{
|
||||
var result = Attempt.Fail(new PublishStatus(ContentEntity, PublishStatusType.FailedCancelledByEvent, new EventMessages()));
|
||||
foreach (var property in GenericProperties)
|
||||
{
|
||||
ContentEntity.SetValue(property.PropertyType.Alias, property.Value);
|
||||
@@ -901,32 +897,28 @@ namespace umbraco.cms.businesslogic.web
|
||||
|
||||
var saveArgs = new SaveEventArgs();
|
||||
FireBeforeSave(saveArgs);
|
||||
if (saveArgs.Cancel) return Attempt.Fail(new PublishStatus(ContentEntity, PublishStatusType.FailedCancelledByEvent, new EventMessages()));
|
||||
|
||||
if (!saveArgs.Cancel)
|
||||
{
|
||||
var publishArgs = new PublishEventArgs();
|
||||
FireBeforePublish(publishArgs);
|
||||
var publishArgs = new PublishEventArgs();
|
||||
FireBeforePublish(publishArgs);
|
||||
if (publishArgs.Cancel) return Attempt.Fail(new PublishStatus(ContentEntity, PublishStatusType.FailedCancelledByEvent, new EventMessages()));
|
||||
|
||||
if (!publishArgs.Cancel)
|
||||
{
|
||||
//NOTE: The 'false' parameter will cause the PublishingStrategy events to fire which will ensure that the cache is refreshed.
|
||||
result = ApplicationContext.Current.Services.ContentService
|
||||
.SaveAndPublishWithStatus(ContentEntity, userId);
|
||||
base.VersionDate = ContentEntity.UpdateDate;
|
||||
this.UpdateDate = ContentEntity.UpdateDate;
|
||||
//NOTE: The 'false' parameter will cause the PublishingStrategy events to fire which will ensure that the cache is refreshed.
|
||||
var result = ApplicationContext.Current.Services.ContentService
|
||||
.SaveAndPublishWithStatus(ContentEntity, userId);
|
||||
VersionDate = ContentEntity.UpdateDate;
|
||||
UpdateDate = ContentEntity.UpdateDate;
|
||||
|
||||
//NOTE: This is just going to call the CMSNode Save which will launch into the CMSNode.BeforeSave and CMSNode.AfterSave evenths
|
||||
// which actually do dick all and there's no point in even having them there but just in case for some insane reason someone
|
||||
// has bound to those events, I suppose we'll need to keep this here.
|
||||
base.Save();
|
||||
//NOTE: This is just going to call the CMSNode Save which will launch into the CMSNode.BeforeSave and CMSNode.AfterSave evenths
|
||||
// which actually do dick all and there's no point in even having them there but just in case for some insane reason someone
|
||||
// has bound to those events, I suppose we'll need to keep this here.
|
||||
base.Save();
|
||||
|
||||
//Launch the After Save event since we're doing 2 things in one operation: Saving and publishing.
|
||||
FireAfterSave(saveArgs);
|
||||
//Launch the After Save event since we're doing 2 things in one operation: Saving and publishing.
|
||||
FireAfterSave(saveArgs);
|
||||
|
||||
//Now we need to fire the After publish event
|
||||
FireAfterPublish(publishArgs);
|
||||
}
|
||||
}
|
||||
//Now we need to fire the After publish event
|
||||
FireAfterPublish(publishArgs);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user