Port v7@2aa0dfb2c5 - WIP
This commit is contained in:
@@ -38,5 +38,13 @@ namespace Umbraco.Core.Scoping
|
||||
/// <para>The action boolean parameter indicates whether the scope completed or not.</para>
|
||||
/// </remarks>
|
||||
T Enlist<T>(string key, Func<T> creator, Action<bool, T> action = null, int priority = 100);
|
||||
|
||||
/// <summary>
|
||||
/// Gets an enlisted object.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of the object.</typeparam>
|
||||
/// <param name="key">The object unique identifier.</param>
|
||||
/// <returns>The enlisted object, if any, else the default value.</returns>
|
||||
T GetEnlisted<T>(string key);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<T> : 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; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Used to emit events for editor models in the back office
|
||||
/// </summary>
|
||||
@@ -42,23 +12,30 @@ namespace Umbraco.Web.Editors
|
||||
public static event TypedEventHandler<HttpActionExecutedContext, EditorModelEventArgs<ContentItemDisplay>> SendingContentModel;
|
||||
public static event TypedEventHandler<HttpActionExecutedContext, EditorModelEventArgs<MediaItemDisplay>> SendingMediaModel;
|
||||
public static event TypedEventHandler<HttpActionExecutedContext, EditorModelEventArgs<MemberDisplay>> SendingMemberModel;
|
||||
public static event TypedEventHandler<HttpActionExecutedContext, EditorModelEventArgs<UserDisplay>> SendingUserModel;
|
||||
|
||||
private static void OnSendingUserModel(HttpActionExecutedContext sender, EditorModelEventArgs<UserDisplay> e)
|
||||
{
|
||||
var handler = SendingUserModel;
|
||||
handler?.Invoke(sender, e);
|
||||
}
|
||||
|
||||
private static void OnSendingContentModel(HttpActionExecutedContext sender, EditorModelEventArgs<ContentItemDisplay> e)
|
||||
{
|
||||
var handler = SendingContentModel;
|
||||
if (handler != null) handler(sender, e);
|
||||
handler?.Invoke(sender, e);
|
||||
}
|
||||
|
||||
private static void OnSendingMediaModel(HttpActionExecutedContext sender, EditorModelEventArgs<MediaItemDisplay> e)
|
||||
{
|
||||
var handler = SendingMediaModel;
|
||||
if (handler != null) handler(sender, e);
|
||||
handler?.Invoke(sender, e);
|
||||
}
|
||||
|
||||
private static void OnSendingMemberModel(HttpActionExecutedContext sender, EditorModelEventArgs<MemberDisplay> e)
|
||||
{
|
||||
var handler = SendingMemberModel;
|
||||
if (handler != null) handler(sender, e);
|
||||
handler?.Invoke(sender, e);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -68,24 +45,17 @@ namespace Umbraco.Web.Editors
|
||||
/// <param name="e"></param>
|
||||
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<ContentItemDisplay>(e));
|
||||
}
|
||||
|
||||
var mediaItemDisplay = e.Model as MediaItemDisplay;
|
||||
if (mediaItemDisplay != null)
|
||||
{
|
||||
if (e.Model is MediaItemDisplay)
|
||||
OnSendingMediaModel(sender, new EditorModelEventArgs<MediaItemDisplay>(e));
|
||||
}
|
||||
|
||||
var memberItemDisplay = e.Model as MemberDisplay;
|
||||
if (memberItemDisplay != null)
|
||||
{
|
||||
if (e.Model is MemberDisplay)
|
||||
OnSendingMemberModel(sender, new EditorModelEventArgs<MemberDisplay>(e));
|
||||
}
|
||||
}
|
||||
|
||||
if (e.Model is UserDisplay)
|
||||
OnSendingUserModel(sender, new EditorModelEventArgs<UserDisplay>(e));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -108,7 +108,43 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache
|
||||
|
||||
public override IEnumerable<IPublishedContent> 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<PublishedMediaCache>("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<PublishedMediaCache>("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));
|
||||
|
||||
@@ -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<XmlDocument> refresh, Action<XmlDocument, bool> 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<SafeXmlReaderWriter>(EnlistKey);
|
||||
}
|
||||
|
||||
public static SafeXmlReaderWriter Get(IScopeProvider scopeProvider, AsyncLock xmlLock, XmlDocument xml, Action<XmlDocument> refresh, Action<XmlDocument, bool> 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)
|
||||
|
||||
@@ -38,7 +38,7 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache
|
||||
/// then passed to all <see cref="PublishedContentCache"/> instances that are created (one per request).</para>
|
||||
/// <para>This class should *not* be public.</para>
|
||||
/// </remarks>
|
||||
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)
|
||||
{
|
||||
|
||||
@@ -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<ExamineComponent>("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<UmbracoContentIndexer>()
|
||||
// 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<BaseUmbracoIndexer>()
|
||||
//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<UmbracoContentIndexer>()
|
||||
// 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);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -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.
|
||||
/// </param>
|
||||
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<BaseUmbracoIndexer>()
|
||||
// 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);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -390,5 +386,162 @@ namespace Umbraco.Web.Search
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
private class DeferedActions
|
||||
{
|
||||
private readonly List<DeferedAction> _actions = new List<DeferedAction>();
|
||||
|
||||
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<UmbracoContentIndexer>()
|
||||
|
||||
//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<UmbracoContentIndexer>()
|
||||
|
||||
//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<BaseUmbracoIndexer>()
|
||||
//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<UmbracoContentIndexer>()
|
||||
|
||||
//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));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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
|
||||
{
|
||||
|
||||
@@ -52,6 +52,12 @@ namespace Umbraco.Web.Security.Identity
|
||||
/// </summary>
|
||||
public Action<BackOfficeIdentityUser, ExternalLoginInfo> OnAutoLinking { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// A callback executed during every time a user authenticates using an external login.
|
||||
/// returns a boolean indicating if sign in should continue or not.
|
||||
/// </summary>
|
||||
public Func<BackOfficeIdentityUser, ExternalLoginInfo, bool> OnExternalLogin { get; set; }
|
||||
|
||||
[EditorBrowsable(EditorBrowsableState.Never)]
|
||||
[Obsolete("Use the overload specifying user groups instead")]
|
||||
public string GetDefaultUserType(UmbracoContext umbracoContext, ExternalLoginInfo loginInfo)
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -14,18 +14,13 @@ namespace Umbraco.Web.Security
|
||||
internal static class WebAuthExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// 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
|
||||
/// </summary>
|
||||
/// <param name="request"></param>
|
||||
/// <param name="user"></param>
|
||||
/// <param name="principal"></param>
|
||||
/// <returns></returns>
|
||||
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<UserData>(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
|
||||
/// </summary>
|
||||
/// <param name="httpContext"></param>
|
||||
/// <param name="userData"></param>
|
||||
/// <param name="principal"></param>
|
||||
/// <returns></returns>
|
||||
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.
|
||||
|
||||
@@ -23,7 +23,7 @@ namespace Umbraco.Web.Security
|
||||
/// <summary>
|
||||
/// A utility class used for dealing with USER security in Umbraco
|
||||
/// </summary>
|
||||
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<UserData>(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<UserData>(user));
|
||||
return ticket;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Clears the current login for the currently logged in user
|
||||
/// </summary>
|
||||
|
||||
@@ -131,14 +131,17 @@
|
||||
<Compile Include="Editors\BackOfficeServerVariables.cs" />
|
||||
<Compile Include="Editors\CodeFileController.cs" />
|
||||
<Compile Include="Editors\DashboardHelper.cs" />
|
||||
<Compile Include="Editors\EditorModelEventArgs.cs" />
|
||||
<Compile Include="Editors\EditorValidatorCollection.cs" />
|
||||
<Compile Include="Editors\EditorValidatorCollectionBuilder.cs" />
|
||||
<Compile Include="Editors\EditorValidatorOfT.cs" />
|
||||
<Compile Include="Editors\FromJsonPathAttribute.cs" />
|
||||
<Compile Include="Editors\HelpController.cs" />
|
||||
<Compile Include="Editors\IsCurrentUserModelFilterAttribute.cs" />
|
||||
<Compile Include="Editors\ParameterSwapControllerActionSelector.cs" />
|
||||
<Compile Include="Editors\PasswordChanger.cs" />
|
||||
<Compile Include="Editors\TemplateController.cs" />
|
||||
<Compile Include="Editors\TourController.cs" />
|
||||
<Compile Include="Editors\UserEditorAuthorizationHelper.cs" />
|
||||
<Compile Include="Editors\UserGroupAuthorizationAttribute.cs" />
|
||||
<Compile Include="Editors\UserGroupEditorAuthorizationHelper.cs" />
|
||||
@@ -199,6 +202,10 @@
|
||||
<Compile Include="HealthCheck\StatusResultType.cs" />
|
||||
<Compile Include="HealthCheck\Checks\DataIntegrity\XmlDataIntegrityHealthCheck.cs" />
|
||||
<Compile Include="Media\ImageUrlProviderCollection.cs" />
|
||||
<Compile Include="Models\BackOfficeTour.cs" />
|
||||
<Compile Include="Models\BackOfficeTourFile.cs" />
|
||||
<Compile Include="Models\BackOfficeTourFilter.cs" />
|
||||
<Compile Include="Models\BackOfficeTourStep.cs" />
|
||||
<Compile Include="Models\ContentEditing\AssignedContentPermissions.cs" />
|
||||
<Compile Include="Models\ContentEditing\AssignedUserGroupPermissions.cs" />
|
||||
<Compile Include="Models\ContentEditing\CodeFileDisplay.cs" />
|
||||
@@ -251,6 +258,7 @@
|
||||
<Compile Include="Models\RelatedLinkType.cs" />
|
||||
<Compile Include="Models\SendCodeViewModel.cs" />
|
||||
<Compile Include="Models\Trees\ExportMember.cs" />
|
||||
<Compile Include="Models\UserTourStatus.cs" />
|
||||
<Compile Include="Mvc\FilteredControllerFactoryCollection.cs" />
|
||||
<Compile Include="Mvc\FilteredControllerFactoryCollectionBuilder.cs" />
|
||||
<Compile Include="Mvc\SurfaceControllerTypeCollection.cs" />
|
||||
@@ -386,6 +394,7 @@
|
||||
<Compile Include="Tour\TourFilterCollection.cs" />
|
||||
<Compile Include="Tour\TourFilterCollectionBuilder.cs" />
|
||||
<Compile Include="Trees\ContentBlueprintTreeController.cs" />
|
||||
<Compile Include="Trees\MacroTreeController.cs" />
|
||||
<Compile Include="Trees\RelationTypeTreeController.cs" />
|
||||
<Compile Include="Trees\MacrosTreeController.cs" />
|
||||
<Compile Include="Models\ContentEditing\MemberGroupDisplay.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;
|
||||
|
||||
@@ -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)
|
||||
//{
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
// }
|
||||
|
||||
// /// <summary>
|
||||
// /// Required method for Designer support - do not modify
|
||||
// /// the contents of this method with the code editor.
|
||||
// /// </summary>
|
||||
// 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)) + "</p><p><a href='#' onclick='" + ClientTools.Scripts.CloseModalWindow() + "'>" + Services.TextService.Localize("closeThisWindow") + "</a>";
|
||||
// diffPanel.Visible = false;
|
||||
// pl_buttons.Visible = false;
|
||||
//
|
||||
// ClientTools.ReloadLocationIfMatched(string.Format("/content/content/edit/{0}", d.Id));
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
<style type="text/css">
|
||||
.gridHeader{border-bottom:2px solid #D9D7D7;}
|
||||
.gridItem{border-color: #D9D7D7;}
|
||||
html, body {overflow: initial;}
|
||||
</style>
|
||||
|
||||
</asp:Content>
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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 {
|
||||
|
||||
|
||||
@@ -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<int>();
|
||||
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
|
||||
|
||||
Reference in New Issue
Block a user