From d40a835701f7e18f687bf98e5b9335173697c708 Mon Sep 17 00:00:00 2001
From: Stephan
Date: Tue, 27 Mar 2018 10:51:41 +0200
Subject: [PATCH] Port v7@2aa0dfb2c5 - WIP
---
src/Umbraco.Core/Scoping/IScopeContext.cs | 8 +
.../Editors/EditorModelEventManager.cs | 64 ++---
.../PublishedContentCache.cs | 5 +
.../XmlPublishedCache/PublishedMediaCache.cs | 38 ++-
.../XmlPublishedCache/SafeXmlReaderWriter.cs | 15 +-
.../XmlPublishedCache/XmlStore.cs | 5 +-
src/Umbraco.Web/Search/ExamineComponent.cs | 239 ++++++++++++++----
src/Umbraco.Web/Search/UmbracoTreeSearcher.cs | 4 +
.../Identity/BackOfficeCookieManager.cs | 1 +
.../Identity/ExternalSignInAutoLinkOptions.cs | 6 +
...ForceRenewalCookieAuthenticationHandler.cs | 2 +-
.../Identity/GetUserSecondsMiddleWare.cs | 16 ++
src/Umbraco.Web/Security/WebAuthExtensions.cs | 22 +-
src/Umbraco.Web/Security/WebSecurity.cs | 30 +--
src/Umbraco.Web/Umbraco.Web.csproj | 9 +
.../developer/Macros/editMacro.aspx.cs | 4 +-
.../developer/Packages/editPackage.aspx.cs | 7 +
.../umbraco/developer/Xslt/editXslt.aspx.cs | 2 +-
.../dialogs/importDocumenttype.aspx.cs | 2 +
.../umbraco/dialogs/notifications.aspx.cs | 2 +-
.../umbraco/dialogs/rollBack.aspx.cs | 35 +--
.../umbraco/dialogs/viewAuditTrail.aspx | 1 +
.../umbraco/translation/default.aspx.cs | 1 -
.../umbraco/translation/details.aspx.cs | 1 -
.../umbraco/webservices/nodeSorter.asmx.cs | 15 +-
25 files changed, 363 insertions(+), 171 deletions(-)
diff --git a/src/Umbraco.Core/Scoping/IScopeContext.cs b/src/Umbraco.Core/Scoping/IScopeContext.cs
index f4fb652bc7..093ebef4f7 100644
--- a/src/Umbraco.Core/Scoping/IScopeContext.cs
+++ b/src/Umbraco.Core/Scoping/IScopeContext.cs
@@ -38,5 +38,13 @@ namespace Umbraco.Core.Scoping
/// The action boolean parameter indicates whether the scope completed or not.
///
T Enlist(string key, Func creator, Action action = null, int priority = 100);
+
+ ///
+ /// Gets an enlisted object.
+ ///
+ /// The type of the object.
+ /// The object unique identifier.
+ /// The enlisted object, if any, else the default value.
+ T GetEnlisted(string key);
}
}
diff --git a/src/Umbraco.Web/Editors/EditorModelEventManager.cs b/src/Umbraco.Web/Editors/EditorModelEventManager.cs
index 0dea726142..e2a248cb88 100644
--- a/src/Umbraco.Web/Editors/EditorModelEventManager.cs
+++ b/src/Umbraco.Web/Editors/EditorModelEventManager.cs
@@ -1,39 +1,9 @@
-using System;
-using System.Web.Http.Filters;
+using System.Web.Http.Filters;
using Umbraco.Core.Events;
using Umbraco.Web.Models.ContentEditing;
namespace Umbraco.Web.Editors
{
- public class EditorModelEventArgs : EventArgs
- {
- public EditorModelEventArgs(object model, UmbracoContext umbracoContext)
- {
- Model = model;
- UmbracoContext = umbracoContext;
- }
-
- public object Model { get; private set; }
- public UmbracoContext UmbracoContext { get; private set; }
- }
-
- public sealed class EditorModelEventArgs : EditorModelEventArgs
- {
- public EditorModelEventArgs(EditorModelEventArgs baseArgs)
- : base(baseArgs.Model, baseArgs.UmbracoContext)
- {
- Model = (T)baseArgs.Model;
- }
-
- public EditorModelEventArgs(T model, UmbracoContext umbracoContext)
- : base(model, umbracoContext)
- {
- Model = model;
- }
-
- public new T Model { get; private set; }
- }
-
///
/// Used to emit events for editor models in the back office
///
@@ -42,23 +12,30 @@ namespace Umbraco.Web.Editors
public static event TypedEventHandler> SendingContentModel;
public static event TypedEventHandler> SendingMediaModel;
public static event TypedEventHandler> SendingMemberModel;
+ public static event TypedEventHandler> SendingUserModel;
+
+ private static void OnSendingUserModel(HttpActionExecutedContext sender, EditorModelEventArgs e)
+ {
+ var handler = SendingUserModel;
+ handler?.Invoke(sender, e);
+ }
private static void OnSendingContentModel(HttpActionExecutedContext sender, EditorModelEventArgs e)
{
var handler = SendingContentModel;
- if (handler != null) handler(sender, e);
+ handler?.Invoke(sender, e);
}
private static void OnSendingMediaModel(HttpActionExecutedContext sender, EditorModelEventArgs e)
{
var handler = SendingMediaModel;
- if (handler != null) handler(sender, e);
+ handler?.Invoke(sender, e);
}
private static void OnSendingMemberModel(HttpActionExecutedContext sender, EditorModelEventArgs e)
{
var handler = SendingMemberModel;
- if (handler != null) handler(sender, e);
+ handler?.Invoke(sender, e);
}
///
@@ -68,24 +45,17 @@ namespace Umbraco.Web.Editors
///
internal static void EmitEvent(HttpActionExecutedContext sender, EditorModelEventArgs e)
{
- var contentItemDisplay = e.Model as ContentItemDisplay;
- if (contentItemDisplay != null)
- {
+ if (e.Model is ContentItemDisplay)
OnSendingContentModel(sender, new EditorModelEventArgs(e));
- }
- var mediaItemDisplay = e.Model as MediaItemDisplay;
- if (mediaItemDisplay != null)
- {
+ if (e.Model is MediaItemDisplay)
OnSendingMediaModel(sender, new EditorModelEventArgs(e));
- }
- var memberItemDisplay = e.Model as MemberDisplay;
- if (memberItemDisplay != null)
- {
+ if (e.Model is MemberDisplay)
OnSendingMemberModel(sender, new EditorModelEventArgs(e));
- }
- }
+ if (e.Model is UserDisplay)
+ OnSendingUserModel(sender, new EditorModelEventArgs(e));
+ }
}
}
diff --git a/src/Umbraco.Web/PublishedCache/XmlPublishedCache/PublishedContentCache.cs b/src/Umbraco.Web/PublishedCache/XmlPublishedCache/PublishedContentCache.cs
index f3829c35c0..58d856ad8d 100644
--- a/src/Umbraco.Web/PublishedCache/XmlPublishedCache/PublishedContentCache.cs
+++ b/src/Umbraco.Web/PublishedCache/XmlPublishedCache/PublishedContentCache.cs
@@ -486,7 +486,12 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache
// not trying to be thread-safe here, that's not the point
if (preview == false)
+ {
+ // if there's a current enlisted reader/writer, use its xml
+ var tempXml = _xmlStore.TempXml;
+ if (tempXml != null) return tempXml;
return _xml;
+ }
// Xml cache does not support retrieving preview content when not previewing
if (_previewContent == null)
diff --git a/src/Umbraco.Web/PublishedCache/XmlPublishedCache/PublishedMediaCache.cs b/src/Umbraco.Web/PublishedCache/XmlPublishedCache/PublishedMediaCache.cs
index 690ae7e5b7..df4b7990ad 100644
--- a/src/Umbraco.Web/PublishedCache/XmlPublishedCache/PublishedMediaCache.cs
+++ b/src/Umbraco.Web/PublishedCache/XmlPublishedCache/PublishedMediaCache.cs
@@ -108,7 +108,43 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache
public override IEnumerable GetAtRoot(bool preview)
{
- //TODO: We should be able to look these ids first in Examine!
+ var searchProvider = GetSearchProviderSafe();
+
+ if (searchProvider != null)
+ {
+ try
+ {
+ // first check in Examine for the cache values
+ // +(+parentID:-1) +__IndexType:media
+
+ var criteria = searchProvider.CreateSearchCriteria("media");
+ var filter = criteria.ParentId(-1).Not().Field(UmbracoContentIndexer.IndexPathFieldName, "-1,-21,".MultipleCharacterWildcard());
+
+ var result = searchProvider.Search(filter.Compile());
+ if (result != null)
+ return result.Select(x => CreateFromCacheValues(ConvertFromSearchResult(x)));
+ }
+ catch (Exception ex)
+ {
+ if (ex is FileNotFoundException)
+ {
+ //Currently examine is throwing FileNotFound exceptions when we have a loadbalanced filestore and a node is published in umbraco
+ //See this thread: http://examine.cdodeplex.com/discussions/264341
+ //Catch the exception here for the time being, and just fallback to GetMedia
+ //TODO: Need to fix examine in LB scenarios!
+ Current.Logger.Error("Could not load data from Examine index for media", ex);
+ }
+ else if (ex is AlreadyClosedException)
+ {
+ //If the app domain is shutting down and the site is under heavy load the index reader will be closed and it really cannot
+ //be re-opened since the app domain is shutting down. In this case we have no option but to try to load the data from the db.
+ Current.Logger.Error("Could not load data from Examine index for media, the app domain is most likely in a shutdown state", ex);
+ }
+ else throw;
+ }
+ }
+
+ //something went wrong, fetch from the db
var rootMedia = _mediaService.GetRootMedia();
return rootMedia.Select(m => GetUmbracoMedia(m.Id));
diff --git a/src/Umbraco.Web/PublishedCache/XmlPublishedCache/SafeXmlReaderWriter.cs b/src/Umbraco.Web/PublishedCache/XmlPublishedCache/SafeXmlReaderWriter.cs
index a84d443397..7f6a6fd704 100644
--- a/src/Umbraco.Web/PublishedCache/XmlPublishedCache/SafeXmlReaderWriter.cs
+++ b/src/Umbraco.Web/PublishedCache/XmlPublishedCache/SafeXmlReaderWriter.cs
@@ -17,6 +17,11 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache
private bool _using;
private bool _registerXmlChange;
+ // the default enlist priority is 100
+ // enlist with a lower priority to ensure that anything "default" has a clean xml
+ private const int EnlistPriority = 60;
+ private const string EnlistKey = "safeXmlReaderWriter";
+
private SafeXmlReaderWriter(IDisposable releaser, XmlDocument xml, Action refresh, Action apply, bool isWriter, bool scoped)
{
_releaser = releaser;
@@ -29,6 +34,12 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache
_xml = IsWriter ? Clone(xml) : xml;
}
+ public static SafeXmlReaderWriter Get(IScopeProvider scopeProvider)
+ {
+ var scopeContext = scopeProvider.Context;
+ return scopeContext?.GetEnlisted(EnlistKey);
+ }
+
public static SafeXmlReaderWriter Get(IScopeProvider scopeProvider, AsyncLock xmlLock, XmlDocument xml, Action refresh, Action apply, bool writer)
{
var scopeContext = scopeProvider.Context;
@@ -42,7 +53,7 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache
}
// get or create an enlisted reader/writer
- var rw = scopeContext.Enlist("safeXmlReaderWriter",
+ var rw = scopeContext.Enlist(EnlistKey,
() => // creator
{
// obtain exclusive access to xml and create reader/writer
@@ -52,7 +63,7 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache
(completed, item) => // action
{
item.DisposeForReal(completed);
- });
+ }, EnlistPriority);
// ensure it's not already in-use - should never happen, just being super safe
if (rw._using)
diff --git a/src/Umbraco.Web/PublishedCache/XmlPublishedCache/XmlStore.cs b/src/Umbraco.Web/PublishedCache/XmlPublishedCache/XmlStore.cs
index 8c56ffd87f..a0e7f6370d 100644
--- a/src/Umbraco.Web/PublishedCache/XmlPublishedCache/XmlStore.cs
+++ b/src/Umbraco.Web/PublishedCache/XmlPublishedCache/XmlStore.cs
@@ -38,7 +38,7 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache
/// then passed to all instances that are created (one per request).
/// This class should *not* be public.
///
- class XmlStore : IDisposable
+ internal class XmlStore : IDisposable
{
private readonly IDocumentRepository _documentRepository;
private readonly IMediaRepository _mediaRepository;
@@ -329,6 +329,9 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache
}
}
+ // Gets the temp. Xml managed by SafeXmlReaderWrite, if any
+ public XmlDocument TempXml => SafeXmlReaderWriter.Get(_scopeProvider)?.Xml;
+
// assumes xml lock
private void SetXmlLocked(XmlDocument xml, bool registerXmlChange)
{
diff --git a/src/Umbraco.Web/Search/ExamineComponent.cs b/src/Umbraco.Web/Search/ExamineComponent.cs
index 411f77fb3f..50940ecf4c 100644
--- a/src/Umbraco.Web/Search/ExamineComponent.cs
+++ b/src/Umbraco.Web/Search/ExamineComponent.cs
@@ -14,6 +14,7 @@ using Umbraco.Core.Components;
using Umbraco.Core.Logging;
using Umbraco.Core.Models;
using Umbraco.Core.PropertyEditors;
+using Umbraco.Core.Scoping;
using Umbraco.Core.Services;
using Umbraco.Core.Services.Changes;
using Umbraco.Core.Services.Implement;
@@ -31,8 +32,17 @@ namespace Umbraco.Web.Search
[RuntimeLevel(MinLevel = RuntimeLevel.Run)]
public sealed class ExamineComponent : UmbracoComponentBase, IUmbracoCoreComponent
{
- public void Initialize(IRuntimeState runtime, PropertyEditorCollection propertyEditors, IExamineIndexCollectionAccessor indexCollection, ILogger logger)
+ private IScopeProvider _scopeProvider;
+
+ // the default enlist priority is 100
+ // enlist with a lower priority to ensure that anything "default" runs after us
+ // but greater that SafeXmlReaderWriter priority which is 60
+ private const int EnlistPriority = 80;
+
+ public void Initialize(IRuntimeState runtime, PropertyEditorCollection propertyEditors, IExamineIndexCollectionAccessor indexCollection, IScopeProvider scopeProvider, ILogger logger)
{
+ _scopeProvider = scopeProvider;
+
logger.Info("Starting initialize async background thread.");
// make it async in order not to slow down the boot
@@ -119,7 +129,7 @@ namespace Umbraco.Web.Search
i.DocumentWriting += grid.DocumentWriting;
}
- static void MemberCacheRefresherUpdated(MemberCacheRefresher sender, CacheRefresherEventArgs args)
+ void MemberCacheRefresherUpdated(MemberCacheRefresher sender, CacheRefresherEventArgs args)
{
if (Suspendable.ExamineEvents.CanIndex == false)
return;
@@ -164,7 +174,7 @@ namespace Umbraco.Web.Search
}
}
- static void MediaCacheRefresherUpdated(MediaCacheRefresher sender, CacheRefresherEventArgs args)
+ void MediaCacheRefresherUpdated(MediaCacheRefresher sender, CacheRefresherEventArgs args)
{
if (Suspendable.ExamineEvents.CanIndex == false)
return;
@@ -213,7 +223,7 @@ namespace Umbraco.Web.Search
}
}
- static void ContentCacheRefresherUpdated(ContentCacheRefresher sender, CacheRefresherEventArgs args)
+ void ContentCacheRefresherUpdated(ContentCacheRefresher sender, CacheRefresherEventArgs args)
{
if (Suspendable.ExamineEvents.CanIndex == false)
return;
@@ -289,7 +299,7 @@ namespace Umbraco.Web.Search
}
}
- private static void ReIndexForContent(IContent content, IContent published)
+ private void ReIndexForContent(IContent content, IContent published)
{
if (published != null && content.VersionId == published.VersionId)
{
@@ -311,42 +321,30 @@ namespace Umbraco.Web.Search
}
}
- private static void ReIndexForContent(IContent sender, bool? supportUnpublished = null)
+ private void ReIndexForContent(IContent sender, bool? supportUnpublished = null)
{
- var xml = sender.ToXml();
- //add an icon attribute to get indexed
- xml.Add(new XAttribute("icon", sender.ContentType.Icon));
-
- ExamineManager.Instance.ReIndexNode(
- xml, IndexTypes.Content,
- ExamineManager.Instance.IndexProviderCollection.OfType()
- // only for the specified indexers
- .Where(x => supportUnpublished.HasValue == false || supportUnpublished.Value == x.SupportUnpublishedContent)
- .Where(x => x.EnableDefaultEventHandler));
+ var actions = DeferedActions.Get(_scopeProvider);
+ if (actions != null)
+ actions.Add(new DeferedReIndexForContent(sender, supportUnpublished));
+ else
+ DeferedReIndexForContent.Execute(sender, supportUnpublished);
}
- private static void ReIndexForMember(IMember member)
+ private void ReIndexForMember(IMember member)
{
- ExamineManager.Instance.ReIndexNode(
- member.ToXml(), IndexTypes.Member,
- ExamineManager.Instance.IndexProviderCollection.OfType()
- //ensure that only the providers are flagged to listen execute
- .Where(x => x.EnableDefaultEventHandler));
- }
+ var actions = DeferedActions.Get(_scopeProvider);
+ if (actions != null)
+ actions.Add(new DeferedReIndexForMember(member));
+ else
+ DeferedReIndexForMember.Execute(member); }
- private static void ReIndexForMedia(IMedia sender, bool isMediaPublished)
+ private void ReIndexForMedia(IMedia sender, bool isMediaPublished)
{
- var xml = sender.ToXml();
- //add an icon attribute to get indexed
- xml.Add(new XAttribute("icon", sender.ContentType.Icon));
-
- ExamineManager.Instance.ReIndexNode(
- xml, IndexTypes.Media,
- ExamineManager.Instance.IndexProviderCollection.OfType()
- // index this item for all indexers if the media is not trashed, otherwise if the item is trashed
- // then only index this for indexers supporting unpublished media
- .Where(x => isMediaPublished || (x.SupportUnpublishedContent))
- .Where(x => x.EnableDefaultEventHandler));
+ var actions = DeferedActions.Get(_scopeProvider);
+ if (actions != null)
+ actions.Add(new DeferedReIndexForMedia(sender, isMediaPublished));
+ else
+ DeferedReIndexForMedia.Execute(sender, isMediaPublished);
}
///
@@ -357,15 +355,13 @@ namespace Umbraco.Web.Search
/// If true, indicates that we will only delete this item from indexes that don't support unpublished content.
/// If false it will delete this from all indexes regardless.
///
- private static void DeleteIndexForEntity(int entityId, bool keepIfUnpublished)
+ private void DeleteIndexForEntity(int entityId, bool keepIfUnpublished)
{
- ExamineManager.Instance.DeleteFromIndex(
- entityId.ToString(CultureInfo.InvariantCulture),
- ExamineManager.Instance.IndexProviderCollection.OfType()
- // if keepIfUnpublished == true then only delete this item from indexes not supporting unpublished content,
- // otherwise if keepIfUnpublished == false then remove from all indexes
- .Where(x => keepIfUnpublished == false || (x is UmbracoContentIndexer && ((UmbracoContentIndexer)x).SupportUnpublishedContent == false))
- .Where(x => x.EnableDefaultEventHandler));
+ var actions = DeferedActions.Get(_scopeProvider);
+ if (actions != null)
+ actions.Add(new DeferedDeleteIndex(entityId, keepIfUnpublished));
+ else
+ DeferedDeleteIndex.Execute(entityId, keepIfUnpublished);
}
///
@@ -390,5 +386,162 @@ namespace Umbraco.Web.Search
));
}
}
+
+ private class DeferedActions
+ {
+ private readonly List _actions = new List();
+
+ public static DeferedActions Get(IScopeProvider scopeProvider)
+ {
+ var scopeContext = scopeProvider.Context;
+ if (scopeContext == null) return null;
+
+ return scopeContext.Enlist("examineEvents",
+ () => new DeferedActions(), // creator
+ (completed, actions) => // action
+ {
+ if (completed) actions.Execute();
+ }, EnlistPriority);
+ }
+
+ public void Add(DeferedAction action)
+ {
+ _actions.Add(action);
+ }
+
+ private void Execute()
+ {
+ foreach (var action in _actions)
+ action.Execute();
+ }
+ }
+
+ private abstract class DeferedAction
+ {
+ public virtual void Execute()
+ { }
+ }
+
+ private class DeferedReIndexForContent : DeferedAction
+ {
+ private readonly IContent _content;
+ private readonly bool? _supportUnpublished;
+
+ public DeferedReIndexForContent(IContent content, bool? supportUnpublished)
+ {
+ _content = content;
+ _supportUnpublished = supportUnpublished;
+ }
+
+ public override void Execute()
+ {
+ Execute(_content, _supportUnpublished);
+ }
+
+ public static void Execute(IContent content, bool? supportUnpublished)
+ {
+ var xml = content.ToXml();
+ //add an icon attribute to get indexed
+ xml.Add(new XAttribute("icon", content.ContentType.Icon));
+
+ ExamineManager.Instance.ReIndexNode(
+ xml, IndexTypes.Content,
+ ExamineManager.Instance.IndexProviderCollection.OfType()
+
+ //Index this item for all indexers if the content is published, otherwise if the item is not published
+ // then only index this for indexers supporting unpublished content
+
+ .Where(x => supportUnpublished.HasValue == false || supportUnpublished.Value == x.SupportUnpublishedContent)
+ .Where(x => x.EnableDefaultEventHandler));
+ }
+ }
+
+ private class DeferedReIndexForMedia : DeferedAction
+ {
+ private readonly IMedia _media;
+ private readonly bool _isPublished;
+
+ public DeferedReIndexForMedia(IMedia media, bool isPublished)
+ {
+ _media = media;
+ _isPublished = isPublished;
+ }
+
+ public override void Execute()
+ {
+ Execute(_media, _isPublished);
+ }
+
+ public static void Execute(IMedia media, bool isPublished)
+ {
+ var xml = media.ToXml();
+ //add an icon attribute to get indexed
+ xml.Add(new XAttribute("icon", media.ContentType.Icon));
+
+ ExamineManager.Instance.ReIndexNode(
+ xml, IndexTypes.Media,
+ ExamineManager.Instance.IndexProviderCollection.OfType()
+
+ //Index this item for all indexers if the media is not trashed, otherwise if the item is trashed
+ // then only index this for indexers supporting unpublished media
+
+ .Where(x => isPublished || (x.SupportUnpublishedContent))
+ .Where(x => x.EnableDefaultEventHandler));
+ }
+ }
+
+ private class DeferedReIndexForMember : DeferedAction
+ {
+ private readonly IMember _member;
+
+ public DeferedReIndexForMember(IMember member)
+ {
+ _member = member;
+ }
+
+ public override void Execute()
+ {
+ Execute(_member);
+ }
+
+ public static void Execute(IMember member)
+ {
+ ExamineManager.Instance.ReIndexNode(
+ member.ToXml(), IndexTypes.Member,
+ ExamineManager.Instance.IndexProviderCollection.OfType()
+ //ensure that only the providers are flagged to listen execute
+ .Where(x => x.EnableDefaultEventHandler));
+ }
+ }
+
+ private class DeferedDeleteIndex : DeferedAction
+ {
+ private readonly int _id;
+ private readonly bool _keepIfUnpublished;
+
+ public DeferedDeleteIndex(int id, bool keepIfUnpublished)
+ {
+ _id = id;
+ _keepIfUnpublished = keepIfUnpublished;
+ }
+
+ public override void Execute()
+ {
+ Execute(_id, _keepIfUnpublished);
+ }
+
+ public static void Execute(int id, bool keepIfUnpublished)
+ {
+ ExamineManager.Instance.DeleteFromIndex(
+ id.ToString(CultureInfo.InvariantCulture),
+ ExamineManager.Instance.IndexProviderCollection.OfType()
+
+ //if keepIfUnpublished == true then only delete this item from indexes not supporting unpublished content,
+ // otherwise if keepIfUnpublished == false then remove from all indexes
+
+ .Where(x => keepIfUnpublished == false || x.SupportUnpublishedContent == false)
+ .Where(x => x.EnableDefaultEventHandler));
+ }
+ }
}
}
diff --git a/src/Umbraco.Web/Search/UmbracoTreeSearcher.cs b/src/Umbraco.Web/Search/UmbracoTreeSearcher.cs
index 917df00c4c..eb565b127e 100644
--- a/src/Umbraco.Web/Search/UmbracoTreeSearcher.cs
+++ b/src/Umbraco.Web/Search/UmbracoTreeSearcher.cs
@@ -208,6 +208,10 @@ namespace Umbraco.Web.Search
if (sb == null) throw new ArgumentNullException("sb");
if (entityService == null) throw new ArgumentNullException("entityService");
+ Udi udi;
+ Udi.TryParse(searchFrom, true, out udi);
+ searchFrom = udi == null ? searchFrom : entityService.GetIdForUdi(udi).Result.ToString();
+
int searchFromId;
var entityPath = int.TryParse(searchFrom, out searchFromId) && searchFromId > 0
? entityService.GetAllPaths(objectType, searchFromId).FirstOrDefault()
diff --git a/src/Umbraco.Web/Security/Identity/BackOfficeCookieManager.cs b/src/Umbraco.Web/Security/Identity/BackOfficeCookieManager.cs
index c5ad3d121c..98493db1c7 100644
--- a/src/Umbraco.Web/Security/Identity/BackOfficeCookieManager.cs
+++ b/src/Umbraco.Web/Security/Identity/BackOfficeCookieManager.cs
@@ -6,6 +6,7 @@ using Microsoft.Owin;
using Microsoft.Owin.Infrastructure;
using Umbraco.Core;
using Umbraco.Core.Configuration;
+using Umbraco.Core.IO;
namespace Umbraco.Web.Security.Identity
{
diff --git a/src/Umbraco.Web/Security/Identity/ExternalSignInAutoLinkOptions.cs b/src/Umbraco.Web/Security/Identity/ExternalSignInAutoLinkOptions.cs
index a808f7eb62..43738aec17 100644
--- a/src/Umbraco.Web/Security/Identity/ExternalSignInAutoLinkOptions.cs
+++ b/src/Umbraco.Web/Security/Identity/ExternalSignInAutoLinkOptions.cs
@@ -52,6 +52,12 @@ namespace Umbraco.Web.Security.Identity
///
public Action OnAutoLinking { get; set; }
+ ///
+ /// A callback executed during every time a user authenticates using an external login.
+ /// returns a boolean indicating if sign in should continue or not.
+ ///
+ public Func OnExternalLogin { get; set; }
+
[EditorBrowsable(EditorBrowsableState.Never)]
[Obsolete("Use the overload specifying user groups instead")]
public string GetDefaultUserType(UmbracoContext umbracoContext, ExternalLoginInfo loginInfo)
diff --git a/src/Umbraco.Web/Security/Identity/ForceRenewalCookieAuthenticationHandler.cs b/src/Umbraco.Web/Security/Identity/ForceRenewalCookieAuthenticationHandler.cs
index 86b1a0522c..f3c7013701 100644
--- a/src/Umbraco.Web/Security/Identity/ForceRenewalCookieAuthenticationHandler.cs
+++ b/src/Umbraco.Web/Security/Identity/ForceRenewalCookieAuthenticationHandler.cs
@@ -1,7 +1,7 @@
using System;
using Umbraco.Core;
using System.Threading.Tasks;
-using Microsoft.Owin;
+using Umbraco.Core.Security;
using Microsoft.Owin.Security;
using Microsoft.Owin.Security.Cookies;
using Microsoft.Owin.Security.Infrastructure;
diff --git a/src/Umbraco.Web/Security/Identity/GetUserSecondsMiddleWare.cs b/src/Umbraco.Web/Security/Identity/GetUserSecondsMiddleWare.cs
index 68a356fa82..0efb0b66a6 100644
--- a/src/Umbraco.Web/Security/Identity/GetUserSecondsMiddleWare.cs
+++ b/src/Umbraco.Web/Security/Identity/GetUserSecondsMiddleWare.cs
@@ -2,12 +2,15 @@
using System.Diagnostics;
using System.Globalization;
using System.Threading.Tasks;
+using System.Web;
+using System.Web.Security;
using Microsoft.Owin;
using Microsoft.Owin.Logging;
using Microsoft.Owin.Security.Cookies;
using Umbraco.Core;
using Umbraco.Core.Configuration;
using Umbraco.Core.Configuration.UmbracoSettings;
+using Umbraco.Core.Security;
namespace Umbraco.Web.Security.Identity
{
@@ -82,6 +85,9 @@ namespace Umbraco.Web.Security.Identity
//if it's time to renew, then do it
if (timeRemaining < timeElapsed)
{
+ //TODO: This would probably be simpler just to do: context.OwinContext.Authentication.SignIn(context.Properties, identity);
+ // this will invoke the default Cookie middleware to basically perform this logic for us.
+
ticket.Properties.IssuedUtc = currentUtc;
var timeSpan = expiresUtc.Value.Subtract(issuedUtc.Value);
ticket.Properties.ExpiresUtc = currentUtc.Add(timeSpan);
@@ -99,6 +105,9 @@ namespace Umbraco.Web.Security.Identity
remainingSeconds = (ticket.Properties.ExpiresUtc.Value - currentUtc).TotalSeconds;
}
}
+
+ //We also need to re-validate the user's session if we are relying on this ping to keep their session alive
+ await SessionIdValidator.ValidateSessionAsync(TimeSpan.FromMinutes(1), context, _authOptions.CookieManager, _authOptions.SystemClock, issuedUtc, ticket.Identity);
}
else if (remainingSeconds <= 30)
{
@@ -114,6 +123,13 @@ namespace Umbraco.Web.Security.Identity
return;
}
}
+
+ //Hack! we need to suppress the stupid forms authentcation module but we can only do that by using non owin stuff
+ if (HttpContext.Current != null && HttpContext.Current.Response != null)
+ {
+ HttpContext.Current.Response.SuppressFormsAuthenticationRedirect = true;
+ }
+
response.StatusCode = 401;
}
else if (Next != null)
diff --git a/src/Umbraco.Web/Security/WebAuthExtensions.cs b/src/Umbraco.Web/Security/WebAuthExtensions.cs
index 138dd36b67..d38345e48c 100644
--- a/src/Umbraco.Web/Security/WebAuthExtensions.cs
+++ b/src/Umbraco.Web/Security/WebAuthExtensions.cs
@@ -14,18 +14,13 @@ namespace Umbraco.Web.Security
internal static class WebAuthExtensions
{
///
- /// This will set a an authenticated IPrincipal to the current request given the IUser object
+ /// This will set a an authenticated IPrincipal to the current request for webforms & webapi
///
///
- ///
+ ///
///
- internal static IPrincipal SetPrincipalForRequest(this HttpRequestMessage request, IUser user)
+ internal static IPrincipal SetPrincipalForRequest(this HttpRequestMessage request, IPrincipal principal)
{
- var principal = new ClaimsPrincipal(
- new UmbracoBackOfficeIdentity(
- new ClaimsIdentity(),
- Mapper.Map(user)));
-
//It is actually not good enough to set this on the current app Context and the thread, it also needs
// to be set explicitly on the HttpContext.Current !! This is a strange web api thing that is actually
// an underlying fault of asp.net not propogating the User correctly.
@@ -50,15 +45,10 @@ namespace Umbraco.Web.Security
/// This will set a an authenticated IPrincipal to the current request given the IUser object
///
///
- ///
+ ///
///
- internal static IPrincipal SetPrincipalForRequest(this HttpContextBase httpContext, UserData userData)
- {
- var principal = new ClaimsPrincipal(
- new UmbracoBackOfficeIdentity(
- new ClaimsIdentity(),
- userData));
-
+ internal static IPrincipal SetPrincipalForRequest(this HttpContextBase httpContext, IPrincipal principal)
+ {
//It is actually not good enough to set this on the current app Context and the thread, it also needs
// to be set explicitly on the HttpContext.Current !! This is a strange web api thing that is actually
// an underlying fault of asp.net not propogating the User correctly.
diff --git a/src/Umbraco.Web/Security/WebSecurity.cs b/src/Umbraco.Web/Security/WebSecurity.cs
index d8d001bab9..547b2d3595 100644
--- a/src/Umbraco.Web/Security/WebSecurity.cs
+++ b/src/Umbraco.Web/Security/WebSecurity.cs
@@ -23,7 +23,7 @@ namespace Umbraco.Web.Security
///
/// A utility class used for dealing with USER security in Umbraco
///
- public class WebSecurity : DisposableObject
+ public class WebSecurity : DisposableObjectSlim
{
private HttpContextBase _httpContext;
private readonly IUserService _userService;
@@ -112,34 +112,14 @@ namespace Umbraco.Web.Security
owinCtx.Authentication.SignOut(Constants.Security.BackOfficeExternalAuthenticationType);
var user = UserManager.FindByIdAsync(userId).Result;
- var userData = Mapper.Map(user);
- _httpContext.SetPrincipalForRequest(userData);
SignInManager.SignInAsync(user, isPersistent: true, rememberBrowser: false).Wait();
+
+ _httpContext.SetPrincipalForRequest(owinCtx.Request.User);
+
return TimeSpan.FromMinutes(GlobalSettings.TimeOutInMinutes).TotalSeconds;
}
-
- [Obsolete("This method should not be used, login is performed by the OWIN pipeline, use the overload that returns double and accepts a UserId instead")]
- public virtual FormsAuthenticationTicket PerformLogin(IUser user)
- {
- //clear the external cookie - we do this first without owin context because we're writing cookies directly to httpcontext
- // and cookie handling is different with httpcontext vs webapi and owin, normally we'd just do:
- //_httpContext.GetOwinContext().Authentication.SignOut(Constants.Security.BackOfficeExternalAuthenticationType);
-
- var externalLoginCookie = _httpContext.Request.Cookies.Get(Constants.Security.BackOfficeExternalCookieName);
- if (externalLoginCookie != null)
- {
- externalLoginCookie.Expires = DateTime.Now.AddYears(-1);
- _httpContext.Response.Cookies.Set(externalLoginCookie);
- }
-
- //ensure it's done for owin too
- _httpContext.GetOwinContext().Authentication.SignOut(Constants.Security.BackOfficeExternalAuthenticationType);
-
- var ticket = _httpContext.CreateUmbracoAuthTicket(Mapper.Map(user));
- return ticket;
- }
-
+
///
/// Clears the current login for the currently logged in user
///
diff --git a/src/Umbraco.Web/Umbraco.Web.csproj b/src/Umbraco.Web/Umbraco.Web.csproj
index e3753ed994..0f44de7c14 100644
--- a/src/Umbraco.Web/Umbraco.Web.csproj
+++ b/src/Umbraco.Web/Umbraco.Web.csproj
@@ -131,14 +131,17 @@
+
+
+
@@ -199,6 +202,10 @@
+
+
+
+
@@ -251,6 +258,7 @@
+
@@ -386,6 +394,7 @@
+
diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/developer/Macros/editMacro.aspx.cs b/src/Umbraco.Web/umbraco.presentation/umbraco/developer/Macros/editMacro.aspx.cs
index a3ff49e545..420002f9b3 100644
--- a/src/Umbraco.Web/umbraco.presentation/umbraco/developer/Macros/editMacro.aspx.cs
+++ b/src/Umbraco.Web/umbraco.presentation/umbraco/developer/Macros/editMacro.aspx.cs
@@ -46,7 +46,7 @@ namespace umbraco.cms.presentation.developer
{
ClientTools
.SetActiveTreeType(Constants.Trees.Macros)
- .SyncTree("-1,init," + _macro.Id, false);
+ .SyncTree("-1," + _macro.Id, false);
string tempMacroAssembly = _macro.ControlAssembly ?? "";
string tempMacroType = _macro.ControlType ?? "";
@@ -291,7 +291,7 @@ namespace umbraco.cms.presentation.developer
ClientTools
.SetActiveTreeType(Constants.Trees.Macros)
- .SyncTree("-1,init," + _macro.Id.ToInvariantString(), true); //true forces the reload
+ .SyncTree("-1," + _macro.Id.ToInvariantString(), true); //true forces the reload
var tempMacroAssembly = macroAssembly.Text;
var tempMacroType = macroType.Text;
diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/developer/Packages/editPackage.aspx.cs b/src/Umbraco.Web/umbraco.presentation/umbraco/developer/Packages/editPackage.aspx.cs
index 35f36f9733..56830f8230 100644
--- a/src/Umbraco.Web/umbraco.presentation/umbraco/developer/Packages/editPackage.aspx.cs
+++ b/src/Umbraco.Web/umbraco.presentation/umbraco/developer/Packages/editPackage.aspx.cs
@@ -167,6 +167,13 @@ namespace umbraco.presentation.developer.packages
///*Data types */
//cms.businesslogic.datatype.DataTypeDefinition[] umbDataType = cms.businesslogic.datatype.DataTypeDefinition.GetAll();
+
+ // sort array by name
+ Array.Sort(umbDataType, delegate(cms.businesslogic.datatype.DataTypeDefinition umbDataType1, cms.businesslogic.datatype.DataTypeDefinition umbDataType2)
+ {
+ return umbDataType1.Text.CompareTo(umbDataType2.Text);
+ });
+
//foreach (cms.businesslogic.datatype.DataTypeDefinition umbDtd in umbDataType)
//{
diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/developer/Xslt/editXslt.aspx.cs b/src/Umbraco.Web/umbraco.presentation/umbraco/developer/Xslt/editXslt.aspx.cs
index 0768475133..392a914eb3 100644
--- a/src/Umbraco.Web/umbraco.presentation/umbraco/developer/Xslt/editXslt.aspx.cs
+++ b/src/Umbraco.Web/umbraco.presentation/umbraco/developer/Xslt/editXslt.aspx.cs
@@ -28,7 +28,7 @@ namespace umbraco.cms.presentation.developer
if (!IsPostBack)
{
string file = Request.QueryString["file"];
- string path = BaseTree.GetTreePathFromFilePath(file);
+ string path = BaseTree.GetTreePathFromFilePath(file, false, tree);
ClientTools
.SetActiveTreeType(Constants.Trees.Xslt)
.SyncTree(path, false);
diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/dialogs/importDocumenttype.aspx.cs b/src/Umbraco.Web/umbraco.presentation/umbraco/dialogs/importDocumenttype.aspx.cs
index d934ea725b..59720e8e63 100644
--- a/src/Umbraco.Web/umbraco.presentation/umbraco/dialogs/importDocumenttype.aspx.cs
+++ b/src/Umbraco.Web/umbraco.presentation/umbraco/dialogs/importDocumenttype.aspx.cs
@@ -73,6 +73,7 @@ namespace umbraco.presentation.umbraco.dialogs
private void import_Click(object sender, EventArgs e)
{
var xd = new XmlDocument();
+ xd.XmlResolver = null;
xd.Load(tempFile.Value);
var userId = Security.GetUserId();
@@ -107,6 +108,7 @@ namespace umbraco.presentation.umbraco.dialogs
documentTypeFile.PostedFile.SaveAs(fileName);
var xd = new XmlDocument();
+ xd.XmlResolver = null;
xd.Load(fileName);
dtName.Text = xd.DocumentElement.SelectSingleNode("//DocumentType/Info/Name").FirstChild.Value;
dtAlias.Text = xd.DocumentElement.SelectSingleNode("//DocumentType/Info/Alias").FirstChild.Value;
diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/dialogs/notifications.aspx.cs b/src/Umbraco.Web/umbraco.presentation/umbraco/dialogs/notifications.aspx.cs
index 74fc5af5e0..268e604547 100644
--- a/src/Umbraco.Web/umbraco.presentation/umbraco/dialogs/notifications.aspx.cs
+++ b/src/Umbraco.Web/umbraco.presentation/umbraco/dialogs/notifications.aspx.cs
@@ -30,7 +30,7 @@ namespace umbraco.dialogs
protected void Page_Load(object sender, EventArgs e)
{
Button1.Text = Services.TextService.Localize("update");
- pane_form.Text = Services.TextService.Localize("notifications/editNotifications", new[] { node.Name});
+ pane_form.Text = Services.TextService.Localize("notifications/editNotifications", new[] { Server.HtmlEncode(node.Name) });
}
#region Web Form Designer generated code
diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/dialogs/rollBack.aspx.cs b/src/Umbraco.Web/umbraco.presentation/umbraco/dialogs/rollBack.aspx.cs
index 03558ad847..1bf5e1b158 100644
--- a/src/Umbraco.Web/umbraco.presentation/umbraco/dialogs/rollBack.aspx.cs
+++ b/src/Umbraco.Web/umbraco.presentation/umbraco/dialogs/rollBack.aspx.cs
@@ -5,6 +5,7 @@
//using System.ComponentModel;
//using System.Data;
//using System.Drawing;
+//using System.Linq;
//using System.Web;
//using System.Web.SessionState;
//using System.Web.UI;
@@ -119,36 +120,22 @@
// if (!IsPostBack) {
// allVersions.Items.Add(new ListItem(Services.TextService.Localize("rollback/selectVersion")+ "...", ""));
-// foreach (DocumentVersionList dl in currentDoc.GetVersions()) {
+
+// foreach (DocumentVersionList dl in currentDoc.GetVersions())
+// {
+// //we don't need to show the current version
+// if (dl.Version == currentDoc.Version)
+// continue;
+//
// allVersions.Items.Add(new ListItem(dl.Text + " (" + Services.TextService.Localize("content/createDate") + ": " + dl.Date.ToShortDateString() + " " + dl.Date.ToShortTimeString() + ")", dl.Version.ToString()));
// }
// Button1.Text = Services.TextService.Localize("actions/rollback");
// }
// }
-
-// #region Web Form Designer generated code
-// override protected void OnInit(EventArgs e)
-// {
-// //
-// // CODEGEN: This call is required by the ASP.NET Web Form Designer.
-// //
-// InitializeComponent();
-// base.OnInit(e);
-// }
-
-// ///
-// /// Required method for Designer support - do not modify
-// /// the contents of this method with the code editor.
-// ///
-// private void InitializeComponent()
-// {
-
-// }
-// #endregion
-
// protected void doRollback_Click(object sender, System.EventArgs e)
// {
-// if (allVersions.SelectedValue.Trim() != "") {
+// if (allVersions.SelectedValue.Trim() != "")
+// {
// Document d = new Document(int.Parse(helper.Request("nodeId")));
// d.RollBack(new Guid(allVersions.SelectedValue), Security.CurrentUser);
@@ -161,6 +148,8 @@
// feedBackMsg.Text = ui.Text("rollback", "documentRolledBack", vars, new global::umbraco.BusinessLogic.User(0)) + "
" + Services.TextService.Localize("closeThisWindow") + "";
// diffPanel.Visible = false;
// pl_buttons.Visible = false;
+//
+// ClientTools.ReloadLocationIfMatched(string.Format("/content/content/edit/{0}", d.Id));
// }
// }
// }
diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/dialogs/viewAuditTrail.aspx b/src/Umbraco.Web/umbraco.presentation/umbraco/dialogs/viewAuditTrail.aspx
index 1c703d25a0..159b55421f 100644
--- a/src/Umbraco.Web/umbraco.presentation/umbraco/dialogs/viewAuditTrail.aspx
+++ b/src/Umbraco.Web/umbraco.presentation/umbraco/dialogs/viewAuditTrail.aspx
@@ -7,6 +7,7 @@
diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/translation/default.aspx.cs b/src/Umbraco.Web/umbraco.presentation/umbraco/translation/default.aspx.cs
index 1995cdb580..4d7e033635 100644
--- a/src/Umbraco.Web/umbraco.presentation/umbraco/translation/default.aspx.cs
+++ b/src/Umbraco.Web/umbraco.presentation/umbraco/translation/default.aspx.cs
@@ -3,7 +3,6 @@ using System.Data;
using System.IO;
using System.Text;
using System.Xml;
-using Umbraco.Core;
using Umbraco.Core.Services;
using Umbraco.Core.IO;
using System.Collections.Generic;
diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/translation/details.aspx.cs b/src/Umbraco.Web/umbraco.presentation/umbraco/translation/details.aspx.cs
index 8736baf6e8..79c22c3c08 100644
--- a/src/Umbraco.Web/umbraco.presentation/umbraco/translation/details.aspx.cs
+++ b/src/Umbraco.Web/umbraco.presentation/umbraco/translation/details.aspx.cs
@@ -5,7 +5,6 @@ using Umbraco.Core;
using Umbraco.Core.Services;
using Umbraco.Web.Composing;
using Umbraco.Web._Legacy.BusinessLogic;
-
namespace umbraco.presentation.umbraco.translation {
public partial class details : Umbraco.Web.UI.Pages.UmbracoEnsuredPage {
diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/webservices/nodeSorter.asmx.cs b/src/Umbraco.Web/umbraco.presentation/umbraco/webservices/nodeSorter.asmx.cs
index 672aa151cf..9574d4e3bf 100644
--- a/src/Umbraco.Web/umbraco.presentation/umbraco/webservices/nodeSorter.asmx.cs
+++ b/src/Umbraco.Web/umbraco.presentation/umbraco/webservices/nodeSorter.asmx.cs
@@ -168,13 +168,16 @@ namespace umbraco.presentation.webservices
{
var contentService = Services.ContentService;
try
- {
- var intIds = ids.Select(int.Parse).ToArray();
- var allContent = contentService.GetByIds(intIds).ToDictionary(x => x.Id, x => x);
- var sortedContent = intIds.Select(x => allContent[x]);
-
+ {
// Save content with new sort order and update db+cache accordingly
- var sorted = contentService.Sort(sortedContent);
+ var intIds = new List();
+ foreach (var stringId in ids)
+ {
+ int intId;
+ if (int.TryParse(stringId, out intId))
+ intIds.Add(intId);
+ }
+ var sorted = contentService.Sort(intIds.ToArray());
// refresh sort order on cached xml
// but no... this is not distributed - solely relying on content service & events should be enough