Compare commits
52 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 0074f57970 | |||
| 9b4a6f2761 | |||
| f889b5206c | |||
| 507d7dd4f0 | |||
| 55c5b41693 | |||
| b58d8677b3 | |||
| f30d7fc68c | |||
| d504d8f54e | |||
| b519d68e1a | |||
| deb8d652c5 | |||
| 12dd01dd29 | |||
| c64e3a8908 | |||
| 74e8390bda | |||
| 4f9bbdf7f7 | |||
| 67fc8b6215 | |||
| 3840ac2fed | |||
| 7c64f1aa0d | |||
| 82c18955b0 | |||
| 0dc0e68174 | |||
| aaa9c8f194 | |||
| 308a61895e | |||
| 1901fbb6ed | |||
| 1de52482e3 | |||
| 49c2ae3673 | |||
| 5f22c81994 | |||
| f13427f5fb | |||
| 2785eaeda5 | |||
| a31cfb86f8 | |||
| e4802b592f | |||
| 548f9fd01f | |||
| 843796c9a4 | |||
| c060856782 | |||
| 857063ebd8 | |||
| 2ba5627e2c | |||
| 2669c68df0 | |||
| 912a6a52cc | |||
| b4450ca7f2 | |||
| 7004765acf | |||
| 77a91bc017 | |||
| 14b96da588 | |||
| 99324035e5 | |||
| 8b9a5000be | |||
| c82bddb7dd | |||
| f2fd45576b | |||
| d5a687cf3d | |||
| 26acecb720 | |||
| 7c42cd26c2 | |||
| 13758418ac | |||
| 77b04d74d4 | |||
| bf797bccc7 | |||
| 4336f5201d | |||
| ed019a90e2 |
@@ -44,37 +44,37 @@
|
||||
</dependencies>
|
||||
</metadata>
|
||||
<files>
|
||||
<file src="..\_BuildOutput\WebApp\bin\businesslogic.dll" target="lib\net462\businesslogic.dll" />
|
||||
<file src="..\_BuildOutput\WebApp\bin\businesslogic.xml" target="lib\net462\businesslogic.xml" />
|
||||
<file src="..\_BuildOutput\WebApp\bin\cms.dll" target="lib\net462\cms.dll" />
|
||||
<file src="..\_BuildOutput\WebApp\bin\cms.xml" target="lib\net462\cms.xml" />
|
||||
<file src="..\_BuildOutput\WebApp\bin\controls.dll" target="lib\net462\controls.dll" />
|
||||
<file src="..\_BuildOutput\WebApp\bin\controls.xml" target="lib\net462\controls.xml" />
|
||||
<file src="..\_BuildOutput\WebApp\bin\interfaces.dll" target="lib\net462\interfaces.dll" />
|
||||
<file src="..\_BuildOutput\WebApp\bin\interfaces.xml" target="lib\net462\interfaces.xml" />
|
||||
<file src="..\_BuildOutput\WebApp\bin\log4net.dll" target="lib\net462\log4net.dll" />
|
||||
<file src="..\_BuildOutput\WebApp\bin\Microsoft.ApplicationBlocks.Data.dll" target="lib\net462\Microsoft.ApplicationBlocks.Data.dll" />
|
||||
<file src="..\_BuildOutput\WebApp\bin\SQLCE4Umbraco.dll" target="lib\net462\SQLCE4Umbraco.dll" />
|
||||
<file src="..\_BuildOutput\WebApp\bin\SQLCE4Umbraco.xml" target="lib\net462\SQLCE4Umbraco.xml" />
|
||||
<file src="..\_BuildOutput\WebApp\bin\System.Data.SqlServerCe.dll" target="lib\net462\System.Data.SqlServerCe.dll" />
|
||||
<file src="..\_BuildOutput\WebApp\bin\System.Data.SqlServerCe.Entity.dll" target="lib\net462\System.Data.SqlServerCe.Entity.dll" />
|
||||
<file src="..\_BuildOutput\WebApp\bin\TidyNet.dll" target="lib\net462\TidyNet.dll" />
|
||||
<file src="..\_BuildOutput\WebApp\bin\Umbraco.Core.dll" target="lib\net462\Umbraco.Core.dll" />
|
||||
<file src="..\_BuildOutput\WebApp\bin\Umbraco.Core.xml" target="lib\net462\Umbraco.Core.xml" />
|
||||
<file src="..\_BuildOutput\WebApp\bin\umbraco.DataLayer.dll" target="lib\net462\umbraco.DataLayer.dll" />
|
||||
<file src="..\_BuildOutput\WebApp\bin\umbraco.DataLayer.xml" target="lib\net462\umbraco.DataLayer.xml" />
|
||||
<file src="..\_BuildOutput\WebApp\bin\umbraco.dll" target="lib\net462\umbraco.dll" />
|
||||
<file src="..\_BuildOutput\WebApp\bin\umbraco.xml" target="lib\net462\umbraco.xml" />
|
||||
<file src="..\_BuildOutput\WebApp\bin\umbraco.editorControls.dll" target="lib\net462\umbraco.editorControls.dll" />
|
||||
<file src="..\_BuildOutput\WebApp\bin\umbraco.editorControls.xml" target="lib\net462\umbraco.editorControls.xml" />
|
||||
<file src="..\_BuildOutput\WebApp\bin\umbraco.MacroEngines.dll" target="lib\net462\umbraco.MacroEngines.dll" />
|
||||
<file src="..\_BuildOutput\WebApp\bin\umbraco.MacroEngines.xml" target="lib\net462\umbraco.MacroEngines.xml" />
|
||||
<file src="..\_BuildOutput\WebApp\bin\umbraco.providers.dll" target="lib\net462\umbraco.providers.dll" />
|
||||
<file src="..\_BuildOutput\WebApp\bin\umbraco.providers.xml" target="lib\net462\umbraco.providers.xml" />
|
||||
<file src="..\_BuildOutput\WebApp\bin\Umbraco.Web.UI.dll" target="lib\net462\Umbraco.Web.UI.dll" />
|
||||
<file src="..\_BuildOutput\WebApp\bin\Umbraco.Web.UI.xml" target="lib\net462\Umbraco.Web.UI.xml" />
|
||||
<file src="..\_BuildOutput\WebApp\bin\UmbracoExamine.dll" target="lib\net462\UmbracoExamine.dll" />
|
||||
<file src="..\_BuildOutput\WebApp\bin\UmbracoExamine.xml" target="lib\net462\UmbracoExamine.xml" />
|
||||
<file src="..\_BuildOutput\WebApp\bin\businesslogic.dll" target="lib\net45\businesslogic.dll" />
|
||||
<file src="..\_BuildOutput\WebApp\bin\businesslogic.xml" target="lib\net45\businesslogic.xml" />
|
||||
<file src="..\_BuildOutput\WebApp\bin\cms.dll" target="lib\net45\cms.dll" />
|
||||
<file src="..\_BuildOutput\WebApp\bin\cms.xml" target="lib\net45\cms.xml" />
|
||||
<file src="..\_BuildOutput\WebApp\bin\controls.dll" target="lib\net45\controls.dll" />
|
||||
<file src="..\_BuildOutput\WebApp\bin\controls.xml" target="lib\net45\controls.xml" />
|
||||
<file src="..\_BuildOutput\WebApp\bin\interfaces.dll" target="lib\net45\interfaces.dll" />
|
||||
<file src="..\_BuildOutput\WebApp\bin\interfaces.xml" target="lib\net45\interfaces.xml" />
|
||||
<file src="..\_BuildOutput\WebApp\bin\log4net.dll" target="lib\net45\log4net.dll" />
|
||||
<file src="..\_BuildOutput\WebApp\bin\Microsoft.ApplicationBlocks.Data.dll" target="lib\net45\Microsoft.ApplicationBlocks.Data.dll" />
|
||||
<file src="..\_BuildOutput\WebApp\bin\SQLCE4Umbraco.dll" target="lib\net45\SQLCE4Umbraco.dll" />
|
||||
<file src="..\_BuildOutput\WebApp\bin\SQLCE4Umbraco.xml" target="lib\net45\SQLCE4Umbraco.xml" />
|
||||
<file src="..\_BuildOutput\WebApp\bin\System.Data.SqlServerCe.dll" target="lib\net45\System.Data.SqlServerCe.dll" />
|
||||
<file src="..\_BuildOutput\WebApp\bin\System.Data.SqlServerCe.Entity.dll" target="lib\net45\System.Data.SqlServerCe.Entity.dll" />
|
||||
<file src="..\_BuildOutput\WebApp\bin\TidyNet.dll" target="lib\net45\TidyNet.dll" />
|
||||
<file src="..\_BuildOutput\WebApp\bin\Umbraco.Core.dll" target="lib\net45\Umbraco.Core.dll" />
|
||||
<file src="..\_BuildOutput\WebApp\bin\Umbraco.Core.xml" target="lib\net45\Umbraco.Core.xml" />
|
||||
<file src="..\_BuildOutput\WebApp\bin\umbraco.DataLayer.dll" target="lib\net45\umbraco.DataLayer.dll" />
|
||||
<file src="..\_BuildOutput\WebApp\bin\umbraco.DataLayer.xml" target="lib\net45\umbraco.DataLayer.xml" />
|
||||
<file src="..\_BuildOutput\WebApp\bin\umbraco.dll" target="lib\net45\umbraco.dll" />
|
||||
<file src="..\_BuildOutput\WebApp\bin\umbraco.xml" target="lib\net45\umbraco.xml" />
|
||||
<file src="..\_BuildOutput\WebApp\bin\umbraco.editorControls.dll" target="lib\net45\umbraco.editorControls.dll" />
|
||||
<file src="..\_BuildOutput\WebApp\bin\umbraco.editorControls.xml" target="lib\net45\umbraco.editorControls.xml" />
|
||||
<file src="..\_BuildOutput\WebApp\bin\umbraco.MacroEngines.dll" target="lib\net45\umbraco.MacroEngines.dll" />
|
||||
<file src="..\_BuildOutput\WebApp\bin\umbraco.MacroEngines.xml" target="lib\net45\umbraco.MacroEngines.xml" />
|
||||
<file src="..\_BuildOutput\WebApp\bin\umbraco.providers.dll" target="lib\net45\umbraco.providers.dll" />
|
||||
<file src="..\_BuildOutput\WebApp\bin\umbraco.providers.xml" target="lib\net45\umbraco.providers.xml" />
|
||||
<file src="..\_BuildOutput\WebApp\bin\Umbraco.Web.UI.dll" target="lib\net45\Umbraco.Web.UI.dll" />
|
||||
<file src="..\_BuildOutput\WebApp\bin\Umbraco.Web.UI.xml" target="lib\net45\Umbraco.Web.UI.xml" />
|
||||
<file src="..\_BuildOutput\WebApp\bin\UmbracoExamine.dll" target="lib\net45\UmbracoExamine.dll" />
|
||||
<file src="..\_BuildOutput\WebApp\bin\UmbracoExamine.xml" target="lib\net45\UmbracoExamine.xml" />
|
||||
<file src="tools\install.core.ps1" target="tools\install.ps1" />
|
||||
|
||||
<!-- Added to be able to produce a symbols package -->
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
# Usage: on line 2 put the release version, on line 3 put the version comment (example: beta)
|
||||
7.6.0
|
||||
alpha061
|
||||
alpha063
|
||||
|
||||
+1
-1
@@ -12,4 +12,4 @@ using System.Resources;
|
||||
[assembly: AssemblyVersion("1.0.*")]
|
||||
|
||||
[assembly: AssemblyFileVersion("7.6.0")]
|
||||
[assembly: AssemblyInformationalVersion("7.6.0-alpha061")]
|
||||
[assembly: AssemblyInformationalVersion("7.6.0-alpha063")]
|
||||
@@ -24,7 +24,7 @@ namespace Umbraco.Core.Configuration
|
||||
/// Gets the version comment (like beta or RC).
|
||||
/// </summary>
|
||||
/// <value>The version comment.</value>
|
||||
public static string CurrentComment { get { return "alpha061"; } }
|
||||
public static string CurrentComment { get { return "alpha063"; } }
|
||||
|
||||
// Get the version of the umbraco.dll by looking at a class in that dll
|
||||
// Had to do it like this due to medium trust issues, see: http://haacked.com/archive/2010/11/04/assembly-location-and-medium-trust.aspx
|
||||
|
||||
@@ -40,8 +40,8 @@ namespace Umbraco.Core
|
||||
|
||||
// forms
|
||||
public const string FormsForm = "forms-form";
|
||||
public const string FormsWorkflow = "forms-workflow";
|
||||
public const string FormsRecord = "forms-record";
|
||||
public const string FormsPreValue = "forms-prevalue";
|
||||
public const string FormsDataSource = "forms-datasource";
|
||||
|
||||
// string entity types
|
||||
|
||||
@@ -89,10 +89,10 @@ namespace Umbraco.Core
|
||||
return RelationType;
|
||||
case UmbracoObjectTypes.FormsForm:
|
||||
return FormsForm;
|
||||
case UmbracoObjectTypes.FormsWorkflow:
|
||||
return FormsWorkflow;
|
||||
case UmbracoObjectTypes.FormsRecord:
|
||||
return FormsRecord;
|
||||
case UmbracoObjectTypes.FormsPreValue:
|
||||
return FormsPreValue;
|
||||
case UmbracoObjectTypes.FormsDataSource:
|
||||
return FormsDataSource;
|
||||
}
|
||||
throw new NotSupportedException(string.Format("UmbracoObjectType \"{0}\" does not have a matching EntityType.", umbracoObjectType));
|
||||
}
|
||||
@@ -131,10 +131,10 @@ namespace Umbraco.Core
|
||||
return UmbracoObjectTypes.RelationType;
|
||||
case FormsForm:
|
||||
return UmbracoObjectTypes.FormsForm;
|
||||
case FormsWorkflow:
|
||||
return UmbracoObjectTypes.FormsWorkflow;
|
||||
case FormsRecord:
|
||||
return UmbracoObjectTypes.FormsRecord;
|
||||
case FormsPreValue:
|
||||
return UmbracoObjectTypes.FormsPreValue;
|
||||
case FormsDataSource:
|
||||
return UmbracoObjectTypes.FormsDataSource;
|
||||
}
|
||||
throw new NotSupportedException(
|
||||
string.Format("EntityType \"{0}\" does not have a matching UmbracoObjectType.", entityType));
|
||||
|
||||
@@ -174,24 +174,24 @@ namespace Umbraco.Core
|
||||
public static readonly Guid FormsFormGuid = new Guid(FormsForm);
|
||||
|
||||
/// <summary>
|
||||
/// Guid for a Forms Workflow.
|
||||
/// Guid for a Forms PreValue Source.
|
||||
/// </summary>
|
||||
public const string FormsWorkflow = "42D7BF9B-A362-4FEE-B45A-674D5C064B70";
|
||||
public const string FormsPreValue = "42D7BF9B-A362-4FEE-B45A-674D5C064B70";
|
||||
|
||||
/// <summary>
|
||||
/// Guid for a Forms Workflow.
|
||||
/// Guid for a Forms PreValue Source.
|
||||
/// </summary>
|
||||
public static readonly Guid FormsWorkflowGuid = new Guid(FormsWorkflow);
|
||||
public static readonly Guid FormsPreValueGuid = new Guid(FormsPreValue);
|
||||
|
||||
/// <summary>
|
||||
/// Guid for a Forms Record.
|
||||
/// Guid for a Forms DataSource.
|
||||
/// </summary>
|
||||
public const string FormsRecord = "CFED6CE4-9359-443E-9977-9956FEB1D867";
|
||||
public const string FormsDataSource = "CFED6CE4-9359-443E-9977-9956FEB1D867";
|
||||
|
||||
/// <summary>
|
||||
/// Guid for a Forms Record.
|
||||
/// Guid for a Forms DataSource.
|
||||
/// </summary>
|
||||
public static readonly Guid FormsRecordGuid = new Guid(FormsRecord);
|
||||
public static readonly Guid FormsDataSourceGuid = new Guid(FormsDataSource);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3,7 +3,7 @@ using System.Collections.Generic;
|
||||
|
||||
namespace Umbraco.Core.Events
|
||||
{
|
||||
public class DeleteEventArgs<TEntity> : CancellableObjectEventArgs<IEnumerable<TEntity>>, IEquatable<DeleteEventArgs<TEntity>>
|
||||
public class DeleteEventArgs<TEntity> : CancellableObjectEventArgs<IEnumerable<TEntity>>, IEquatable<DeleteEventArgs<TEntity>>, IDeletingMediaFilesEventArgs
|
||||
{
|
||||
/// <summary>
|
||||
/// Constructor accepting multiple entities that are used in the delete operation
|
||||
|
||||
@@ -1,29 +0,0 @@
|
||||
namespace Umbraco.Core.Events
|
||||
{
|
||||
public enum EventsDispatchMode
|
||||
{
|
||||
// in 7.5 we'd do:
|
||||
//
|
||||
// using (var uow = ...)
|
||||
// { ... }
|
||||
// Done.RaiseEvent(...);
|
||||
//
|
||||
// and so the event would trigger only *after* the transaction has completed,
|
||||
// so actually PassThrough is more aggressive than what we had in 7.5 and should
|
||||
// not be used. now in 7.6 we do:
|
||||
//
|
||||
// using (var uow = ...)
|
||||
// {
|
||||
// ...
|
||||
// uow.Events.Dispatch(Done, ...);
|
||||
// }
|
||||
//
|
||||
// so the event can be collected, so the default "kinda compatible" more has to be
|
||||
// the Scope mode, and Passive is for Deploy only
|
||||
|
||||
Unspecified = 0,
|
||||
PassThrough, // both Doing and Done trigger immediately
|
||||
Scope, // Doing triggers immediately, Done queued and triggered when & if the scope completes
|
||||
Passive // Doing never triggers, Done queued and needs to be handled by custom code
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Umbraco.Core.Events
|
||||
{
|
||||
internal interface IDeletingMediaFilesEventArgs
|
||||
{
|
||||
List<string> MediaFilesToDelete { get; }
|
||||
}
|
||||
}
|
||||
@@ -5,7 +5,7 @@ using Umbraco.Core.Models;
|
||||
|
||||
namespace Umbraco.Core.Events
|
||||
{
|
||||
public class RecycleBinEventArgs : CancellableEventArgs, IEquatable<RecycleBinEventArgs>
|
||||
public class RecycleBinEventArgs : CancellableEventArgs, IEquatable<RecycleBinEventArgs>, IDeletingMediaFilesEventArgs
|
||||
{
|
||||
public RecycleBinEventArgs(Guid nodeObjectType, Dictionary<int, IEnumerable<Property>> allPropertyData, bool emptiedSuccessfully)
|
||||
: base(false)
|
||||
@@ -97,6 +97,8 @@ namespace Umbraco.Core.Events
|
||||
/// </remarks>
|
||||
public List<string> Files { get; private set; }
|
||||
|
||||
public List<string> MediaFilesToDelete { get { return Files; } }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the list of all property data associated with a content id
|
||||
/// </summary>
|
||||
|
||||
@@ -1,126 +1,48 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Linq;
|
||||
using Umbraco.Core.IO;
|
||||
|
||||
namespace Umbraco.Core.Events
|
||||
{
|
||||
|
||||
{
|
||||
/// <summary>
|
||||
/// This event manager is created for each scope and is aware of if it is nested in an outer scope
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The outer scope is the only scope that can raise events, the inner scope's will defer to the outer scope
|
||||
/// </remarks>
|
||||
internal class ScopeEventDispatcher : IEventDispatcher
|
||||
internal class ScopeEventDispatcher : ScopeEventDispatcherBase
|
||||
{
|
||||
private readonly EventsDispatchMode _mode;
|
||||
private List<IEventDefinition> _events;
|
||||
public ScopeEventDispatcher()
|
||||
: base(true)
|
||||
{ }
|
||||
|
||||
public ScopeEventDispatcher(EventsDispatchMode mode)
|
||||
{
|
||||
_mode = mode;
|
||||
}
|
||||
|
||||
private List<IEventDefinition> Events { get { return _events ?? (_events = new List<IEventDefinition>()); } }
|
||||
|
||||
private bool PassThroughCancelable { get { return _mode == EventsDispatchMode.PassThrough || _mode == EventsDispatchMode.Scope; } }
|
||||
|
||||
private bool PassThrough { get { return _mode == EventsDispatchMode.PassThrough; } }
|
||||
|
||||
private bool RaiseEvents { get { return _mode == EventsDispatchMode.Scope; } }
|
||||
|
||||
public bool DispatchCancelable(EventHandler eventHandler, object sender, CancellableEventArgs args, string eventName = null)
|
||||
{
|
||||
if (eventHandler == null) return args.Cancel;
|
||||
if (PassThroughCancelable == false) return args.Cancel;
|
||||
eventHandler(sender, args);
|
||||
return args.Cancel;
|
||||
}
|
||||
|
||||
public bool DispatchCancelable<TArgs>(EventHandler<TArgs> eventHandler, object sender, TArgs args, string eventName = null)
|
||||
where TArgs : CancellableEventArgs
|
||||
{
|
||||
if (eventHandler == null) return args.Cancel;
|
||||
if (PassThroughCancelable == false) return args.Cancel;
|
||||
eventHandler(sender, args);
|
||||
return args.Cancel;
|
||||
}
|
||||
|
||||
public bool DispatchCancelable<TSender, TArgs>(TypedEventHandler<TSender, TArgs> eventHandler, TSender sender, TArgs args, string eventName = null)
|
||||
where TArgs : CancellableEventArgs
|
||||
{
|
||||
if (eventHandler == null) return args.Cancel;
|
||||
if (PassThroughCancelable == false) return args.Cancel;
|
||||
eventHandler(sender, args);
|
||||
return args.Cancel;
|
||||
}
|
||||
|
||||
public void Dispatch(EventHandler eventHandler, object sender, EventArgs args, string eventName = null)
|
||||
{
|
||||
if (eventHandler == null) return;
|
||||
if (PassThrough)
|
||||
eventHandler(sender, args);
|
||||
else
|
||||
Events.Add(new EventDefinition(eventHandler, sender, args, eventName));
|
||||
}
|
||||
|
||||
public void Dispatch<TArgs>(EventHandler<TArgs> eventHandler, object sender, TArgs args, string eventName = null)
|
||||
{
|
||||
if (eventHandler == null) return;
|
||||
if (PassThrough)
|
||||
eventHandler(sender, args);
|
||||
else
|
||||
Events.Add(new EventDefinition<TArgs>(eventHandler, sender, args, eventName));
|
||||
}
|
||||
|
||||
public void Dispatch<TSender, TArgs>(TypedEventHandler<TSender, TArgs> eventHandler, TSender sender, TArgs args, string eventName = null)
|
||||
{
|
||||
if (eventHandler == null) return;
|
||||
if (PassThrough)
|
||||
eventHandler(sender, args);
|
||||
else
|
||||
Events.Add(new EventDefinition<TSender, TArgs>(eventHandler, sender, args, eventName));
|
||||
}
|
||||
|
||||
public IEnumerable<IEventDefinition> GetEvents(EventDefinitionFilter filter)
|
||||
{
|
||||
if (_events == null)
|
||||
return Enumerable.Empty<IEventDefinition>();
|
||||
|
||||
switch (filter)
|
||||
{
|
||||
case EventDefinitionFilter.All:
|
||||
return _events;
|
||||
case EventDefinitionFilter.FirstIn:
|
||||
var l1 = new OrderedHashSet<IEventDefinition>();
|
||||
foreach (var e in _events)
|
||||
{
|
||||
l1.Add(e);
|
||||
}
|
||||
return l1;
|
||||
case EventDefinitionFilter.LastIn:
|
||||
var l2 = new OrderedHashSet<IEventDefinition>(keepOldest:false);
|
||||
foreach (var e in _events)
|
||||
{
|
||||
l2.Add(e);
|
||||
}
|
||||
return l2;
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException("filter", filter, null);
|
||||
}
|
||||
}
|
||||
|
||||
public void ScopeExit(bool completed)
|
||||
protected override void ScopeExitCompleted()
|
||||
{
|
||||
// fixme - we'd need to de-duplicate events somehow, etc - and the deduplication should be last in wins
|
||||
|
||||
if (_events == null) return;
|
||||
foreach (var e in GetEvents(EventDefinitionFilter.All))
|
||||
{
|
||||
e.RaiseEvent();
|
||||
|
||||
if (RaiseEvents && completed)
|
||||
foreach (var e in _events)
|
||||
e.RaiseEvent();
|
||||
_events.Clear();
|
||||
// fixme - not sure I like doing it here - but then where? how?
|
||||
var delete = e.Args as IDeletingMediaFilesEventArgs;
|
||||
if (delete != null && delete.MediaFilesToDelete.Count > 0)
|
||||
MediaFileSystem.DeleteMediaFiles(delete.MediaFilesToDelete);
|
||||
}
|
||||
}
|
||||
|
||||
private MediaFileSystem _mediaFileSystem;
|
||||
|
||||
private MediaFileSystem MediaFileSystem
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_mediaFileSystem != null) return _mediaFileSystem;
|
||||
|
||||
// fixme - insane! reading config goes cross AppDomain and serializes context?
|
||||
using (new SafeCallContext())
|
||||
{
|
||||
return _mediaFileSystem = FileSystemProviderManager.Current.MediaFileSystem;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,101 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace Umbraco.Core.Events
|
||||
{
|
||||
public abstract class ScopeEventDispatcherBase : IEventDispatcher
|
||||
{
|
||||
private List<IEventDefinition> _events;
|
||||
private readonly bool _raiseCancelable;
|
||||
|
||||
protected ScopeEventDispatcherBase(bool raiseCancelable)
|
||||
{
|
||||
_raiseCancelable = raiseCancelable;
|
||||
}
|
||||
|
||||
private List<IEventDefinition> Events { get { return _events ?? (_events = new List<IEventDefinition>()); } }
|
||||
|
||||
public bool DispatchCancelable(EventHandler eventHandler, object sender, CancellableEventArgs args, string eventName = null)
|
||||
{
|
||||
if (eventHandler == null) return args.Cancel;
|
||||
if (_raiseCancelable == false) return args.Cancel;
|
||||
eventHandler(sender, args);
|
||||
return args.Cancel;
|
||||
}
|
||||
|
||||
public bool DispatchCancelable<TArgs>(EventHandler<TArgs> eventHandler, object sender, TArgs args, string eventName = null)
|
||||
where TArgs : CancellableEventArgs
|
||||
{
|
||||
if (eventHandler == null) return args.Cancel;
|
||||
if (_raiseCancelable == false) return args.Cancel;
|
||||
eventHandler(sender, args);
|
||||
return args.Cancel;
|
||||
}
|
||||
|
||||
public bool DispatchCancelable<TSender, TArgs>(TypedEventHandler<TSender, TArgs> eventHandler, TSender sender, TArgs args, string eventName = null)
|
||||
where TArgs : CancellableEventArgs
|
||||
{
|
||||
if (eventHandler == null) return args.Cancel;
|
||||
if (_raiseCancelable == false) return args.Cancel;
|
||||
eventHandler(sender, args);
|
||||
return args.Cancel;
|
||||
}
|
||||
|
||||
public void Dispatch(EventHandler eventHandler, object sender, EventArgs args, string eventName = null)
|
||||
{
|
||||
if (eventHandler == null) return;
|
||||
Events.Add(new EventDefinition(eventHandler, sender, args, eventName));
|
||||
}
|
||||
|
||||
public void Dispatch<TArgs>(EventHandler<TArgs> eventHandler, object sender, TArgs args, string eventName = null)
|
||||
{
|
||||
if (eventHandler == null) return;
|
||||
Events.Add(new EventDefinition<TArgs>(eventHandler, sender, args, eventName));
|
||||
}
|
||||
|
||||
public void Dispatch<TSender, TArgs>(TypedEventHandler<TSender, TArgs> eventHandler, TSender sender, TArgs args, string eventName = null)
|
||||
{
|
||||
if (eventHandler == null) return;
|
||||
Events.Add(new EventDefinition<TSender, TArgs>(eventHandler, sender, args, eventName));
|
||||
}
|
||||
|
||||
public IEnumerable<IEventDefinition> GetEvents(EventDefinitionFilter filter)
|
||||
{
|
||||
if (_events == null)
|
||||
return Enumerable.Empty<IEventDefinition>();
|
||||
|
||||
switch (filter)
|
||||
{
|
||||
case EventDefinitionFilter.All:
|
||||
return _events;
|
||||
case EventDefinitionFilter.FirstIn:
|
||||
var l1 = new OrderedHashSet<IEventDefinition>();
|
||||
foreach (var e in _events)
|
||||
{
|
||||
l1.Add(e);
|
||||
}
|
||||
return l1;
|
||||
case EventDefinitionFilter.LastIn:
|
||||
var l2 = new OrderedHashSet<IEventDefinition>(keepOldest: false);
|
||||
foreach (var e in _events)
|
||||
{
|
||||
l2.Add(e);
|
||||
}
|
||||
return l2;
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException("filter", filter, null);
|
||||
}
|
||||
}
|
||||
|
||||
public void ScopeExit(bool completed)
|
||||
{
|
||||
if (_events == null) return;
|
||||
if (completed)
|
||||
ScopeExitCompleted();
|
||||
_events.Clear();
|
||||
}
|
||||
|
||||
protected abstract void ScopeExitCompleted();
|
||||
}
|
||||
}
|
||||
@@ -109,5 +109,24 @@ namespace Umbraco.Core.IO
|
||||
var wrapped2 = Wrapped as IFileSystem2;
|
||||
return wrapped2 == null ? Wrapped.GetSize(path) : wrapped2.GetSize(path);
|
||||
}
|
||||
|
||||
// explicitely implementing - not breaking
|
||||
bool IFileSystem2.CanAddPhysical
|
||||
{
|
||||
get
|
||||
{
|
||||
var wrapped2 = Wrapped as IFileSystem2;
|
||||
return wrapped2 != null && wrapped2.CanAddPhysical;
|
||||
}
|
||||
}
|
||||
|
||||
// explicitely implementing - not breaking
|
||||
void IFileSystem2.AddFile(string path, string physicalPath, bool overrideIfExists, bool copy)
|
||||
{
|
||||
var wrapped2 = Wrapped as IFileSystem2;
|
||||
if (wrapped2 == null)
|
||||
throw new NotSupportedException();
|
||||
wrapped2.AddFile(path, physicalPath, overrideIfExists, copy);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -44,6 +44,10 @@ namespace Umbraco.Core.IO
|
||||
{
|
||||
long GetSize(string path);
|
||||
|
||||
bool CanAddPhysical { get; }
|
||||
|
||||
void AddFile(string path, string physicalPath, bool overrideIfExists = true, bool copy = false);
|
||||
|
||||
// TODO: implement these
|
||||
//
|
||||
//void CreateDirectory(string path);
|
||||
|
||||
@@ -7,6 +7,7 @@ using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Umbraco.Core.Configuration;
|
||||
using Umbraco.Core.Configuration.UmbracoSettings;
|
||||
using Umbraco.Core.Logging;
|
||||
@@ -24,6 +25,7 @@ namespace Umbraco.Core.IO
|
||||
{
|
||||
private readonly IContentSection _contentConfig;
|
||||
private readonly UploadAutoFillProperties _uploadAutoFillProperties;
|
||||
private readonly ILogger _logger;
|
||||
|
||||
private readonly object _folderCounterLock = new object();
|
||||
private long _folderCounter;
|
||||
@@ -42,6 +44,7 @@ namespace Umbraco.Core.IO
|
||||
public MediaFileSystem(IFileSystem wrapped, IContentSection contentConfig, ILogger logger)
|
||||
: base(wrapped)
|
||||
{
|
||||
_logger = logger;
|
||||
_contentConfig = contentConfig;
|
||||
_uploadAutoFillProperties = new UploadAutoFillProperties(this, logger, contentConfig);
|
||||
}
|
||||
@@ -99,7 +102,7 @@ namespace Umbraco.Core.IO
|
||||
}
|
||||
else
|
||||
{
|
||||
// new scheme: path is "<cuid>-<puid>/<filename>" OR "<cuid>-<puid>-<filename>"
|
||||
// new scheme: path is "<xuid>/<filename>" where xuid is a combination of cuid and puid
|
||||
// default media filesystem maps to "~/media/<filepath>"
|
||||
// assumes that cuid and puid keys can be trusted - and that a single property type
|
||||
// for a single content cannot store two different files with the same name
|
||||
@@ -435,6 +438,64 @@ namespace Umbraco.Core.IO
|
||||
}
|
||||
}
|
||||
|
||||
public void DeleteMediaFiles(IEnumerable<string> files)
|
||||
{
|
||||
files = files.Distinct();
|
||||
|
||||
Parallel.ForEach(files, file =>
|
||||
{
|
||||
try
|
||||
{
|
||||
if (file.IsNullOrWhiteSpace()) return;
|
||||
|
||||
if (FileExists(file) == false) return;
|
||||
DeleteFile(file, true);
|
||||
|
||||
if (UseTheNewMediaPathScheme == false)
|
||||
{
|
||||
// old scheme: filepath is "<int>/<filename>" OR "<int>-<filename>"
|
||||
// remove the directory if any
|
||||
var dir = Path.GetDirectoryName(file);
|
||||
if (string.IsNullOrWhiteSpace(dir) == false)
|
||||
DeleteDirectory(dir, true);
|
||||
}
|
||||
else
|
||||
{
|
||||
// new scheme: path is "<xuid>/<filename>" where xuid is a combination of cuid and puid
|
||||
// remove the directory
|
||||
var dir = Path.GetDirectoryName(file);
|
||||
DeleteDirectory(dir, true);
|
||||
}
|
||||
|
||||
// I don't even understand...
|
||||
/*
|
||||
|
||||
var relativeFilePath = GetRelativePath(file); // fixme - should be relative already
|
||||
if (FileExists(relativeFilePath) == false) return;
|
||||
|
||||
var parentDirectory = Path.GetDirectoryName(relativeFilePath);
|
||||
|
||||
// don't want to delete the media folder if not using directories.
|
||||
if (_contentSection.UploadAllowDirectories && parentDirectory != GetRelativePath("/"))
|
||||
{
|
||||
//issue U4-771: if there is a parent directory the recursive parameter should be true
|
||||
DeleteDirectory(parentDirectory, string.IsNullOrEmpty(parentDirectory) == false);
|
||||
}
|
||||
else
|
||||
{
|
||||
DeleteFile(file, true);
|
||||
}
|
||||
|
||||
*/
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
_logger.Error<MediaFileSystem>("Failed to delete attached file \"" + file + "\".", e);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
#endregion
|
||||
|
||||
#region GenerateThumbnails
|
||||
|
||||
@@ -364,6 +364,29 @@ namespace Umbraco.Core.IO
|
||||
return file.Exists ? file.Length : -1;
|
||||
}
|
||||
|
||||
public bool CanAddPhysical { get { return true; } }
|
||||
|
||||
public void AddFile(string path, string physicalPath, bool overrideIfExists = true, bool copy = false)
|
||||
{
|
||||
var fullPath = GetFullPath(path);
|
||||
|
||||
if (File.Exists(fullPath))
|
||||
{
|
||||
if (overrideIfExists == false)
|
||||
throw new InvalidOperationException(string.Format("A file at path '{0}' already exists", path));
|
||||
File.Delete(fullPath);
|
||||
}
|
||||
|
||||
var directory = Path.GetDirectoryName(fullPath);
|
||||
if (directory == null) throw new InvalidOperationException("Could not get directory.");
|
||||
Directory.CreateDirectory(directory); // ensure it exists
|
||||
|
||||
if (copy)
|
||||
File.Copy(physicalPath, fullPath);
|
||||
else
|
||||
File.Move(physicalPath, fullPath);
|
||||
}
|
||||
|
||||
#region Helper Methods
|
||||
|
||||
protected virtual void EnsureDirectory(string path)
|
||||
|
||||
@@ -33,8 +33,16 @@ namespace Umbraco.Core.IO
|
||||
{
|
||||
try
|
||||
{
|
||||
using (var stream = _sfs.OpenFile(kvp.Key))
|
||||
_fs.AddFile(kvp.Key, stream, true);
|
||||
var fs2 = _fs as IFileSystem2;
|
||||
if (fs2 != null && fs2.CanAddPhysical)
|
||||
{
|
||||
fs2.AddFile(kvp.Key, _sfs.GetFullPath(kvp.Key)); // overwrite, move
|
||||
}
|
||||
else
|
||||
{
|
||||
using (var stream = _sfs.OpenFile(kvp.Key))
|
||||
_fs.AddFile(kvp.Key, stream, true);
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
@@ -282,5 +290,36 @@ namespace Umbraco.Core.IO
|
||||
if (sf.IsDelete || sf.IsDir) throw new InvalidOperationException("Invalid path.");
|
||||
return _sfs.GetSize(path);
|
||||
}
|
||||
|
||||
public bool CanAddPhysical { get { return true; } }
|
||||
|
||||
public void AddFile(string path, string physicalPath, bool overrideIfExists = true, bool copy = false)
|
||||
{
|
||||
ShadowNode sf;
|
||||
var normPath = NormPath(path);
|
||||
if (Nodes.TryGetValue(normPath, out sf) && sf.IsExist && (sf.IsDir || overrideIfExists == false))
|
||||
throw new InvalidOperationException(string.Format("A file at path '{0}' already exists", path));
|
||||
|
||||
var parts = normPath.Split('/');
|
||||
for (var i = 0; i < parts.Length - 1; i++)
|
||||
{
|
||||
var dirPath = string.Join("/", parts.Take(i + 1));
|
||||
ShadowNode sd;
|
||||
if (Nodes.TryGetValue(dirPath, out sd))
|
||||
{
|
||||
if (sd.IsFile) throw new InvalidOperationException("Invalid path.");
|
||||
if (sd.IsDelete) Nodes[dirPath] = new ShadowNode(false, true);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (_fs.DirectoryExists(dirPath)) continue;
|
||||
if (_fs.FileExists(dirPath)) throw new InvalidOperationException("Invalid path.");
|
||||
Nodes[dirPath] = new ShadowNode(false, true);
|
||||
}
|
||||
}
|
||||
|
||||
_sfs.AddFile(path, physicalPath, overrideIfExists, copy);
|
||||
Nodes[normPath] = new ShadowNode(false, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -164,5 +164,22 @@ namespace Umbraco.Core.IO
|
||||
var filesystem2 = filesystem as IFileSystem2;
|
||||
return filesystem2 == null ? filesystem.GetSize(path) : filesystem2.GetSize(path);
|
||||
}
|
||||
|
||||
public bool CanAddPhysical
|
||||
{
|
||||
get
|
||||
{
|
||||
var fileSystem2 = FileSystem as IFileSystem2;
|
||||
return fileSystem2 != null && fileSystem2.CanAddPhysical;
|
||||
}
|
||||
}
|
||||
|
||||
public void AddFile(string path, string physicalPath, bool overrideIfExists = true, bool copy = false)
|
||||
{
|
||||
var fileSystem2 = FileSystem as IFileSystem2;
|
||||
if (fileSystem2 == null)
|
||||
throw new NotSupportedException();
|
||||
fileSystem2.AddFile(path, physicalPath, overrideIfExists, copy);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -153,17 +153,17 @@ namespace Umbraco.Core.Models
|
||||
FormsForm,
|
||||
|
||||
/// <summary>
|
||||
/// Forms Workflow
|
||||
/// Forms PreValue
|
||||
/// </summary>
|
||||
[UmbracoObjectType(Constants.ObjectTypes.FormsWorkflow)]
|
||||
[FriendlyName("Workflow")]
|
||||
FormsWorkflow,
|
||||
[UmbracoObjectType(Constants.ObjectTypes.FormsPreValue)]
|
||||
[FriendlyName("PreValue")]
|
||||
FormsPreValue,
|
||||
|
||||
/// <summary>
|
||||
/// Forms Record
|
||||
/// Forms DataSource
|
||||
/// </summary>
|
||||
[UmbracoObjectType(Constants.ObjectTypes.FormsRecord)]
|
||||
[FriendlyName("Record")]
|
||||
FormsRecord
|
||||
[UmbracoObjectType(Constants.ObjectTypes.FormsDataSource)]
|
||||
[FriendlyName("DataSource")]
|
||||
FormsDataSource
|
||||
}
|
||||
}
|
||||
@@ -647,5 +647,10 @@ namespace Umbraco.Core
|
||||
return "[GetPropertyValueException]";
|
||||
}
|
||||
}
|
||||
|
||||
internal static Guid AsGuid(this object value)
|
||||
{
|
||||
return value is Guid ? (Guid) value : Guid.Empty;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -105,8 +105,8 @@ namespace Umbraco.Core.Persistence
|
||||
|
||||
// try to update
|
||||
var rowCount = updateCommand.IsNullOrWhiteSpace()
|
||||
? db.Update(poco)
|
||||
: db.Update<T>(updateCommand, updateArgs);
|
||||
? db.Update(poco)
|
||||
: db.Update<T>(updateCommand, updateArgs);
|
||||
if (rowCount > 0)
|
||||
return RecordPersistenceType.Update;
|
||||
|
||||
@@ -162,7 +162,7 @@ namespace Umbraco.Core.Persistence
|
||||
|
||||
[Obsolete("Use the DatabaseSchemaHelper instead")]
|
||||
public static void CreateTable<T>(this Database db)
|
||||
where T : new()
|
||||
where T : new()
|
||||
{
|
||||
var creator = new DatabaseSchemaHelper(db, LoggerResolver.Current.Logger, SqlSyntaxContext.SqlSyntaxProvider);
|
||||
creator.CreateTable<T>();
|
||||
@@ -170,7 +170,7 @@ namespace Umbraco.Core.Persistence
|
||||
|
||||
[Obsolete("Use the DatabaseSchemaHelper instead")]
|
||||
public static void CreateTable<T>(this Database db, bool overwrite)
|
||||
where T : new()
|
||||
where T : new()
|
||||
{
|
||||
var creator = new DatabaseSchemaHelper(db, LoggerResolver.Current.Logger, SqlSyntaxContext.SqlSyntaxProvider);
|
||||
creator.CreateTable<T>(overwrite);
|
||||
@@ -231,6 +231,24 @@ namespace Umbraco.Core.Persistence
|
||||
ISqlSyntaxProvider syntaxProvider,
|
||||
bool useNativeSqlPlatformBulkInsert = true,
|
||||
bool commitTrans = false)
|
||||
{
|
||||
db.OpenSharedConnection();
|
||||
try
|
||||
{
|
||||
return BulkInsertRecordsTry(db, collection, tr, syntaxProvider, useNativeSqlPlatformBulkInsert, commitTrans);
|
||||
}
|
||||
finally
|
||||
{
|
||||
db.CloseSharedConnection();
|
||||
}
|
||||
}
|
||||
|
||||
public static int BulkInsertRecordsTry<T>(this Database db,
|
||||
IEnumerable<T> collection,
|
||||
Transaction tr,
|
||||
ISqlSyntaxProvider syntaxProvider,
|
||||
bool useNativeSqlPlatformBulkInsert = true,
|
||||
bool commitTrans = false)
|
||||
{
|
||||
if (commitTrans && tr == null)
|
||||
throw new ArgumentNullException("tr", "The transaction cannot be null if commitTrans is true.");
|
||||
@@ -241,15 +259,15 @@ namespace Umbraco.Core.Persistence
|
||||
return 0;
|
||||
}
|
||||
|
||||
var pd = Database.PocoData.ForType(typeof(T));
|
||||
if (pd == null) throw new InvalidOperationException("Could not find PocoData for " + typeof(T));
|
||||
var pd = Database.PocoData.ForType(typeof (T));
|
||||
if (pd == null) throw new InvalidOperationException("Could not find PocoData for " + typeof (T));
|
||||
|
||||
try
|
||||
{
|
||||
int processed = 0;
|
||||
|
||||
var usedNativeSqlPlatformInserts = useNativeSqlPlatformBulkInsert
|
||||
&& NativeSqlPlatformBulkInsertRecords(db, syntaxProvider, pd, collection, out processed);
|
||||
&& NativeSqlPlatformBulkInsertRecords(db, syntaxProvider, pd, collection, out processed);
|
||||
|
||||
if (usedNativeSqlPlatformInserts == false)
|
||||
{
|
||||
@@ -283,19 +301,13 @@ namespace Umbraco.Core.Persistence
|
||||
}
|
||||
|
||||
if (commitTrans)
|
||||
{
|
||||
if (tr == null) throw new ArgumentNullException("The transaction cannot be null if commitTrans is true");
|
||||
tr.Complete();
|
||||
}
|
||||
return processed;
|
||||
}
|
||||
catch
|
||||
{
|
||||
if (commitTrans)
|
||||
{
|
||||
if (tr == null) throw new ArgumentNullException("The transaction cannot be null if commitTrans is true");
|
||||
tr.Dispose();
|
||||
}
|
||||
throw;
|
||||
}
|
||||
|
||||
@@ -337,13 +349,16 @@ namespace Umbraco.Core.Persistence
|
||||
IEnumerable<T> collection,
|
||||
out string[] sql)
|
||||
{
|
||||
if (db == null) throw new ArgumentNullException("db");
|
||||
if (db.Connection == null) throw new ArgumentException("db.Connection is null.");
|
||||
|
||||
var tableName = db.EscapeTableName(pd.TableInfo.TableName);
|
||||
|
||||
//get all columns to include and format for sql
|
||||
var cols = string.Join(", ",
|
||||
pd.Columns
|
||||
.Where(c => IncludeColumn(pd, c))
|
||||
.Select(c => tableName + "." + db.EscapeSqlIdentifier(c.Key)).ToArray());
|
||||
.Where(c => IncludeColumn(pd, c))
|
||||
.Select(c => tableName + "." + db.EscapeSqlIdentifier(c.Key)).ToArray());
|
||||
|
||||
var itemArray = collection.ToArray();
|
||||
|
||||
@@ -357,9 +372,9 @@ namespace Umbraco.Core.Persistence
|
||||
// 4168 / 262 = 15.908... = there will be 16 trans in total
|
||||
|
||||
//all items will be included if we have disabled db parameters
|
||||
var itemsPerTrans = Math.Floor(2000.00 / paramsPerItem);
|
||||
var itemsPerTrans = Math.Floor(2000.00/paramsPerItem);
|
||||
//there will only be one transaction if we have disabled db parameters
|
||||
var numTrans = Math.Ceiling(itemArray.Length / itemsPerTrans);
|
||||
var numTrans = Math.Ceiling(itemArray.Length/itemsPerTrans);
|
||||
|
||||
var sqlQueries = new List<string>();
|
||||
var commands = new List<IDbCommand>();
|
||||
@@ -367,8 +382,8 @@ namespace Umbraco.Core.Persistence
|
||||
for (var tIndex = 0; tIndex < numTrans; tIndex++)
|
||||
{
|
||||
var itemsForTrans = itemArray
|
||||
.Skip(tIndex * (int)itemsPerTrans)
|
||||
.Take((int)itemsPerTrans);
|
||||
.Skip(tIndex*(int) itemsPerTrans)
|
||||
.Take((int) itemsPerTrans);
|
||||
|
||||
var cmd = db.CreateCommand(db.Connection, string.Empty);
|
||||
var pocoValues = new List<string>();
|
||||
@@ -418,7 +433,6 @@ namespace Umbraco.Core.Persistence
|
||||
/// <param name="processed">The number of records inserted</param>
|
||||
private static bool NativeSqlPlatformBulkInsertRecords<T>(Database db, ISqlSyntaxProvider syntaxProvider, Database.PocoData pd, IEnumerable<T> collection, out int processed)
|
||||
{
|
||||
|
||||
var dbConnection = db.Connection;
|
||||
|
||||
//unwrap the profiled connection if there is one
|
||||
@@ -447,7 +461,6 @@ namespace Umbraco.Core.Persistence
|
||||
//could not use the SQL server's specific bulk insert operations
|
||||
processed = 0;
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
using System.Collections.Generic;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Umbraco.Core.Persistence.Repositories
|
||||
{
|
||||
// cannot kill in v7 because it is public, kill in v8
|
||||
[Obsolete("Use MediaFileSystem.DeleteMediaFiles instead.", false)]
|
||||
public interface IDeleteMediaFilesRepository
|
||||
{
|
||||
/// <summary>
|
||||
@@ -9,6 +12,7 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
/// </summary>
|
||||
/// <param name="files"></param>
|
||||
/// <returns></returns>
|
||||
[Obsolete("Use MediaFileSystem.DeleteMediaFiles instead.", false)]
|
||||
bool DeleteMediaFiles(IEnumerable<string> files);
|
||||
}
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
using System;
|
||||
|
||||
namespace Umbraco.Core.Scoping
|
||||
{
|
||||
[Flags]
|
||||
public enum ActionTime
|
||||
{
|
||||
None = 0,
|
||||
BeforeCommit = 1,
|
||||
BeforeEvents = 2,
|
||||
BeforeDispose = 4
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
using System;
|
||||
|
||||
namespace Umbraco.Core.Scoping
|
||||
{
|
||||
public interface IInstanceIdentifiable
|
||||
{
|
||||
Guid InstanceId { get; }
|
||||
}
|
||||
}
|
||||
@@ -8,7 +8,7 @@ namespace Umbraco.Core.Scoping
|
||||
/// <summary>
|
||||
/// Represents a scope.
|
||||
/// </summary>
|
||||
public interface IScope : IDisposable
|
||||
public interface IScope : IDisposable, IInstanceIdentifiable
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the scope database.
|
||||
@@ -38,10 +38,8 @@ namespace Umbraco.Core.Scoping
|
||||
/// <summary>
|
||||
/// Completes the scope.
|
||||
/// </summary>
|
||||
void Complete();
|
||||
|
||||
#if DEBUG_SCOPES
|
||||
Guid InstanceId { get; }
|
||||
#endif
|
||||
/// <returns>A value indicating whether the scope has been successfully completed.</returns>
|
||||
/// <remarks>Can return false if any child scope has not completed.</remarks>
|
||||
bool Complete();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@ namespace Umbraco.Core.Scoping
|
||||
internal interface IScopeInternal : IScope
|
||||
{
|
||||
IScopeInternal ParentScope { get; }
|
||||
EventsDispatchMode DispatchMode { get; }
|
||||
bool CallContext { get; }
|
||||
IsolationLevel IsolationLevel { get; }
|
||||
UmbracoDatabase DatabaseOrNull { get; }
|
||||
EventMessages MessagesOrNull { get; }
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using System.Data;
|
||||
using System;
|
||||
using System.Data;
|
||||
using Umbraco.Core.Events;
|
||||
#if DEBUG_SCOPES
|
||||
using System.Collections.Generic;
|
||||
@@ -23,8 +24,9 @@ namespace Umbraco.Core.Scoping
|
||||
IScope CreateScope(
|
||||
IsolationLevel isolationLevel = IsolationLevel.Unspecified,
|
||||
RepositoryCacheMode repositoryCacheMode = RepositoryCacheMode.Unspecified,
|
||||
EventsDispatchMode dispatchMode = EventsDispatchMode.Unspecified,
|
||||
bool? scopeFileSystems = null);
|
||||
IEventDispatcher eventDispatcher = null,
|
||||
bool? scopeFileSystems = null,
|
||||
bool callContext = false);
|
||||
|
||||
/// <summary>
|
||||
/// Creates a detached scope.
|
||||
@@ -37,17 +39,18 @@ namespace Umbraco.Core.Scoping
|
||||
IScope CreateDetachedScope(
|
||||
IsolationLevel isolationLevel = IsolationLevel.Unspecified,
|
||||
RepositoryCacheMode repositoryCacheMode = RepositoryCacheMode.Unspecified,
|
||||
EventsDispatchMode dispatchMode = EventsDispatchMode.Unspecified,
|
||||
IEventDispatcher eventDispatcher = null,
|
||||
bool? scopeFileSystems = null);
|
||||
|
||||
/// <summary>
|
||||
/// Attaches a scope.
|
||||
/// </summary>
|
||||
/// <param name="scope">The scope to attach.</param>
|
||||
/// <param name="callContext">A value indicating whether to force usage of call context.</param>
|
||||
/// <remarks>
|
||||
/// <para>Only a scope created by <see cref="CreateDetachedScope"/> can be attached.</para>
|
||||
/// </remarks>
|
||||
void AttachScope(IScope scope);
|
||||
void AttachScope(IScope scope, bool callContext = false);
|
||||
|
||||
/// <summary>
|
||||
/// Detaches a scope.
|
||||
@@ -64,7 +67,9 @@ namespace Umbraco.Core.Scoping
|
||||
ScopeContext Context { get; }
|
||||
|
||||
#if DEBUG_SCOPES
|
||||
Dictionary<Guid, object> CallContextObjects { get; }
|
||||
IEnumerable<ScopeInfo> ScopeInfos { get; }
|
||||
ScopeInfo GetScopeInfo(IScope scope);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,10 +24,11 @@ namespace Umbraco.Core.Scoping
|
||||
#endif
|
||||
}
|
||||
|
||||
#if DEBUG_SCOPES
|
||||
private readonly Guid _instanceId = Guid.NewGuid();
|
||||
public Guid InstanceId { get { return _instanceId; } }
|
||||
#endif
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool CallContext { get { return false; } }
|
||||
|
||||
/// <inheritdoc />
|
||||
public RepositoryCacheMode RepositoryCacheMode
|
||||
@@ -75,7 +76,7 @@ namespace Umbraco.Core.Scoping
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Complete()
|
||||
public bool Complete()
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
@@ -100,15 +101,13 @@ namespace Umbraco.Core.Scoping
|
||||
if (_database != null)
|
||||
_database.Dispose();
|
||||
|
||||
_scopeProvider.AmbientScope = null;
|
||||
_scopeProvider.AmbientContext = null;
|
||||
_scopeProvider.SetAmbient(null);
|
||||
|
||||
_disposed = true;
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
public IScopeInternal ParentScope { get { return null; } }
|
||||
public EventsDispatchMode DispatchMode { get {return EventsDispatchMode.Unspecified; } }
|
||||
public IsolationLevel IsolationLevel { get {return IsolationLevel.Unspecified; } }
|
||||
public bool ScopedFileSystems { get { return false; } }
|
||||
public void ChildCompleted(bool? completed) { }
|
||||
|
||||
@@ -16,9 +16,9 @@ namespace Umbraco.Core.Scoping
|
||||
private readonly ScopeProvider _scopeProvider;
|
||||
private readonly IsolationLevel _isolationLevel;
|
||||
private readonly RepositoryCacheMode _repositoryCacheMode;
|
||||
private readonly EventsDispatchMode _dispatchMode;
|
||||
private readonly bool? _scopeFileSystem;
|
||||
private readonly ScopeContext _scopeContext;
|
||||
private bool _callContext;
|
||||
private bool _disposed;
|
||||
private bool? _completed;
|
||||
|
||||
@@ -35,15 +35,17 @@ namespace Umbraco.Core.Scoping
|
||||
Scope parent, ScopeContext scopeContext, bool detachable,
|
||||
IsolationLevel isolationLevel = IsolationLevel.Unspecified,
|
||||
RepositoryCacheMode repositoryCacheMode = RepositoryCacheMode.Unspecified,
|
||||
EventsDispatchMode dispatchMode = EventsDispatchMode.Unspecified,
|
||||
bool? scopeFileSystems = null)
|
||||
IEventDispatcher eventDispatcher = null,
|
||||
bool? scopeFileSystems = null,
|
||||
bool callContext = false)
|
||||
{
|
||||
_scopeProvider = scopeProvider;
|
||||
_scopeContext = scopeContext;
|
||||
_isolationLevel = isolationLevel;
|
||||
_repositoryCacheMode = repositoryCacheMode;
|
||||
_dispatchMode = dispatchMode;
|
||||
_eventDispatcher = eventDispatcher;
|
||||
_scopeFileSystem = scopeFileSystems;
|
||||
_callContext = callContext;
|
||||
Detachable = detachable;
|
||||
|
||||
#if DEBUG_SCOPES
|
||||
@@ -74,9 +76,9 @@ namespace Umbraco.Core.Scoping
|
||||
if (repositoryCacheMode != RepositoryCacheMode.Unspecified && parent.RepositoryCacheMode != repositoryCacheMode)
|
||||
throw new ArgumentException("Cannot be different from parent.", "repositoryCacheMode");
|
||||
|
||||
// cannot specify a different mode!
|
||||
if (_dispatchMode != EventsDispatchMode.Unspecified && parent._dispatchMode != dispatchMode)
|
||||
throw new ArgumentException("Cannot be different from parent.", "dispatchMode");
|
||||
// cannot specify a dispatcher!
|
||||
if (_eventDispatcher != null)
|
||||
throw new ArgumentException("Cannot be specified on nested scope.", "eventDispatcher");
|
||||
|
||||
// cannot specify a different fs scope!
|
||||
if (scopeFileSystems != null && parent._scopeFileSystem != scopeFileSystems)
|
||||
@@ -97,30 +99,31 @@ namespace Umbraco.Core.Scoping
|
||||
ScopeContext scopeContext,
|
||||
IsolationLevel isolationLevel = IsolationLevel.Unspecified,
|
||||
RepositoryCacheMode repositoryCacheMode = RepositoryCacheMode.Unspecified,
|
||||
EventsDispatchMode dispatchMode = EventsDispatchMode.Unspecified,
|
||||
bool? scopeFileSystems = null)
|
||||
: this(scopeProvider, null, scopeContext, detachable, isolationLevel, repositoryCacheMode, dispatchMode, scopeFileSystems)
|
||||
{
|
||||
}
|
||||
IEventDispatcher eventDispatcher = null,
|
||||
bool? scopeFileSystems = null,
|
||||
bool callContext = false)
|
||||
: this(scopeProvider, null, scopeContext, detachable, isolationLevel, repositoryCacheMode, eventDispatcher, scopeFileSystems, callContext)
|
||||
{ }
|
||||
|
||||
// initializes a new scope in a nested scopes chain, with its parent
|
||||
public Scope(ScopeProvider scopeProvider, Scope parent,
|
||||
IsolationLevel isolationLevel = IsolationLevel.Unspecified,
|
||||
RepositoryCacheMode repositoryCacheMode = RepositoryCacheMode.Unspecified,
|
||||
EventsDispatchMode dispatchMode = EventsDispatchMode.Unspecified,
|
||||
bool? scopeFileSystems = null)
|
||||
: this(scopeProvider, parent, null, false, isolationLevel, repositoryCacheMode, dispatchMode, scopeFileSystems)
|
||||
{
|
||||
}
|
||||
IEventDispatcher eventDispatcher = null,
|
||||
bool? scopeFileSystems = null,
|
||||
bool callContext = false)
|
||||
: this(scopeProvider, parent, null, false, isolationLevel, repositoryCacheMode, eventDispatcher, scopeFileSystems, callContext)
|
||||
{ }
|
||||
|
||||
// initializes a new scope, replacing a NoScope instance
|
||||
public Scope(ScopeProvider scopeProvider, NoScope noScope,
|
||||
ScopeContext scopeContext,
|
||||
IsolationLevel isolationLevel = IsolationLevel.Unspecified,
|
||||
RepositoryCacheMode repositoryCacheMode = RepositoryCacheMode.Unspecified,
|
||||
EventsDispatchMode dispatchMode = EventsDispatchMode.Unspecified,
|
||||
bool? scopeFileSystems = null)
|
||||
: this(scopeProvider, null, scopeContext, false, isolationLevel, repositoryCacheMode, dispatchMode, scopeFileSystems)
|
||||
IEventDispatcher eventDispatcher = null,
|
||||
bool? scopeFileSystems = null,
|
||||
bool callContext = false)
|
||||
: this(scopeProvider, null, scopeContext, false, isolationLevel, repositoryCacheMode, eventDispatcher, scopeFileSystems, callContext)
|
||||
{
|
||||
// steal everything from NoScope
|
||||
_database = noScope.DatabaseOrNull;
|
||||
@@ -130,10 +133,20 @@ namespace Umbraco.Core.Scoping
|
||||
throw new Exception("NoScope instance is not free.");
|
||||
}
|
||||
|
||||
#if DEBUG_SCOPES
|
||||
private readonly Guid _instanceId = Guid.NewGuid();
|
||||
public Guid InstanceId { get { return _instanceId; } }
|
||||
#endif
|
||||
|
||||
// a value indicating whether to force call-context
|
||||
public bool CallContext
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_callContext) return true;
|
||||
if (ParentScope != null) return ParentScope.CallContext;
|
||||
return false;
|
||||
}
|
||||
set { _callContext = value; }
|
||||
}
|
||||
|
||||
public bool ScopedFileSystems
|
||||
{
|
||||
@@ -144,16 +157,6 @@ namespace Umbraco.Core.Scoping
|
||||
}
|
||||
}
|
||||
|
||||
public EventsDispatchMode DispatchMode
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_dispatchMode != EventsDispatchMode.Unspecified) return _dispatchMode;
|
||||
if (ParentScope != null) return ParentScope.DispatchMode;
|
||||
return EventsDispatchMode.Scope;
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public RepositoryCacheMode RepositoryCacheMode
|
||||
{
|
||||
@@ -184,6 +187,8 @@ namespace Umbraco.Core.Scoping
|
||||
// the parent scope (in a nested scopes chain)
|
||||
public IScopeInternal ParentScope { get; set; }
|
||||
|
||||
public bool Attached { get; set; }
|
||||
|
||||
// the original scope (when attaching a detachable scope)
|
||||
public IScopeInternal OrigScope { get; set; }
|
||||
|
||||
@@ -290,15 +295,16 @@ namespace Umbraco.Core.Scoping
|
||||
{
|
||||
EnsureNotDisposed();
|
||||
if (ParentScope != null) return ParentScope.Events;
|
||||
return _eventDispatcher ?? (_eventDispatcher = new ScopeEventDispatcher(DispatchMode));
|
||||
return _eventDispatcher ?? (_eventDispatcher = new ScopeEventDispatcher());
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Complete()
|
||||
public bool Complete()
|
||||
{
|
||||
if (_completed.HasValue == false)
|
||||
_completed = true;
|
||||
return _completed.Value;
|
||||
}
|
||||
|
||||
public void Reset()
|
||||
@@ -324,7 +330,18 @@ namespace Umbraco.Core.Scoping
|
||||
EnsureNotDisposed();
|
||||
|
||||
if (this != _scopeProvider.AmbientScope)
|
||||
{
|
||||
#if DEBUG_SCOPES
|
||||
var ambient = _scopeProvider.AmbientScope;
|
||||
Logging.LogHelper.Debug<Scope>("Dispose error (" + (ambient == null ? "no" : "other") + " ambient)");
|
||||
if (ambient == null)
|
||||
throw new InvalidOperationException("Not the ambient scope (no ambient scope).");
|
||||
var infos = _scopeProvider.GetScopeInfo(ambient);
|
||||
throw new InvalidOperationException("Not the ambient scope (see current ambient ctor stack trace).\r\n" + infos.CtorStack);
|
||||
#else
|
||||
throw new InvalidOperationException("Not the ambient scope.");
|
||||
#endif
|
||||
}
|
||||
|
||||
#if DEBUG_SCOPES
|
||||
_scopeProvider.Disposed(this);
|
||||
@@ -418,7 +435,7 @@ namespace Umbraco.Core.Scoping
|
||||
}
|
||||
finally
|
||||
{
|
||||
_scopeProvider.AmbientContext = null;
|
||||
_scopeProvider.SetAmbient(null);
|
||||
}
|
||||
}
|
||||
}, () =>
|
||||
@@ -426,8 +443,10 @@ namespace Umbraco.Core.Scoping
|
||||
if (Detachable)
|
||||
{
|
||||
// get out of the way, restore original
|
||||
_scopeProvider.AmbientScope = OrigScope;
|
||||
_scopeProvider.AmbientContext = OrigContext;
|
||||
_scopeProvider.SetAmbient(OrigScope, OrigContext);
|
||||
Attached = false;
|
||||
OrigScope = null;
|
||||
OrigContext = null;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@ using System.Collections.Generic;
|
||||
|
||||
namespace Umbraco.Core.Scoping
|
||||
{
|
||||
public class ScopeContext
|
||||
public class ScopeContext : IInstanceIdentifiable
|
||||
{
|
||||
private Dictionary<string, IEnlistedObject> _enlisted;
|
||||
|
||||
@@ -27,6 +27,9 @@ namespace Umbraco.Core.Scoping
|
||||
throw new AggregateException("Exceptions were thrown by listed actions.", exceptions);
|
||||
}
|
||||
|
||||
private readonly Guid _instanceId = Guid.NewGuid();
|
||||
public Guid InstanceId { get { return _instanceId; } }
|
||||
|
||||
private IDictionary<string, IEnlistedObject> Enlisted
|
||||
{
|
||||
get
|
||||
|
||||
@@ -1,11 +1,14 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Data;
|
||||
using System.Linq;
|
||||
using System.Runtime.Remoting.Messaging;
|
||||
using System.Web;
|
||||
using Umbraco.Core.Events;
|
||||
using Umbraco.Core.Persistence;
|
||||
#if DEBUG_SCOPES
|
||||
using System.Linq;
|
||||
#endif
|
||||
|
||||
namespace Umbraco.Core.Scoping
|
||||
{
|
||||
@@ -24,17 +27,17 @@ namespace Umbraco.Core.Scoping
|
||||
SafeCallContext.Register(
|
||||
() =>
|
||||
{
|
||||
var scope = StaticAmbientScope;
|
||||
var context = StaticAmbientContext;
|
||||
StaticAmbientScope = null;
|
||||
StaticAmbientContext = null;
|
||||
var scope = GetCallContextObject<IScopeInternal>(ScopeItemKey);
|
||||
var context = GetCallContextObject<ScopeContext>(ContextItemKey);
|
||||
SetCallContextObject(ScopeItemKey, null);
|
||||
SetCallContextObject(ContextItemKey, null);
|
||||
return Tuple.Create(scope, context);
|
||||
},
|
||||
o =>
|
||||
{
|
||||
// cannot re-attached over leaked scope/context
|
||||
// except of course over NoScope (which leaks)
|
||||
var ambientScope = StaticAmbientScope;
|
||||
var ambientScope = GetCallContextObject<IScope>(ScopeItemKey);
|
||||
if (ambientScope != null)
|
||||
{
|
||||
var ambientNoScope = ambientScope as NoScope;
|
||||
@@ -44,59 +47,210 @@ namespace Umbraco.Core.Scoping
|
||||
// this should rollback any pending transaction
|
||||
ambientNoScope.Dispose();
|
||||
}
|
||||
if (StaticAmbientContext != null) throw new Exception("Found leaked context when restoring call context.");
|
||||
if (GetCallContextObject<ScopeContext>(ContextItemKey) != null)
|
||||
throw new Exception("Found leaked context when restoring call context.");
|
||||
|
||||
var t = (Tuple<IScopeInternal, ScopeContext>)o;
|
||||
StaticAmbientScope = t.Item1;
|
||||
StaticAmbientContext = t.Item2;
|
||||
var t = (Tuple<IScopeInternal, ScopeContext>) o;
|
||||
SetCallContextObject(ScopeItemKey, t.Item1);
|
||||
SetCallContextObject(ContextItemKey, t.Item2);
|
||||
});
|
||||
}
|
||||
|
||||
public IDatabaseFactory2 DatabaseFactory { get; private set; }
|
||||
|
||||
#region Context
|
||||
|
||||
// objects that go into the logical call context better be serializable else they'll eventually
|
||||
// cause issues whenever some cross-AppDomain code executes - could be due to ReSharper running
|
||||
// tests, any other things (see https://msdn.microsoft.com/en-us/library/dn458353(v=vs.110).aspx),
|
||||
// but we don't want to make all of our objects serializable since they are *not* meant to be
|
||||
// used in cross-AppDomain scenario anyways.
|
||||
// in addition, whatever goes into the logical call context is serialized back and forth any
|
||||
// time cross-AppDomain code executes, so if we put an "object" there, we'll can *another*
|
||||
// "object" instance - and so we cannot use a random object as a key.
|
||||
// so what we do is: we register a guid in the call context, and we keep a table mapping those
|
||||
// guids to the actual objects. the guid serializes back and forth without causing any issue,
|
||||
// and we can retrieve the actual objects from the table.
|
||||
// only issue: how are we supposed to clear the table? we can't, really. objects should take
|
||||
// care of de-registering themselves from context.
|
||||
// everything we use does, except the NoScope scope, which just stays there
|
||||
//
|
||||
// during tests, NoScope can to into call context... nothing much we can do about it
|
||||
|
||||
private static readonly object StaticCallContextObjectsLock = new object();
|
||||
private static readonly Dictionary<Guid, object> StaticCallContextObjects
|
||||
= new Dictionary<Guid, object>();
|
||||
|
||||
#if DEBUG_SCOPES
|
||||
public Dictionary<Guid, object> CallContextObjects
|
||||
{
|
||||
get
|
||||
{
|
||||
lock (StaticCallContextObjectsLock)
|
||||
{
|
||||
// capture in a dictionary
|
||||
return StaticCallContextObjects.ToDictionary(x => x.Key, x => x.Value);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
private static T GetCallContextObject<T>(string key)
|
||||
where T : class
|
||||
{
|
||||
var objectKey = CallContext.LogicalGetData(key).AsGuid();
|
||||
lock (StaticCallContextObjectsLock)
|
||||
{
|
||||
object callContextObject;
|
||||
if (StaticCallContextObjects.TryGetValue(objectKey, out callContextObject))
|
||||
{
|
||||
#if DEBUG_SCOPES
|
||||
//Logging.LogHelper.Debug<ScopeProvider>("GotObject " + objectKey.ToString("N").Substring(0, 8));
|
||||
#endif
|
||||
return (T) callContextObject;
|
||||
}
|
||||
#if DEBUG_SCOPES
|
||||
//Logging.LogHelper.Debug<ScopeProvider>("MissedObject " + objectKey.ToString("N").Substring(0, 8));
|
||||
#endif
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private static void SetCallContextObject(string key, IInstanceIdentifiable value)
|
||||
{
|
||||
#if DEBUG_SCOPES
|
||||
// manage the 'context' that contains the scope (null, "http" or "call")
|
||||
// only for scopes of course!
|
||||
if (key == ScopeItemKey)
|
||||
{
|
||||
// first, null-register the existing value
|
||||
var ambientKey = CallContext.LogicalGetData(ScopeItemKey).AsGuid();
|
||||
object o = null;
|
||||
lock (StaticCallContextObjectsLock)
|
||||
{
|
||||
if (ambientKey != default(Guid))
|
||||
StaticCallContextObjects.TryGetValue(ambientKey, out o);
|
||||
}
|
||||
var ambientScope = o as IScope;
|
||||
if (ambientScope != null) RegisterContext(ambientScope, null);
|
||||
// then register the new value
|
||||
var scope = value as IScope;
|
||||
if (scope != null) RegisterContext(scope, "call");
|
||||
}
|
||||
#endif
|
||||
if (value == null)
|
||||
{
|
||||
var objectKey = CallContext.LogicalGetData(key).AsGuid();
|
||||
CallContext.FreeNamedDataSlot(key);
|
||||
if (objectKey == default (Guid)) return;
|
||||
lock (StaticCallContextObjectsLock)
|
||||
{
|
||||
#if DEBUG_SCOPES
|
||||
//Logging.LogHelper.Debug<ScopeProvider>("RemoveObject " + objectKey.ToString("N").Substring(0, 8));
|
||||
#endif
|
||||
StaticCallContextObjects.Remove(objectKey);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// note - we are *not* detecting an already-existing value
|
||||
// because our code in this class *always* sets to null before
|
||||
// setting to a real value
|
||||
var objectKey = value.InstanceId;
|
||||
lock (StaticCallContextObjectsLock)
|
||||
{
|
||||
#if DEBUG_SCOPES
|
||||
//Logging.LogHelper.Debug<ScopeProvider>("AddObject " + objectKey.ToString("N").Substring(0, 8));
|
||||
#endif
|
||||
StaticCallContextObjects.Add(objectKey, value);
|
||||
}
|
||||
CallContext.LogicalSetData(key, objectKey);
|
||||
}
|
||||
}
|
||||
|
||||
internal static Func<IDictionary> HttpContextItemsGetter { get; set; }
|
||||
|
||||
private static IDictionary HttpContextItems
|
||||
{
|
||||
get
|
||||
{
|
||||
return HttpContextItemsGetter == null
|
||||
? (HttpContext.Current == null ? null : HttpContext.Current.Items)
|
||||
: HttpContextItemsGetter();
|
||||
}
|
||||
}
|
||||
|
||||
public static T GetHttpContextObject<T>(string key, bool required = true)
|
||||
where T : class
|
||||
{
|
||||
var httpContextItems = HttpContextItems;
|
||||
if (httpContextItems != null)
|
||||
return (T)httpContextItems[key];
|
||||
if (required)
|
||||
throw new Exception("HttpContext.Current is null.");
|
||||
return null;
|
||||
}
|
||||
|
||||
private static bool SetHttpContextObject(string key, object value, bool required = true)
|
||||
{
|
||||
var httpContextItems = HttpContextItems;
|
||||
if (httpContextItems == null)
|
||||
{
|
||||
if (required)
|
||||
throw new Exception("HttpContext.Current is null.");
|
||||
return false;
|
||||
}
|
||||
#if DEBUG_SCOPES
|
||||
// manage the 'context' that contains the scope (null, "http" or "call")
|
||||
// only for scopes of course!
|
||||
if (key == ScopeItemKey)
|
||||
{
|
||||
// first, null-register the existing value
|
||||
var ambientScope = (IScope)httpContextItems[ScopeItemKey];
|
||||
if (ambientScope != null) RegisterContext(ambientScope, null);
|
||||
// then register the new value
|
||||
var scope = value as IScope;
|
||||
if (scope != null) RegisterContext(scope, "http");
|
||||
}
|
||||
#endif
|
||||
if (value == null)
|
||||
httpContextItems.Remove(key);
|
||||
else
|
||||
httpContextItems[key] = value;
|
||||
return true;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Ambient Context
|
||||
|
||||
internal const string ContextItemKey = "Umbraco.Core.Scoping.ScopeContext";
|
||||
|
||||
private static ScopeContext CallContextContextValue
|
||||
internal static ScopeContext AmbientContextInternal
|
||||
{
|
||||
get { return (ScopeContext)CallContext.LogicalGetData(ContextItemKey); }
|
||||
set
|
||||
get
|
||||
{
|
||||
if (value == null) CallContext.FreeNamedDataSlot(ContextItemKey);
|
||||
else CallContext.LogicalSetData(ContextItemKey, value);
|
||||
// try http context, fallback onto call context
|
||||
var value = GetHttpContextObject<ScopeContext>(ContextItemKey, false);
|
||||
return value ?? GetCallContextObject<ScopeContext>(ContextItemKey);
|
||||
}
|
||||
}
|
||||
|
||||
private static ScopeContext HttpContextContextValue
|
||||
{
|
||||
get { return (ScopeContext)HttpContext.Current.Items[ContextItemKey]; }
|
||||
set
|
||||
{
|
||||
if (value == null)
|
||||
HttpContext.Current.Items.Remove(ContextItemKey);
|
||||
else
|
||||
HttpContext.Current.Items[ContextItemKey] = value;
|
||||
}
|
||||
}
|
||||
// clear both
|
||||
SetHttpContextObject(ContextItemKey, null, false);
|
||||
SetCallContextObject(ContextItemKey, null);
|
||||
if (value == null) return;
|
||||
|
||||
private static ScopeContext StaticAmbientContext
|
||||
{
|
||||
get { return HttpContext.Current == null ? CallContextContextValue : HttpContextContextValue; }
|
||||
set
|
||||
{
|
||||
if (HttpContext.Current == null)
|
||||
CallContextContextValue = value;
|
||||
else
|
||||
HttpContextContextValue = value;
|
||||
// set http/call context
|
||||
if (SetHttpContextObject(ContextItemKey, value, false) == false)
|
||||
SetCallContextObject(ContextItemKey, value);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public ScopeContext AmbientContext
|
||||
{
|
||||
get { return StaticAmbientContext; }
|
||||
set { StaticAmbientContext = value; }
|
||||
get { return AmbientContextInternal; }
|
||||
}
|
||||
|
||||
#endregion
|
||||
@@ -109,86 +263,84 @@ namespace Umbraco.Core.Scoping
|
||||
// only 1 instance which can be disposed and disposed again
|
||||
private static readonly ScopeReference StaticScopeReference = new ScopeReference(new ScopeProvider(null));
|
||||
|
||||
private static IScopeInternal CallContextValue
|
||||
internal static IScopeInternal AmbientScopeInternal
|
||||
{
|
||||
get { return (IScopeInternal) CallContext.LogicalGetData(ScopeItemKey); }
|
||||
set
|
||||
get
|
||||
{
|
||||
#if DEBUG_SCOPES
|
||||
// manage the 'context' that contains the scope (null, "http" or "lcc")
|
||||
var ambientScope = (IScope) CallContext.LogicalGetData(ScopeItemKey);
|
||||
if (ambientScope != null) RegisterContext(ambientScope, null);
|
||||
if (value != null) RegisterContext(value, "lcc");
|
||||
#endif
|
||||
if (value == null) CallContext.FreeNamedDataSlot(ScopeItemKey);
|
||||
else CallContext.LogicalSetData(ScopeItemKey, value);
|
||||
// try http context, fallback onto call context
|
||||
var value = GetHttpContextObject<IScopeInternal>(ScopeItemKey, false);
|
||||
return value ?? GetCallContextObject<IScopeInternal>(ScopeItemKey);
|
||||
}
|
||||
}
|
||||
|
||||
private static IScopeInternal HttpContextValue
|
||||
{
|
||||
get { return (IScopeInternal) HttpContext.Current.Items[ScopeItemKey]; }
|
||||
set
|
||||
{
|
||||
#if DEBUG_SCOPES
|
||||
// manage the 'context' that contains the scope (null, "http" or "lcc")
|
||||
var ambientScope = (IScope) HttpContext.Current.Items[ScopeItemKey];
|
||||
if (ambientScope != null) RegisterContext(ambientScope, null);
|
||||
if (value != null) RegisterContext(value, "http");
|
||||
#endif
|
||||
if (value == null)
|
||||
{
|
||||
HttpContext.Current.Items.Remove(ScopeItemKey);
|
||||
HttpContext.Current.Items.Remove(ScopeRefItemKey);
|
||||
}
|
||||
else
|
||||
{
|
||||
HttpContext.Current.Items[ScopeItemKey] = value;
|
||||
if (HttpContext.Current.Items[ScopeRefItemKey] == null)
|
||||
HttpContext.Current.Items[ScopeRefItemKey] = StaticScopeReference;
|
||||
}
|
||||
}
|
||||
}
|
||||
// clear both
|
||||
SetHttpContextObject(ScopeItemKey, null, false);
|
||||
SetHttpContextObject(ScopeRefItemKey, null, false);
|
||||
SetCallContextObject(ScopeItemKey, null);
|
||||
if (value == null) return;
|
||||
|
||||
private static IScopeInternal StaticAmbientScope
|
||||
{
|
||||
get { return HttpContext.Current == null ? CallContextValue : HttpContextValue; }
|
||||
set
|
||||
{
|
||||
if (HttpContext.Current == null)
|
||||
CallContextValue = value;
|
||||
// set http/call context
|
||||
if (value.CallContext == false && SetHttpContextObject(ScopeItemKey, value, false))
|
||||
SetHttpContextObject(ScopeRefItemKey, StaticScopeReference);
|
||||
else
|
||||
HttpContextValue = value;
|
||||
SetCallContextObject(ScopeItemKey, value);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public IScopeInternal AmbientScope
|
||||
{
|
||||
get { return StaticAmbientScope; }
|
||||
set { StaticAmbientScope = value; }
|
||||
get { return AmbientScopeInternal; }
|
||||
internal set { AmbientScopeInternal = value; }
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public IScopeInternal GetAmbientOrNoScope()
|
||||
{
|
||||
return AmbientScope ?? (AmbientScope = new NoScope(this));
|
||||
return AmbientScope ?? (AmbientScope = new NoScope(this));
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
public void SetAmbient(IScopeInternal scope, ScopeContext context = null)
|
||||
{
|
||||
// clear all
|
||||
SetHttpContextObject(ScopeItemKey, null, false);
|
||||
SetHttpContextObject(ScopeRefItemKey, null, false);
|
||||
SetCallContextObject(ScopeItemKey, null);
|
||||
SetHttpContextObject(ContextItemKey, null, false);
|
||||
SetCallContextObject(ContextItemKey, null);
|
||||
if (scope == null)
|
||||
{
|
||||
if (context != null)
|
||||
throw new ArgumentException("Must be null if scope is null.", "context");
|
||||
return;
|
||||
}
|
||||
|
||||
if (scope.CallContext == false && SetHttpContextObject(ScopeItemKey, scope, false))
|
||||
{
|
||||
SetHttpContextObject(ScopeRefItemKey, StaticScopeReference);
|
||||
SetHttpContextObject(ContextItemKey, context);
|
||||
}
|
||||
else
|
||||
{
|
||||
SetCallContextObject(ScopeItemKey, scope);
|
||||
SetCallContextObject(ContextItemKey, context);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public IScope CreateDetachedScope(
|
||||
IsolationLevel isolationLevel = IsolationLevel.Unspecified,
|
||||
RepositoryCacheMode repositoryCacheMode = RepositoryCacheMode.Unspecified,
|
||||
EventsDispatchMode dispatchMode = EventsDispatchMode.Unspecified,
|
||||
IEventDispatcher eventDispatcher = null,
|
||||
bool? scopeFileSystems = null)
|
||||
{
|
||||
return new Scope(this, true, null, isolationLevel, repositoryCacheMode, dispatchMode, scopeFileSystems);
|
||||
return new Scope(this, true, null, isolationLevel, repositoryCacheMode, eventDispatcher, scopeFileSystems);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void AttachScope(IScope other)
|
||||
public void AttachScope(IScope other, bool callContext = false)
|
||||
{
|
||||
var otherScope = other as Scope;
|
||||
if (otherScope == null)
|
||||
@@ -197,10 +349,15 @@ namespace Umbraco.Core.Scoping
|
||||
if (otherScope.Detachable == false)
|
||||
throw new ArgumentException("Not a detachable scope.");
|
||||
|
||||
if (otherScope.Attached)
|
||||
throw new InvalidOperationException("Already attached.");
|
||||
|
||||
otherScope.Attached = true;
|
||||
otherScope.OrigScope = AmbientScope;
|
||||
otherScope.OrigContext = AmbientContext;
|
||||
AmbientScope = otherScope;
|
||||
AmbientContext = otherScope.Context;
|
||||
|
||||
otherScope.CallContext = callContext;
|
||||
SetAmbient(otherScope, otherScope.Context);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
@@ -221,10 +378,10 @@ namespace Umbraco.Core.Scoping
|
||||
if (scope.Detachable == false)
|
||||
throw new InvalidOperationException("Ambient scope is not detachable.");
|
||||
|
||||
AmbientScope = scope.OrigScope;
|
||||
AmbientContext = scope.OrigContext;
|
||||
SetAmbient(scope.OrigScope, scope.OrigContext);
|
||||
scope.OrigScope = null;
|
||||
scope.OrigContext = null;
|
||||
scope.Attached = false;
|
||||
return scope;
|
||||
}
|
||||
|
||||
@@ -232,16 +389,19 @@ namespace Umbraco.Core.Scoping
|
||||
public IScope CreateScope(
|
||||
IsolationLevel isolationLevel = IsolationLevel.Unspecified,
|
||||
RepositoryCacheMode repositoryCacheMode = RepositoryCacheMode.Unspecified,
|
||||
EventsDispatchMode dispatchMode = EventsDispatchMode.Unspecified,
|
||||
bool? scopeFileSystems = null)
|
||||
IEventDispatcher eventDispatcher = null,
|
||||
bool? scopeFileSystems = null,
|
||||
bool callContext = false)
|
||||
{
|
||||
var ambient = AmbientScope;
|
||||
if (ambient == null)
|
||||
{
|
||||
var context = AmbientContext == null ? new ScopeContext() : null;
|
||||
var scope = new Scope(this, false, context, isolationLevel, repositoryCacheMode, dispatchMode, scopeFileSystems);
|
||||
if (AmbientContext == null) AmbientContext = context; // assign only if scope creation did not throw!
|
||||
return AmbientScope = scope;
|
||||
var ambientContext = AmbientContext;
|
||||
var newContext = ambientContext == null ? new ScopeContext() : null;
|
||||
var scope = new Scope(this, false, newContext, isolationLevel, repositoryCacheMode, eventDispatcher, scopeFileSystems, callContext);
|
||||
// assign only if scope creation did not throw!
|
||||
SetAmbient(scope, newContext ?? ambientContext);
|
||||
return scope;
|
||||
}
|
||||
|
||||
// replace noScope with a real one
|
||||
@@ -255,16 +415,20 @@ namespace Umbraco.Core.Scoping
|
||||
var database = noScope.DatabaseOrNull;
|
||||
if (database != null && database.InTransaction)
|
||||
throw new Exception("NoScope is in a transaction.");
|
||||
var context = AmbientContext == null ? new ScopeContext() : null;
|
||||
var scope = new Scope(this, noScope, context, isolationLevel, repositoryCacheMode, dispatchMode, scopeFileSystems);
|
||||
if (AmbientContext == null) AmbientContext = context; // assign only if scope creation did not throw!
|
||||
return AmbientScope = scope;
|
||||
var ambientContext = AmbientContext;
|
||||
var newContext = ambientContext == null ? new ScopeContext() : null;
|
||||
var scope = new Scope(this, noScope, newContext, isolationLevel, repositoryCacheMode, eventDispatcher, scopeFileSystems, callContext);
|
||||
// assign only if scope creation did not throw!
|
||||
SetAmbient(scope, newContext ?? ambientContext);
|
||||
return scope;
|
||||
}
|
||||
|
||||
var ambientScope = ambient as Scope;
|
||||
if (ambientScope == null) throw new Exception("Ambient scope is not a Scope instance.");
|
||||
|
||||
return AmbientScope = new Scope(this, ambientScope, isolationLevel, repositoryCacheMode, dispatchMode, scopeFileSystems);
|
||||
var nested = new Scope(this, ambientScope, isolationLevel, repositoryCacheMode, eventDispatcher, scopeFileSystems, callContext);
|
||||
SetAmbient(nested, AmbientContext);
|
||||
return nested;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
@@ -309,7 +473,7 @@ namespace Umbraco.Core.Scoping
|
||||
|
||||
// all scope instances that are currently beeing tracked
|
||||
private static readonly object StaticScopeInfosLock = new object();
|
||||
private static readonly List<ScopeInfo> StaticScopeInfos = new List<ScopeInfo>();
|
||||
private static readonly Dictionary<IScope, ScopeInfo> StaticScopeInfos = new Dictionary<IScope, ScopeInfo>();
|
||||
|
||||
public IEnumerable<ScopeInfo> ScopeInfos
|
||||
{
|
||||
@@ -317,37 +481,53 @@ namespace Umbraco.Core.Scoping
|
||||
{
|
||||
lock (StaticScopeInfosLock)
|
||||
{
|
||||
return StaticScopeInfos.ToArray(); // capture in an array
|
||||
return StaticScopeInfos.Values.ToArray(); // capture in an array
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public ScopeInfo GetScopeInfo(IScope scope)
|
||||
{
|
||||
lock (StaticScopeInfosLock)
|
||||
{
|
||||
ScopeInfo scopeInfo;
|
||||
return StaticScopeInfos.TryGetValue(scope, out scopeInfo) ? scopeInfo : null;
|
||||
}
|
||||
}
|
||||
|
||||
//private static void Log(string message, UmbracoDatabase database)
|
||||
//{
|
||||
// LogHelper.Debug<ScopeProvider>(message + " (" + (database == null ? "" : database.InstanceSid) + ").");
|
||||
//}
|
||||
|
||||
// register a scope and capture its ctor stacktrace
|
||||
public void RegisterScope(IScope scope)
|
||||
{
|
||||
lock (StaticScopeInfosLock)
|
||||
{
|
||||
if (StaticScopeInfos.Any(x => x.Scope == scope)) throw new Exception("oops: already registered.");
|
||||
StaticScopeInfos.Add(new ScopeInfo(scope, Environment.StackTrace));
|
||||
if (StaticScopeInfos.ContainsKey(scope)) throw new Exception("oops: already registered.");
|
||||
//Logging.LogHelper.Debug<ScopeProvider>("Register " + scope.InstanceId.ToString("N").Substring(0, 8));
|
||||
StaticScopeInfos[scope] = new ScopeInfo(scope, Environment.StackTrace);
|
||||
}
|
||||
}
|
||||
|
||||
// 'context' that contains the scope (null, "http" or "lcc")
|
||||
// register that a scope is in a 'context'
|
||||
// 'context' that contains the scope (null, "http" or "call")
|
||||
public static void RegisterContext(IScope scope, string context)
|
||||
{
|
||||
lock (StaticScopeInfosLock)
|
||||
{
|
||||
var info = StaticScopeInfos.FirstOrDefault(x => x.Scope == scope);
|
||||
ScopeInfo info;
|
||||
if (StaticScopeInfos.TryGetValue(scope, out info) == false) info = null;
|
||||
if (info == null)
|
||||
{
|
||||
if (context == null) return;
|
||||
throw new Exception("oops: unregistered scope.");
|
||||
}
|
||||
//Logging.LogHelper.Debug<ScopeProvider>("Register context " + (context ?? "null") + " for " + scope.InstanceId.ToString("N").Substring(0, 8));
|
||||
if (context == null) info.NullStack = Environment.StackTrace;
|
||||
//if (context == null)
|
||||
// Logging.LogHelper.Debug<ScopeProvider>("STACK\r\n" + info.NullStack);
|
||||
info.Context = context;
|
||||
}
|
||||
}
|
||||
@@ -356,11 +536,12 @@ namespace Umbraco.Core.Scoping
|
||||
{
|
||||
lock (StaticScopeInfosLock)
|
||||
{
|
||||
var info = StaticScopeInfos.FirstOrDefault(x => x.Scope == scope);
|
||||
if (info != null)
|
||||
if (StaticScopeInfos.ContainsKey(scope))
|
||||
{
|
||||
// enable this by default
|
||||
StaticScopeInfos.Remove(info);
|
||||
//Console.WriteLine("unregister " + scope.InstanceId.ToString("N").Substring(0, 8));
|
||||
StaticScopeInfos.Remove(scope);
|
||||
//Logging.LogHelper.Debug<ScopeProvider>("Remove " + scope.InstanceId.ToString("N").Substring(0, 8));
|
||||
|
||||
// instead, enable this to keep *all* scopes
|
||||
// beware, there can be a lot of scopes!
|
||||
|
||||
@@ -179,7 +179,7 @@ namespace Umbraco.Core.Services
|
||||
uow.Events.Dispatch(Created, this, new NewEventArgs<IContent>(content, false, contentTypeAlias, parentId));
|
||||
//Created.RaiseEvent(new NewEventArgs<IContent>(content, false, contentTypeAlias, parentId), this, uow.Events);
|
||||
|
||||
// fixme
|
||||
// fixme
|
||||
var auditRepo = RepositoryFactory.CreateAuditRepository(uow);
|
||||
auditRepo.AddOrUpdate(new AuditItem(content.Id, string.Format("Content '{0}' was created", name), AuditType.New, content.CreatorId));
|
||||
uow.Commit();
|
||||
@@ -1051,9 +1051,9 @@ namespace Umbraco.Core.Services
|
||||
UnPublish(content, userId);
|
||||
}
|
||||
content.WriterId = userId;
|
||||
content.ChangeTrashedState(true);
|
||||
content.ChangeTrashedState(true);
|
||||
repository.AddOrUpdate(content);
|
||||
|
||||
|
||||
//Loop through descendants to update their trash state, but ensuring structure by keeping the ParentId
|
||||
foreach (var descendant in descendants)
|
||||
{
|
||||
@@ -1063,14 +1063,14 @@ namespace Umbraco.Core.Services
|
||||
descendant.ChangeTrashedState(true, descendant.ParentId);
|
||||
repository.AddOrUpdate(descendant);
|
||||
|
||||
moveInfo.Add(new MoveEventInfo<IContent>(descendant, descendant.Path, descendant.ParentId));
|
||||
moveInfo.Add(new MoveEventInfo<IContent>(descendant, descendant.Path, descendant.ParentId));
|
||||
}
|
||||
|
||||
uow.Commit();
|
||||
|
||||
uow.Events.Dispatch(Trashed, this, new MoveEventArgs<IContent>(false, evtMsgs, moveInfo.ToArray()), "Trashed");
|
||||
}
|
||||
|
||||
|
||||
Audit(AuditType.Move, "Move Content to Recycle Bin performed by user", userId, content.Id);
|
||||
|
||||
return OperationStatus.Success(evtMsgs);
|
||||
@@ -1285,9 +1285,6 @@ namespace Umbraco.Core.Services
|
||||
|
||||
var args = new DeleteEventArgs<IContent>(content, false, evtMsgs);
|
||||
uow.Events.Dispatch(Deleted, this, args, "Deleted"); // fixme why the event name?!
|
||||
|
||||
//remove any flagged media files
|
||||
repository.DeleteMediaFiles(args.MediaFilesToDelete);
|
||||
}
|
||||
|
||||
Audit(AuditType.Delete, "Delete Content performed by user", userId, content.Id);
|
||||
@@ -1335,7 +1332,7 @@ namespace Umbraco.Core.Services
|
||||
|
||||
/// <summary>
|
||||
/// Deletes all content of the specified types. All Descendants of deleted content that is not of these types is moved to Recycle Bin.
|
||||
/// </summary>
|
||||
/// </summary>
|
||||
/// <param name="contentTypeIds">Id of the <see cref="IContentType"/></param>
|
||||
/// <param name="userId">Optional Id of the user issueing the delete operation</param>
|
||||
public void DeleteContentOfTypes(IEnumerable<int> contentTypeIds, int userId = 0)
|
||||
@@ -1344,7 +1341,7 @@ namespace Umbraco.Core.Services
|
||||
using (var uow = UowProvider.GetUnitOfWork())
|
||||
{
|
||||
var repository = RepositoryFactory.CreateContentRepository(uow);
|
||||
|
||||
|
||||
//track the 'root' items of the collection of nodes discovered to delete, we need to use
|
||||
//these items to lookup descendants that are not of this doc type so they can be transfered
|
||||
//to the recycle bin
|
||||
@@ -1559,9 +1556,6 @@ namespace Umbraco.Core.Services
|
||||
|
||||
success = repository.EmptyRecycleBin();
|
||||
|
||||
if (success)
|
||||
repository.DeleteMediaFiles(files);
|
||||
|
||||
uow.Events.Dispatch(EmptiedRecycleBin, this, new RecycleBinEventArgs(nodeObjectType, entities, files, success));
|
||||
uow.Commit();
|
||||
}
|
||||
@@ -1610,7 +1604,6 @@ namespace Umbraco.Core.Services
|
||||
// fixme mess!
|
||||
using (var scope = UowProvider.ScopeProvider.CreateScope())
|
||||
{
|
||||
|
||||
using (var uow = UowProvider.GetUnitOfWork())
|
||||
{
|
||||
if (uow.Events.DispatchCancelable(Copying, this, new CopyEventArgs<IContent>(content, copy, parentId)))
|
||||
|
||||
@@ -756,6 +756,7 @@ namespace Umbraco.Core.Services
|
||||
using (var scope = UowProvider.ScopeProvider.CreateScope()) // fixme what a mess
|
||||
{
|
||||
scope.Events.Dispatch(SavedContentType, this, new SaveEventArgs<IContentType>(contentType, false));
|
||||
scope.Complete();
|
||||
}
|
||||
Audit(AuditType.Save, "Save ContentType performed by user", userId, contentType.Id);
|
||||
}
|
||||
@@ -800,8 +801,9 @@ namespace Umbraco.Core.Services
|
||||
using (var scope = UowProvider.ScopeProvider.CreateScope()) // fixme what a mess
|
||||
{
|
||||
scope.Events.Dispatch(SavedContentType, this, new SaveEventArgs<IContentType>(asArray, false));
|
||||
scope.Complete();
|
||||
}
|
||||
Audit(AuditType.Save, "Save ContentTypes performed by user", userId, -1);
|
||||
Audit(AuditType.Save, "Save ContentTypes performed by user", userId, -1);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -1211,6 +1213,7 @@ namespace Umbraco.Core.Services
|
||||
using (var scope = UowProvider.ScopeProvider.CreateScope()) // fixme what a mess
|
||||
{
|
||||
scope.Events.Dispatch(SavedMediaType, this, new SaveEventArgs<IMediaType>(mediaType, false));
|
||||
scope.Complete();
|
||||
}
|
||||
Audit(AuditType.Save, "Save MediaType performed by user", userId, mediaType.Id);
|
||||
}
|
||||
@@ -1255,6 +1258,7 @@ namespace Umbraco.Core.Services
|
||||
using (var scope = UowProvider.ScopeProvider.CreateScope()) // fixme what a mess
|
||||
{
|
||||
scope.Events.Dispatch(SavedMediaType, this, new SaveEventArgs<IMediaType>(asArray, false));
|
||||
scope.Complete();
|
||||
}
|
||||
Audit(AuditType.Save, "Save MediaTypes performed by user", userId, -1);
|
||||
}
|
||||
|
||||
@@ -831,9 +831,6 @@ namespace Umbraco.Core.Services
|
||||
|
||||
var args = new DeleteEventArgs<IMedia>(media, false, evtMsgs);
|
||||
uow.Events.Dispatch(Deleted, this, args);
|
||||
|
||||
//remove any flagged media files
|
||||
repository.DeleteMediaFiles(args.MediaFilesToDelete);
|
||||
}
|
||||
|
||||
Audit(AuditType.Delete, "Delete Media performed by user", userId, media.Id);
|
||||
@@ -958,9 +955,6 @@ namespace Umbraco.Core.Services
|
||||
success = repository.EmptyRecycleBin();
|
||||
// FIXME shouldn't we commit here?!
|
||||
uow.Events.Dispatch(EmptiedRecycleBin, this, new RecycleBinEventArgs(nodeObjectType, entities, files, success));
|
||||
|
||||
if (success)
|
||||
repository.DeleteMediaFiles(files);
|
||||
}
|
||||
}
|
||||
Audit(AuditType.Delete, "Empty Media Recycle Bin performed by user", 0, -21);
|
||||
|
||||
@@ -45,7 +45,7 @@ namespace Umbraco.Core.Services
|
||||
/// <summary>
|
||||
/// Gets the default MemberType alias
|
||||
/// </summary>
|
||||
/// <remarks>By default we'll return the 'writer', but we need to check it exists. If it doesn't we'll
|
||||
/// <remarks>By default we'll return the 'writer', but we need to check it exists. If it doesn't we'll
|
||||
/// return the first type that is not an admin, otherwise if there's only one we will return that one.</remarks>
|
||||
/// <returns>Alias of the default MemberType</returns>
|
||||
public string GetDefaultMemberType()
|
||||
@@ -86,7 +86,7 @@ namespace Umbraco.Core.Services
|
||||
/// <summary>
|
||||
/// This is simply a helper method which essentially just wraps the MembershipProvider's ChangePassword method
|
||||
/// </summary>
|
||||
/// <remarks>This method exists so that Umbraco developers can use one entry point to create/update
|
||||
/// <remarks>This method exists so that Umbraco developers can use one entry point to create/update
|
||||
/// Members if they choose to. </remarks>
|
||||
/// <param name="member">The Member to save the password for</param>
|
||||
/// <param name="password">The password to encrypt and save</param>
|
||||
@@ -776,7 +776,7 @@ namespace Umbraco.Core.Services
|
||||
/// <summary>
|
||||
/// Creates and persists a Member
|
||||
/// </summary>
|
||||
/// <remarks>Using this method will persist the Member object before its returned
|
||||
/// <remarks>Using this method will persist the Member object before its returned
|
||||
/// meaning that it will have an Id available (unlike the CreateMember method)</remarks>
|
||||
/// <param name="username">Username of the Member to create</param>
|
||||
/// <param name="email">Email of the Member to create</param>
|
||||
@@ -792,7 +792,7 @@ namespace Umbraco.Core.Services
|
||||
/// <summary>
|
||||
/// Creates and persists a Member
|
||||
/// </summary>
|
||||
/// <remarks>Using this method will persist the Member object before its returned
|
||||
/// <remarks>Using this method will persist the Member object before its returned
|
||||
/// meaning that it will have an Id available (unlike the CreateMember method)</remarks>
|
||||
/// <param name="username">Username of the Member to create</param>
|
||||
/// <param name="email">Email of the Member to create</param>
|
||||
@@ -806,7 +806,7 @@ namespace Umbraco.Core.Services
|
||||
/// <summary>
|
||||
/// Creates and persists a Member
|
||||
/// </summary>
|
||||
/// <remarks>Using this method will persist the Member object before its returned
|
||||
/// <remarks>Using this method will persist the Member object before its returned
|
||||
/// meaning that it will have an Id available (unlike the CreateMember method)</remarks>
|
||||
/// <param name="username">Username of the Member to create</param>
|
||||
/// <param name="email">Email of the Member to create</param>
|
||||
@@ -836,7 +836,7 @@ namespace Umbraco.Core.Services
|
||||
/// <summary>
|
||||
/// Creates and persists a Member
|
||||
/// </summary>
|
||||
/// <remarks>Using this method will persist the Member object before its returned
|
||||
/// <remarks>Using this method will persist the Member object before its returned
|
||||
/// meaning that it will have an Id available (unlike the CreateMember method)</remarks>
|
||||
/// <param name="username">Username of the Member to create</param>
|
||||
/// <param name="email">Email of the Member to create</param>
|
||||
@@ -921,7 +921,7 @@ namespace Umbraco.Core.Services
|
||||
/// <returns><see cref="IMember"/></returns>
|
||||
public IMember GetByUsername(string username)
|
||||
{
|
||||
//TODO: Somewhere in here, whether at this level or the repository level, we need to add
|
||||
//TODO: Somewhere in here, whether at this level or the repository level, we need to add
|
||||
// a caching mechanism since this method is used by all the membership providers and could be
|
||||
// called quite a bit when dealing with members.
|
||||
|
||||
@@ -953,9 +953,6 @@ namespace Umbraco.Core.Services
|
||||
|
||||
var args = new DeleteEventArgs<IMember>(member, false);
|
||||
uow.Events.Dispatch(Deleted, this, args);
|
||||
|
||||
//remove any flagged media files
|
||||
repository.DeleteMediaFiles(args.MediaFilesToDelete);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -963,7 +960,7 @@ namespace Umbraco.Core.Services
|
||||
/// Saves an <see cref="IMember"/>
|
||||
/// </summary>
|
||||
/// <param name="entity"><see cref="IMember"/> to Save</param>
|
||||
/// <param name="raiseEvents">Optional parameter to raise events.
|
||||
/// <param name="raiseEvents">Optional parameter to raise events.
|
||||
/// Default is <c>True</c> otherwise set to <c>False</c> to not raise events</param>
|
||||
public void Save(IMember entity, bool raiseEvents = true)
|
||||
{
|
||||
@@ -1004,7 +1001,7 @@ namespace Umbraco.Core.Services
|
||||
/// Saves a list of <see cref="IMember"/> objects
|
||||
/// </summary>
|
||||
/// <param name="entities"><see cref="IEnumerable{IMember}"/> to save</param>
|
||||
/// <param name="raiseEvents">Optional parameter to raise events.
|
||||
/// <param name="raiseEvents">Optional parameter to raise events.
|
||||
/// Default is <c>True</c> otherwise set to <c>False</c> to not raise events</param>
|
||||
public void Save(IEnumerable<IMember> entities, bool raiseEvents = true)
|
||||
{
|
||||
|
||||
@@ -17,7 +17,7 @@ namespace Umbraco.Core.Services
|
||||
private readonly IMemberService _memberService;
|
||||
|
||||
private static readonly ReaderWriterLockSlim Locker = new ReaderWriterLockSlim();
|
||||
|
||||
|
||||
|
||||
public MemberTypeService(IDatabaseUnitOfWorkProvider provider, RepositoryFactory repositoryFactory, ILogger logger, IEventMessagesFactory eventMessagesFactory, IMemberService memberService)
|
||||
: base(provider, repositoryFactory, logger, eventMessagesFactory)
|
||||
@@ -110,7 +110,7 @@ namespace Umbraco.Core.Services
|
||||
public void Save(IEnumerable<IMemberType> memberTypes, int userId = 0)
|
||||
{
|
||||
var asArray = memberTypes.ToArray();
|
||||
|
||||
|
||||
using (new WriteLock(Locker))
|
||||
{
|
||||
using (var uow = UowProvider.GetUnitOfWork())
|
||||
@@ -137,7 +137,7 @@ namespace Umbraco.Core.Services
|
||||
uow.Events.Dispatch(Saved, this, new SaveEventArgs<IMemberType>(asArray, false));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
public void Delete(IMemberType memberType, int userId = 0)
|
||||
@@ -167,7 +167,7 @@ namespace Umbraco.Core.Services
|
||||
public void Delete(IEnumerable<IMemberType> memberTypes, int userId = 0)
|
||||
{
|
||||
var asArray = memberTypes.ToArray();
|
||||
|
||||
|
||||
using (new WriteLock(Locker))
|
||||
{
|
||||
using (var scope = UowProvider.ScopeProvider.CreateScope())
|
||||
|
||||
@@ -313,6 +313,8 @@
|
||||
<Compile Include="Deploy\Direction.cs" />
|
||||
<Compile Include="Deploy\IFileStore.cs" />
|
||||
<Compile Include="Events\EventDefinitionFilter.cs" />
|
||||
<Compile Include="Events\IDeletingMediaFilesEventArgs.cs" />
|
||||
<Compile Include="Events\ScopeEventDispatcherBase.cs" />
|
||||
<Compile Include="OrderedHashSet.cs" />
|
||||
<Compile Include="Events\UninstallPackageEventArgs.cs" />
|
||||
<Compile Include="Models\GridValue.cs" />
|
||||
@@ -362,7 +364,6 @@
|
||||
<Compile Include="Events\MoveEventInfo.cs" />
|
||||
<Compile Include="Events\PublishEventArgs.cs" />
|
||||
<Compile Include="Events\RecycleBinEventArgs.cs" />
|
||||
<Compile Include="Events\EventsDispatchMode.cs" />
|
||||
<Compile Include="Events\TransientMessagesFactory.cs" />
|
||||
<Compile Include="Events\TypedEventHandler.cs" />
|
||||
<Compile Include="Events\MoveEventArgs.cs" />
|
||||
@@ -562,7 +563,7 @@
|
||||
<Compile Include="Publishing\UnPublishedStatusType.cs" />
|
||||
<Compile Include="Publishing\UnPublishStatus.cs" />
|
||||
<Compile Include="SafeCallContext.cs" />
|
||||
<Compile Include="Scoping\ActionTime.cs" />
|
||||
<Compile Include="Scoping\IInstanceIdentifiable.cs" />
|
||||
<Compile Include="Scoping\IScope.cs" />
|
||||
<Compile Include="Scoping\IScopeInternal.cs" />
|
||||
<Compile Include="Scoping\IScopeProvider.cs" />
|
||||
|
||||
@@ -364,7 +364,8 @@ namespace Umbraco.Tests.IO
|
||||
|
||||
ss.Complete();
|
||||
|
||||
Assert.IsTrue(File.Exists(path + "/ShadowSystem/path/to/some/dir/f1.txt")); // *not* cleaning
|
||||
// yes we are cleaning now
|
||||
//Assert.IsTrue(File.Exists(path + "/ShadowSystem/path/to/some/dir/f1.txt")); // *not* cleaning
|
||||
Assert.IsTrue(File.Exists(path + "/ShadowTests/path/to/some/dir/f1.txt"));
|
||||
Assert.IsFalse(File.Exists(path + "/ShadowTests/sub/sub/f2.txt"));
|
||||
}
|
||||
@@ -558,7 +559,7 @@ namespace Umbraco.Tests.IO
|
||||
Assert.AreEqual(1, ae.InnerExceptions.Count);
|
||||
e = ae.InnerExceptions[0];
|
||||
Assert.IsNotNull(e.InnerException);
|
||||
Assert.IsInstanceOf<UnauthorizedAccessException>(e.InnerException);
|
||||
Assert.IsInstanceOf<Exception>(e.InnerException);
|
||||
}
|
||||
|
||||
// still, the rest of the changes has been applied ok
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
using System.Data;
|
||||
using System.Runtime.Remoting.Messaging;
|
||||
using NUnit.Framework;
|
||||
using Umbraco.Core;
|
||||
using Umbraco.Core.Persistence;
|
||||
using Umbraco.Core.Scoping;
|
||||
using Umbraco.Tests.TestHelpers;
|
||||
@@ -38,11 +39,14 @@ namespace Umbraco.Tests.Scoping
|
||||
_database.BeginTransaction(); // opens and maintains a connection
|
||||
|
||||
// the test is leaking a scope with a non-null database
|
||||
var contextScope = CallContext.LogicalGetData(ScopeProvider.ScopeItemKey);
|
||||
Assert.IsNotNull(contextScope);
|
||||
Assert.IsInstanceOf<NoScope>(CallContext.LogicalGetData(ScopeProvider.ScopeItemKey));
|
||||
Assert.IsNotNull(((NoScope) contextScope).DatabaseOrNull);
|
||||
Assert.AreSame(_database, ((NoScope)contextScope).DatabaseOrNull);
|
||||
var contextGuid = CallContext.LogicalGetData(ScopeProvider.ScopeItemKey).AsGuid();
|
||||
Assert.AreNotEqual(Guid.Empty, contextGuid);
|
||||
|
||||
// only if Core.DEBUG_SCOPES are defined
|
||||
//var contextScope = DatabaseContext.ScopeProvider.CallContextObjects[contextGuid] as NoScope;
|
||||
//Assert.IsNotNull(contextScope);
|
||||
//Assert.IsNotNull(contextScope.DatabaseOrNull);
|
||||
//Assert.AreSame(_database, contextScope.DatabaseOrNull);
|
||||
|
||||
// save the connection
|
||||
_connection = _database.Connection;
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Moq;
|
||||
using NUnit.Framework;
|
||||
@@ -8,7 +9,6 @@ using Umbraco.Core.Scoping;
|
||||
|
||||
namespace Umbraco.Tests.Scoping
|
||||
{
|
||||
|
||||
[TestFixture]
|
||||
public class ScopeEventDispatcherTests
|
||||
{
|
||||
@@ -21,11 +21,15 @@ namespace Umbraco.Tests.Scoping
|
||||
DoThing3 = null;
|
||||
}
|
||||
|
||||
[TestCase(EventsDispatchMode.PassThrough, true, true)]
|
||||
[TestCase(EventsDispatchMode.PassThrough, true, false)]
|
||||
[TestCase(EventsDispatchMode.PassThrough, false, true)]
|
||||
[TestCase(EventsDispatchMode.PassThrough, false, false)]
|
||||
public void PassThroughCancelable(EventsDispatchMode mode, bool cancel, bool complete)
|
||||
[TestCase(false, true, true)]
|
||||
[TestCase(false, true, false)]
|
||||
[TestCase(false, false, true)]
|
||||
[TestCase(false, false, false)]
|
||||
[TestCase(true, true, true)]
|
||||
[TestCase(true, true, false)]
|
||||
[TestCase(true, false, true)]
|
||||
[TestCase(true, false, false)]
|
||||
public void EventsHandling(bool passive, bool cancel, bool complete)
|
||||
{
|
||||
var counter1 = 0;
|
||||
var counter2 = 0;
|
||||
@@ -34,7 +38,7 @@ namespace Umbraco.Tests.Scoping
|
||||
DoThing2 += (sender, args) => { counter2++; };
|
||||
|
||||
var scopeProvider = new ScopeProvider(Mock.Of<IDatabaseFactory2>());
|
||||
using (var scope = scopeProvider.CreateScope(dispatchMode: mode))
|
||||
using (var scope = scopeProvider.CreateScope(eventDispatcher: passive ? new PassiveEventDispatcher() : null))
|
||||
{
|
||||
var cancelled = scope.Events.DispatchCancelable(DoThing1, this, new SaveEventArgs<string>("test"));
|
||||
if (cancelled == false)
|
||||
@@ -43,22 +47,15 @@ namespace Umbraco.Tests.Scoping
|
||||
scope.Complete();
|
||||
}
|
||||
|
||||
var expected1 = mode == EventsDispatchMode.Passive ? 0 : 1;
|
||||
var expected1 = passive ? 0 : 1;
|
||||
Assert.AreEqual(expected1, counter1);
|
||||
|
||||
var expected2 = -1;
|
||||
switch (mode)
|
||||
{
|
||||
case EventsDispatchMode.PassThrough:
|
||||
expected2 = cancel ? 0 : 1;
|
||||
break;
|
||||
case EventsDispatchMode.Scope:
|
||||
expected2 = cancel ? 0 : (complete ? 1 : 0);
|
||||
break;
|
||||
case EventsDispatchMode.Passive:
|
||||
expected2 = 0;
|
||||
break;
|
||||
}
|
||||
int expected2;
|
||||
if (passive)
|
||||
expected2 = 0;
|
||||
else
|
||||
expected2 = cancel ? 0 : (complete ? 1 : 0);
|
||||
|
||||
Assert.AreEqual(expected2, counter2);
|
||||
}
|
||||
|
||||
@@ -70,7 +67,7 @@ namespace Umbraco.Tests.Scoping
|
||||
DoThing3 += OnDoThingFail;
|
||||
|
||||
var scopeProvider = new ScopeProvider(Mock.Of<IDatabaseFactory2>());
|
||||
using (var scope = scopeProvider.CreateScope(dispatchMode: EventsDispatchMode.Passive))
|
||||
using (var scope = scopeProvider.CreateScope(eventDispatcher: new PassiveEventDispatcher()))
|
||||
{
|
||||
scope.Events.Dispatch(DoThing1, this, new SaveEventArgs<string>("test"));
|
||||
scope.Events.Dispatch(DoThing2, this, new SaveEventArgs<int>(0));
|
||||
@@ -94,45 +91,14 @@ namespace Umbraco.Tests.Scoping
|
||||
|
||||
[TestCase(true)]
|
||||
[TestCase(false)]
|
||||
public void EventsDispatchmode_PassThrough(bool complete)
|
||||
{
|
||||
var counter = 0;
|
||||
|
||||
DoThing1 += (sender, args) => { counter++; };
|
||||
DoThing2 += (sender, args) => { counter++; };
|
||||
DoThing3 += (sender, args) => { counter++; };
|
||||
|
||||
var scopeProvider = new ScopeProvider(Mock.Of<IDatabaseFactory2>());
|
||||
using (var scope = scopeProvider.CreateScope(dispatchMode: EventsDispatchMode.PassThrough))
|
||||
{
|
||||
scope.Events.Dispatch(DoThing1, this, new SaveEventArgs<string>("test"));
|
||||
scope.Events.Dispatch(DoThing2, this, new SaveEventArgs<int>(0));
|
||||
scope.Events.Dispatch(DoThing3, this, new SaveEventArgs<decimal>(0));
|
||||
|
||||
// events have not been queued
|
||||
Assert.IsEmpty(scope.Events.GetEvents(EventDefinitionFilter.All));
|
||||
|
||||
// events have been raised
|
||||
Assert.AreEqual(3, counter);
|
||||
|
||||
if (complete)
|
||||
scope.Complete();
|
||||
}
|
||||
|
||||
// nothing has changed
|
||||
Assert.AreEqual(3, counter);
|
||||
}
|
||||
|
||||
[TestCase(true)]
|
||||
[TestCase(false)]
|
||||
public void EventsDispatchMode_Passive(bool complete)
|
||||
public void EventsDispatching_Passive(bool complete)
|
||||
{
|
||||
DoThing1 += OnDoThingFail;
|
||||
DoThing2 += OnDoThingFail;
|
||||
DoThing3 += OnDoThingFail;
|
||||
|
||||
var scopeProvider = new ScopeProvider(Mock.Of<IDatabaseFactory2>());
|
||||
using (var scope = scopeProvider.CreateScope(dispatchMode: EventsDispatchMode.Passive))
|
||||
using (var scope = scopeProvider.CreateScope(eventDispatcher: new PassiveEventDispatcher()))
|
||||
{
|
||||
scope.Events.Dispatch(DoThing1, this, new SaveEventArgs<string>("test"));
|
||||
scope.Events.Dispatch(DoThing2, this, new SaveEventArgs<int>(0));
|
||||
@@ -150,7 +116,7 @@ namespace Umbraco.Tests.Scoping
|
||||
|
||||
[TestCase(true)]
|
||||
[TestCase(false)]
|
||||
public void EventsDispatchMode_Scope(bool complete)
|
||||
public void EventsDispatching_Scope(bool complete)
|
||||
{
|
||||
var counter = 0;
|
||||
IScope ambientScope = null;
|
||||
@@ -170,7 +136,7 @@ namespace Umbraco.Tests.Scoping
|
||||
};
|
||||
|
||||
Guid guid;
|
||||
using (var scope = scopeProvider.CreateScope(dispatchMode: EventsDispatchMode.Scope))
|
||||
using (var scope = scopeProvider.CreateScope())
|
||||
{
|
||||
Assert.IsNotNull(scopeProvider.AmbientContext);
|
||||
guid = scopeProvider.Context.Enlist("value", Guid.NewGuid, (c, o) => { });
|
||||
@@ -181,6 +147,7 @@ namespace Umbraco.Tests.Scoping
|
||||
|
||||
// events have been queued
|
||||
Assert.AreEqual(3, scope.Events.GetEvents(EventDefinitionFilter.All).Count());
|
||||
Assert.AreEqual(0, counter);
|
||||
|
||||
if (complete)
|
||||
scope.Complete();
|
||||
@@ -215,5 +182,17 @@ namespace Umbraco.Tests.Scoping
|
||||
public static event EventHandler<SaveEventArgs<int>> DoThing2;
|
||||
|
||||
public static event TypedEventHandler<ScopeEventDispatcherTests, SaveEventArgs<decimal>> DoThing3;
|
||||
}
|
||||
|
||||
public class PassiveEventDispatcher : ScopeEventDispatcherBase
|
||||
{
|
||||
public PassiveEventDispatcher()
|
||||
: base(false)
|
||||
{ }
|
||||
|
||||
protected override void ScopeExitCompleted()
|
||||
{
|
||||
// do nothing
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,10 +1,12 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Runtime.Remoting.Messaging;
|
||||
using System.Threading;
|
||||
using NUnit.Framework;
|
||||
using Umbraco.Core;
|
||||
using Umbraco.Core.Persistence;
|
||||
using Umbraco.Core.Scoping;
|
||||
using Umbraco.Tests.TestHelpers;
|
||||
using Umbraco.Core.Events;
|
||||
|
||||
namespace Umbraco.Tests.Scoping
|
||||
{
|
||||
@@ -97,6 +99,54 @@ namespace Umbraco.Tests.Scoping
|
||||
Assert.IsNull(scopeProvider.AmbientScope);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void NestedMigrateScope()
|
||||
{
|
||||
var scopeProvider = DatabaseContext.ScopeProvider;
|
||||
Assert.IsNull(scopeProvider.AmbientScope);
|
||||
|
||||
var httpContextItems = new Hashtable();
|
||||
ScopeProvider.HttpContextItemsGetter = () => httpContextItems;
|
||||
try
|
||||
{
|
||||
using (var scope = scopeProvider.CreateScope())
|
||||
{
|
||||
Assert.IsInstanceOf<Scope>(scope);
|
||||
Assert.IsNotNull(scopeProvider.AmbientScope);
|
||||
Assert.AreSame(scope, scopeProvider.AmbientScope);
|
||||
Assert.AreSame(scope, httpContextItems[ScopeProvider.ScopeItemKey]);
|
||||
|
||||
// only if Core.DEBUG_SCOPES are defined
|
||||
//Assert.IsEmpty(scopeProvider.CallContextObjects);
|
||||
|
||||
using (var nested = scopeProvider.CreateScope(callContext: true))
|
||||
{
|
||||
Assert.IsInstanceOf<Scope>(nested);
|
||||
Assert.IsNotNull(scopeProvider.AmbientScope);
|
||||
Assert.AreSame(nested, scopeProvider.AmbientScope);
|
||||
Assert.AreSame(scope, ((Scope) nested).ParentScope);
|
||||
|
||||
// it's moved over to call context
|
||||
Assert.IsNull(httpContextItems[ScopeProvider.ScopeItemKey]);
|
||||
var callContextKey = CallContext.LogicalGetData(ScopeProvider.ScopeItemKey).AsGuid();
|
||||
Assert.AreNotEqual(Guid.Empty, callContextKey);
|
||||
|
||||
// only if Core.DEBUG_SCOPES are defined
|
||||
//var ccnested = scopeProvider.CallContextObjects[callContextKey];
|
||||
//Assert.AreSame(nested, ccnested);
|
||||
}
|
||||
|
||||
// it's naturally back in http context
|
||||
Assert.AreSame(scope, httpContextItems[ScopeProvider.ScopeItemKey]);
|
||||
}
|
||||
Assert.IsNull(scopeProvider.AmbientScope);
|
||||
}
|
||||
finally
|
||||
{
|
||||
ScopeProvider.HttpContextItemsGetter = null;
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void NestedCreateScopeContext()
|
||||
{
|
||||
@@ -478,17 +528,82 @@ namespace Umbraco.Tests.Scoping
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void CallContextScope()
|
||||
public void CallContextScope1()
|
||||
{
|
||||
var scopeProvider = DatabaseContext.ScopeProvider;
|
||||
var scope = scopeProvider.CreateScope();
|
||||
Assert.IsNotNull(scopeProvider.AmbientScope);
|
||||
using (new SafeCallContext())
|
||||
using (var scope = scopeProvider.CreateScope())
|
||||
{
|
||||
Assert.IsNull(scopeProvider.AmbientScope);
|
||||
Assert.IsNotNull(scopeProvider.AmbientScope);
|
||||
Assert.IsNotNull(scopeProvider.AmbientContext);
|
||||
using (new SafeCallContext())
|
||||
{
|
||||
Assert.IsNull(scopeProvider.AmbientScope);
|
||||
Assert.IsNull(scopeProvider.AmbientContext);
|
||||
|
||||
using (var newScope = scopeProvider.CreateScope())
|
||||
{
|
||||
Assert.IsNotNull(scopeProvider.AmbientScope);
|
||||
Assert.IsNull(scopeProvider.AmbientScope.ParentScope);
|
||||
Assert.IsNotNull(scopeProvider.AmbientContext);
|
||||
}
|
||||
|
||||
Assert.IsNull(scopeProvider.AmbientScope);
|
||||
Assert.IsNull(scopeProvider.AmbientContext);
|
||||
}
|
||||
Assert.IsNotNull(scopeProvider.AmbientScope);
|
||||
Assert.AreSame(scope, scopeProvider.AmbientScope);
|
||||
}
|
||||
|
||||
Assert.IsNull(scopeProvider.AmbientScope);
|
||||
Assert.IsNull(scopeProvider.AmbientContext);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void CallContextScope2()
|
||||
{
|
||||
var scopeProvider = DatabaseContext.ScopeProvider;
|
||||
Assert.IsNull(scopeProvider.AmbientScope);
|
||||
|
||||
var httpContextItems = new Hashtable();
|
||||
ScopeProvider.HttpContextItemsGetter = () => httpContextItems;
|
||||
try
|
||||
{
|
||||
using (var scope = scopeProvider.CreateScope())
|
||||
{
|
||||
Assert.IsNotNull(scopeProvider.AmbientScope);
|
||||
Assert.IsNotNull(scopeProvider.AmbientContext);
|
||||
using (new SafeCallContext())
|
||||
{
|
||||
// pretend it's another thread
|
||||
ScopeProvider.HttpContextItemsGetter = null;
|
||||
|
||||
Assert.IsNull(scopeProvider.AmbientScope);
|
||||
Assert.IsNull(scopeProvider.AmbientContext);
|
||||
|
||||
using (var newScope = scopeProvider.CreateScope())
|
||||
{
|
||||
Assert.IsNotNull(scopeProvider.AmbientScope);
|
||||
Assert.IsNull(scopeProvider.AmbientScope.ParentScope);
|
||||
Assert.IsNotNull(scopeProvider.AmbientContext);
|
||||
}
|
||||
|
||||
Assert.IsNull(scopeProvider.AmbientScope);
|
||||
Assert.IsNull(scopeProvider.AmbientContext);
|
||||
|
||||
// back to original thread
|
||||
ScopeProvider.HttpContextItemsGetter = () => httpContextItems;
|
||||
}
|
||||
Assert.IsNotNull(scopeProvider.AmbientScope);
|
||||
Assert.AreSame(scope, scopeProvider.AmbientScope);
|
||||
}
|
||||
|
||||
Assert.IsNull(scopeProvider.AmbientScope);
|
||||
Assert.IsNull(scopeProvider.AmbientContext);
|
||||
}
|
||||
finally
|
||||
{
|
||||
ScopeProvider.HttpContextItemsGetter = null;
|
||||
}
|
||||
Assert.IsNotNull(scopeProvider.AmbientScope);
|
||||
Assert.AreSame(scope, scopeProvider.AmbientScope);
|
||||
}
|
||||
|
||||
[Test]
|
||||
|
||||
@@ -213,12 +213,8 @@ namespace Umbraco.Tests.Scoping
|
||||
|
||||
var scopeProvider = ApplicationContext.Current.ScopeProvider as IScopeProviderInternal;
|
||||
Assert.IsNotNull(scopeProvider);
|
||||
if (complete)
|
||||
// because some event handlers trigger xml refresh with directly uses the DB
|
||||
Assert.IsNotNull(scopeProvider.AmbientScope);
|
||||
else
|
||||
// because nothing happened
|
||||
Assert.IsNull(scopeProvider.AmbientScope);
|
||||
// ambient scope may be null, or maybe not, depending on whether the code that
|
||||
// was called did proper scoped work, or some direct (NoScope) use of the database
|
||||
Assert.IsNull(scopeProvider.AmbientContext);
|
||||
|
||||
// limited number of clones!
|
||||
|
||||
@@ -72,11 +72,12 @@ namespace Umbraco.Tests.TestHelpers
|
||||
GetDbProviderName(),
|
||||
Logger);
|
||||
|
||||
// fixme - bah - this is needed to reset static properties? Stephen to update this note
|
||||
// ensure we start tests in a clean state ie without any scope in context
|
||||
// anything that used a true 'Scope' would have removed it, but there could
|
||||
// be a rogue 'NoScope' there - and we want to make sure it is gone
|
||||
var scopeProvider = new ScopeProvider(null);
|
||||
if (scopeProvider.AmbientScope != null)
|
||||
scopeProvider.AmbientScope.Dispose();
|
||||
scopeProvider.AmbientScope = null;
|
||||
scopeProvider.AmbientScope.Dispose(); // removes scope from context
|
||||
|
||||
base.Initialize();
|
||||
|
||||
|
||||
+38
-15
@@ -40,27 +40,50 @@ Use this directive to generate a list of breadcrumbs.
|
||||
|
||||
@param {array} ancestors Array of ancestors
|
||||
@param {string} entityType The content entity type (member, media, content).
|
||||
@param {callback} Callback when an ancestor is clicked. It will override the default link behaviour.
|
||||
**/
|
||||
|
||||
(function() {
|
||||
'use strict';
|
||||
(function () {
|
||||
'use strict';
|
||||
|
||||
function BreadcrumbsDirective() {
|
||||
function BreadcrumbsDirective() {
|
||||
|
||||
var directive = {
|
||||
restrict: 'E',
|
||||
replace: true,
|
||||
templateUrl: 'views/components/editor/umb-breadcrumbs.html',
|
||||
scope: {
|
||||
ancestors: "=",
|
||||
entityType: "@"
|
||||
}
|
||||
};
|
||||
function link(scope, el, attr, ctrl) {
|
||||
|
||||
return directive;
|
||||
scope.allowOnOpen = false;
|
||||
|
||||
}
|
||||
scope.open = function(ancestor) {
|
||||
if(scope.onOpen && scope.allowOnOpen) {
|
||||
scope.onOpen({'ancestor': ancestor});
|
||||
}
|
||||
};
|
||||
|
||||
angular.module('umbraco.directives').directive('umbBreadcrumbs', BreadcrumbsDirective);
|
||||
function onInit() {
|
||||
if ("onOpen" in attr) {
|
||||
scope.allowOnOpen = true;
|
||||
}
|
||||
}
|
||||
|
||||
onInit();
|
||||
|
||||
}
|
||||
|
||||
var directive = {
|
||||
restrict: 'E',
|
||||
replace: true,
|
||||
templateUrl: 'views/components/editor/umb-breadcrumbs.html',
|
||||
scope: {
|
||||
ancestors: "=",
|
||||
entityType: "@",
|
||||
onOpen: "&"
|
||||
},
|
||||
link: link
|
||||
};
|
||||
|
||||
return directive;
|
||||
|
||||
}
|
||||
|
||||
angular.module('umbraco.directives').directive('umbBreadcrumbs', BreadcrumbsDirective);
|
||||
|
||||
})();
|
||||
|
||||
+3
-2
@@ -21,7 +21,8 @@ function umbTreeDirective($compile, $log, $q, $rootScope, treeService, notificat
|
||||
customtreeparams: '@',
|
||||
eventhandler: '=',
|
||||
enablecheckboxes: '@',
|
||||
enablelistviewsearch: '@'
|
||||
enablelistviewsearch: '@',
|
||||
enablelistviewexpand: '@'
|
||||
},
|
||||
|
||||
compile: function(element, attrs) {
|
||||
@@ -35,7 +36,7 @@ function umbTreeDirective($compile, $log, $q, $rootScope, treeService, notificat
|
||||
'<a class="umb-options" ng-hide="tree.root.isContainer || !tree.root.menuUrl" ng-click="options(tree.root, $event)" ng-swipe-right="options(tree.root, $event)"><i></i><i></i><i></i></a>' +
|
||||
'</div>';
|
||||
template += '<ul>' +
|
||||
'<umb-tree-item ng-repeat="child in tree.root.children" eventhandler="eventhandler" node="child" current-node="currentNode" tree="this" section="{{section}}" ng-animate="animation()"></umb-tree-item>' +
|
||||
'<umb-tree-item ng-repeat="child in tree.root.children" enablelistviewexpand="{{enablelistviewexpand}}" eventhandler="eventhandler" node="child" current-node="currentNode" tree="this" section="{{section}}" ng-animate="animation()"></umb-tree-item>' +
|
||||
'</ul>' +
|
||||
'</li>' +
|
||||
'</ul>';
|
||||
|
||||
+8
-6
@@ -27,6 +27,7 @@ angular.module("umbraco.directives")
|
||||
section: '@',
|
||||
eventhandler: '=',
|
||||
currentNode: '=',
|
||||
enablelistviewexpand: '@',
|
||||
node: '=',
|
||||
tree: '='
|
||||
},
|
||||
@@ -38,7 +39,7 @@ angular.module("umbraco.directives")
|
||||
'<div ng-class="getNodeCssClass(node)" ng-swipe-right="options(node, $event)" >' +
|
||||
//NOTE: This ins element is used to display the search icon if the node is a container/listview and the tree is currently in dialog
|
||||
//'<ins ng-if="tree.enablelistviewsearch && node.metaData.isContainer" class="umb-tree-node-search icon-search" ng-click="searchNode(node, $event)" alt="searchAltText"></ins>' +
|
||||
'<ins ng-class="{\'icon-navigation-right\': !node.expanded, \'icon-navigation-down\': node.expanded}" ng-click="load(node)"> </ins>' +
|
||||
'<ins ng-class="{\'icon-navigation-right\': !node.expanded || node.metaData.isContainer, \'icon-navigation-down\': node.expanded && !node.metaData.isContainer}" ng-click="load(node)"> </ins>' +
|
||||
'<i class="icon umb-tree-icon sprTree" ng-click="select(node, $event)"></i>' +
|
||||
'<a href="#/{{node.routePath}}" ng-click="select(node, $event)"></a>' +
|
||||
//NOTE: These are the 'option' elipses
|
||||
@@ -74,11 +75,12 @@ angular.module("umbraco.directives")
|
||||
|
||||
//toggle visibility of last 'ins' depending on children
|
||||
//visibility still ensure the space is "reserved", so both nodes with and without children are aligned.
|
||||
if (!node.hasChildren) {
|
||||
element.find("ins").last().css("visibility", "hidden");
|
||||
|
||||
if (node.hasChildren || node.metaData.isContainer && scope.enablelistviewexpand === "true") {
|
||||
element.find("ins").last().css("visibility", "visible");
|
||||
}
|
||||
else {
|
||||
element.find("ins").last().css("visibility", "visible");
|
||||
element.find("ins").last().css("visibility", "hidden");
|
||||
}
|
||||
|
||||
var icon = element.find("i:first");
|
||||
@@ -192,7 +194,7 @@ angular.module("umbraco.directives")
|
||||
emits treeNodeCollapsing event if already expanded and treeNodeExpanding if collapsed
|
||||
*/
|
||||
scope.load = function (node) {
|
||||
if (node.expanded) {
|
||||
if (node.expanded && !node.metaData.isContainer) {
|
||||
deleteAnimations = false;
|
||||
emitEvent("treeNodeCollapsing", { tree: scope.tree, node: node, element: element });
|
||||
node.expanded = false;
|
||||
@@ -227,7 +229,7 @@ angular.module("umbraco.directives")
|
||||
|
||||
setupNodeDom(scope.node, scope.tree);
|
||||
|
||||
var template = '<ul ng-class="{collapsed: !node.expanded}"><umb-tree-item ng-repeat="child in node.children" eventhandler="eventhandler" tree="tree" current-node="currentNode" node="child" section="{{section}}" ng-animate="animation()"></umb-tree-item></ul>';
|
||||
var template = '<ul ng-class="{collapsed: !node.expanded}"><umb-tree-item ng-repeat="child in node.children" enablelistviewexpand="{{enablelistviewexpand}}" eventhandler="eventhandler" tree="tree" current-node="currentNode" node="child" section="{{section}}" ng-animate="animation()"></umb-tree-item></ul>';
|
||||
var newElement = angular.element(template);
|
||||
$compile(newElement)(scope);
|
||||
element.append(newElement);
|
||||
|
||||
+209
@@ -0,0 +1,209 @@
|
||||
(function () {
|
||||
'use strict';
|
||||
|
||||
function MiniListViewDirective(contentResource, memberResource, mediaResource) {
|
||||
|
||||
function link(scope, el, attr, ctrl) {
|
||||
|
||||
scope.search = "";
|
||||
scope.miniListViews = [];
|
||||
scope.breadcrumb = [];
|
||||
|
||||
var miniListViewsHistory = [];
|
||||
var goingForward = true;
|
||||
var skipAnimation = true;
|
||||
|
||||
function onInit() {
|
||||
open(scope.node);
|
||||
}
|
||||
|
||||
function open(node) {
|
||||
|
||||
goingForward = true;
|
||||
|
||||
var miniListView = {
|
||||
node: node,
|
||||
loading: true,
|
||||
pagination: {
|
||||
pageSize: 10,
|
||||
pageNumber: 1,
|
||||
filter: '',
|
||||
orderDirection: "Ascending",
|
||||
orderBy: "SortOrder",
|
||||
orderBySystemField: true
|
||||
}
|
||||
};
|
||||
|
||||
// clear and push mini list view in dom so we only render 1 view
|
||||
scope.miniListViews = [];
|
||||
scope.miniListViews.push(miniListView);
|
||||
|
||||
// store in history so we quickly can navigate back
|
||||
miniListViewsHistory.push(miniListView);
|
||||
|
||||
// get children
|
||||
getChildrenForMiniListView(miniListView);
|
||||
|
||||
makeBreadcrumb();
|
||||
|
||||
}
|
||||
|
||||
function getChildrenForMiniListView(miniListView) {
|
||||
|
||||
// start loading animation list view
|
||||
miniListView.loading = true;
|
||||
|
||||
// setup the correct resource depending on section
|
||||
var resource = "";
|
||||
|
||||
if (scope.entityType === "Member") {
|
||||
resource = memberResource.getPagedResults;
|
||||
} else if (scope.entityType === "Media") {
|
||||
resource = mediaResource.getChildren;
|
||||
} else {
|
||||
resource = contentResource.getChildren;
|
||||
}
|
||||
|
||||
resource(miniListView.node.id, miniListView.pagination)
|
||||
.then(function (data) {
|
||||
// update children
|
||||
miniListView.children = data.items;
|
||||
// update pagination
|
||||
miniListView.pagination.totalItems = data.totalItems;
|
||||
miniListView.pagination.totalPages = data.totalPages;
|
||||
// stop load indicator
|
||||
miniListView.loading = false;
|
||||
});
|
||||
}
|
||||
|
||||
scope.openNode = function(event, node) {
|
||||
open(node);
|
||||
event.stopPropagation();
|
||||
};
|
||||
|
||||
scope.selectNode = function(node) {
|
||||
if(scope.onSelect) {
|
||||
scope.onSelect({'node': node});
|
||||
}
|
||||
};
|
||||
|
||||
/* Pagination */
|
||||
scope.goToPage = function(pageNumber, miniListView) {
|
||||
// set new page number
|
||||
miniListView.pagination.pageNumber = pageNumber;
|
||||
// get children
|
||||
getChildrenForMiniListView(miniListView);
|
||||
};
|
||||
|
||||
/* Breadcrumb */
|
||||
scope.clickBreadcrumb = function(ancestor) {
|
||||
|
||||
var found = false;
|
||||
goingForward = false;
|
||||
|
||||
angular.forEach(miniListViewsHistory, function(historyItem, index){
|
||||
// We need to make sure we can compare the two id's.
|
||||
// Some id's are integers and others are strings.
|
||||
// Members have string ids like "all-members".
|
||||
if(historyItem.node.id.toString() === ancestor.id.toString()) {
|
||||
// load the list view from history
|
||||
scope.miniListViews = [];
|
||||
scope.miniListViews.push(historyItem);
|
||||
// clean up history - remove all children after
|
||||
miniListViewsHistory.splice(index + 1, miniListViewsHistory.length);
|
||||
found = true;
|
||||
}
|
||||
});
|
||||
|
||||
if(!found) {
|
||||
// if we can't find the view in the history - close the list view
|
||||
scope.exitMiniListView();
|
||||
}
|
||||
|
||||
// update the breadcrumb
|
||||
makeBreadcrumb();
|
||||
|
||||
};
|
||||
|
||||
scope.showBackButton = function() {
|
||||
// don't show the back button if the start node is a list view
|
||||
if(scope.node.metaData && scope.node.metaData.IsContainer || scope.node.isContainer) {
|
||||
return false;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
scope.exitMiniListView = function() {
|
||||
miniListViewsHistory = [];
|
||||
scope.miniListViews = [];
|
||||
if(scope.onClose) {
|
||||
scope.onClose();
|
||||
}
|
||||
};
|
||||
|
||||
function makeBreadcrumb() {
|
||||
scope.breadcrumb = [];
|
||||
angular.forEach(miniListViewsHistory, function(historyItem){
|
||||
scope.breadcrumb.push(historyItem.node);
|
||||
});
|
||||
}
|
||||
|
||||
/* Search */
|
||||
scope.searchMiniListView = function(search, miniListView) {
|
||||
// set search value
|
||||
miniListView.pagination.filter = search;
|
||||
// reset pagination
|
||||
miniListView.pagination.pageNumber = 1;
|
||||
// start loading animation list view
|
||||
miniListView.loading = true;
|
||||
searchMiniListView(miniListView);
|
||||
};
|
||||
|
||||
var searchMiniListView = _.debounce(function (miniListView) {
|
||||
scope.$apply(function () {
|
||||
getChildrenForMiniListView(miniListView);
|
||||
});
|
||||
}, 500);
|
||||
|
||||
/* Animation */
|
||||
scope.getMiniListViewAnimation = function() {
|
||||
|
||||
// disable the first "slide-in-animation"" if the start node is a list view
|
||||
if(scope.node.metaData && scope.node.metaData.IsContainer && skipAnimation || scope.node.isContainer && skipAnimation) {
|
||||
skipAnimation = false;
|
||||
return;
|
||||
}
|
||||
|
||||
if(goingForward) {
|
||||
return 'umb-mini-list-view--forward';
|
||||
} else {
|
||||
return 'umb-mini-list-view--backwards';
|
||||
}
|
||||
};
|
||||
|
||||
onInit();
|
||||
|
||||
}
|
||||
|
||||
var directive = {
|
||||
restrict: 'E',
|
||||
replace: true,
|
||||
templateUrl: 'views/components/umb-mini-list-view.html',
|
||||
scope: {
|
||||
node: "=",
|
||||
entityType: "@",
|
||||
startNodeId: "=",
|
||||
onSelect: "&",
|
||||
onClose: "&"
|
||||
},
|
||||
link: link
|
||||
};
|
||||
|
||||
return directive;
|
||||
|
||||
}
|
||||
|
||||
angular.module('umbraco.directives').directive('umbMiniListView', MiniListViewDirective);
|
||||
|
||||
})();
|
||||
+32
-16
@@ -134,25 +134,40 @@ Use this directive to generate a pagination.
|
||||
|
||||
}
|
||||
|
||||
scope.next = function() {
|
||||
if (scope.onNext && scope.pageNumber < scope.totalPages) {
|
||||
scope.pageNumber++;
|
||||
scope.onNext(scope.pageNumber);
|
||||
}
|
||||
scope.next = function () {
|
||||
if (scope.pageNumber < scope.totalPages) {
|
||||
scope.pageNumber++;
|
||||
if (scope.onNext) {
|
||||
scope.onNext(scope.pageNumber);
|
||||
}
|
||||
if (scope.onChange) {
|
||||
scope.onChange({ "pageNumber": scope.pageNumber });
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
scope.prev = function(pageNumber) {
|
||||
if (scope.onPrev && scope.pageNumber > 1) {
|
||||
scope.pageNumber--;
|
||||
scope.onPrev(scope.pageNumber);
|
||||
}
|
||||
scope.prev = function (pageNumber) {
|
||||
if (scope.pageNumber > 1) {
|
||||
scope.pageNumber--;
|
||||
if (scope.onPrev) {
|
||||
scope.onPrev(scope.pageNumber);
|
||||
}
|
||||
if (scope.onChange) {
|
||||
scope.onChange({ "pageNumber": scope.pageNumber });
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
scope.goToPage = function(pageNumber) {
|
||||
if(scope.onGoToPage) {
|
||||
scope.pageNumber = pageNumber + 1;
|
||||
scope.onGoToPage(scope.pageNumber);
|
||||
}
|
||||
scope.goToPage = function (pageNumber) {
|
||||
scope.pageNumber = pageNumber + 1;
|
||||
if (scope.onGoToPage) {
|
||||
scope.onGoToPage(scope.pageNumber);
|
||||
}
|
||||
if (scope.onChange) {
|
||||
if (scope.onChange) {
|
||||
scope.onChange({ "pageNumber": scope.pageNumber });
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
var unbindPageNumberWatcher = scope.$watch('pageNumber', function(newValue, oldValue){
|
||||
@@ -176,7 +191,8 @@ Use this directive to generate a pagination.
|
||||
totalPages: "=",
|
||||
onNext: "=",
|
||||
onPrev: "=",
|
||||
onGoToPage: "="
|
||||
onGoToPage: "=",
|
||||
onChange: "&"
|
||||
},
|
||||
link: link
|
||||
};
|
||||
|
||||
@@ -118,6 +118,8 @@
|
||||
@import "components/umb-avatar.less";
|
||||
@import "components/umb-progress-bar.less";
|
||||
@import "components/umb-querybuilder.less";
|
||||
@import "components/umb-pagination.less";
|
||||
@import "components/umb-mini-list-view.less";
|
||||
|
||||
@import "components/buttons/umb-button.less";
|
||||
@import "components/buttons/umb-button-group.less";
|
||||
|
||||
@@ -0,0 +1,97 @@
|
||||
.umb-mini-list-view__title {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.umb-mini-list-view__title-text {
|
||||
font-size: 16px;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.umb-mini-list-view__title-icon {
|
||||
font-size: 20px;
|
||||
margin-right: 5px;
|
||||
}
|
||||
|
||||
.umb-mini-list-view__back {
|
||||
font-size: 11px;
|
||||
margin-right: 5px;
|
||||
color: @gray;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.umb-mini-list-view__back-icon {
|
||||
margin-right: 4px;
|
||||
height: 11px;
|
||||
line-height: 11px;
|
||||
}
|
||||
|
||||
.umb-mini-list-view__back-text {
|
||||
text-decoration: underline;
|
||||
margin-right: 5px;
|
||||
}
|
||||
|
||||
.umb-mini-list-view__back:hover {
|
||||
opacity: 1;
|
||||
text-decoration: none;
|
||||
color: @black;
|
||||
}
|
||||
|
||||
/* Animations */
|
||||
|
||||
/* Forward */
|
||||
|
||||
.umb-mini-list-view--forward-enter,
|
||||
.umb-mini-list-view--forward-leave
|
||||
{
|
||||
transition: 120ms cubic-bezier(0.250, 0.250, 0.750, 0.750) all;
|
||||
position: relative;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.umb-mini-list-view--forward-enter {
|
||||
left: 100%;
|
||||
}
|
||||
|
||||
.umb-mini-list-view--forward-enter.umb-mini-list-view--forward-enter-active {
|
||||
left: 0;
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.umb-mini-list-view--forward-leave {
|
||||
left: 0;
|
||||
}
|
||||
|
||||
.umb-mini-list-view--forward-leave.umb-mini-list-view--forward-leave-active{
|
||||
left: -100%;
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
/* Backwards */
|
||||
|
||||
.umb-mini-list-view--backwards-enter,
|
||||
.umb-mini-list-view--backwards-leave
|
||||
{
|
||||
transition: 120ms cubic-bezier(0.250, 0.250, 0.750, 0.750) all;
|
||||
position: relative;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.umb-mini-list-view--backwards-enter {
|
||||
right: 100%;
|
||||
}
|
||||
|
||||
.umb-mini-list-view--backwards-enter.umb-mini-list-view--backwards-enter-active {
|
||||
right: 0;
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.umb-mini-list-view--backwards-leave {
|
||||
left: 0;
|
||||
}
|
||||
|
||||
.umb-mini-list-view--backwards-leave.umb-mini-list-view--backwards-leave-active{
|
||||
right: -100%;
|
||||
opacity: 0;
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
.umb-pagination ul {
|
||||
box-shadow: none;
|
||||
}
|
||||
@@ -10,7 +10,7 @@
|
||||
flex-wrap: nowrap;
|
||||
justify-content: space-between;
|
||||
|
||||
min-width: 640px;
|
||||
min-width: auto;
|
||||
}
|
||||
|
||||
.umb-table.umb-table-inactive {
|
||||
@@ -103,11 +103,17 @@ input.umb-table__input {
|
||||
|
||||
|
||||
// Table Body Styles
|
||||
.umb-table-body {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.umb-table-body .umb-table-row {
|
||||
color: fade(@gray, 75%);
|
||||
border-top: 1px solid @grayLight;
|
||||
cursor: pointer;
|
||||
font-size: 13px;
|
||||
position: relative;
|
||||
min-height: 32px;
|
||||
|
||||
&:hover {
|
||||
background-color: fade(@grayLighter, 90%);
|
||||
@@ -198,30 +204,33 @@ input.umb-table__input {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// Table Row Styles
|
||||
.umb-table-row {
|
||||
display: flex;
|
||||
flex-flow: row nowrap;
|
||||
align-items: center;
|
||||
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.umb-table-body .umb-table-row--empty {
|
||||
flex: 1 1 auto;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
padding: 5px 0;
|
||||
cursor: auto;
|
||||
user-select: auto;
|
||||
}
|
||||
|
||||
.umb-table-body .umb-table-row--empty:hover {
|
||||
background-color: transparent;
|
||||
cursor: auto;
|
||||
}
|
||||
|
||||
.umb-table-row.-selected,
|
||||
.umb-table-row.-selected:hover {
|
||||
background-color: fade(@blueDark, 4%);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
// Table Cell Styles
|
||||
.umb-table-cell {
|
||||
display: flex;
|
||||
@@ -250,9 +259,44 @@ input.umb-table__input {
|
||||
padding: 15px 0;
|
||||
}
|
||||
|
||||
.umb-table-cell--auto-width {
|
||||
flex: 0 0 auto !important;
|
||||
}
|
||||
|
||||
.umb-table-cell--faded {
|
||||
opacity: 0.4;
|
||||
}
|
||||
|
||||
|
||||
// Increases the space for the name cell
|
||||
.umb-table__name {
|
||||
flex: 1 1 25%;
|
||||
max-width: 25%;
|
||||
}
|
||||
|
||||
.umb-table__loading-overlay {
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background-color: rgba(255, 255, 255, 0.7);
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.umb-table__row-expand {
|
||||
font-size: 12px;
|
||||
text-decoration: none;
|
||||
color: @black;
|
||||
}
|
||||
|
||||
.umb-table--condensed {
|
||||
|
||||
.umb-table-cell:first-of-type:not(.not-fixed) {
|
||||
padding-top: 10px;
|
||||
padding-bottom: 10px;
|
||||
}
|
||||
|
||||
.umb-table-body__icon {
|
||||
font-size: 20px;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -36,6 +36,7 @@
|
||||
|
||||
.red{color: @red;}
|
||||
.blue{color: @blue;}
|
||||
.black{color: @black;}
|
||||
|
||||
|
||||
//icon colors for tree icons
|
||||
@@ -351,4 +352,4 @@
|
||||
// SORTABLE
|
||||
// --------------------------------------------------
|
||||
@sortableHelperBg: rgba(4, 156, 219, 0.5);
|
||||
@sortablePlaceholderBg : @blue;
|
||||
@sortablePlaceholderBg : @blue;
|
||||
|
||||
+43
-29
@@ -1,34 +1,48 @@
|
||||
<div ng-controller="Umbraco.Overlays.TreePickerController" ng-init="init('content')">
|
||||
|
||||
<div class="umb-control-group">
|
||||
<umb-tree-search-box
|
||||
hide-search-callback="hideSearch"
|
||||
search-callback="onSearchResults"
|
||||
search-from-id="{{searchInfo.searchFromId}}"
|
||||
search-from-name="{{searchInfo.searchFromName}}"
|
||||
show-search="{{searchInfo.showSearch}}"
|
||||
section="content">
|
||||
</umb-tree-search-box>
|
||||
</div>
|
||||
<div ng-hide="miniListView">
|
||||
|
||||
<umb-tree-search-results
|
||||
ng-if="searchInfo.showSearch"
|
||||
results="searchInfo.results"
|
||||
select-result-callback="selectResult">
|
||||
</umb-tree-search-results>
|
||||
<div class="umb-control-group">
|
||||
<umb-tree-search-box
|
||||
hide-search-callback="hideSearch"
|
||||
search-callback="onSearchResults"
|
||||
search-from-id="{{searchInfo.searchFromId}}"
|
||||
search-from-name="{{searchInfo.searchFromName}}"
|
||||
show-search="{{searchInfo.showSearch}}"
|
||||
section="content">
|
||||
</umb-tree-search-box>
|
||||
</div>
|
||||
|
||||
<div ng-hide="searchInfo.showSearch" ng-animate="'tree-fade-out'">
|
||||
<umb-tree
|
||||
section="content"
|
||||
treealias="content"
|
||||
hideheader="{{hideHeader}}"
|
||||
hideoptions="true"
|
||||
isdialog="true"
|
||||
customtreeparams="{{customTreeParams}}"
|
||||
eventhandler="dialogTreeEventHandler"
|
||||
enablelistviewsearch="true"
|
||||
enablecheckboxes="{{multiPicker}}">
|
||||
</umb-tree>
|
||||
</div>
|
||||
<umb-tree-search-results
|
||||
ng-if="searchInfo.showSearch"
|
||||
results="searchInfo.results"
|
||||
select-result-callback="selectResult">
|
||||
</umb-tree-search-results>
|
||||
|
||||
</div>
|
||||
<div ng-hide="searchInfo.showSearch" ng-animate="'tree-fade-out'">
|
||||
<umb-tree
|
||||
section="content"
|
||||
treealias="content"
|
||||
hideheader="{{hideHeader}}"
|
||||
hideoptions="true"
|
||||
isdialog="true"
|
||||
customtreeparams="{{customTreeParams}}"
|
||||
eventhandler="dialogTreeEventHandler"
|
||||
enablelistviewsearch="true"
|
||||
enablelistviewexpand="true"
|
||||
enablecheckboxes="{{multiPicker}}">
|
||||
</umb-tree>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<umb-mini-list-view
|
||||
ng-if="miniListView"
|
||||
node="miniListView"
|
||||
entity-type="{{entityType}}"
|
||||
start-node-id="model.startNodeId"
|
||||
on-select="selectListViewNode(node)"
|
||||
on-close="closeMiniListView()">
|
||||
</umb-mini-list-view>
|
||||
|
||||
</div>
|
||||
+41
-28
@@ -1,34 +1,47 @@
|
||||
<div ng-controller="Umbraco.Overlays.TreePickerController" ng-init="init('member')">
|
||||
|
||||
<div class="umb-control-group">
|
||||
<umb-tree-search-box
|
||||
hide-search-callback="hideSearch"
|
||||
search-callback="onSearchResults"
|
||||
search-from-id="{{searchInfo.searchFromId}}"
|
||||
search-from-name="{{searchInfo.searchFromName}}"
|
||||
show-search="{{searchInfo.showSearch}}"
|
||||
section="member">
|
||||
</umb-tree-search-box>
|
||||
</div>
|
||||
<div ng-hide="miniListView">
|
||||
|
||||
<umb-tree-search-results
|
||||
ng-if="searchInfo.showSearch"
|
||||
results="searchInfo.results"
|
||||
select-result-callback="selectResult">
|
||||
</umb-tree-search-results>
|
||||
<div class="umb-control-group">
|
||||
<umb-tree-search-box
|
||||
hide-search-callback="hideSearch"
|
||||
search-callback="onSearchResults"
|
||||
search-from-id="{{searchInfo.searchFromId}}"
|
||||
search-from-name="{{searchInfo.searchFromName}}"
|
||||
show-search="{{searchInfo.showSearch}}"
|
||||
section="member">
|
||||
</umb-tree-search-box>
|
||||
</div>
|
||||
|
||||
<div ng-hide="searchInfo.showSearch" ng-animate="'tree-fade-out'">
|
||||
<umb-tree
|
||||
section="member"
|
||||
treealias="member"
|
||||
hideheader="{{hideHeader}}"
|
||||
hideoptions="true"
|
||||
isdialog="true"
|
||||
customtreeparams="{{customTreeParams}}"
|
||||
eventhandler="dialogTreeEventHandler"
|
||||
enablelistviewsearch="true"
|
||||
enablecheckboxes="{{multiPicker}}">
|
||||
</umb-tree>
|
||||
</div>
|
||||
<umb-tree-search-results
|
||||
ng-if="searchInfo.showSearch"
|
||||
results="searchInfo.results"
|
||||
select-result-callback="selectResult">
|
||||
</umb-tree-search-results>
|
||||
|
||||
<div ng-hide="searchInfo.showSearch" ng-animate="'tree-fade-out'">
|
||||
<umb-tree
|
||||
section="member"
|
||||
treealias="member"
|
||||
hideheader="{{hideHeader}}"
|
||||
hideoptions="true"
|
||||
isdialog="true"
|
||||
customtreeparams="{{customTreeParams}}"
|
||||
eventhandler="dialogTreeEventHandler"
|
||||
enablelistviewsearch="true"
|
||||
enablelistviewexpand="true"
|
||||
enablecheckboxes="{{multiPicker}}">
|
||||
</umb-tree>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<umb-mini-list-view
|
||||
ng-if="miniListView"
|
||||
node="miniListView"
|
||||
entity-type="{{entityType}}"
|
||||
start-node-id="model.startNodeId"
|
||||
on-select="selectListViewNode(node)"
|
||||
on-close="closeMiniListView()">
|
||||
</umb-mini-list-view>
|
||||
|
||||
</div>
|
||||
|
||||
+47
-71
@@ -1,6 +1,6 @@
|
||||
//used for the media picker dialog
|
||||
angular.module("umbraco").controller("Umbraco.Overlays.TreePickerController",
|
||||
function ($scope, entityResource, eventsService, $log, searchService, angularHelper, $timeout, localizationService, treeService) {
|
||||
function ($scope, $q, entityResource, eventsService, $log, searchService, angularHelper, $timeout, localizationService, treeService, contentResource, mediaResource, memberResource) {
|
||||
|
||||
var tree = null;
|
||||
var dialogOptions = $scope.model;
|
||||
@@ -22,27 +22,22 @@ angular.module("umbraco").controller("Umbraco.Overlays.TreePickerController",
|
||||
$scope.init = function(contentType) {
|
||||
|
||||
if(contentType === "content") {
|
||||
entityType = "Document";
|
||||
$scope.entityType = "Document";
|
||||
if(!$scope.model.title) {
|
||||
$scope.model.title = localizationService.localize("defaultdialogs_selectContent");
|
||||
}
|
||||
} else if(contentType === "member") {
|
||||
entityType = "Member";
|
||||
$scope.entityType = "Member";
|
||||
if(!$scope.model.title) {
|
||||
$scope.model.title = localizationService.localize("defaultdialogs_selectMember");
|
||||
}
|
||||
} else if(contentType === "media") {
|
||||
entityType = "Media";
|
||||
$scope.entityType = "Media";
|
||||
if(!$scope.model.title) {
|
||||
$scope.model.title = localizationService.localize("defaultdialogs_selectMedia");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Search is only working for content, media and member section so we will remove it from everything else
|
||||
if($scope.section === "content" || $scope.section === "media" || $scope.section === "member" ) {
|
||||
$scope.enableSearh = true;
|
||||
}
|
||||
|
||||
//create the custom query string param for this tree
|
||||
$scope.customTreeParams = dialogOptions.startNodeId ? "startNodeId=" + dialogOptions.startNodeId : "";
|
||||
@@ -54,7 +49,7 @@ angular.module("umbraco").controller("Umbraco.Overlays.TreePickerController",
|
||||
});
|
||||
|
||||
// Allow the entity type to be passed in but defaults to Document for backwards compatibility.
|
||||
var entityType = dialogOptions.entityType ? dialogOptions.entityType : "Document";
|
||||
$scope.entityType = dialogOptions.entityType ? dialogOptions.entityType : "Document";
|
||||
|
||||
|
||||
//min / max values
|
||||
@@ -66,10 +61,24 @@ angular.module("umbraco").controller("Umbraco.Overlays.TreePickerController",
|
||||
}
|
||||
|
||||
if (dialogOptions.section === "member") {
|
||||
entityType = "Member";
|
||||
$scope.entityType = "Member";
|
||||
}
|
||||
else if (dialogOptions.section === "media") {
|
||||
entityType = "Media";
|
||||
$scope.entityType = "Media";
|
||||
}
|
||||
|
||||
// Search and listviews is only working for content, media and member section so we will remove it from everything else
|
||||
if ($scope.section === "content" || $scope.section === "media" || $scope.section === "member") {
|
||||
$scope.enableSearh = true;
|
||||
|
||||
//if a alternative startnode is used, we need to check if it is a container
|
||||
if (dialogOptions.startNodeId && dialogOptions.startNodeId !== -1) {
|
||||
entityResource.getById(dialogOptions.startNodeId, $scope.entityType).then(function (node) {
|
||||
if (node.metaData.IsContainer) {
|
||||
openMiniListView(node);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
//Configures filtering
|
||||
@@ -101,54 +110,17 @@ angular.module("umbraco").controller("Umbraco.Overlays.TreePickerController",
|
||||
}
|
||||
|
||||
function nodeExpandedHandler(ev, args) {
|
||||
|
||||
// open mini list view for list views
|
||||
if (args.node.metaData.isContainer) {
|
||||
openMiniListView(args.node);
|
||||
}
|
||||
|
||||
if (angular.isArray(args.children)) {
|
||||
|
||||
//iterate children
|
||||
_.each(args.children, function (child) {
|
||||
|
||||
//check if any of the items are list views, if so we need to add some custom
|
||||
// children: A node to activate the search, any nodes that have already been
|
||||
// selected in the search
|
||||
if (child.metaData.isContainer) {
|
||||
child.hasChildren = true;
|
||||
child.children = [
|
||||
{
|
||||
level: child.level + 1,
|
||||
hasChildren: false,
|
||||
parent: function () {
|
||||
return child;
|
||||
},
|
||||
name: searchText,
|
||||
metaData: {
|
||||
listViewNode: child,
|
||||
},
|
||||
cssClass: "icon-search",
|
||||
cssClasses: ["not-published"]
|
||||
}
|
||||
];
|
||||
//add base transition classes to this node
|
||||
child.cssClasses.push("tree-node-slide-up");
|
||||
|
||||
var listViewResults = _.filter($scope.searchInfo.selectedSearchResults, function(i) {
|
||||
return i.parentId == child.id;
|
||||
});
|
||||
_.each(listViewResults, function(item) {
|
||||
child.children.unshift({
|
||||
id: item.id,
|
||||
name: item.name,
|
||||
cssClass: "icon umb-tree-icon sprTree " + item.icon,
|
||||
level: child.level + 1,
|
||||
metaData: {
|
||||
isSearchResult: true
|
||||
},
|
||||
hasChildren: false,
|
||||
parent: function () {
|
||||
return child;
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
//now we need to look in the already selected search results and
|
||||
// toggle the check boxes for those ones that are listed
|
||||
var exists = _.find($scope.searchInfo.selectedSearchResults, function (selected) {
|
||||
@@ -174,18 +146,7 @@ angular.module("umbraco").controller("Umbraco.Overlays.TreePickerController",
|
||||
args.event.preventDefault();
|
||||
args.event.stopPropagation();
|
||||
|
||||
if (args.node.metaData.listViewNode) {
|
||||
//check if list view 'search' node was selected
|
||||
|
||||
$scope.searchInfo.showSearch = true;
|
||||
$scope.searchInfo.searchFromId = args.node.metaData.listViewNode.id;
|
||||
$scope.searchInfo.searchFromName = args.node.metaData.listViewNode.name;
|
||||
|
||||
//add transition classes
|
||||
var listViewNode = args.node.parent();
|
||||
listViewNode.cssClasses.push('tree-node-slide-up-hide-active');
|
||||
}
|
||||
else if (args.node.metaData.isSearchResult) {
|
||||
if (args.node.metaData.isSearchResult) {
|
||||
//check if the item selected was a search result from a list view
|
||||
|
||||
//unselect
|
||||
@@ -232,7 +193,7 @@ angular.module("umbraco").controller("Umbraco.Overlays.TreePickerController",
|
||||
multiSelectItem(entity);
|
||||
} else {
|
||||
//otherwise we have to get it from the server
|
||||
entityResource.getById(id, entityType).then(function (ent) {
|
||||
entityResource.getById(id, $scope.entityType).then(function (ent) {
|
||||
multiSelectItem(ent);
|
||||
});
|
||||
}
|
||||
@@ -257,7 +218,7 @@ angular.module("umbraco").controller("Umbraco.Overlays.TreePickerController",
|
||||
multiSelectItem(entity);
|
||||
} else {
|
||||
//otherwise we have to get it from the server
|
||||
entityResource.getById(id, entityType).then(function (ent) {
|
||||
entityResource.getById(id, $scope.entityType).then(function (ent) {
|
||||
multiSelectItem(ent);
|
||||
});
|
||||
}
|
||||
@@ -274,7 +235,7 @@ angular.module("umbraco").controller("Umbraco.Overlays.TreePickerController",
|
||||
$scope.model.submit($scope.model);
|
||||
} else {
|
||||
//otherwise we have to get it from the server
|
||||
entityResource.getById(id, entityType).then(function (ent) {
|
||||
entityResource.getById(id, $scope.entityType).then(function (ent) {
|
||||
$scope.model.selection.push(ent);
|
||||
$scope.model.submit($scope.model);
|
||||
});
|
||||
@@ -355,7 +316,7 @@ angular.module("umbraco").controller("Umbraco.Overlays.TreePickerController",
|
||||
}
|
||||
|
||||
$scope.multiSubmit = function (result) {
|
||||
entityResource.getByIds(result, entityType).then(function (ents) {
|
||||
entityResource.getByIds(result, $scope.entityType).then(function (ents) {
|
||||
$scope.submit(ents);
|
||||
});
|
||||
};
|
||||
@@ -504,4 +465,19 @@ angular.module("umbraco").controller("Umbraco.Overlays.TreePickerController",
|
||||
$scope.dialogTreeEventHandler.unbind("treeNodeExpanded", nodeExpandedHandler);
|
||||
$scope.dialogTreeEventHandler.unbind("treeNodeSelect", nodeSelectHandler);
|
||||
});
|
||||
|
||||
$scope.selectListViewNode = function(node) {
|
||||
select(node.name, node.id);
|
||||
//toggle checked state
|
||||
node.selected = node.selected === true ? false : true;
|
||||
};
|
||||
|
||||
$scope.closeMiniListView = function() {
|
||||
$scope.miniListView = undefined;
|
||||
};
|
||||
|
||||
function openMiniListView(node) {
|
||||
$scope.miniListView = node;
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
@@ -1,35 +1,49 @@
|
||||
<div ng-controller="Umbraco.Overlays.TreePickerController">
|
||||
|
||||
<div class="umb-control-group">
|
||||
<umb-tree-search-box
|
||||
ng-if="enableSearh"
|
||||
hide-search-callback="hideSearch"
|
||||
search-callback="onSearchResults"
|
||||
search-from-id="{{searchInfo.searchFromId}}"
|
||||
search-from-name="{{searchInfo.searchFromName}}"
|
||||
show-search="{{searchInfo.showSearch}}"
|
||||
section="{{section}}">
|
||||
</umb-tree-search-box>
|
||||
</div>
|
||||
<div ng-hide="miniListView">
|
||||
|
||||
<umb-tree-search-results
|
||||
ng-if="searchInfo.showSearch"
|
||||
results="searchInfo.results"
|
||||
select-result-callback="selectResult">
|
||||
</umb-tree-search-results>
|
||||
<div class="umb-control-group">
|
||||
<umb-tree-search-box
|
||||
ng-if="enableSearh"
|
||||
hide-search-callback="hideSearch"
|
||||
search-callback="onSearchResults"
|
||||
search-from-id="{{searchInfo.searchFromId}}"
|
||||
search-from-name="{{searchInfo.searchFromName}}"
|
||||
show-search="{{searchInfo.showSearch}}"
|
||||
section="{{section}}">
|
||||
</umb-tree-search-box>
|
||||
</div>
|
||||
|
||||
<div ng-hide="searchInfo.showSearch" ng-animate="'tree-fade-out'">
|
||||
<umb-tree
|
||||
section="{{section}}"
|
||||
treealias="{{treeAlias}}"
|
||||
hideheader="{{hideHeader}}"
|
||||
hideoptions="true"
|
||||
isdialog="true"
|
||||
customtreeparams="{{customTreeParams}}"
|
||||
eventhandler="dialogTreeEventHandler"
|
||||
enablelistviewsearch="true"
|
||||
enablecheckboxes="{{multiPicker}}">
|
||||
</umb-tree>
|
||||
</div>
|
||||
<umb-tree-search-results
|
||||
ng-if="searchInfo.showSearch"
|
||||
results="searchInfo.results"
|
||||
select-result-callback="selectResult">
|
||||
</umb-tree-search-results>
|
||||
|
||||
</div>
|
||||
<div ng-hide="searchInfo.showSearch" ng-animate="'tree-fade-out'">
|
||||
<umb-tree
|
||||
section="{{section}}"
|
||||
treealias="{{treeAlias}}"
|
||||
hideheader="{{hideHeader}}"
|
||||
hideoptions="true"
|
||||
isdialog="true"
|
||||
customtreeparams="{{customTreeParams}}"
|
||||
eventhandler="dialogTreeEventHandler"
|
||||
enablelistviewsearch="true"
|
||||
enablelistviewexpand="true"
|
||||
enablecheckboxes="{{multiPicker}}">
|
||||
</umb-tree>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<umb-mini-list-view
|
||||
ng-if="miniListView"
|
||||
node="miniListView"
|
||||
entity-type="{{entityType}}"
|
||||
start-node-id="model.startNodeId"
|
||||
on-select="selectListViewNode(node)"
|
||||
on-close="closeMiniListView()">
|
||||
</umb-mini-list-view>
|
||||
|
||||
</div>
|
||||
@@ -1,10 +1,15 @@
|
||||
<ul class="umb-breadcrumbs">
|
||||
<li class="umb-breadcrumbs__ancestor" ng-repeat="ancestor in ancestors">
|
||||
<li class="umb-breadcrumbs__ancestor" ng-repeat="ancestor in ancestors">
|
||||
|
||||
<!-- go to node on click -->
|
||||
<a ng-if="!$last && !allowOnOpen" href="#/{{entityType}}/{{entityType}}/edit/{{ancestor.id}}" class="umb-breadcrumbs__ancestor-link" title="{{ancestor.name}}">{{ancestor.name}}</a>
|
||||
|
||||
<a ng-if="!$last" href="#/{{entityType}}/{{entityType}}/edit/{{ancestor.id}}" class="umb-breadcrumbs__ancestor-link" title="{{ancestor.name}}">{{ancestor.name}}</a>
|
||||
<span ng-if="!$last" class="umb-breadcrumbs__seperator">/</span>
|
||||
<!-- use callback to handle click -->
|
||||
<a ng-if="!$last && allowOnOpen" href="#" ng-click="open(ancestor)" class="umb-breadcrumbs__ancestor-link" title="{{ancestor.name}}" prevent-default>{{ancestor.name}}</a>
|
||||
|
||||
<span class="umb-breadcrumbs__ancestor-text" ng-if="$last" title="{{ancestor.name}}">{{ancestor.name}}</span>
|
||||
<span ng-if="!$last" class="umb-breadcrumbs__seperator">/</span>
|
||||
|
||||
</li>
|
||||
</ul>
|
||||
<span class="umb-breadcrumbs__ancestor-text" ng-if="$last" title="{{ancestor.name}}">{{ancestor.name}}</span>
|
||||
|
||||
</li>
|
||||
</ul>
|
||||
@@ -0,0 +1,101 @@
|
||||
<div>
|
||||
|
||||
<div ng-repeat="miniListView in miniListViews" ng-animate="getMiniListViewAnimation()">
|
||||
|
||||
<div class="umb-mini-list-view__title">
|
||||
<i class="umb-mini-list-view__title-icon {{ miniListView.node.icon }}"></i>
|
||||
<h4 class="umb-mini-list-view__title-text">{{ miniListView.node.name }}</h4>
|
||||
</div>
|
||||
|
||||
<div class="flex" style="margin-bottom: 10px;">
|
||||
|
||||
<a ng-if="showBackButton()" href="" class="umb-mini-list-view__back" ng-click="exitMiniListView()">
|
||||
<i class="icon-arrow-left umb-mini-list-view__back-icon"></i>
|
||||
<span class="umb-mini-list-view__back-text">Back</span> /
|
||||
</a>
|
||||
|
||||
<umb-breadcrumbs
|
||||
ng-if="breadcrumb && breadcrumb.length > 0"
|
||||
ancestors="breadcrumb"
|
||||
entity-type="content"
|
||||
on-open="clickBreadcrumb(ancestor)">
|
||||
</umb-breadcrumbs>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="umb-table umb-table--condensed">
|
||||
|
||||
<!-- Head -->
|
||||
<div class="umb-table-head">
|
||||
<div class="umb-table-row">
|
||||
<div class="umb-table-cell" style="display: none;"></div>
|
||||
<div class="umb-table-cell" style="padding-top: 8px; padding-bottom: 8px;">
|
||||
<form class="form-search -no-margin-bottom" style="width: 100%; margin-right: 0;" novalidate>
|
||||
<div class="inner-addon left-addon">
|
||||
<i class="icon icon-search" style="font-size: 14px;"></i>
|
||||
<input
|
||||
style="width: 100%;"
|
||||
class="form-control search-input"
|
||||
type="text"
|
||||
localize="placeholder"
|
||||
placeholder="@general_typeToSearch"
|
||||
ng-model="search"
|
||||
ng-change="searchMiniListView(search, miniListView)"
|
||||
prevent-enter-submit
|
||||
no-dirty-check>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Body -->
|
||||
<div class="umb-table-body">
|
||||
|
||||
<!-- Load indicator when the list has items -->
|
||||
<div class="umb-table__loading-overlay" ng-if="miniListView.loading && miniListView.children.length > 0">
|
||||
<umb-load-indicator></umb-load-indicator>
|
||||
</div>
|
||||
|
||||
<!-- Items -->
|
||||
<div class="umb-table-row"
|
||||
ng-repeat="child in miniListView.children"
|
||||
ng-click="selectNode(child)"
|
||||
ng-class="{'-selected':child.selected}">
|
||||
<div class="umb-table-cell umb-table-cell--auto-width" ng-class="{'umb-table-cell--faded':!child.published && entityType === 'Document'}">
|
||||
<div class="flex items-center">
|
||||
<ins class="icon-navigation-right umb-table__row-expand" ng-click="openNode($event, child)"> </ins>
|
||||
<i class="umb-table-body__icon umb-table-body__fileicon {{child.icon}}"></i>
|
||||
<i class="umb-table-body__icon umb-table-body__checkicon icon-check"></i>
|
||||
</div>
|
||||
</div>
|
||||
<div class="umb-table-cell black" ng-class="{'umb-table-cell--faded':!child.published && entityType === 'Document'}">{{ child.name }}</div>
|
||||
</div>
|
||||
|
||||
<!-- Load indicator when the list doesn't have items -->
|
||||
<div ng-if="!miniListView.loading && !miniListView.children" class="umb-table-row umb-table-row--empty">
|
||||
<span ng-if="search === ''"><localize key="general_noItemsInList"></localize></span>
|
||||
<span ng-if="search !== ''"><localize key="general_searchNoResult"></localize></span>
|
||||
</div>
|
||||
|
||||
<!-- Load indicator when the list doesn't have items -->
|
||||
<div ng-if="miniListView.loading && !miniListView.children" class="umb-table-row umb-table-row--empty">
|
||||
<umb-load-indicator></umb-load-indicator>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="flex justify-center">
|
||||
<umb-pagination
|
||||
ng-if="miniListView.pagination.totalPages > 0 && !miniListView.loading"
|
||||
page-number="miniListView.pagination.pageNumber"
|
||||
total-pages="miniListView.pagination.totalPages"
|
||||
on-change="goToPage(pageNumber, miniListView)">
|
||||
</umb-pagination>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
@@ -1,4 +1,4 @@
|
||||
<div class="pagination" ng-show="pagination.length > 1">
|
||||
<div class="umb-pagination pagination" ng-show="pagination.length > 1">
|
||||
|
||||
<ul>
|
||||
<li ng-class="{disabled:pageNumber <= 1}">
|
||||
|
||||
@@ -11,10 +11,10 @@
|
||||
<!--Settings-->
|
||||
<add initialize="true" sortOrder="0" alias="documentTypes" application="settings" title="Document Types" iconClosed="icon-folder" iconOpen="icon-folder-open" type="Umbraco.Web.Trees.ContentTypeTreeController, umbraco" />
|
||||
<add application="settings" alias="templates" title="Templates" iconClosed="icon-folder" iconOpen="icon-folder-open" type="Umbraco.Web.Trees.TemplatesTreeController, umbraco" initialize="true" sortOrder="1" />
|
||||
<add application="settings" alias="partialViews" title="Partial Views" silent="false" initialize="true" iconClosed="icon-folder" iconOpen="icon-folder" type="Umbraco.Web.Trees.PartialViewsTree, umbraco" sortOrder="2" />
|
||||
<add application="settings" alias="partialViews" title="Partial Views" silent="false" initialize="true" iconClosed="icon-folder" iconOpen="icon-folder" type="Umbraco.Web.Trees.PartialViewsTreeController, umbraco" sortOrder="2" />
|
||||
<add application="settings" alias="stylesheets" title="Stylesheets" type="umbraco.loadStylesheets, umbraco" iconClosed="icon-folder" iconOpen="icon-folder" sortOrder="3" />
|
||||
<add application="settings" alias="stylesheetProperty" title="Stylesheet Property" type="umbraco.loadStylesheetProperty, umbraco" iconClosed="" iconOpen="" initialize="false" sortOrder="0" />
|
||||
<add application="settings" alias="scripts" title="Scripts" type="umbraco.loadScripts, umbraco" iconClosed="icon-folder" iconOpen="icon-folder" sortOrder="4" />
|
||||
<add application="settings" alias="scripts" title="Scripts" type="Umbraco.Web.Trees.ScriptTreeController, umbraco" iconClosed="icon-folder" iconOpen="icon-folder" sortOrder="4" />
|
||||
<add application="settings" alias="languages" title="Languages" iconClosed="icon-folder" iconOpen="icon-folder-open" type="Umbraco.Web.Trees.LanguageTreeController, umbraco" sortOrder="5" />
|
||||
<add application="settings" alias="dictionary" title="Dictionary" type="umbraco.loadDictionary, umbraco" iconClosed="icon-folder" iconOpen="icon-folder" sortOrder="6" />
|
||||
<add initialize="true" sortOrder="7" alias="mediaTypes" application="settings" title="Media Types" iconClosed="icon-folder" iconOpen="icon-folder-open" type="Umbraco.Web.Trees.MediaTypeTreeController, umbraco" />
|
||||
@@ -25,7 +25,7 @@
|
||||
<add application="developer" alias="macros" title="Macros" type="umbraco.loadMacros, umbraco" iconClosed="icon-folder" iconOpen="icon-folder" sortOrder="2" />
|
||||
<add application="developer" alias="relationTypes" title="Relation Types" type="umbraco.loadRelationTypes, umbraco" iconClosed="icon-folder" iconOpen="icon-folder" sortOrder="4" />
|
||||
<add application="developer" alias="xslt" title="XSLT Files" type="umbraco.loadXslt, umbraco" iconClosed="icon-folder" iconOpen="icon-folder" sortOrder="5" />
|
||||
<add application="developer" alias="partialViewMacros" type="Umbraco.Web.Trees.PartialViewMacrosTree, umbraco" silent="false" initialize="true" sortOrder="6" title="Partial View Macro Files" iconClosed="icon-folder" iconOpen="icon-folder" />
|
||||
<add application="developer" alias="partialViewMacros" type="Umbraco.Web.Trees.PartialViewMacrosTreeController, umbraco" silent="false" initialize="true" sortOrder="6" title="Partial View Macro Files" iconClosed="icon-folder" iconOpen="icon-folder" />
|
||||
|
||||
<!--Users-->
|
||||
<add application="users" alias="users" title="Users" type="umbraco.loadUsers, umbraco" iconClosed="icon-folder" iconOpen="icon-folder" sortOrder="0" />
|
||||
|
||||
@@ -496,6 +496,8 @@
|
||||
<key alias="retry">Retry</key>
|
||||
<key alias="rights">Permissions</key>
|
||||
<key alias="search">Search</key>
|
||||
<key alias="searchNoResult">Sorry, we can not find what you are looking for</key>
|
||||
<key alias="noItemsInList">No items have been added</key>
|
||||
<key alias="server">Server</key>
|
||||
<key alias="show">Show</key>
|
||||
<key alias="showPageOnSend">Show page on Send</key>
|
||||
|
||||
@@ -243,6 +243,20 @@ namespace Umbraco.Web.Cache
|
||||
|
||||
#region Publishing
|
||||
|
||||
// IPublishingStrategy (obsolete) events are proxied into ContentService, which works fine when
|
||||
// events are actually raised, but not when they are handled by HandleEvents, so we have to have
|
||||
// these proxy methods that are *not* registered against any event *but* used by HandleEvents.
|
||||
|
||||
static void PublishingStrategy_UnPublished(IPublishingStrategy sender, PublishEventArgs<IContent> e)
|
||||
{
|
||||
ContentService_UnPublished(sender, e);
|
||||
}
|
||||
|
||||
static void PublishingStrategy_Published(IPublishingStrategy sender, PublishEventArgs<IContent> e)
|
||||
{
|
||||
ContentService_Published(sender, e);
|
||||
}
|
||||
|
||||
static void ContentService_UnPublished(IPublishingStrategy sender, PublishEventArgs<IContent> e)
|
||||
{
|
||||
if (e.PublishedEntities.Any())
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using AutoMapper;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Net.Http;
|
||||
@@ -266,29 +267,46 @@ namespace Umbraco.Web.Editors
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(type) == false && string.IsNullOrWhiteSpace(virtualPath) == false)
|
||||
{
|
||||
virtualPath = System.Web.HttpUtility.UrlDecode(virtualPath);
|
||||
|
||||
switch (type)
|
||||
{
|
||||
case Core.Constants.Trees.PartialViews:
|
||||
if (IsDirectory(virtualPath, SystemDirectories.PartialViews))
|
||||
{
|
||||
Services.FileService.DeletePartialViewFolder(virtualPath);
|
||||
return Request.CreateResponse(HttpStatusCode.OK);
|
||||
}
|
||||
if (Services.FileService.DeletePartialView(virtualPath, Security.CurrentUser.Id))
|
||||
{
|
||||
return Request.CreateResponse(HttpStatusCode.OK);
|
||||
}
|
||||
return Request.CreateErrorResponse(HttpStatusCode.NotFound, "No Partial View found with the specified path");
|
||||
return Request.CreateErrorResponse(HttpStatusCode.NotFound, "No Partial View or folder found with the specified path");
|
||||
|
||||
case Core.Constants.Trees.PartialViewMacros:
|
||||
if (IsDirectory(virtualPath, SystemDirectories.MacroPartials))
|
||||
{
|
||||
Services.FileService.DeletePartialViewMacroFolder(virtualPath);
|
||||
return Request.CreateResponse(HttpStatusCode.OK);
|
||||
}
|
||||
if (Services.FileService.DeletePartialViewMacro(virtualPath, Security.CurrentUser.Id))
|
||||
{
|
||||
return Request.CreateResponse(HttpStatusCode.OK);
|
||||
}
|
||||
return Request.CreateErrorResponse(HttpStatusCode.NotFound, "No Partial View Macro found with the specified path");
|
||||
return Request.CreateErrorResponse(HttpStatusCode.NotFound, "No Partial View Macro or folder found with the specified path");
|
||||
|
||||
case Core.Constants.Trees.Scripts:
|
||||
if (IsDirectory(virtualPath, SystemDirectories.Scripts))
|
||||
{
|
||||
Services.FileService.DeleteScriptFolder(virtualPath);
|
||||
return Request.CreateResponse(HttpStatusCode.OK);
|
||||
}
|
||||
if (Services.FileService.GetScriptByName(virtualPath) != null)
|
||||
{
|
||||
Services.FileService.DeleteScript(virtualPath, Security.CurrentUser.Id);
|
||||
return Request.CreateResponse(HttpStatusCode.OK);
|
||||
}
|
||||
return Request.CreateErrorResponse(HttpStatusCode.NotFound, "No Script found with the specified path");
|
||||
return Request.CreateErrorResponse(HttpStatusCode.NotFound, "No Script or folder found with the specified path");
|
||||
|
||||
default:
|
||||
return Request.CreateResponse(HttpStatusCode.NotFound);
|
||||
@@ -442,5 +460,12 @@ namespace Umbraco.Web.Editors
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
private bool IsDirectory(string virtualPath, string systemDirectory)
|
||||
{
|
||||
var path = IOHelper.MapPath(systemDirectory + "/" + virtualPath);
|
||||
var dirInfo = new DirectoryInfo(path);
|
||||
return dirInfo.Attributes == FileAttributes.Directory;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -79,10 +79,11 @@ namespace Umbraco.Web.Scheduling
|
||||
|
||||
// running on a background task, and Log.CleanLogs uses the old SqlHelper,
|
||||
// better wrap in a scope and ensure it's all cleaned up and nothing leaks
|
||||
using (ApplicationContext.Current.ScopeProvider.CreateScope())
|
||||
using (var scope = ApplicationContext.Current.ScopeProvider.CreateScope())
|
||||
using (DisposableTimer.DebugDuration<LogScrubber>("Log scrubbing executing", "Log scrubbing complete"))
|
||||
{
|
||||
Log.CleanLogs(GetLogScrubbingMaximumAge(_settings));
|
||||
scope.Complete();
|
||||
}
|
||||
|
||||
return true; // repeat
|
||||
|
||||
@@ -85,10 +85,11 @@ namespace Umbraco.Web.Scheduling
|
||||
// running on a background task, requires its own (safe) scope
|
||||
// (GetAuthenticationHeaderValue uses UserService to load the current user, hence requires a database)
|
||||
// (might not need a scope but we don't know really)
|
||||
using (ApplicationContext.Current.ScopeProvider.CreateScope())
|
||||
using (var scope = ApplicationContext.Current.ScopeProvider.CreateScope())
|
||||
{
|
||||
//pass custom the authorization header
|
||||
request.Headers.Authorization = AdminTokenAuthorizeAttribute.GetAuthenticationHeaderValue(_appContext);
|
||||
scope.Complete();
|
||||
}
|
||||
|
||||
var result = await wc.SendAsync(request, token);
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Net.Http.Formatting;
|
||||
using umbraco.BusinessLogic.Actions;
|
||||
using Umbraco.Core;
|
||||
using Umbraco.Core.IO;
|
||||
using Umbraco.Core.Services;
|
||||
using Umbraco.Web.Models.Trees;
|
||||
|
||||
namespace Umbraco.Web.Trees
|
||||
@@ -27,7 +27,7 @@ namespace Umbraco.Web.Trees
|
||||
/// <param name="xNode"></param>
|
||||
protected virtual void OnRenderFolderNode(ref TreeNode treeNode) { }
|
||||
|
||||
protected override Models.Trees.TreeNodeCollection GetTreeNodes(string id, System.Net.Http.Formatting.FormDataCollection queryStrings)
|
||||
protected override TreeNodeCollection GetTreeNodes(string id, FormDataCollection queryStrings)
|
||||
{
|
||||
string orgPath = "";
|
||||
string path = "";
|
||||
@@ -90,6 +90,64 @@ namespace Umbraco.Web.Trees
|
||||
}
|
||||
|
||||
return nodes;
|
||||
}
|
||||
}
|
||||
|
||||
protected override MenuItemCollection GetMenuForNode(string id, FormDataCollection queryStrings)
|
||||
{
|
||||
var menu = new MenuItemCollection();
|
||||
|
||||
//if root node no need to visit the filesystem so lets just create the menu and return it
|
||||
if (id == Constants.System.Root.ToInvariantString())
|
||||
{
|
||||
//set the default to create
|
||||
menu.DefaultMenuAlias = ActionNew.Instance.Alias;
|
||||
//create action
|
||||
menu.Items.Add<ActionNew>(Services.TextService.Localize(string.Format("actions/{0}", ActionNew.Instance.Alias)));
|
||||
//refresh action
|
||||
menu.Items.Add<RefreshNode, ActionRefresh>(Services.TextService.Localize(string.Format("actions/{0}", ActionRefresh.Instance.Alias)), true);
|
||||
|
||||
return menu;
|
||||
}
|
||||
|
||||
string path;
|
||||
if (string.IsNullOrEmpty(id) == false)
|
||||
{
|
||||
var orgPath = System.Web.HttpUtility.UrlDecode(id);
|
||||
path = IOHelper.MapPath(FilePath + "/" + orgPath);
|
||||
}
|
||||
else
|
||||
{
|
||||
path = IOHelper.MapPath(FilePath);
|
||||
}
|
||||
|
||||
var dirInfo = new DirectoryInfo(path);
|
||||
//check if it's a directory
|
||||
if (dirInfo.Attributes == FileAttributes.Directory)
|
||||
{
|
||||
//set the default to create
|
||||
menu.DefaultMenuAlias = ActionNew.Instance.Alias;
|
||||
//create action
|
||||
menu.Items.Add<ActionNew>(Services.TextService.Localize(string.Format("actions/{0}", ActionNew.Instance.Alias)));
|
||||
|
||||
var hasChildren = dirInfo.GetFiles().Length > 0 || dirInfo.GetDirectories().Length > 0;
|
||||
//We can only delete folders if it doesn't have any children (folders or files)
|
||||
if (hasChildren == false)
|
||||
{
|
||||
//delete action
|
||||
menu.Items.Add<ActionDelete>(Services.TextService.Localize(string.Format("actions/{0}", ActionDelete.Instance.Alias)), true);
|
||||
}
|
||||
|
||||
//refresh action
|
||||
menu.Items.Add<RefreshNode, ActionRefresh>(Services.TextService.Localize(string.Format("actions/{0}", ActionRefresh.Instance.Alias)), true);
|
||||
}
|
||||
//if it's not a directory then we only allow to delete the item
|
||||
else
|
||||
{
|
||||
//delete action
|
||||
menu.Items.Add<ActionDelete>(Services.TextService.Localize(string.Format("actions/{0}", ActionDelete.Instance.Alias)));
|
||||
}
|
||||
|
||||
return menu;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,16 +1,13 @@
|
||||
using Umbraco.Core;
|
||||
using Umbraco.Core.IO;
|
||||
using umbraco.BusinessLogic.Actions;
|
||||
using Umbraco.Web.Models.Trees;
|
||||
using System.Net.Http.Formatting;
|
||||
using Umbraco.Core.Services;
|
||||
|
||||
namespace Umbraco.Web.Trees
|
||||
{
|
||||
/// <summary>
|
||||
/// Tree for displaying partial view macros in the developer app
|
||||
/// </summary>
|
||||
[Tree(Constants.Applications.Developer, "partialViewMacros", "Partial View Macro Files", sortOrder: 6)]
|
||||
/// <summary>
|
||||
/// Tree for displaying partial view macros in the developer app
|
||||
/// </summary>
|
||||
[Tree(Constants.Applications.Developer, "partialViewMacros", "Partial View Macro Files", sortOrder: 6)]
|
||||
public class PartialViewMacrosTreeController : FileSystemTreeController
|
||||
{
|
||||
protected override string FilePath
|
||||
@@ -28,37 +25,6 @@ namespace Umbraco.Web.Trees
|
||||
get { return "icon-article"; }
|
||||
}
|
||||
|
||||
protected override MenuItemCollection GetMenuForNode(string id, FormDataCollection queryStrings)
|
||||
{
|
||||
var menu = new MenuItemCollection();
|
||||
|
||||
if (id == Constants.System.Root.ToInvariantString())
|
||||
{
|
||||
//set the default to create
|
||||
menu.DefaultMenuAlias = ActionNew.Instance.Alias;
|
||||
//create action
|
||||
menu.Items.Add<ActionNew>(Services.TextService.Localize(string.Format("actions/{0}", ActionNew.Instance.Alias)));
|
||||
//refresh action
|
||||
menu.Items.Add<RefreshNode, ActionRefresh>(Services.TextService.Localize(string.Format("actions/{0}", ActionRefresh.Instance.Alias)), true);
|
||||
|
||||
return menu;
|
||||
}
|
||||
|
||||
if (id.EndsWith(FileSearchPattern.TrimStart("*")) == false)
|
||||
{
|
||||
//set the default to create
|
||||
menu.DefaultMenuAlias = ActionNew.Instance.Alias;
|
||||
//create action
|
||||
menu.Items.Add<ActionNew>(Services.TextService.Localize(string.Format("actions/{0}", ActionNew.Instance.Alias)));
|
||||
//refresh action
|
||||
menu.Items.Add<RefreshNode, ActionRefresh>(Services.TextService.Localize(string.Format("actions/{0}", ActionRefresh.Instance.Alias)), true);
|
||||
}
|
||||
|
||||
// TODO: Wire up new delete dialog
|
||||
menu.Items.Add<ActionDelete>(Services.TextService.Localize(string.Format("actions/{0}", ActionDelete.Instance.Alias)));
|
||||
return menu;
|
||||
}
|
||||
|
||||
protected override void OnRenderFileNode(ref TreeNode treeNode)
|
||||
{
|
||||
base.OnRenderFileNode(ref treeNode);
|
||||
|
||||
@@ -1,9 +1,6 @@
|
||||
using Umbraco.Core;
|
||||
using Umbraco.Core.IO;
|
||||
using umbraco.BusinessLogic.Actions;
|
||||
using Umbraco.Web.Models.Trees;
|
||||
using System.Net.Http.Formatting;
|
||||
using Umbraco.Core.Services;
|
||||
|
||||
namespace Umbraco.Web.Trees
|
||||
{
|
||||
@@ -27,37 +24,6 @@ namespace Umbraco.Web.Trees
|
||||
get { return "icon-article"; }
|
||||
}
|
||||
|
||||
protected override MenuItemCollection GetMenuForNode(string id, FormDataCollection queryStrings)
|
||||
{
|
||||
var menu = new MenuItemCollection();
|
||||
|
||||
if (id == Constants.System.Root.ToInvariantString())
|
||||
{
|
||||
//set the default to create
|
||||
menu.DefaultMenuAlias = ActionNew.Instance.Alias;
|
||||
//create action
|
||||
menu.Items.Add<ActionNew>(Services.TextService.Localize(string.Format("actions/{0}", ActionNew.Instance.Alias)));
|
||||
//refresh action
|
||||
menu.Items.Add<RefreshNode, ActionRefresh>(Services.TextService.Localize(string.Format("actions/{0}", ActionRefresh.Instance.Alias)), true);
|
||||
|
||||
return menu;
|
||||
}
|
||||
|
||||
if (id.EndsWith(FileSearchPattern.TrimStart("*")) == false)
|
||||
{
|
||||
//set the default to create
|
||||
menu.DefaultMenuAlias = ActionNew.Instance.Alias;
|
||||
//create action
|
||||
menu.Items.Add<ActionNew>(Services.TextService.Localize(string.Format("actions/{0}", ActionNew.Instance.Alias)));
|
||||
//refresh action
|
||||
menu.Items.Add<RefreshNode, ActionRefresh>(Services.TextService.Localize(string.Format("actions/{0}", ActionRefresh.Instance.Alias)), true);
|
||||
}
|
||||
|
||||
// TODO: Wire up new delete dialog
|
||||
menu.Items.Add<ActionDelete>(Services.TextService.Localize(string.Format("actions/{0}", ActionDelete.Instance.Alias)));
|
||||
return menu;
|
||||
}
|
||||
|
||||
protected override void OnRenderFileNode(ref TreeNode treeNode)
|
||||
{
|
||||
base.OnRenderFileNode(ref treeNode);
|
||||
|
||||
@@ -1,10 +1,6 @@
|
||||
using System.Linq;
|
||||
using Umbraco.Core;
|
||||
using Umbraco.Core;
|
||||
using Umbraco.Core.IO;
|
||||
using umbraco.BusinessLogic.Actions;
|
||||
using Umbraco.Web.Models.Trees;
|
||||
using System.Net.Http.Formatting;
|
||||
using Umbraco.Core.Services;
|
||||
|
||||
namespace Umbraco.Web.Trees
|
||||
{
|
||||
@@ -25,37 +21,6 @@ namespace Umbraco.Web.Trees
|
||||
get { return "icon-script"; }
|
||||
}
|
||||
|
||||
protected override MenuItemCollection GetMenuForNode(string id, FormDataCollection queryStrings)
|
||||
{
|
||||
var menu = new MenuItemCollection();
|
||||
|
||||
if (id == Constants.System.Root.ToInvariantString())
|
||||
{
|
||||
//set the default to create
|
||||
menu.DefaultMenuAlias = ActionNew.Instance.Alias;
|
||||
//create action
|
||||
menu.Items.Add<ActionNew>(Services.TextService.Localize(string.Format("actions/{0}", ActionNew.Instance.Alias)));
|
||||
//refresh action
|
||||
menu.Items.Add<RefreshNode, ActionRefresh>(Services.TextService.Localize(string.Format("actions/{0}", ActionRefresh.Instance.Alias)), true);
|
||||
|
||||
return menu;
|
||||
}
|
||||
|
||||
if (id.EndsWith(FileSearchPattern.TrimStart("*")) == false)
|
||||
{
|
||||
//set the default to create
|
||||
menu.DefaultMenuAlias = ActionNew.Instance.Alias;
|
||||
//create action
|
||||
menu.Items.Add<ActionNew>(Services.TextService.Localize(string.Format("actions/{0}", ActionNew.Instance.Alias)));
|
||||
//refresh action
|
||||
menu.Items.Add<RefreshNode, ActionRefresh>(Services.TextService.Localize(string.Format("actions/{0}", ActionRefresh.Instance.Alias)), true);
|
||||
}
|
||||
|
||||
// TODO: Wire up new delete dialog
|
||||
menu.Items.Add<ActionDelete>(Services.TextService.Localize(string.Format("actions/{0}", ActionDelete.Instance.Alias)));
|
||||
return menu;
|
||||
}
|
||||
|
||||
protected override void OnRenderFolderNode(ref TreeNode treeNode)
|
||||
{
|
||||
//TODO: This isn't the best way to ensure a noop process for clicking a node but it works for now.
|
||||
|
||||
@@ -57,7 +57,7 @@ namespace UmbracoExamine.DataServices
|
||||
[Obsolete("This should no longer be used, latest content will be indexed by using the IContentService directly")]
|
||||
public XDocument GetLatestContentByXPath(string xpath)
|
||||
{
|
||||
using (ApplicationContext.Current.ScopeProvider.CreateScope())
|
||||
using (var scope = ApplicationContext.Current.ScopeProvider.CreateScope())
|
||||
{
|
||||
var xmlContent = XDocument.Parse("<content></content>");
|
||||
var rootContent = _applicationContext.Services.ContentService.GetRootContent();
|
||||
@@ -67,6 +67,7 @@ namespace UmbracoExamine.DataServices
|
||||
xmlContent.Root.Add(c.ToDeepXml(_applicationContext.Services.PackagingService));
|
||||
}
|
||||
var result = ((IEnumerable)xmlContent.XPathEvaluate(xpath)).Cast<XElement>();
|
||||
scope.Complete();
|
||||
return result.ToXDocument();
|
||||
}
|
||||
}
|
||||
@@ -79,9 +80,11 @@ namespace UmbracoExamine.DataServices
|
||||
/// <returns></returns>
|
||||
public bool IsProtected(int nodeId, string path)
|
||||
{
|
||||
using (ApplicationContext.Current.ScopeProvider.CreateScope())
|
||||
using (var scope = ApplicationContext.Current.ScopeProvider.CreateScope())
|
||||
{
|
||||
return _applicationContext.Services.PublicAccessService.IsProtected(path.EnsureEndsWith("," + nodeId));
|
||||
var ret = _applicationContext.Services.PublicAccessService.IsProtected(path.EnsureEndsWith("," + nodeId));
|
||||
scope.Complete();
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -92,11 +95,12 @@ namespace UmbracoExamine.DataServices
|
||||
|
||||
public IEnumerable<string> GetAllUserPropertyNames()
|
||||
{
|
||||
using (ApplicationContext.Current.ScopeProvider.CreateScope())
|
||||
using (var scope = ApplicationContext.Current.ScopeProvider.CreateScope())
|
||||
{
|
||||
try
|
||||
{
|
||||
var result = _applicationContext.DatabaseContext.Database.Fetch<string>("select distinct alias from cmsPropertyType order by alias");
|
||||
scope.Complete();
|
||||
return result;
|
||||
}
|
||||
catch (Exception ex)
|
||||
|
||||
Reference in New Issue
Block a user