Compare commits
269 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 5218d31f23 | |||
| 1101fc199f | |||
| 05fa58694e | |||
| ecc8575454 | |||
| df93dfe63c | |||
| 711981adab | |||
| 199c5a9606 | |||
| 54efe86020 | |||
| 589704437f | |||
| 9a99ad1416 | |||
| a4077df5b7 | |||
| 5d298e4313 | |||
| 46ef9146c4 | |||
| 3c75693e20 | |||
| b67286f40d | |||
| 32ad98cde2 | |||
| dabeaa26e6 | |||
| 6c3f1c0e32 | |||
| f4b1df1ede | |||
| 13cf9f46e7 | |||
| d062b51326 | |||
| 2b0f2577b5 | |||
| c2aa137293 | |||
| 555e0a2281 | |||
| 091a89b162 | |||
| 0eab0b606c | |||
| 1fe06a0c9e | |||
| 6022c87540 | |||
| 06d7ae3861 | |||
| 3d4f999c07 | |||
| 3b7a83ab14 | |||
| 796d665913 | |||
| 458e890c08 | |||
| e32604183c | |||
| 099b05dbbc | |||
| 4cf7ae4b4d | |||
| 757b720a68 | |||
| c8968edd65 | |||
| 58b4201eeb | |||
| a39557a3a7 | |||
| 46eda4a3c3 | |||
| 89ebad2fbc | |||
| 815a288bcb | |||
| 67bcd517bf | |||
| 915503fb51 | |||
| ddefdf2627 | |||
| 2239316889 | |||
| 2289938486 | |||
| 2b8db9d1b5 | |||
| 14c903f6a3 | |||
| b8a50b8da3 | |||
| 09300bba71 | |||
| f716479d01 | |||
| fa5044b011 | |||
| 0aefbc045d | |||
| 09788f1ab4 | |||
| 3a58cb35f8 | |||
| 8f47722d7d | |||
| ebed7c40ed | |||
| 8c54e65861 | |||
| 157f9f3285 | |||
| f2cefac866 | |||
| cbcbb895ec | |||
| b98e96f758 | |||
| 9b033a3204 | |||
| 235014c8ca | |||
| d04947a2d3 | |||
| d3582e2296 | |||
| 49ff36155c | |||
| 512de60693 | |||
| a064ef4aa7 | |||
| 2210bc3f79 | |||
| b6bc83ba71 | |||
| 8f43e9450a | |||
| f3242e8d6a | |||
| f9835d3ec5 | |||
| 49bcf413a4 | |||
| bcfea6986c | |||
| 29b297e072 | |||
| 70accfa95a | |||
| b764a47e7e | |||
| fc7cba1da9 | |||
| 883980ab8d | |||
| 23abbdb288 | |||
| 652a7a3655 | |||
| 8f73514c1d | |||
| 304b863b02 | |||
| f0bdb1a2e9 | |||
| 6fb3c91d3f | |||
| b9095629ed | |||
| f1ec692a73 | |||
| 1a2814a964 | |||
| 28d6622269 | |||
| 86971179ee | |||
| 9c4eacf330 | |||
| 251a67d653 | |||
| abe92eabba | |||
| 0e76b27a2d | |||
| 1a37d88855 | |||
| 2d5ffabe1e | |||
| f61b7579a2 | |||
| 86a3780c61 | |||
| 552909fde2 | |||
| 7adae4d345 | |||
| d747cf69c3 | |||
| bc5e3b4d66 | |||
| febeb82d10 | |||
| 1c2b2c1409 | |||
| b4eb9f333e | |||
| af00a421cd | |||
| fed06c88e8 | |||
| 5dec2861d9 | |||
| 23b3bb37ee | |||
| 4dabb606e7 | |||
| d5f66ebe02 | |||
| c7783d1cd1 | |||
| 2104b109f2 | |||
| 578ca7d41d | |||
| bd38dde1fc | |||
| 42c23da9f5 | |||
| cab1536a30 | |||
| bbd9e4f0fd | |||
| bf9a408c50 | |||
| 33d35fd9f7 | |||
| cc885c1231 | |||
| 422dd1a627 | |||
| 759e444624 | |||
| 2a7ef5de26 | |||
| 43e892563d | |||
| 4d1e64b279 | |||
| 975c97cfb0 | |||
| 0c613347ae | |||
| 83f75fdaff | |||
| 487792920b | |||
| 6ae06ad2e0 | |||
| 8387c435d4 | |||
| 5807bbccc4 | |||
| 1bfc041bbe | |||
| a5ca058cb2 | |||
| 88a1ad04aa | |||
| e619c402b4 | |||
| c9294e5281 | |||
| 0074f57970 | |||
| 9b4a6f2761 | |||
| f889b5206c | |||
| 507d7dd4f0 | |||
| 55c5b41693 | |||
| b58d8677b3 | |||
| f30d7fc68c | |||
| f5a204d1de | |||
| d504d8f54e | |||
| 7c049b55a8 | |||
| b519d68e1a | |||
| deb8d652c5 | |||
| 051b3f2e6a | |||
| 21f947efbd | |||
| a68f4e284a | |||
| 90b43584d2 | |||
| 9461e35028 | |||
| 12dd01dd29 | |||
| c64e3a8908 | |||
| d554417fde | |||
| 74e8390bda | |||
| 4f9bbdf7f7 | |||
| 67fc8b6215 | |||
| 3840ac2fed | |||
| 7c64f1aa0d | |||
| 771f8d92f2 | |||
| eedcfc122b | |||
| 8cf3110708 | |||
| 0dc0e68174 | |||
| 5226762528 | |||
| 7889f82a9a | |||
| aaa9c8f194 | |||
| 3d1a44b32c | |||
| 724ec9ab73 | |||
| aa0eaf590c | |||
| 2b82520067 | |||
| 5aed6334c4 | |||
| 91157caf0e | |||
| dbe489c0f2 | |||
| 2652a583e0 | |||
| bc1758f569 | |||
| a898c2d697 | |||
| e612bcae58 | |||
| d27fee8a5b | |||
| 7aef8cbb67 | |||
| a7e28a97d9 | |||
| 9c5526f281 | |||
| 9c803cc234 | |||
| f24fd42a69 | |||
| 9b7f459c44 | |||
| 273a5cef28 | |||
| ade3c4ee6c | |||
| 7da7c1e5a1 | |||
| 1901fbb6ed | |||
| 1de52482e3 | |||
| 49c2ae3673 | |||
| 5f22c81994 | |||
| f13427f5fb | |||
| 2785eaeda5 | |||
| bb31eb7c35 | |||
| a31cfb86f8 | |||
| e4802b592f | |||
| 548f9fd01f | |||
| 843796c9a4 | |||
| 7648c76f6f | |||
| 308ed3bba7 | |||
| 7bcb2b11f6 | |||
| c060856782 | |||
| 857063ebd8 | |||
| c36000c4dd | |||
| 999de5ae1d | |||
| 00982d4278 | |||
| cda47ed536 | |||
| a20cc60c10 | |||
| 846ac573cc | |||
| 2ba5627e2c | |||
| 122295c89f | |||
| 2669c68df0 | |||
| 912a6a52cc | |||
| 925c343a30 | |||
| 0e727f91da | |||
| 179523224a | |||
| 19e4bee34e | |||
| f1709f6066 | |||
| f2be235f85 | |||
| a3c7b1fcfe | |||
| 02785eea42 | |||
| d97cc36c53 | |||
| db86f6e1ee | |||
| a03e0dfb53 | |||
| b4450ca7f2 | |||
| 7004765acf | |||
| 77a91bc017 | |||
| 14b96da588 | |||
| 99324035e5 | |||
| 8b9a5000be | |||
| c82bddb7dd | |||
| f2fd45576b | |||
| 32d757b13c | |||
| d5a687cf3d | |||
| ff6ea0b42e | |||
| 384a31e3c6 | |||
| 69b913c608 | |||
| 83757c60b5 | |||
| 26acecb720 | |||
| 7c42cd26c2 | |||
| 13758418ac | |||
| 77b04d74d4 | |||
| e95cb14d48 | |||
| 86b2dac231 | |||
| 2f67a485aa | |||
| f192f241cf | |||
| bf797bccc7 | |||
| 4336f5201d | |||
| ed019a90e2 | |||
| 8d598cd37c | |||
| 30c2f95844 | |||
| 0078a5d8cc | |||
| 09085e3bcf | |||
| a31b03b3e2 | |||
| 9ef53536bf | |||
| 547351a719 | |||
| 371c4ae1a3 | |||
| 060137c798 | |||
| a5af5ba1a9 | |||
| ca1f43d048 | |||
| e962a48138 |
@@ -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
|
||||
alpha060
|
||||
alpha072
|
||||
|
||||
+1
-1
@@ -12,4 +12,4 @@ using System.Resources;
|
||||
[assembly: AssemblyVersion("1.0.*")]
|
||||
|
||||
[assembly: AssemblyFileVersion("7.6.0")]
|
||||
[assembly: AssemblyInformationalVersion("7.6.0-alpha060")]
|
||||
[assembly: AssemblyInformationalVersion("7.6.0-alpha072")]
|
||||
@@ -0,0 +1,15 @@
|
||||
using System;
|
||||
|
||||
namespace Umbraco.Core.CodeAnnotations
|
||||
{
|
||||
[AttributeUsage(AttributeTargets.Field, AllowMultiple = false, Inherited = false)]
|
||||
internal class UmbracoUdiTypeAttribute : Attribute
|
||||
{
|
||||
public string UdiType { get; private set; }
|
||||
|
||||
public UmbracoUdiTypeAttribute(string udiType)
|
||||
{
|
||||
UdiType = udiType;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -159,6 +159,12 @@ namespace Umbraco.Core.Configuration.UmbracoSettings
|
||||
get { return GetOptionalTextElement("defaultDocumentTypeProperty", "Textstring"); }
|
||||
}
|
||||
|
||||
[ConfigurationProperty("showDeprecatedPropertyEditors")]
|
||||
internal InnerTextConfigurationElement<bool> ShowDeprecatedPropertyEditors
|
||||
{
|
||||
get { return GetOptionalTextElement("showDeprecatedPropertyEditors", false); }
|
||||
}
|
||||
|
||||
[ConfigurationProperty("EnableInheritedDocumentTypes")]
|
||||
internal InnerTextConfigurationElement<bool> EnableInheritedDocumentTypes
|
||||
{
|
||||
@@ -306,6 +312,11 @@ namespace Umbraco.Core.Configuration.UmbracoSettings
|
||||
get { return DefaultDocumentTypeProperty; }
|
||||
}
|
||||
|
||||
bool IContentSection.ShowDeprecatedPropertyEditors
|
||||
{
|
||||
get { return ShowDeprecatedPropertyEditors; }
|
||||
}
|
||||
|
||||
bool IContentSection.EnableInheritedDocumentTypes
|
||||
{
|
||||
get { return EnableInheritedDocumentTypes; }
|
||||
|
||||
@@ -60,6 +60,12 @@ namespace Umbraco.Core.Configuration.UmbracoSettings
|
||||
|
||||
string DefaultDocumentTypeProperty { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The default for this is false but if you would like deprecated property editors displayed
|
||||
/// in the data type editor you can enable this
|
||||
/// </summary>
|
||||
bool ShowDeprecatedPropertyEditors { get; }
|
||||
|
||||
bool EnableInheritedDocumentTypes { get; }
|
||||
|
||||
bool EnableInheritedMediaTypes { get; }
|
||||
|
||||
@@ -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 "alpha060"; } }
|
||||
public static string CurrentComment { get { return "alpha072"; } }
|
||||
|
||||
// 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
|
||||
|
||||
@@ -109,6 +109,11 @@ namespace Umbraco.Core
|
||||
/// </summary>
|
||||
public const string Member = "39EB0F98-B348-42A1-8662-E7EB18487560";
|
||||
|
||||
/// <summary>
|
||||
/// Guid for a Media Type object.
|
||||
/// </summary>
|
||||
public static readonly Guid MemberGuid = new Guid(Member);
|
||||
|
||||
/// <summary>
|
||||
/// Guid for a Member Group object.
|
||||
/// </summary>
|
||||
@@ -174,24 +179,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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -42,10 +42,14 @@ namespace Umbraco.Core
|
||||
[Obsolete("GUIDs are no longer used to reference Property Editors, use the Alias constant instead. This will be removed in future versions")]
|
||||
public const string ContentPicker = "158AA029-24ED-4948-939E-C3DA209E5FBA";
|
||||
|
||||
|
||||
[Obsolete("This is an obsoleted content picker, use ContentPicker2Alias instead")]
|
||||
public const string ContentPickerAlias = "Umbraco.ContentPickerAlias";
|
||||
|
||||
/// <summary>
|
||||
/// Alias for the Content Picker datatype.
|
||||
/// </summary>
|
||||
public const string ContentPickerAlias = "Umbraco.ContentPickerAlias";
|
||||
public const string ContentPicker2Alias = "Umbraco.ContentPicker2";
|
||||
|
||||
/// <summary>
|
||||
/// Guid for the Date datatype.
|
||||
@@ -192,11 +196,15 @@ namespace Umbraco.Core
|
||||
[Obsolete("GUIDs are no longer used to reference Property Editors, use the Alias constant instead. This will be removed in future versions")]
|
||||
public const string MediaPicker = "EAD69342-F06D-4253-83AC-28000225583B";
|
||||
|
||||
[Obsolete("This is an obsoleted picker, use MediaPicker2Alias instead")]
|
||||
public const string MediaPickerAlias = "Umbraco.MediaPicker";
|
||||
|
||||
/// <summary>
|
||||
/// Alias for the Media Picker datatype.
|
||||
/// </summary>
|
||||
public const string MediaPickerAlias = "Umbraco.MediaPicker";
|
||||
public const string MediaPicker2Alias = "Umbraco.MediaPicker2";
|
||||
|
||||
[Obsolete("This is an obsoleted picker, use MediaPicker2Alias instead")]
|
||||
public const string MultipleMediaPickerAlias = "Umbraco.MultipleMediaPicker";
|
||||
|
||||
/// <summary>
|
||||
@@ -205,26 +213,32 @@ namespace Umbraco.Core
|
||||
[Obsolete("GUIDs are no longer used to reference Property Editors, use the Alias constant instead. This will be removed in future versions")]
|
||||
public const string MemberPicker = "39F533E4-0551-4505-A64B-E0425C5CE775";
|
||||
|
||||
[Obsolete("This is an obsoleted picker, use MemberPicker2Alias instead")]
|
||||
public const string MemberPickerAlias = "Umbraco.MemberPicker";
|
||||
|
||||
/// <summary>
|
||||
/// Alias for the Member Picker datatype.
|
||||
/// </summary>
|
||||
public const string MemberPickerAlias = "Umbraco.MemberPicker";
|
||||
public const string MemberPicker2Alias = "Umbraco.MemberPicker2";
|
||||
|
||||
/// <summary>
|
||||
/// Alias for the Member Group Picker datatype.
|
||||
/// </summary>
|
||||
public const string MemberGroupPickerAlias = "Umbraco.MemberGroupPicker";
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Guid for the Multi-Node Tree Picker datatype
|
||||
/// </summary>
|
||||
[Obsolete("GUIDs are no longer used to reference Property Editors, use the Alias constant instead. This will be removed in future versions")]
|
||||
public const string MultiNodeTreePicker = "7E062C13-7C41-4AD9-B389-41D88AEEF87C";
|
||||
|
||||
[Obsolete("This is an obsoleted picker, use MultiNodeTreePicker2Alias instead")]
|
||||
public const string MultiNodeTreePickerAlias = "Umbraco.MultiNodeTreePicker";
|
||||
|
||||
/// <summary>
|
||||
/// Alias for the Multi-Node Tree Picker datatype
|
||||
/// </summary>
|
||||
public const string MultiNodeTreePickerAlias = "Umbraco.MultiNodeTreePicker";
|
||||
public const string MultiNodeTreePicker2Alias = "Umbraco.MultiNodeTreePicker2";
|
||||
|
||||
/// <summary>
|
||||
/// Guid for the Multiple Textstring datatype.
|
||||
|
||||
@@ -16,6 +16,7 @@ namespace Umbraco.Core.Deploy
|
||||
if (udi == null)
|
||||
throw new ArgumentNullException("udi");
|
||||
Udi = udi;
|
||||
Name = Udi.ToString();
|
||||
|
||||
Dependencies = dependencies ?? Enumerable.Empty<ArtifactDependency>();
|
||||
_checksum = new Lazy<string>(GetChecksum);
|
||||
@@ -48,5 +49,8 @@ namespace Umbraco.Core.Deploy
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
public string Name { get; set; }
|
||||
public string Alias { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -4,5 +4,8 @@ namespace Umbraco.Core.Deploy
|
||||
/// Represents an artifact ie an object that can be transfered between environments.
|
||||
/// </summary>
|
||||
public interface IArtifact : IArtifactSignature
|
||||
{ }
|
||||
{
|
||||
string Name { get; }
|
||||
string Alias { get; }
|
||||
}
|
||||
}
|
||||
@@ -66,10 +66,20 @@ namespace Umbraco.Core.Deploy
|
||||
/// <returns>The length of the file, or -1 if the file does not exist.</returns>
|
||||
Task<long> GetFileLengthAsync(StringUdi udi, CancellationToken token);
|
||||
|
||||
// fixme - doc
|
||||
void GetFiles(IEnumerable<StringUdi> udis, IFileStore fileStore);
|
||||
/// <summary>
|
||||
/// Gets files and store them using a file store.
|
||||
/// </summary>
|
||||
/// <param name="udis">The udis of the files to get.</param>
|
||||
/// <param name="fileTypes">A collection of file types which can store the files.</param>
|
||||
void GetFiles(IEnumerable<StringUdi> udis, IFileTypeCollection fileTypes);
|
||||
|
||||
Task GetFilesAsync(IEnumerable<StringUdi> udis, IFileStore fileStore, CancellationToken token);
|
||||
/// <summary>
|
||||
/// Gets files and store them using a file store.
|
||||
/// </summary>
|
||||
/// <param name="udis">The udis of the files to get.</param>
|
||||
/// <param name="fileTypes">A collection of file types which can store the files.</param>
|
||||
/// <param name="token">A cancellation token.</param>
|
||||
Task GetFilesAsync(IEnumerable<StringUdi> udis, IFileTypeCollection fileTypes, CancellationToken token);
|
||||
|
||||
///// <summary>
|
||||
///// Gets the content of a file as a bytes array.
|
||||
|
||||
@@ -1,9 +0,0 @@
|
||||
using System.IO;
|
||||
|
||||
namespace Umbraco.Core.Deploy
|
||||
{
|
||||
public interface IFileStore
|
||||
{
|
||||
void SaveStream(StringUdi udi, Stream stream);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
using System.IO;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Umbraco.Core.Deploy
|
||||
{
|
||||
public interface IFileType
|
||||
{
|
||||
Stream GetStream(StringUdi udi);
|
||||
|
||||
Task<Stream> GetStreamAsync(StringUdi udi, CancellationToken token);
|
||||
|
||||
Stream GetChecksumStream(StringUdi udi);
|
||||
|
||||
long GetLength(StringUdi udi);
|
||||
|
||||
void SetStream(StringUdi udi, Stream stream);
|
||||
|
||||
Task SetStreamAsync(StringUdi udi, Stream stream, CancellationToken token);
|
||||
|
||||
bool CanSetPhysical { get; }
|
||||
|
||||
void Set(StringUdi udi, string physicalPath, bool copy = false);
|
||||
|
||||
// this is not pretty as *everywhere* in Deploy we take care of ignoring
|
||||
// the physical path and always rely on Core's virtual IFileSystem but
|
||||
// Cloud wants to add some of these files to Git and needs the path...
|
||||
string GetPhysicalPath(StringUdi udi);
|
||||
|
||||
string GetVirtualPath(StringUdi udi);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
namespace Umbraco.Core.Deploy
|
||||
{
|
||||
public interface IFileTypeCollection
|
||||
{
|
||||
IFileType this[string entityType] { get; }
|
||||
|
||||
bool Contains(string entityType);
|
||||
}
|
||||
}
|
||||
@@ -1,8 +0,0 @@
|
||||
namespace Umbraco.Core.Deploy
|
||||
{
|
||||
public interface IPrettyArtifact
|
||||
{
|
||||
string Name { get; }
|
||||
string Alias { get; }
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
@@ -67,22 +68,15 @@ namespace Umbraco.Core
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>The for each.</summary>
|
||||
/// <param name="items">The items.</param>
|
||||
/// <param name="func">The func.</param>
|
||||
/// <typeparam name="TItem">item type</typeparam>
|
||||
/// <typeparam name="TResult">Result type</typeparam>
|
||||
/// <returns>the Results</returns>
|
||||
[EditorBrowsable(EditorBrowsableState.Never)]
|
||||
[Obsolete("Use a normal foreach loop instead, this adds more allocations than necessary")]
|
||||
public static TResult[] ForEach<TItem, TResult>(this IEnumerable<TItem> items, Func<TItem, TResult> func)
|
||||
{
|
||||
return items.Select(func).ToArray();
|
||||
}
|
||||
|
||||
/// <summary>The for each.</summary>
|
||||
/// <param name="items">The items.</param>
|
||||
/// <param name="action">The action.</param>
|
||||
/// <typeparam name="TItem">Item type</typeparam>
|
||||
/// <returns>list of TItem</returns>
|
||||
[EditorBrowsable(EditorBrowsableState.Never)]
|
||||
[Obsolete("Use a normal foreach loop instead, this adds more allocations than necessary")]
|
||||
public static IEnumerable<TItem> ForEach<TItem>(this IEnumerable<TItem> items, Action<TItem> action)
|
||||
{
|
||||
if (items != null)
|
||||
@@ -101,6 +95,7 @@ namespace Umbraco.Core
|
||||
/// <param name="f">The select child.</param>
|
||||
/// <typeparam name="T">Item type</typeparam>
|
||||
/// <returns>list of TItem</returns>
|
||||
[EditorBrowsable(EditorBrowsableState.Never)]
|
||||
[Obsolete("Do not use, use SelectRecursive instead which has far less potential of re-iterating an iterator which may cause significantly more SQL queries")]
|
||||
public static IEnumerable<T> FlattenList<T>(this IEnumerable<T> e, Func<T, IEnumerable<T>> f)
|
||||
{
|
||||
|
||||
@@ -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; }
|
||||
}
|
||||
}
|
||||
@@ -23,6 +23,11 @@ namespace Umbraco.Core.Events
|
||||
{
|
||||
get { return _packageMetaData; }
|
||||
}
|
||||
|
||||
public IEnumerable<TEntity> InstallationSummary
|
||||
{
|
||||
get { return EventObject; }
|
||||
}
|
||||
|
||||
public bool Equals(ImportPackageEventArgs<TEntity> other)
|
||||
{
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
using System;
|
||||
using Umbraco.Core.Scoping;
|
||||
|
||||
namespace Umbraco.Core.Events
|
||||
{
|
||||
/// <summary>
|
||||
/// Stores the instance of EventMessages in the current scope.
|
||||
/// </summary>
|
||||
internal class ScopeLifespanMessagesFactory : IEventMessagesFactory
|
||||
{
|
||||
public const string ContextKey = "Umbraco.Core.Events.ScopeLifespanMessagesFactory";
|
||||
|
||||
private readonly IHttpContextAccessor _contextAccessor;
|
||||
private readonly IScopeProviderInternal _scopeProvider;
|
||||
|
||||
public static ScopeLifespanMessagesFactory Current { get; private set; }
|
||||
|
||||
public ScopeLifespanMessagesFactory(IHttpContextAccessor contextAccesor, IScopeProvider scopeProvider)
|
||||
{
|
||||
if (contextAccesor == null) throw new ArgumentNullException("contextAccesor");
|
||||
if (scopeProvider == null) throw new ArgumentNullException("scopeProvider");
|
||||
if (scopeProvider is IScopeProviderInternal == false) throw new ArgumentException("Not IScopeProviderInternal.", "scopeProvider");
|
||||
_contextAccessor = contextAccesor;
|
||||
_scopeProvider = (IScopeProviderInternal) scopeProvider;
|
||||
Current = this;
|
||||
}
|
||||
|
||||
public EventMessages Get()
|
||||
{
|
||||
var messages = GetFromHttpContext();
|
||||
if (messages != null) return messages;
|
||||
|
||||
var scope = _scopeProvider.GetAmbientOrNoScope();
|
||||
return scope.Messages;
|
||||
}
|
||||
|
||||
public EventMessages GetFromHttpContext()
|
||||
{
|
||||
if (_contextAccessor == null || _contextAccessor.Value == null) return null;
|
||||
return (EventMessages)_contextAccessor.Value.Items[ContextKey];
|
||||
}
|
||||
|
||||
public EventMessages TryGet()
|
||||
{
|
||||
var messages = GetFromHttpContext();
|
||||
if (messages != null) return messages;
|
||||
|
||||
var scope = _scopeProvider.AmbientScope;
|
||||
return scope == null ? null : scope.MessagesOrNull;
|
||||
}
|
||||
|
||||
public void Set(EventMessages messages)
|
||||
{
|
||||
if (_contextAccessor.Value == null) return;
|
||||
_contextAccessor.Value.Items[ContextKey] = messages;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
using System.Collections.Generic;
|
||||
using Umbraco.Core.Packaging.Models;
|
||||
|
||||
namespace Umbraco.Core.Events
|
||||
{
|
||||
internal class UninstallPackageEventArgs<TEntity> : CancellableObjectEventArgs<IEnumerable<TEntity>>
|
||||
{
|
||||
private readonly MetaData _packageMetaData;
|
||||
|
||||
public UninstallPackageEventArgs(TEntity eventObject, bool canCancel)
|
||||
: base(new[] { eventObject }, canCancel)
|
||||
{
|
||||
}
|
||||
|
||||
public UninstallPackageEventArgs(TEntity eventObject, MetaData packageMetaData)
|
||||
: base(new[] { eventObject })
|
||||
{
|
||||
_packageMetaData = packageMetaData;
|
||||
}
|
||||
|
||||
public MetaData PackageMetaData
|
||||
{
|
||||
get { return _packageMetaData; }
|
||||
}
|
||||
|
||||
public IEnumerable<TEntity> UninstallationSummary
|
||||
{
|
||||
get { return EventObject; }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
using System.Web;
|
||||
|
||||
namespace Umbraco.Core
|
||||
{
|
||||
public interface IHttpContextAccessor
|
||||
{
|
||||
HttpContextBase Value { get; }
|
||||
}
|
||||
}
|
||||
@@ -4,11 +4,11 @@ using System.Globalization;
|
||||
using System.Reflection;
|
||||
using System.IO;
|
||||
using System.Configuration;
|
||||
using System.Linq;
|
||||
using System.Web;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Web.Hosting;
|
||||
using Umbraco.Core.Configuration;
|
||||
using Umbraco.Core.Logging;
|
||||
|
||||
namespace Umbraco.Core.IO
|
||||
{
|
||||
@@ -351,7 +351,54 @@ namespace Umbraco.Core.IO
|
||||
writer.Write(contents);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if a given path is a full path including drive letter
|
||||
/// </summary>
|
||||
/// <param name="path"></param>
|
||||
/// <returns></returns>
|
||||
// From: http://stackoverflow.com/a/35046453/5018
|
||||
internal static bool IsFullPath(this string path)
|
||||
{
|
||||
return string.IsNullOrWhiteSpace(path) == false
|
||||
&& path.IndexOfAny(Path.GetInvalidPathChars().ToArray()) == -1
|
||||
&& Path.IsPathRooted(path)
|
||||
&& Path.GetPathRoot(path).Equals(Path.DirectorySeparatorChar.ToString(), StringComparison.Ordinal) == false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get properly formatted relative path from an existing absolute or relative path
|
||||
/// </summary>
|
||||
/// <param name="path"></param>
|
||||
/// <returns></returns>
|
||||
internal static string GetRelativePath(this string path)
|
||||
{
|
||||
if (path.IsFullPath())
|
||||
{
|
||||
var rootDirectory = GetRootDirectorySafe();
|
||||
var relativePath = path.ToLowerInvariant().Replace(rootDirectory.ToLowerInvariant(), string.Empty);
|
||||
path = relativePath;
|
||||
}
|
||||
|
||||
return path.EnsurePathIsApplicationRootPrefixed();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Ensures that a path has `~/` as prefix
|
||||
/// </summary>
|
||||
/// <param name="path"></param>
|
||||
/// <returns></returns>
|
||||
internal static string EnsurePathIsApplicationRootPrefixed(this string path)
|
||||
{
|
||||
if (path.StartsWith("~/"))
|
||||
return path;
|
||||
if (path.StartsWith("/") == false && path.StartsWith("\\") == false)
|
||||
path = string.Format("/{0}", path);
|
||||
if (path.StartsWith("~") == false)
|
||||
path = string.Format("~{0}", path);
|
||||
return 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
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace Umbraco.Core.IO
|
||||
{
|
||||
@@ -209,19 +210,20 @@ namespace Umbraco.Core.IO
|
||||
|
||||
public IEnumerable<string> GetFiles(string path)
|
||||
{
|
||||
var normPath = NormPath(path);
|
||||
var shadows = Nodes.Where(kvp => IsChild(normPath, kvp.Key)).ToArray();
|
||||
var files = _fs.GetFiles(path);
|
||||
return files
|
||||
.Except(shadows.Where(kvp => (kvp.Value.IsFile && kvp.Value.IsDelete) || kvp.Value.IsDir)
|
||||
.Select(kvp => kvp.Key))
|
||||
.Union(shadows.Where(kvp => kvp.Value.IsFile && kvp.Value.IsExist).Select(kvp => kvp.Key))
|
||||
.Distinct();
|
||||
return GetFiles(path, null);
|
||||
}
|
||||
|
||||
public IEnumerable<string> GetFiles(string path, string filter)
|
||||
{
|
||||
return _fs.GetFiles(path, filter);
|
||||
var normPath = NormPath(path);
|
||||
var shadows = Nodes.Where(kvp => IsChild(normPath, kvp.Key)).ToArray();
|
||||
var files = filter != null ? _fs.GetFiles(path, filter) : _fs.GetFiles(path);
|
||||
var regexFilter = FilterToRegex(filter);
|
||||
return files
|
||||
.Except(shadows.Where(kvp => (kvp.Value.IsFile && kvp.Value.IsDelete) || kvp.Value.IsDir)
|
||||
.Select(kvp => kvp.Key))
|
||||
.Union(shadows.Where(kvp => kvp.Value.IsFile && kvp.Value.IsExist && FilterByRegex(kvp.Key, regexFilter)).Select(kvp => kvp.Key))
|
||||
.Distinct();
|
||||
}
|
||||
|
||||
public Stream OpenFile(string path)
|
||||
@@ -253,6 +255,9 @@ namespace Umbraco.Core.IO
|
||||
|
||||
public string GetFullPath(string path)
|
||||
{
|
||||
ShadowNode sf;
|
||||
if (Nodes.TryGetValue(NormPath(path), out sf))
|
||||
return sf.IsDir || sf.IsDelete ? null : _sfs.GetFullPath(path);
|
||||
return _fs.GetFullPath(path);
|
||||
}
|
||||
|
||||
@@ -321,5 +326,32 @@ namespace Umbraco.Core.IO
|
||||
_sfs.AddFile(path, physicalPath, overrideIfExists, copy);
|
||||
Nodes[normPath] = new ShadowNode(false, false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Helper function for filtering keys by Regex if a filter is specified.
|
||||
/// </summary>
|
||||
/// <param name="input"></param>
|
||||
/// <param name="regexFilter"></param>
|
||||
/// <returns></returns>
|
||||
internal static bool FilterByRegex(string input, string regexFilter)
|
||||
{
|
||||
if (regexFilter == null) return true;
|
||||
return regexFilter != string.Empty && Regex.IsMatch(input, regexFilter);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Transforms a filter pattern into a Regex pattern
|
||||
/// </summary>
|
||||
/// <param name="pattern"></param>
|
||||
/// <returns></returns>
|
||||
/// <remarks>
|
||||
/// Appending '$' only if not containing wildcard is stupid and broken.
|
||||
/// It is however what seems to be what they're doing in .NET so we need to match the functionality.
|
||||
/// </remarks>
|
||||
internal static string FilterToRegex(string pattern)
|
||||
{
|
||||
if (pattern == null) return null;
|
||||
return "^" + Regex.Escape(pattern).Replace("\\*", ".*").Replace("\\?", ".") + (pattern.Contains("*") ? string.Empty : "$");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,7 +27,7 @@ namespace Umbraco.Core.IO
|
||||
// on ShadowFileSystemsScope.None - and if None is false then we should be running
|
||||
// in a single thread anyways
|
||||
|
||||
var virt = "~/App_Data/Shadow/" + id + "/" + _shadowPath;
|
||||
var virt = "~/App_Data/TEMP/ShadowFs/" + id + "/" + _shadowPath;
|
||||
_shadowDir = IOHelper.MapPath(virt);
|
||||
Directory.CreateDirectory(_shadowDir);
|
||||
var tempfs = new PhysicalFileSystem(virt);
|
||||
|
||||
@@ -30,7 +30,8 @@ namespace Umbraco.Core.Models
|
||||
return descendants;
|
||||
}
|
||||
|
||||
throw new NotSupportedException("The content type must be " + typeof(IContentType) + " or " + typeof(IMediaType));
|
||||
//No other content types have children (i.e. member types)
|
||||
return Enumerable.Empty<IContentTypeBase>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -13,6 +13,7 @@ namespace Umbraco.Core.Models.Identity
|
||||
public override void ConfigureMappings(IConfiguration config, ApplicationContext applicationContext)
|
||||
{
|
||||
config.CreateMap<IUser, BackOfficeIdentityUser>()
|
||||
.ForMember(user => user.LastLoginDateUtc, expression => expression.MapFrom(user => user.LastLoginDate.ToUniversalTime()))
|
||||
.ForMember(user => user.Email, expression => expression.MapFrom(user => user.Email))
|
||||
.ForMember(user => user.Id, expression => expression.MapFrom(user => user.Id))
|
||||
.ForMember(user => user.LockoutEndDateUtc, expression => expression.MapFrom(user => user.IsLockedOut ? DateTime.MaxValue.ToUniversalTime() : (DateTime?)null))
|
||||
|
||||
@@ -24,12 +24,17 @@ namespace Umbraco.Core.Models.Identity
|
||||
///
|
||||
/// </summary>
|
||||
public IdentityUser()
|
||||
{
|
||||
{
|
||||
this.Claims = new List<TClaim>();
|
||||
this.Roles = new List<TRole>();
|
||||
this.Logins = new List<TLogin>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Last login date
|
||||
/// </summary>
|
||||
public virtual DateTime? LastLoginDateUtc { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Email
|
||||
///
|
||||
|
||||
@@ -9,7 +9,7 @@ namespace Umbraco.Core.Models.Rdbms
|
||||
internal class DataTypePreValueDto
|
||||
{
|
||||
[Column("id")]
|
||||
[PrimaryKeyColumn(IdentitySeed = 6)]
|
||||
[PrimaryKeyColumn(IdentitySeed = 10)]
|
||||
public int Id { get; set; }
|
||||
|
||||
[Column("datatypeNodeId")]
|
||||
|
||||
@@ -22,6 +22,7 @@ namespace Umbraco.Core.Models.Rdbms
|
||||
[Column("LoginName")]
|
||||
[Length(1000)]
|
||||
[Constraint(Default = "''")]
|
||||
[Index(IndexTypes.NonClustered, Name = "IX_cmsMember_LoginName")]
|
||||
public string LoginName { get; set; }
|
||||
|
||||
[Column("Password")]
|
||||
|
||||
@@ -16,6 +16,7 @@ namespace Umbraco.Core.Models.Rdbms
|
||||
|
||||
[Column("parentId")]
|
||||
[ForeignKey(typeof(NodeDto), Name = "FK_umbracoRelation_umbracoNode")]
|
||||
[Index(IndexTypes.UniqueNonClustered, Name = "IX_umbracoRelation_parentChildType", ForColumns = "parentId,childId,relType")]
|
||||
public int ParentId { get; set; }
|
||||
|
||||
[Column("childId")]
|
||||
|
||||
@@ -29,11 +29,13 @@ namespace Umbraco.Core.Models.Rdbms
|
||||
public Guid ChildObjectType { get; set; }
|
||||
|
||||
[Column("name")]
|
||||
[Index(IndexTypes.UniqueNonClustered, Name = "IX_umbracoRelationType_name")]
|
||||
public string Name { get; set; }
|
||||
|
||||
[Column("alias")]
|
||||
[NullSetting(NullSetting = NullSettings.Null)]
|
||||
[Length(100)]
|
||||
[Index(IndexTypes.UniqueNonClustered, Name = "IX_umbracoRelationType_alias")]
|
||||
public string Alias { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -15,6 +15,7 @@ namespace Umbraco.Core.Models.Rdbms
|
||||
|
||||
[Column("nodeId")]
|
||||
[ForeignKey(typeof(NodeDto))]
|
||||
[Index(IndexTypes.NonClustered, Name = "IX_umbracoUser2NodePermission_nodeId")]
|
||||
public int NodeId { get; set; }
|
||||
|
||||
[Column("permission")]
|
||||
|
||||
@@ -113,6 +113,11 @@ namespace Umbraco.Core.Models
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// When resolved from EntityService this checks if the entity has the HasChildren flag
|
||||
/// </summary>
|
||||
/// <param name="entity"></param>
|
||||
/// <returns></returns>
|
||||
public static bool HasChildren(this IUmbracoEntity entity)
|
||||
{
|
||||
if (entity.AdditionalData.ContainsKey("HasChildren"))
|
||||
@@ -133,6 +138,11 @@ namespace Umbraco.Core.Models
|
||||
return entity.AdditionalData.GetValueIgnoreCase(key, defaultVal);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// When resolved from EntityService this checks if the entity has the IsContainer flag
|
||||
/// </summary>
|
||||
/// <param name="entity"></param>
|
||||
/// <returns></returns>
|
||||
public static bool IsContainer(this IUmbracoEntity entity)
|
||||
{
|
||||
if (entity.AdditionalData.ContainsKeyIgnoreCase("IsContainer") == false) return false;
|
||||
|
||||
@@ -26,7 +26,7 @@ namespace Umbraco.Core.Models
|
||||
/// <summary>
|
||||
/// Root
|
||||
/// </summary>
|
||||
[UmbracoObjectType(Constants.ObjectTypes.SystemRoot)]
|
||||
[UmbracoObjectType(Constants.ObjectTypes.SystemRoot)]
|
||||
[FriendlyName("Root")]
|
||||
ROOT,
|
||||
|
||||
@@ -35,6 +35,7 @@ namespace Umbraco.Core.Models
|
||||
/// </summary>
|
||||
[UmbracoObjectType(Constants.ObjectTypes.Document, typeof(IContent))]
|
||||
[FriendlyName("Document")]
|
||||
[UmbracoUdiType(Constants.UdiEntityType.Document)]
|
||||
Document,
|
||||
|
||||
/// <summary>
|
||||
@@ -42,6 +43,7 @@ namespace Umbraco.Core.Models
|
||||
/// </summary>
|
||||
[UmbracoObjectType(Constants.ObjectTypes.Media, typeof(IMedia))]
|
||||
[FriendlyName("Media")]
|
||||
[UmbracoUdiType(Constants.UdiEntityType.Media)]
|
||||
Media,
|
||||
|
||||
/// <summary>
|
||||
@@ -49,6 +51,7 @@ namespace Umbraco.Core.Models
|
||||
/// </summary>
|
||||
[UmbracoObjectType(Constants.ObjectTypes.MemberType, typeof(IMemberType))]
|
||||
[FriendlyName("Member Type")]
|
||||
[UmbracoUdiType(Constants.UdiEntityType.MemberType)]
|
||||
MemberType,
|
||||
|
||||
/// <summary>
|
||||
@@ -56,6 +59,7 @@ namespace Umbraco.Core.Models
|
||||
/// </summary>
|
||||
[UmbracoObjectType(Constants.ObjectTypes.Template, typeof(ITemplate))]
|
||||
[FriendlyName("Template")]
|
||||
[UmbracoUdiType(Constants.UdiEntityType.Template)]
|
||||
Template,
|
||||
|
||||
/// <summary>
|
||||
@@ -63,6 +67,7 @@ namespace Umbraco.Core.Models
|
||||
/// </summary>
|
||||
[UmbracoObjectType(Constants.ObjectTypes.MemberGroup)]
|
||||
[FriendlyName("Member Group")]
|
||||
[UmbracoUdiType(Constants.UdiEntityType.MemberGroup)]
|
||||
MemberGroup,
|
||||
|
||||
//TODO: What is a 'Content Item' supposed to be???
|
||||
@@ -80,6 +85,7 @@ namespace Umbraco.Core.Models
|
||||
/// </summary>
|
||||
[UmbracoObjectType(Constants.ObjectTypes.MediaType, typeof(IMediaType))]
|
||||
[FriendlyName("Media Type")]
|
||||
[UmbracoUdiType(Constants.UdiEntityType.MediaType)]
|
||||
MediaType,
|
||||
|
||||
/// <summary>
|
||||
@@ -87,13 +93,14 @@ namespace Umbraco.Core.Models
|
||||
/// </summary>
|
||||
[UmbracoObjectType(Constants.ObjectTypes.DocumentType, typeof(IContentType))]
|
||||
[FriendlyName("Document Type")]
|
||||
[UmbracoUdiType(Constants.UdiEntityType.DocumentType)]
|
||||
DocumentType,
|
||||
|
||||
/// <summary>
|
||||
/// Recycle Bin
|
||||
/// </summary>
|
||||
[UmbracoObjectType(Constants.ObjectTypes.ContentRecycleBin)]
|
||||
[FriendlyName("Recycle Bin")]
|
||||
[FriendlyName("Recycle Bin")]
|
||||
RecycleBin,
|
||||
|
||||
/// <summary>
|
||||
@@ -101,6 +108,7 @@ namespace Umbraco.Core.Models
|
||||
/// </summary>
|
||||
[UmbracoObjectType(Constants.ObjectTypes.Stylesheet)]
|
||||
[FriendlyName("Stylesheet")]
|
||||
[UmbracoUdiType(Constants.UdiEntityType.Stylesheet)]
|
||||
Stylesheet,
|
||||
|
||||
/// <summary>
|
||||
@@ -108,6 +116,7 @@ namespace Umbraco.Core.Models
|
||||
/// </summary>
|
||||
[UmbracoObjectType(Constants.ObjectTypes.Member, typeof(IMember))]
|
||||
[FriendlyName("Member")]
|
||||
[UmbracoUdiType(Constants.UdiEntityType.Member)]
|
||||
Member,
|
||||
|
||||
/// <summary>
|
||||
@@ -115,6 +124,7 @@ namespace Umbraco.Core.Models
|
||||
/// </summary>
|
||||
[UmbracoObjectType(Constants.ObjectTypes.DataType, typeof(IDataTypeDefinition))]
|
||||
[FriendlyName("Data Type")]
|
||||
[UmbracoUdiType(Constants.UdiEntityType.DataType)]
|
||||
DataType,
|
||||
|
||||
/// <summary>
|
||||
@@ -122,6 +132,7 @@ namespace Umbraco.Core.Models
|
||||
/// </summary>
|
||||
[UmbracoObjectType(Constants.ObjectTypes.DocumentTypeContainer)]
|
||||
[FriendlyName("Document Type Container")]
|
||||
[UmbracoUdiType(Constants.UdiEntityType.DocumentTypeContainer)]
|
||||
DocumentTypeContainer,
|
||||
|
||||
/// <summary>
|
||||
@@ -129,6 +140,7 @@ namespace Umbraco.Core.Models
|
||||
/// </summary>
|
||||
[UmbracoObjectType(Constants.ObjectTypes.MediaTypeContainer)]
|
||||
[FriendlyName("Media Type Container")]
|
||||
[UmbracoUdiType(Constants.UdiEntityType.MediaTypeContainer)]
|
||||
MediaTypeContainer,
|
||||
|
||||
/// <summary>
|
||||
@@ -136,6 +148,7 @@ namespace Umbraco.Core.Models
|
||||
/// </summary>
|
||||
[UmbracoObjectType(Constants.ObjectTypes.DataTypeContainer)]
|
||||
[FriendlyName("Data Type Container")]
|
||||
[UmbracoUdiType(Constants.UdiEntityType.DataTypeContainer)]
|
||||
DataTypeContainer,
|
||||
|
||||
/// <summary>
|
||||
@@ -143,6 +156,7 @@ namespace Umbraco.Core.Models
|
||||
/// </summary>
|
||||
[UmbracoObjectType(Constants.ObjectTypes.RelationType)]
|
||||
[FriendlyName("Relation Type")]
|
||||
[UmbracoUdiType(Constants.UdiEntityType.RelationType)]
|
||||
RelationType,
|
||||
|
||||
/// <summary>
|
||||
@@ -153,17 +167,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
|
||||
}
|
||||
}
|
||||
@@ -12,6 +12,7 @@ namespace Umbraco.Core.Models
|
||||
{
|
||||
//MUST be concurrent to avoid thread collisions!
|
||||
private static readonly ConcurrentDictionary<UmbracoObjectTypes, Guid> UmbracoObjectTypeCache = new ConcurrentDictionary<UmbracoObjectTypes, Guid>();
|
||||
private static readonly ConcurrentDictionary<UmbracoObjectTypes, string> UmbracoObjectTypeUdiCache = new ConcurrentDictionary<UmbracoObjectTypes, string>();
|
||||
|
||||
/// <summary>
|
||||
/// Get an UmbracoObjectTypes value from it's name
|
||||
@@ -43,6 +44,21 @@ namespace Umbraco.Core.Models
|
||||
return umbracoObjectType;
|
||||
}
|
||||
|
||||
public static string GetUdiType(Guid guid)
|
||||
{
|
||||
var umbracoObjectType = Constants.UdiEntityType.Unknown;
|
||||
|
||||
foreach (var name in Enum.GetNames(typeof(UmbracoObjectTypes)))
|
||||
{
|
||||
var objType = GetUmbracoObjectType(name);
|
||||
if (objType.GetGuid() == guid)
|
||||
{
|
||||
umbracoObjectType = GetUdiType(objType);
|
||||
}
|
||||
}
|
||||
return umbracoObjectType;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Extension method for the UmbracoObjectTypes enum to return the enum GUID
|
||||
/// </summary>
|
||||
@@ -68,6 +84,26 @@ namespace Umbraco.Core.Models
|
||||
});
|
||||
}
|
||||
|
||||
public static string GetUdiType(this UmbracoObjectTypes umbracoObjectType)
|
||||
{
|
||||
return UmbracoObjectTypeUdiCache.GetOrAdd(umbracoObjectType, types =>
|
||||
{
|
||||
var type = typeof(UmbracoObjectTypes);
|
||||
var memInfo = type.GetMember(umbracoObjectType.ToString());
|
||||
var attributes = memInfo[0].GetCustomAttributes(typeof(UmbracoUdiTypeAttribute),
|
||||
false);
|
||||
|
||||
if (attributes.Length == 0)
|
||||
return Constants.UdiEntityType.Unknown;
|
||||
|
||||
var attribute = ((UmbracoUdiTypeAttribute)attributes[0]);
|
||||
if (attribute == null)
|
||||
return Constants.UdiEntityType.Unknown;
|
||||
|
||||
return attribute.UdiType;
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Extension method for the UmbracoObjectTypes enum to return the enum name
|
||||
/// </summary>
|
||||
|
||||
@@ -647,5 +647,10 @@ namespace Umbraco.Core
|
||||
return "[GetPropertyValueException]";
|
||||
}
|
||||
}
|
||||
|
||||
internal static Guid AsGuid(this object value)
|
||||
{
|
||||
return value is Guid ? (Guid) value : Guid.Empty;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.Serialization;
|
||||
using Umbraco.Core.Models;
|
||||
|
||||
namespace Umbraco.Core.Packaging.Models
|
||||
{
|
||||
[Serializable]
|
||||
[DataContract(IsReference = true)]
|
||||
internal class UninstallationSummary
|
||||
{
|
||||
public MetaData MetaData { get; set; }
|
||||
public IEnumerable<IDataTypeDefinition> DataTypesUninstalled { get; set; }
|
||||
public IEnumerable<ILanguage> LanguagesUninstalled { get; set; }
|
||||
public IEnumerable<IDictionaryItem> DictionaryItemsUninstalled { get; set; }
|
||||
public IEnumerable<IMacro> MacrosUninstalled { get; set; }
|
||||
public IEnumerable<string> FilesUninstalled { get; set; }
|
||||
public IEnumerable<ITemplate> TemplatesUninstalled { get; set; }
|
||||
public IEnumerable<IContentType> ContentTypesUninstalled { get; set; }
|
||||
public IEnumerable<IFile> StylesheetsUninstalled { get; set; }
|
||||
public IEnumerable<IContent> ContentUninstalled { get; set; }
|
||||
public bool PackageUninstalled { get; set; }
|
||||
}
|
||||
|
||||
internal static class UninstallationSummaryExtentions
|
||||
{
|
||||
public static UninstallationSummary InitEmpty(this UninstallationSummary summary)
|
||||
{
|
||||
summary.ContentUninstalled = new List<IContent>();
|
||||
summary.ContentTypesUninstalled = new List<IContentType>();
|
||||
summary.DataTypesUninstalled = new List<IDataTypeDefinition>();
|
||||
summary.DictionaryItemsUninstalled = new List<IDictionaryItem>();
|
||||
summary.FilesUninstalled = new List<string>();
|
||||
summary.LanguagesUninstalled = new List<ILanguage>();
|
||||
summary.MacrosUninstalled = new List<IMacro>();
|
||||
summary.MetaData = new MetaData();
|
||||
summary.TemplatesUninstalled = new List<ITemplate>();
|
||||
summary.PackageUninstalled = false;
|
||||
return summary;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -27,7 +27,20 @@ namespace Umbraco.Core.Persistence.Factories
|
||||
|
||||
#region Implementation of IEntityFactory<IContent,DocumentDto>
|
||||
|
||||
public static IContent BuildEntity(DocumentDto dto, IContentType contentType)
|
||||
/// <summary>
|
||||
/// Builds a IContent item from the dto(s) and content type
|
||||
/// </summary>
|
||||
/// <param name="dto">
|
||||
/// This DTO can contain all of the information to build an IContent item, however in cases where multiple entities are being built,
|
||||
/// a separate <see cref="DocumentPublishedReadOnlyDto"/> publishedDto entity will be supplied in place of the <see cref="DocumentDto"/>'s own
|
||||
/// ResultColumn DocumentPublishedReadOnlyDto
|
||||
/// </param>
|
||||
/// <param name="contentType"></param>
|
||||
/// <param name="publishedDto">
|
||||
/// When querying for multiple content items the main DTO will not contain the ResultColumn DocumentPublishedReadOnlyDto and a separate publishedDto instance will be supplied
|
||||
/// </param>
|
||||
/// <returns></returns>
|
||||
public static IContent BuildEntity(DocumentDto dto, IContentType contentType, DocumentPublishedReadOnlyDto publishedDto = null)
|
||||
{
|
||||
var content = new Content(dto.Text, dto.ContentVersionDto.ContentDto.NodeDto.ParentId, contentType);
|
||||
|
||||
@@ -52,8 +65,13 @@ namespace Umbraco.Core.Persistence.Factories
|
||||
content.ExpireDate = dto.ExpiresDate.HasValue ? dto.ExpiresDate.Value : (DateTime?)null;
|
||||
content.ReleaseDate = dto.ReleaseDate.HasValue ? dto.ReleaseDate.Value : (DateTime?)null;
|
||||
content.Version = dto.ContentVersionDto.VersionId;
|
||||
|
||||
content.PublishedState = dto.Published ? PublishedState.Published : PublishedState.Unpublished;
|
||||
content.PublishedVersionGuid = dto.DocumentPublishedReadOnlyDto == null ? default(Guid) : dto.DocumentPublishedReadOnlyDto.VersionId;
|
||||
|
||||
//Check if the publishedDto has been supplied, if not the use the dto's own DocumentPublishedReadOnlyDto value
|
||||
content.PublishedVersionGuid = publishedDto == null
|
||||
? (dto.DocumentPublishedReadOnlyDto == null ? default(Guid) : dto.DocumentPublishedReadOnlyDto.VersionId)
|
||||
: publishedDto.VersionId;
|
||||
|
||||
//on initial construction we don't want to have dirty properties tracked
|
||||
// http://issues.umbraco.org/issue/U4-1946
|
||||
|
||||
@@ -50,7 +50,7 @@ namespace Umbraco.Core.Persistence.Factories
|
||||
entity.ContentTypeThumbnail = asDictionary.ContainsKey("thumbnail") ? (d.thumbnail ?? string.Empty) : string.Empty;
|
||||
|
||||
var publishedVersion = default(Guid);
|
||||
//some content items don't have a published version
|
||||
//some content items don't have a published/newest version
|
||||
if (asDictionary.ContainsKey("publishedVersion") && asDictionary["publishedVersion"] != null)
|
||||
{
|
||||
Guid.TryParse(d.publishedVersion.ToString(), out publishedVersion);
|
||||
|
||||
@@ -130,21 +130,21 @@ namespace Umbraco.Core.Persistence.Migrations.Initial
|
||||
_database.Insert("umbracoNode", "id", false, new NodeDto { NodeId = Constants.System.DefaultMembersListViewDataTypeId, Trashed = false, ParentId = -1, UserId = 0, Level = 1, Path = "-1,-97", SortOrder = 2, UniqueId = new Guid("AA2C52A0-CE87-4E65-A47C-7DF09358585D"), Text = Constants.Conventions.DataTypes.ListViewPrefix + "Members", NodeObjectType = new Guid(Constants.ObjectTypes.DataType), CreateDate = DateTime.Now });
|
||||
_database.Insert("umbracoNode", "id", false, new NodeDto { NodeId = 1031, Trashed = false, ParentId = -1, UserId = 0, Level = 1, Path = "-1,1031", SortOrder = 2, UniqueId = new Guid("f38bd2d7-65d0-48e6-95dc-87ce06ec2d3d"), Text = Constants.Conventions.MediaTypes.Folder, NodeObjectType = new Guid(Constants.ObjectTypes.MediaType), CreateDate = DateTime.Now });
|
||||
_database.Insert("umbracoNode", "id", false, new NodeDto { NodeId = 1032, Trashed = false, ParentId = -1, UserId = 0, Level = 1, Path = "-1,1032", SortOrder = 2, UniqueId = new Guid("cc07b313-0843-4aa8-bbda-871c8da728c8"), Text = Constants.Conventions.MediaTypes.Image, NodeObjectType = new Guid(Constants.ObjectTypes.MediaType), CreateDate = DateTime.Now });
|
||||
_database.Insert("umbracoNode", "id", false, new NodeDto { NodeId = 1033, Trashed = false, ParentId = -1, UserId = 0, Level = 1, Path = "-1,1033", SortOrder = 2, UniqueId = new Guid("4c52d8ab-54e6-40cd-999c-7a5f24903e4d"), Text = Constants.Conventions.MediaTypes.File, NodeObjectType = new Guid(Constants.ObjectTypes.MediaType), CreateDate = DateTime.Now });
|
||||
_database.Insert("umbracoNode", "id", false, new NodeDto { NodeId = 1034, Trashed = false, ParentId = -1, UserId = 0, Level = 1, Path = "-1,1034", SortOrder = 2, UniqueId = new Guid("a6857c73-d6e9-480c-b6e6-f15f6ad11125"), Text = "Content Picker", NodeObjectType = new Guid(Constants.ObjectTypes.DataType), CreateDate = DateTime.Now });
|
||||
_database.Insert("umbracoNode", "id", false, new NodeDto { NodeId = 1035, Trashed = false, ParentId = -1, UserId = 0, Level = 1, Path = "-1,1035", SortOrder = 2, UniqueId = new Guid("93929b9a-93a2-4e2a-b239-d99334440a59"), Text = "Media Picker", NodeObjectType = new Guid(Constants.ObjectTypes.DataType), CreateDate = DateTime.Now });
|
||||
_database.Insert("umbracoNode", "id", false, new NodeDto { NodeId = 1036, Trashed = false, ParentId = -1, UserId = 0, Level = 1, Path = "-1,1036", SortOrder = 2, UniqueId = new Guid("2b24165f-9782-4aa3-b459-1de4a4d21f60"), Text = "Member Picker", NodeObjectType = new Guid(Constants.ObjectTypes.DataType), CreateDate = DateTime.Now });
|
||||
_database.Insert("umbracoNode", "id", false, new NodeDto { NodeId = 1033, Trashed = false, ParentId = -1, UserId = 0, Level = 1, Path = "-1,1033", SortOrder = 2, UniqueId = new Guid("4c52d8ab-54e6-40cd-999c-7a5f24903e4d"), Text = Constants.Conventions.MediaTypes.File, NodeObjectType = new Guid(Constants.ObjectTypes.MediaType), CreateDate = DateTime.Now });
|
||||
_database.Insert("umbracoNode", "id", false, new NodeDto { NodeId = 1040, Trashed = false, ParentId = -1, UserId = 0, Level = 1, Path = "-1,1040", SortOrder = 2, UniqueId = new Guid("21e798da-e06e-4eda-a511-ed257f78d4fa"), Text = "Related Links", NodeObjectType = new Guid(Constants.ObjectTypes.DataType), CreateDate = DateTime.Now });
|
||||
_database.Insert("umbracoNode", "id", false, new NodeDto { NodeId = 1041, Trashed = false, ParentId = -1, UserId = 0, Level = 1, Path = "-1,1041", SortOrder = 2, UniqueId = new Guid("b6b73142-b9c1-4bf8-a16d-e1c23320b549"), Text = "Tags", NodeObjectType = new Guid(Constants.ObjectTypes.DataType), CreateDate = DateTime.Now });
|
||||
_database.Insert("umbracoNode", "id", false, new NodeDto { NodeId = 1043, Trashed = false, ParentId = -1, UserId = 0, Level = 1, Path = "-1,1043", SortOrder = 2, UniqueId = new Guid("1df9f033-e6d4-451f-b8d2-e0cbc50a836f"), Text = "Image Cropper", NodeObjectType = new Guid(Constants.ObjectTypes.DataType), CreateDate = DateTime.Now });
|
||||
_database.Insert("umbracoNode", "id", false, new NodeDto { NodeId = 1044, Trashed = false, ParentId = -1, UserId = 0, Level = 1, Path = "-1,1044", SortOrder = 0, UniqueId = new Guid("d59be02f-1df9-4228-aa1e-01917d806cda"), Text = Constants.Conventions.MemberTypes.DefaultAlias, NodeObjectType = new Guid(Constants.ObjectTypes.MemberType), CreateDate = DateTime.Now });
|
||||
_database.Insert("umbracoNode", "id", false, new NodeDto { NodeId = 1045, Trashed = false, ParentId = -1, UserId = 0, Level = 1, Path = "-1,1045", SortOrder = 2, UniqueId = new Guid("7E3962CC-CE20-4FFC-B661-5897A894BA7E"), Text = "Multiple Media Picker", NodeObjectType = new Guid(Constants.ObjectTypes.DataType), CreateDate = DateTime.Now });
|
||||
|
||||
//New UDI pickers with newer Ids
|
||||
_database.Insert("umbracoNode", "id", false, new NodeDto { NodeId = 1046, Trashed = false, ParentId = -1, UserId = 0, Level = 1, Path = "-1,1046", SortOrder = 2, UniqueId = new Guid("FD1E0DA5-5606-4862-B679-5D0CF3A52A59"), Text = "Content Picker", NodeObjectType = new Guid(Constants.ObjectTypes.DataType), CreateDate = DateTime.Now });
|
||||
_database.Insert("umbracoNode", "id", false, new NodeDto { NodeId = 1047, Trashed = false, ParentId = -1, UserId = 0, Level = 1, Path = "-1,1047", SortOrder = 2, UniqueId = new Guid("1EA2E01F-EBD8-4CE1-8D71-6B1149E63548"), Text = "Member Picker", NodeObjectType = new Guid(Constants.ObjectTypes.DataType), CreateDate = DateTime.Now });
|
||||
_database.Insert("umbracoNode", "id", false, new NodeDto { NodeId = 1048, Trashed = false, ParentId = -1, UserId = 0, Level = 1, Path = "-1,1048", SortOrder = 2, UniqueId = new Guid("135D60E0-64D9-49ED-AB08-893C9BA44AE5"), Text = "Media Picker", NodeObjectType = new Guid(Constants.ObjectTypes.DataType), CreateDate = DateTime.Now });
|
||||
_database.Insert("umbracoNode", "id", false, new NodeDto { NodeId = 1049, Trashed = false, ParentId = -1, UserId = 0, Level = 1, Path = "-1,1049", SortOrder = 2, UniqueId = new Guid("9DBBCBBB-2327-434A-B355-AF1B84E5010A"), Text = "Multiple Media Picker", NodeObjectType = new Guid(Constants.ObjectTypes.DataType), CreateDate = DateTime.Now });
|
||||
|
||||
//TODO: We're not creating these for 7.0
|
||||
//_database.Insert("umbracoNode", "id", false, new NodeDto { NodeId = 1039, Trashed = false, ParentId = -1, UserId = 0, Level = 1, Path = "-1,1039", SortOrder = 2, UniqueId = new Guid("06f349a9-c949-4b6a-8660-59c10451af42"), Text = "Ultimate Picker", NodeObjectType = new Guid(Constants.ObjectTypes.DataType), CreateDate = DateTime.Now });
|
||||
//_database.Insert("umbracoNode", "id", false, new NodeDto { NodeId = 1038, Trashed = false, ParentId = -1, UserId = 0, Level = 1, Path = "-1,1038", SortOrder = 2, UniqueId = new Guid("1251c96c-185c-4e9b-93f4-b48205573cbd"), Text = "Simple Editor", NodeObjectType = new Guid(Constants.ObjectTypes.DataType), CreateDate = DateTime.Now });
|
||||
|
||||
//_database.Insert("umbracoNode", "id", false, new NodeDto { NodeId = 1042, Trashed = false, ParentId = -1, UserId = 0, Level = 1, Path = "-1,1042", SortOrder = 2, UniqueId = new Guid("0a452bd5-83f9-4bc3-8403-1286e13fb77e"), Text = "Macro Container", NodeObjectType = new Guid(Constants.ObjectTypes.DataType), CreateDate = DateTime.Now });
|
||||
}
|
||||
|
||||
@@ -246,17 +246,19 @@ namespace Umbraco.Core.Persistence.Migrations.Initial
|
||||
_database.Insert("cmsDataType", "pk", false, new DataTypeDto { PrimaryKey = 12, DataTypeId = -40, PropertyEditorAlias = Constants.PropertyEditors.RadioButtonListAlias, DbType = "Nvarchar" });
|
||||
_database.Insert("cmsDataType", "pk", false, new DataTypeDto { PrimaryKey = 13, DataTypeId = -41, PropertyEditorAlias = Constants.PropertyEditors.DateAlias, DbType = "Date" });
|
||||
_database.Insert("cmsDataType", "pk", false, new DataTypeDto { PrimaryKey = 14, DataTypeId = -42, PropertyEditorAlias = Constants.PropertyEditors.DropDownListAlias, DbType = "Integer" });
|
||||
_database.Insert("cmsDataType", "pk", false, new DataTypeDto { PrimaryKey = 15, DataTypeId = -43, PropertyEditorAlias = Constants.PropertyEditors.CheckBoxListAlias, DbType = "Nvarchar" });
|
||||
_database.Insert("cmsDataType", "pk", false, new DataTypeDto { PrimaryKey = 16, DataTypeId = 1034, PropertyEditorAlias = Constants.PropertyEditors.ContentPickerAlias, DbType = "Integer" });
|
||||
_database.Insert("cmsDataType", "pk", false, new DataTypeDto { PrimaryKey = 18, DataTypeId = 1036, PropertyEditorAlias = Constants.PropertyEditors.MemberPickerAlias, DbType = "Integer" });
|
||||
_database.Insert("cmsDataType", "pk", false, new DataTypeDto { PrimaryKey = 17, DataTypeId = 1035, PropertyEditorAlias = Constants.PropertyEditors.MultipleMediaPickerAlias, DbType = "Nvarchar" });
|
||||
_database.Insert("cmsDataType", "pk", false, new DataTypeDto { PrimaryKey = 15, DataTypeId = -43, PropertyEditorAlias = Constants.PropertyEditors.CheckBoxListAlias, DbType = "Nvarchar" });
|
||||
_database.Insert("cmsDataType", "pk", false, new DataTypeDto { PrimaryKey = 21, DataTypeId = 1040, PropertyEditorAlias = Constants.PropertyEditors.RelatedLinksAlias, DbType = "Ntext" });
|
||||
_database.Insert("cmsDataType", "pk", false, new DataTypeDto { PrimaryKey = 22, DataTypeId = 1041, PropertyEditorAlias = Constants.PropertyEditors.TagsAlias, DbType = "Ntext" });
|
||||
_database.Insert("cmsDataType", "pk", false, new DataTypeDto { PrimaryKey = 24, DataTypeId = 1043, PropertyEditorAlias = Constants.PropertyEditors.ImageCropperAlias, DbType = "Ntext" });
|
||||
_database.Insert("cmsDataType", "pk", false, new DataTypeDto { PrimaryKey = 25, DataTypeId = 1045, PropertyEditorAlias = Constants.PropertyEditors.MultipleMediaPickerAlias, DbType = "Nvarchar" });
|
||||
_database.Insert("cmsDataType", "pk", false, new DataTypeDto { PrimaryKey = -26, DataTypeId = Constants.System.DefaultContentListViewDataTypeId, PropertyEditorAlias = Constants.PropertyEditors.ListViewAlias, DbType = "Nvarchar" });
|
||||
_database.Insert("cmsDataType", "pk", false, new DataTypeDto { PrimaryKey = -27, DataTypeId = Constants.System.DefaultMediaListViewDataTypeId, PropertyEditorAlias = Constants.PropertyEditors.ListViewAlias, DbType = "Nvarchar" });
|
||||
_database.Insert("cmsDataType", "pk", false, new DataTypeDto { PrimaryKey = -28, DataTypeId = Constants.System.DefaultMembersListViewDataTypeId, PropertyEditorAlias = Constants.PropertyEditors.ListViewAlias, DbType = "Nvarchar" });
|
||||
_database.Insert("cmsDataType", "pk", false, new DataTypeDto { PrimaryKey = -28, DataTypeId = Constants.System.DefaultMembersListViewDataTypeId, PropertyEditorAlias = Constants.PropertyEditors.ListViewAlias, DbType = "Nvarchar" });
|
||||
|
||||
//New UDI pickers with newer Ids
|
||||
_database.Insert("cmsDataType", "pk", false, new DataTypeDto { PrimaryKey = 26, DataTypeId = 1046, PropertyEditorAlias = Constants.PropertyEditors.ContentPicker2Alias, DbType = "Nvarchar" });
|
||||
_database.Insert("cmsDataType", "pk", false, new DataTypeDto { PrimaryKey = 27, DataTypeId = 1047, PropertyEditorAlias = Constants.PropertyEditors.MemberPicker2Alias, DbType = "Nvarchar" });
|
||||
_database.Insert("cmsDataType", "pk", false, new DataTypeDto { PrimaryKey = 28, DataTypeId = 1048, PropertyEditorAlias = Constants.PropertyEditors.MediaPicker2Alias, DbType = "Ntext" });
|
||||
_database.Insert("cmsDataType", "pk", false, new DataTypeDto { PrimaryKey = 29, DataTypeId = 1049, PropertyEditorAlias = Constants.PropertyEditors.MediaPicker2Alias, DbType = "Ntext" });
|
||||
|
||||
//TODO: We're not creating these for 7.0
|
||||
//_database.Insert("cmsDataType", "pk", false, new DataTypeDto { PrimaryKey = 19, DataTypeId = 1038, PropertyEditorAlias = Constants.PropertyEditors.MarkdownEditorAlias, DbType = "Ntext" });
|
||||
@@ -268,10 +270,7 @@ namespace Umbraco.Core.Persistence.Migrations.Initial
|
||||
{
|
||||
_database.Insert("cmsDataTypePreValues", "id", false, new DataTypePreValueDto { Id = 3, Alias = "", SortOrder = 0, DataTypeNodeId = -87, Value = ",code,undo,redo,cut,copy,mcepasteword,stylepicker,bold,italic,bullist,numlist,outdent,indent,mcelink,unlink,mceinsertanchor,mceimage,umbracomacro,mceinserttable,umbracoembed,mcecharmap,|1|1,2,3,|0|500,400|1049,|true|" });
|
||||
_database.Insert("cmsDataTypePreValues", "id", false, new DataTypePreValueDto { Id = 4, Alias = "group", SortOrder = 0, DataTypeNodeId = 1041, Value = "default" });
|
||||
|
||||
//default's for MultipleMediaPickerAlias picker
|
||||
_database.Insert("cmsDataTypePreValues", "id", false, new DataTypePreValueDto { Id = 5, Alias = "multiPicker", SortOrder = 0, DataTypeNodeId = 1045, Value = "1" });
|
||||
|
||||
|
||||
//defaults for the member list
|
||||
_database.Insert("cmsDataTypePreValues", "id", false, new DataTypePreValueDto { Id = -1, Alias = "pageSize", SortOrder = 1, DataTypeNodeId = Constants.System.DefaultMembersListViewDataTypeId, Value = "10" });
|
||||
_database.Insert("cmsDataTypePreValues", "id", false, new DataTypePreValueDto { Id = -2, Alias = "orderBy", SortOrder = 2, DataTypeNodeId = Constants.System.DefaultMembersListViewDataTypeId, Value = "username" });
|
||||
@@ -288,6 +287,9 @@ namespace Umbraco.Core.Persistence.Migrations.Initial
|
||||
_database.Insert("cmsDataTypePreValues", "id", false, new DataTypePreValueDto { Id = -7, Alias = "orderDirection", SortOrder = 3, DataTypeNodeId = Constants.System.DefaultMediaListViewDataTypeId, Value = "desc" });
|
||||
_database.Insert("cmsDataTypePreValues", "id", false, new DataTypePreValueDto { Id = -8, Alias = "layouts", SortOrder = 4, DataTypeNodeId = Constants.System.DefaultMediaListViewDataTypeId, Value = "[" + cardLayout + "," + listLayout + "]" });
|
||||
_database.Insert("cmsDataTypePreValues", "id", false, new DataTypePreValueDto { Id = -9, Alias = "includeProperties", SortOrder = 5, DataTypeNodeId = Constants.System.DefaultMediaListViewDataTypeId, Value = "[{\"alias\":\"updateDate\",\"header\":\"Last edited\",\"isSystem\":1},{\"alias\":\"owner\",\"header\":\"Updated by\",\"isSystem\":1}]" });
|
||||
|
||||
//default's for MultipleMediaPickerAlias picker
|
||||
_database.Insert("cmsDataTypePreValues", "id", false, new DataTypePreValueDto { Id = 6, Alias = "multiPicker", SortOrder = 0, DataTypeNodeId = 1049, Value = "1" });
|
||||
}
|
||||
|
||||
private void CreateUmbracoRelationTypeData()
|
||||
|
||||
+35
@@ -0,0 +1,35 @@
|
||||
using System.Linq;
|
||||
using Umbraco.Core.Logging;
|
||||
using Umbraco.Core.Persistence.DatabaseModelDefinitions;
|
||||
using Umbraco.Core.Persistence.SqlSyntax;
|
||||
|
||||
namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSevenSixZero
|
||||
{
|
||||
[Migration("7.6.0", 0, Constants.System.UmbracoMigrationName)]
|
||||
public class AddIndexToCmsMemberLoginName : MigrationBase
|
||||
{
|
||||
public AddIndexToCmsMemberLoginName(ISqlSyntaxProvider sqlSyntax, ILogger logger)
|
||||
: base(sqlSyntax, logger)
|
||||
{ }
|
||||
|
||||
public override void Up()
|
||||
{
|
||||
var dbIndexes = SqlSyntax.GetDefinedIndexesDefinitions(Context.Database);
|
||||
|
||||
//make sure it doesn't already exist
|
||||
if (dbIndexes.Any(x => x.IndexName.InvariantEquals("IX_cmsMember_LoginName")) == false)
|
||||
{
|
||||
Create.Index("IX_cmsMember_LoginName").OnTable("cmsMember")
|
||||
.OnColumn("LoginName")
|
||||
.Ascending()
|
||||
.WithOptions()
|
||||
.NonClustered();
|
||||
}
|
||||
}
|
||||
|
||||
public override void Down()
|
||||
{
|
||||
Delete.Index("IX_cmsMember_LoginName").OnTable("cmsMember");
|
||||
}
|
||||
}
|
||||
}
|
||||
+1
-8
@@ -14,14 +14,7 @@ namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSevenSixZero
|
||||
|
||||
public override void Up()
|
||||
{
|
||||
var dbIndexes = SqlSyntax.GetDefinedIndexes(Context.Database)
|
||||
.Select(x => new DbIndexDefinition()
|
||||
{
|
||||
TableName = x.Item1,
|
||||
IndexName = x.Item2,
|
||||
ColumnName = x.Item3,
|
||||
IsUnique = x.Item4
|
||||
}).ToArray();
|
||||
var dbIndexes = SqlSyntax.GetDefinedIndexesDefinitions(Context.Database);
|
||||
|
||||
//make sure it doesn't already exist
|
||||
if (dbIndexes.Any(x => x.IndexName.InvariantEquals("IX_umbracoNodePath")) == false)
|
||||
|
||||
+34
@@ -0,0 +1,34 @@
|
||||
using System.Linq;
|
||||
using Umbraco.Core.Logging;
|
||||
using Umbraco.Core.Persistence.SqlSyntax;
|
||||
|
||||
namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSevenSixZero
|
||||
{
|
||||
[Migration("7.6.0", 0, Constants.System.UmbracoMigrationName)]
|
||||
public class AddIndexToUser2NodePermission : MigrationBase
|
||||
{
|
||||
public AddIndexToUser2NodePermission(ISqlSyntaxProvider sqlSyntax, ILogger logger)
|
||||
: base(sqlSyntax, logger)
|
||||
{ }
|
||||
|
||||
public override void Up()
|
||||
{
|
||||
var dbIndexes = SqlSyntax.GetDefinedIndexesDefinitions(Context.Database);
|
||||
|
||||
//make sure it doesn't already exist
|
||||
if (dbIndexes.Any(x => x.IndexName.InvariantEquals("IX_umbracoUser2NodePermission_nodeId")) == false)
|
||||
{
|
||||
Create.Index("IX_umbracoUser2NodePermission_nodeId").OnTable("umbracoUser2NodePermission")
|
||||
.OnColumn("nodeId")
|
||||
.Ascending()
|
||||
.WithOptions()
|
||||
.NonClustered();
|
||||
}
|
||||
}
|
||||
|
||||
public override void Down()
|
||||
{
|
||||
Delete.Index("IX_umbracoUser2NodePermission_nodeId").OnTable("cmsMember");
|
||||
}
|
||||
}
|
||||
}
|
||||
+91
@@ -0,0 +1,91 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using Umbraco.Core.Logging;
|
||||
using Umbraco.Core.Persistence.SqlSyntax;
|
||||
|
||||
namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSevenSixZero
|
||||
{
|
||||
[Migration("7.6.0", 0, Constants.System.UmbracoMigrationName)]
|
||||
public class AddIndexesToUmbracoRelationTables : MigrationBase
|
||||
{
|
||||
public AddIndexesToUmbracoRelationTables(ISqlSyntaxProvider sqlSyntax, ILogger logger)
|
||||
: base(sqlSyntax, logger)
|
||||
{ }
|
||||
|
||||
public override void Up()
|
||||
{
|
||||
var dbIndexes = SqlSyntax.GetDefinedIndexesDefinitions(Context.Database).ToArray();
|
||||
|
||||
//make sure it doesn't already exist
|
||||
if (dbIndexes.Any(x => x.IndexName.InvariantEquals("IX_umbracoRelation_parentChildType")) == false)
|
||||
{
|
||||
//This will remove any corrupt/duplicate data in the relation table before the index is applied
|
||||
//Ensure this executes in a defered block which will be done inside of the migration transaction
|
||||
this.Execute.Code(database =>
|
||||
{
|
||||
//We need to check if this index has corrupted data and then clear that data
|
||||
var duplicates = database.Fetch<dynamic>("SELECT parentId,childId,relType FROM umbracoRelation GROUP BY parentId,childId,relType HAVING COUNT(*) > 1");
|
||||
if (duplicates.Count > 0)
|
||||
{
|
||||
//need to fix this there cannot be duplicates so we'll take the latest entries, it's really not going to matter though
|
||||
foreach (var duplicate in duplicates)
|
||||
{
|
||||
var ids = database.Fetch<int>("SELECT id FROM umbracoRelation WHERE parentId=@parentId AND childId=@childId AND relType=@relType ORDER BY datetime DESC",
|
||||
new { parentId = duplicate.parentId, childId = duplicate.childId, relType = duplicate.relType });
|
||||
|
||||
if (ids.Count == 1)
|
||||
{
|
||||
//this is just a safety check, this should absolutely never happen
|
||||
throw new InvalidOperationException("Duplicates were detected but could not be discovered");
|
||||
}
|
||||
|
||||
//delete the others
|
||||
ids = ids.Skip(0).ToList();
|
||||
|
||||
//iterate in groups of 2000 to avoid the max sql parameter limit
|
||||
foreach (var idGroup in ids.InGroupsOf(2000))
|
||||
{
|
||||
database.Execute("DELETE FROM umbracoRelation WHERE id IN (@ids)", new { ids = idGroup });
|
||||
}
|
||||
}
|
||||
}
|
||||
return "";
|
||||
});
|
||||
|
||||
//unique index to prevent duplicates - and for better perf
|
||||
Create.Index("IX_umbracoRelation_parentChildType").OnTable("umbracoRelation")
|
||||
.OnColumn("parentId").Ascending()
|
||||
.OnColumn("childId").Ascending()
|
||||
.OnColumn("relType").Ascending()
|
||||
.WithOptions()
|
||||
.Unique();
|
||||
}
|
||||
|
||||
//need indexes on alias and name for relation type since these are queried against
|
||||
|
||||
//make sure it doesn't already exist
|
||||
if (dbIndexes.Any(x => x.IndexName.InvariantEquals("IX_umbracoRelationType_alias")) == false)
|
||||
{
|
||||
Create.Index("IX_umbracoRelationType_alias").OnTable("umbracoRelationType")
|
||||
.OnColumn("alias")
|
||||
.Ascending()
|
||||
.WithOptions()
|
||||
.NonClustered();
|
||||
}
|
||||
if (dbIndexes.Any(x => x.IndexName.InvariantEquals("IX_umbracoRelationType_name")) == false)
|
||||
{
|
||||
Create.Index("IX_umbracoRelationType_name").OnTable("umbracoRelationType")
|
||||
.OnColumn("name")
|
||||
.Ascending()
|
||||
.WithOptions()
|
||||
.NonClustered();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public override void Down()
|
||||
{
|
||||
Delete.Index("IX_umbracoNodePath").OnTable("umbracoNode");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -703,7 +703,7 @@ namespace Umbraco.Core.Persistence
|
||||
|
||||
static Regex rxColumns = new Regex(@"\A\s*SELECT\s+((?:\((?>\((?<depth>)|\)(?<-depth>)|.?)*(?(depth)(?!))\)|.)*?)(?<!,\s+)\bFROM\b", RegexOptions.IgnoreCase | RegexOptions.Multiline | RegexOptions.Singleline | RegexOptions.Compiled);
|
||||
static Regex rxOrderBy = new Regex(@"\bORDER\s+BY\s+(?:\((?>\((?<depth>)|\)(?<-depth>)|.?)*(?(depth)(?!))\)|[\w\(\)\.])+(?:\s+(?:ASC|DESC))?(?:\s*,\s*(?:\((?>\((?<depth>)|\)(?<-depth>)|.?)*(?(depth)(?!))\)|[\w\(\)\.])+(?:\s+(?:ASC|DESC))?)*", RegexOptions.IgnoreCase | RegexOptions.Multiline | RegexOptions.Singleline | RegexOptions.Compiled);
|
||||
static Regex rxDistinct = new Regex(@"\ADISTINCT\s", RegexOptions.IgnoreCase | RegexOptions.Multiline | RegexOptions.Singleline | RegexOptions.Compiled);
|
||||
static Regex rxDistinct = new Regex(@"\ADISTINCT\s", RegexOptions.IgnoreCase | RegexOptions.Multiline | RegexOptions.Singleline | RegexOptions.Compiled);
|
||||
public static bool SplitSqlForPaging(string sql, out string sqlCount, out string sqlSelectRemoved, out string sqlOrderBy)
|
||||
{
|
||||
sqlSelectRemoved = null;
|
||||
@@ -723,10 +723,9 @@ namespace Umbraco.Core.Persistence
|
||||
sqlCount = sql.Substring(0, g.Index) + "COUNT(" + m.Groups[1].ToString().Trim() + ") " + sql.Substring(g.Index + g.Length);
|
||||
else
|
||||
sqlCount = sql.Substring(0, g.Index) + "COUNT(*) " + sql.Substring(g.Index + g.Length);
|
||||
|
||||
|
||||
// Look for an "ORDER BY <whatever>" clause
|
||||
m = rxOrderBy.Match(sqlCount);
|
||||
|
||||
// Look for an "ORDER BY <whatever>" clause
|
||||
m = rxOrderBy.Match(sqlCount);
|
||||
if (!m.Success)
|
||||
{
|
||||
sqlOrderBy = null;
|
||||
@@ -737,8 +736,7 @@ namespace Umbraco.Core.Persistence
|
||||
sqlOrderBy = g.ToString();
|
||||
sqlCount = sqlCount.Substring(0, g.Index) + sqlCount.Substring(g.Index + g.Length);
|
||||
}
|
||||
|
||||
return true;
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -820,7 +818,7 @@ namespace Umbraco.Core.Persistence
|
||||
result.CurrentPage = page;
|
||||
result.ItemsPerPage = itemsPerPage;
|
||||
result.TotalItems = ExecuteScalar<long>(sqlCount, args);
|
||||
result.TotalPages = result.TotalItems / itemsPerPage;
|
||||
result.TotalPages = result.TotalItems / itemsPerPage;
|
||||
if ((result.TotalItems % itemsPerPage) != 0)
|
||||
result.TotalPages++;
|
||||
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -2,8 +2,30 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
{
|
||||
internal enum BaseQueryType
|
||||
{
|
||||
Full,
|
||||
/// <summary>
|
||||
/// A query to return all information for a single item
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// In some cases this will be the same as <see cref="FullMultiple"/>
|
||||
/// </remarks>
|
||||
FullSingle,
|
||||
|
||||
/// <summary>
|
||||
/// A query to return all information for multiple items
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// In some cases this will be the same as <see cref="FullSingle"/>
|
||||
/// </remarks>
|
||||
FullMultiple,
|
||||
|
||||
/// <summary>
|
||||
/// A query to return the ids for items
|
||||
/// </summary>
|
||||
Ids,
|
||||
|
||||
/// <summary>
|
||||
/// A query to return the count for items
|
||||
/// </summary>
|
||||
Count
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Xml;
|
||||
@@ -53,7 +54,7 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
|
||||
protected override IContent PerformGet(int id)
|
||||
{
|
||||
var sql = GetBaseQuery(false)
|
||||
var sql = GetBaseQuery(BaseQueryType.FullSingle)
|
||||
.Where(GetBaseWhereClause(), new { Id = id })
|
||||
.Where<DocumentDto>(x => x.Newest, SqlSyntax)
|
||||
.OrderByDescending<ContentVersionDto>(x => x.VersionDate, SqlSyntax);
|
||||
@@ -81,15 +82,15 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
return s;
|
||||
};
|
||||
|
||||
var sqlBaseFull = GetBaseQuery(BaseQueryType.Full);
|
||||
var sqlBaseFull = GetBaseQuery(BaseQueryType.FullMultiple);
|
||||
var sqlBaseIds = GetBaseQuery(BaseQueryType.Ids);
|
||||
|
||||
return ProcessQuery(translate(sqlBaseFull), translate(sqlBaseIds));
|
||||
return ProcessQuery(translate(sqlBaseFull), new PagingSqlQuery(translate(sqlBaseIds)));
|
||||
}
|
||||
|
||||
protected override IEnumerable<IContent> PerformGetByQuery(IQuery<IContent> query)
|
||||
{
|
||||
var sqlBaseFull = GetBaseQuery(BaseQueryType.Full);
|
||||
var sqlBaseFull = GetBaseQuery(BaseQueryType.FullMultiple);
|
||||
var sqlBaseIds = GetBaseQuery(BaseQueryType.Ids);
|
||||
|
||||
Func<SqlTranslator<IContent>, Sql> translate = (translator) =>
|
||||
@@ -103,13 +104,25 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
var translatorFull = new SqlTranslator<IContent>(sqlBaseFull, query);
|
||||
var translatorIds = new SqlTranslator<IContent>(sqlBaseIds, query);
|
||||
|
||||
return ProcessQuery(translate(translatorFull), translate(translatorIds));
|
||||
return ProcessQuery(translate(translatorFull), new PagingSqlQuery(translate(translatorIds)));
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Overrides of PetaPocoRepositoryBase<IContent>
|
||||
|
||||
/// <summary>
|
||||
/// Returns the base query to return Content
|
||||
/// </summary>
|
||||
/// <param name="queryType"></param>
|
||||
/// <returns></returns>
|
||||
/// <remarks>
|
||||
/// Content queries will differ depending on what needs to be returned:
|
||||
/// * FullSingle: When querying for a single document, this will include the Outer join to fetch the content item's published version info
|
||||
/// * FullMultiple: When querying for multiple documents, this will exclude the Outer join to fetch the content item's published version info - this info would need to be fetched separately
|
||||
/// * Ids: This would essentially be the same as FullMultiple however the columns specified will only return the Ids for the documents
|
||||
/// * Count: A query to return the count for documents
|
||||
/// </remarks>
|
||||
protected override Sql GetBaseQuery(BaseQueryType queryType)
|
||||
{
|
||||
var sql = new Sql();
|
||||
@@ -122,14 +135,14 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
.InnerJoin<NodeDto>(SqlSyntax)
|
||||
.On<ContentDto, NodeDto>(SqlSyntax, left => left.NodeId, right => right.NodeId);
|
||||
|
||||
if (queryType == BaseQueryType.Full)
|
||||
if (queryType == BaseQueryType.FullSingle)
|
||||
{
|
||||
//The only reason we apply this left outer join is to be able to pull back the DocumentPublishedReadOnlyDto
|
||||
//information with the entire data set, so basically this will get both the latest document and also it's published
|
||||
//version if it has one. When performing a count or when just retrieving Ids like in paging, this is unecessary
|
||||
//version if it has one. When performing a count or when retrieving Ids like in paging, this is unecessary
|
||||
//and causes huge performance overhead for the SQL server, especially when sorting the result.
|
||||
//To fix this perf overhead we'd need another index on :
|
||||
// CREATE NON CLUSTERED INDEX ON cmsDocument.node + cmsDocument.published
|
||||
//We also don't include this outer join when querying for multiple entities since it is much faster to fetch this information
|
||||
//in a separate query. For a single entity this is ok.
|
||||
|
||||
var sqlx = string.Format("LEFT OUTER JOIN {0} {1} ON ({1}.{2}={0}.{2} AND {1}.{3}=1)",
|
||||
SqlSyntax.GetQuotedTableName("cmsDocument"),
|
||||
@@ -151,7 +164,7 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
|
||||
protected override Sql GetBaseQuery(bool isCount)
|
||||
{
|
||||
return GetBaseQuery(isCount ? BaseQueryType.Count : BaseQueryType.Full);
|
||||
return GetBaseQuery(isCount ? BaseQueryType.Count : BaseQueryType.FullSingle);
|
||||
}
|
||||
|
||||
protected override string GetBaseWhereClause()
|
||||
@@ -222,10 +235,10 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
while (true)
|
||||
{
|
||||
// get the next group of nodes
|
||||
var sqlFull = translate(baseId, GetBaseQuery(BaseQueryType.Full));
|
||||
var sqlFull = translate(baseId, GetBaseQuery(BaseQueryType.FullMultiple));
|
||||
var sqlIds = translate(baseId, GetBaseQuery(BaseQueryType.Ids));
|
||||
|
||||
var xmlItems = ProcessQuery(SqlSyntax.SelectTop(sqlFull, groupSize), SqlSyntax.SelectTop(sqlIds, groupSize))
|
||||
var xmlItems = ProcessQuery(SqlSyntax.SelectTop(sqlFull, groupSize), new PagingSqlQuery(SqlSyntax.SelectTop(sqlIds, groupSize)))
|
||||
.Select(x => new ContentXmlDto { NodeId = x.Id, Xml = serializer(x).ToString() })
|
||||
.ToList();
|
||||
|
||||
@@ -245,7 +258,7 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
Logger.Error<MediaRepository>("Could not rebuild XML for nodeId=" + xmlItem.NodeId, e);
|
||||
}
|
||||
}
|
||||
baseId = xmlItems.Last().NodeId;
|
||||
baseId = xmlItems[xmlItems.Count - 1].NodeId;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -257,15 +270,15 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
.OrderByDescending<ContentVersionDto>(x => x.VersionDate, SqlSyntax);
|
||||
};
|
||||
|
||||
var sqlFull = translate(GetBaseQuery(BaseQueryType.Full));
|
||||
var sqlFull = translate(GetBaseQuery(BaseQueryType.FullMultiple));
|
||||
var sqlIds = translate(GetBaseQuery(BaseQueryType.Ids));
|
||||
|
||||
return ProcessQuery(sqlFull, sqlIds, true);
|
||||
return ProcessQuery(sqlFull, new PagingSqlQuery(sqlIds), true);
|
||||
}
|
||||
|
||||
public override IContent GetByVersion(Guid versionId)
|
||||
{
|
||||
var sql = GetBaseQuery(false);
|
||||
var sql = GetBaseQuery(BaseQueryType.FullSingle);
|
||||
sql.Where("cmsContentVersion.VersionId = @VersionId", new { VersionId = versionId });
|
||||
sql.OrderByDescending<ContentVersionDto>(x => x.VersionDate, SqlSyntax);
|
||||
|
||||
@@ -675,12 +688,12 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
// ORDER BY substring(path, 1, len(path) - charindex(',', reverse(path))), sortOrder
|
||||
// but that's probably an overkill - sorting by level,sortOrder should be enough
|
||||
|
||||
var sqlFull = GetBaseQuery(BaseQueryType.Full);
|
||||
var sqlFull = GetBaseQuery(BaseQueryType.FullMultiple);
|
||||
var translatorFull = new SqlTranslator<IContent>(sqlFull, query);
|
||||
var sqlIds = GetBaseQuery(BaseQueryType.Ids);
|
||||
var translatorIds = new SqlTranslator<IContent>(sqlIds, query);
|
||||
|
||||
return ProcessQuery(translate(translatorFull), translate(translatorIds), true);
|
||||
return ProcessQuery(translate(translatorFull), new PagingSqlQuery(translate(translatorIds)), true);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -772,13 +785,8 @@ order by umbracoNode.{2}, umbracoNode.parentID, umbracoNode.sortOrder",
|
||||
|
||||
public void ClearPublished(IContent content)
|
||||
{
|
||||
// race cond!
|
||||
var documentDtos = Database.Fetch<DocumentDto>("WHERE nodeId=@id AND published=@published", new { id = content.Id, published = true });
|
||||
foreach (var documentDto in documentDtos)
|
||||
{
|
||||
documentDto.Published = false;
|
||||
Database.Update(documentDto);
|
||||
}
|
||||
var sql = "UPDATE cmsDocument SET published=0 WHERE nodeId=@id AND published=1";
|
||||
Database.Execute(sql, new {id = content.Id});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -860,7 +868,7 @@ order by umbracoNode.{2}, umbracoNode.parentID, umbracoNode.sortOrder",
|
||||
|
||||
return GetPagedResultsByQuery<DocumentDto>(query, pageIndex, pageSize, out totalRecords,
|
||||
new Tuple<string, string>("cmsDocument", "nodeId"),
|
||||
(sqlFull, sqlIds) => ProcessQuery(sqlFull, sqlIds), orderBy, orderDirection, orderBySystemField,
|
||||
(sqlFull, pagingSqlQuery) => ProcessQuery(sqlFull, pagingSqlQuery), orderBy, orderDirection, orderBySystemField,
|
||||
filterCallback);
|
||||
|
||||
}
|
||||
@@ -890,23 +898,58 @@ order by umbracoNode.{2}, umbracoNode.parentID, umbracoNode.sortOrder",
|
||||
|
||||
return base.GetDatabaseFieldNameForOrderBy(orderBy);
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// This is the underlying method that processes most queries for this repository
|
||||
/// </summary>
|
||||
/// <param name="sqlFull">
|
||||
/// The full SQL with the outer join to return all data required to create an IContent
|
||||
/// The FullMultiple SQL without the outer join to return all data required to create an IContent excluding it's published state data which this will query separately
|
||||
/// </param>
|
||||
/// <param name="sqlIds">
|
||||
/// <param name="pagingSqlQuery">
|
||||
/// The Id SQL without the outer join to just return all document ids - used to process the properties for the content item
|
||||
/// </param>
|
||||
/// <param name="withCache"></param>
|
||||
/// <returns></returns>
|
||||
private IEnumerable<IContent> ProcessQuery(Sql sqlFull, Sql sqlIds, bool withCache = false)
|
||||
private IEnumerable<IContent> ProcessQuery(Sql sqlFull, PagingSqlQuery pagingSqlQuery, bool withCache = false)
|
||||
{
|
||||
// fetch returns a list so it's ok to iterate it in this method
|
||||
var dtos = Database.Fetch<DocumentDto, ContentVersionDto, ContentDto, NodeDto, DocumentPublishedReadOnlyDto>(sqlFull);
|
||||
var dtos = Database.Fetch<DocumentDto, ContentVersionDto, ContentDto, NodeDto>(sqlFull);
|
||||
if (dtos.Count == 0) return Enumerable.Empty<IContent>();
|
||||
|
||||
//Go and get all of the published version data separately for this data, this is because when we are querying
|
||||
//for multiple content items we don't include the outer join to fetch this data in the same query because
|
||||
//it is insanely slow. Instead we just fetch the published version data separately in one query.
|
||||
|
||||
//we need to parse the original SQL statement and reduce the columns to just cmsDocument.nodeId so that we can use
|
||||
// the statement to go get the published data for all of the items by using an inner join
|
||||
var parsedOriginalSql = "SELECT cmsDocument.nodeId " + sqlFull.SQL.Substring(sqlFull.SQL.IndexOf("FROM", StringComparison.Ordinal));
|
||||
//now remove everything from an Orderby clause and beyond
|
||||
if (parsedOriginalSql.InvariantContains("ORDER BY "))
|
||||
{
|
||||
parsedOriginalSql = parsedOriginalSql.Substring(0, parsedOriginalSql.LastIndexOf("ORDER BY ", StringComparison.Ordinal));
|
||||
}
|
||||
|
||||
var publishedSql = new Sql(@"SELECT *
|
||||
FROM cmsDocument AS doc2
|
||||
INNER JOIN
|
||||
(" + parsedOriginalSql + @") as docData
|
||||
ON doc2.nodeId = docData.nodeId
|
||||
WHERE doc2.published = 1
|
||||
ORDER BY doc2.nodeId
|
||||
", sqlFull.Arguments);
|
||||
|
||||
//go and get the published version data, we do a Query here and not a Fetch so we are
|
||||
//not allocating a whole list to memory just to allocate another list in memory since
|
||||
//we are assigning this data to a keyed collection for fast lookup below
|
||||
var publishedData = Database.Query<DocumentPublishedReadOnlyDto>(publishedSql);
|
||||
var publishedDataCollection = new DocumentPublishedReadOnlyDtoCollection();
|
||||
foreach (var publishedDto in publishedData)
|
||||
{
|
||||
//double check that there's no corrupt db data, there should only be a single published item
|
||||
if (publishedDataCollection.Contains(publishedDto.NodeId) == false)
|
||||
publishedDataCollection.Add(publishedDto);
|
||||
}
|
||||
|
||||
|
||||
var content = new IContent[dtos.Count];
|
||||
var defs = new List<DocumentDefinition>();
|
||||
@@ -920,6 +963,8 @@ order by umbracoNode.{2}, umbracoNode.parentID, umbracoNode.sortOrder",
|
||||
for (var i = 0; i < dtos.Count; i++)
|
||||
{
|
||||
var dto = dtos[i];
|
||||
DocumentPublishedReadOnlyDto publishedDto;
|
||||
publishedDataCollection.TryGetValue(dto.NodeId, out publishedDto);
|
||||
|
||||
// if the cache contains the published version, use it
|
||||
if (withCache)
|
||||
@@ -947,7 +992,7 @@ order by umbracoNode.{2}, umbracoNode.parentID, umbracoNode.sortOrder",
|
||||
contentTypes[dto.ContentVersionDto.ContentDto.ContentTypeId] = contentType;
|
||||
}
|
||||
|
||||
content[i] = ContentFactory.BuildEntity(dto, contentType);
|
||||
content[i] = ContentFactory.BuildEntity(dto, contentType, publishedDto);
|
||||
|
||||
// need template
|
||||
if (dto.TemplateId.HasValue && dto.TemplateId.Value > 0)
|
||||
@@ -968,7 +1013,7 @@ order by umbracoNode.{2}, umbracoNode.parentID, umbracoNode.sortOrder",
|
||||
.ToDictionary(x => x.Id, x => x);
|
||||
|
||||
// load all properties for all documents from database in 1 query
|
||||
var propertyData = GetPropertyCollection(sqlIds, defs);
|
||||
var propertyData = GetPropertyCollection(pagingSqlQuery, defs);
|
||||
|
||||
// assign
|
||||
var dtoIndex = 0;
|
||||
@@ -1056,7 +1101,7 @@ order by umbracoNode.{2}, umbracoNode.parentID, umbracoNode.sortOrder",
|
||||
|
||||
return currentName;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Dispose disposable properties
|
||||
/// </summary>
|
||||
@@ -1071,5 +1116,26 @@ order by umbracoNode.{2}, umbracoNode.parentID, umbracoNode.sortOrder",
|
||||
_contentPreviewRepository.Dispose();
|
||||
_contentXmlRepository.Dispose();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A keyed collection for fast lookup when retrieving a separate list of published version data
|
||||
/// </summary>
|
||||
private class DocumentPublishedReadOnlyDtoCollection : KeyedCollection<int, DocumentPublishedReadOnlyDto>
|
||||
{
|
||||
protected override int GetKeyForItem(DocumentPublishedReadOnlyDto item)
|
||||
{
|
||||
return item.NodeId;
|
||||
}
|
||||
|
||||
public bool TryGetValue(int key, out DocumentPublishedReadOnlyDto val)
|
||||
{
|
||||
if (Dictionary == null)
|
||||
{
|
||||
val = null;
|
||||
return false;
|
||||
}
|
||||
return Dictionary.TryGetValue(key, out val);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -9,6 +9,7 @@ using Umbraco.Core.Models;
|
||||
using Umbraco.Core;
|
||||
using Umbraco.Core.Models.EntityBase;
|
||||
using Umbraco.Core.Models.Rdbms;
|
||||
using Umbraco.Core.Persistence.DatabaseModelDefinitions;
|
||||
using Umbraco.Core.Persistence.Factories;
|
||||
using Umbraco.Core.Persistence.Querying;
|
||||
using Umbraco.Core.Persistence.UnitOfWork;
|
||||
@@ -49,6 +50,124 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
|
||||
#region Query Methods
|
||||
|
||||
public IEnumerable<IUmbracoEntity> GetPagedResultsByQuery(IQuery<IUmbracoEntity> query, Guid objectTypeId, long pageIndex, int pageSize, out long totalRecords,
|
||||
string orderBy, Direction orderDirection, IQuery<IUmbracoEntity> filter = null)
|
||||
{
|
||||
bool isContent = objectTypeId == new Guid(Constants.ObjectTypes.Document);
|
||||
bool isMedia = objectTypeId == new Guid(Constants.ObjectTypes.Media);
|
||||
var factory = new UmbracoEntityFactory();
|
||||
|
||||
var sqlClause = GetBaseWhere(GetBase, isContent, isMedia, sql =>
|
||||
{
|
||||
if (filter != null)
|
||||
{
|
||||
foreach (var filterClaus in filter.GetWhereClauses())
|
||||
{
|
||||
sql.Where(filterClaus.Item1, filterClaus.Item2);
|
||||
}
|
||||
}
|
||||
}, objectTypeId);
|
||||
var translator = new SqlTranslator<IUmbracoEntity>(sqlClause, query);
|
||||
var entitySql = translator.Translate();
|
||||
var pagedSql = entitySql.Append(GetGroupBy(isContent, isMedia, false)).OrderBy("umbracoNode.id");
|
||||
|
||||
IEnumerable<IUmbracoEntity> result;
|
||||
|
||||
if (isMedia)
|
||||
{
|
||||
//Treat media differently for now, as an Entity it will be returned with ALL of it's properties in the AdditionalData bag!
|
||||
var pagedResult = _work.Database.Page<dynamic>(pageIndex + 1, pageSize, pagedSql);
|
||||
|
||||
var ids = pagedResult.Items.Select(x => (int) x.id).InGroupsOf(2000);
|
||||
var entities = pagedResult.Items.Select(factory.BuildEntityFromDynamic).Cast<IUmbracoEntity>().ToList();
|
||||
|
||||
//Now we need to merge in the property data since we need paging and we can't do this the way that the big media query was working before
|
||||
foreach (var idGroup in ids)
|
||||
{
|
||||
var propSql = GetPropertySql(Constants.ObjectTypes.Media)
|
||||
.Where("contentNodeId IN (@ids)", new {ids = idGroup})
|
||||
.OrderBy("contentNodeId");
|
||||
|
||||
//This does NOT fetch all data into memory in a list, this will read
|
||||
// over the records as a data reader, this is much better for performance and memory,
|
||||
// but it means that during the reading of this data set, nothing else can be read
|
||||
// from SQL server otherwise we'll get an exception.
|
||||
var allPropertyData = _work.Database.Query<dynamic>(propSql);
|
||||
|
||||
//keep track of the current property data item being enumerated
|
||||
var propertyDataSetEnumerator = allPropertyData.GetEnumerator();
|
||||
var hasCurrent = false; // initially there is no enumerator.Current
|
||||
|
||||
try
|
||||
{
|
||||
//This must be sorted by node id (which is done by SQL) because this is how we are sorting the query to lookup property types above,
|
||||
// which allows us to more efficiently iterate over the large data set of property values.
|
||||
foreach (var entity in entities)
|
||||
{
|
||||
// assemble the dtos for this def
|
||||
// use the available enumerator.Current if any else move to next
|
||||
while (hasCurrent || propertyDataSetEnumerator.MoveNext())
|
||||
{
|
||||
if (propertyDataSetEnumerator.Current.contentNodeId == entity.Id)
|
||||
{
|
||||
hasCurrent = false; // enumerator.Current is not available
|
||||
|
||||
//the property data goes into the additional data
|
||||
entity.AdditionalData[propertyDataSetEnumerator.Current.propertyTypeAlias] = new UmbracoEntity.EntityProperty
|
||||
{
|
||||
PropertyEditorAlias = propertyDataSetEnumerator.Current.propertyEditorAlias,
|
||||
Value = StringExtensions.IsNullOrWhiteSpace(propertyDataSetEnumerator.Current.dataNtext)
|
||||
? propertyDataSetEnumerator.Current.dataNvarchar
|
||||
: StringExtensions.ConvertToJsonIfPossible(propertyDataSetEnumerator.Current.dataNtext)
|
||||
};
|
||||
}
|
||||
else
|
||||
{
|
||||
hasCurrent = true; // enumerator.Current is available for another def
|
||||
break; // no more propertyDataDto for this def
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
propertyDataSetEnumerator.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
result = entities;
|
||||
}
|
||||
else
|
||||
{
|
||||
var pagedResult = _work.Database.Page<dynamic>(pageIndex + 1, pageSize, pagedSql);
|
||||
result = pagedResult.Items.Select(factory.BuildEntityFromDynamic).Cast<IUmbracoEntity>().ToList();
|
||||
}
|
||||
|
||||
//The total items from the PetaPoco page query will be wrong due to the Outer join used on parent, depending on the search this will
|
||||
//return duplicate results when the COUNT is used in conjuction with it, so we need to get the total on our own.
|
||||
|
||||
//generate a query that does not contain the LEFT Join for parent, this would cause
|
||||
//the COUNT(*) query to return the wrong
|
||||
var sqlCountClause = GetBaseWhere(
|
||||
(isC, isM, f) => GetBase(isC, isM, f, true), //true == is a count query
|
||||
isContent, isMedia, sql =>
|
||||
{
|
||||
if (filter != null)
|
||||
{
|
||||
foreach (var filterClaus in filter.GetWhereClauses())
|
||||
{
|
||||
sql.Where(filterClaus.Item1, filterClaus.Item2);
|
||||
}
|
||||
}
|
||||
}, objectTypeId);
|
||||
var translatorCount = new SqlTranslator<IUmbracoEntity>(sqlCountClause, query);
|
||||
var countSql = translatorCount.Translate();
|
||||
|
||||
totalRecords = _work.Database.ExecuteScalar<int>(countSql);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public IUmbracoEntity GetByKey(Guid key)
|
||||
{
|
||||
var sql = GetBaseWhere(GetBase, false, false, key);
|
||||
@@ -71,8 +190,7 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
|
||||
if (isMedia)
|
||||
{
|
||||
//for now treat media differently
|
||||
//TODO: We should really use this methodology for Content/Members too!! since it includes properties and ALL of the dynamic db fields
|
||||
//Treat media differently for now, as an Entity it will be returned with ALL of it's properties in the AdditionalData bag!
|
||||
var entities = _work.Database.Fetch<dynamic, UmbracoPropertyDto, UmbracoEntity>(
|
||||
new UmbracoEntityRelator().Map, sql);
|
||||
|
||||
@@ -115,8 +233,7 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
|
||||
if (isMedia)
|
||||
{
|
||||
//for now treat media differently
|
||||
//TODO: We should really use this methodology for Content/Members too!! since it includes properties and ALL of the dynamic db fields
|
||||
//Treat media differently for now, as an Entity it will be returned with ALL of it's properties in the AdditionalData bag!
|
||||
var entities = _work.Database.Fetch<dynamic, UmbracoPropertyDto, UmbracoEntity>(
|
||||
new UmbracoEntityRelator().Map, sql);
|
||||
|
||||
@@ -171,8 +288,7 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
|
||||
if (isMedia)
|
||||
{
|
||||
//for now treat media differently
|
||||
//TODO: We should really use this methodology for Content/Members too!! since it includes properties and ALL of the dynamic db fields
|
||||
//Treat media differently for now, as an Entity it will be returned with ALL of it's properties in the AdditionalData bag!
|
||||
var entities = _work.Database.Fetch<dynamic, UmbracoPropertyDto, UmbracoEntity>(
|
||||
new UmbracoEntityRelator().Map, sql);
|
||||
foreach (var entity in entities)
|
||||
@@ -231,8 +347,7 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
}
|
||||
});
|
||||
|
||||
//treat media differently for now
|
||||
//TODO: We should really use this methodology for Content/Members too!! since it includes properties and ALL of the dynamic db fields
|
||||
//Treat media differently for now, as an Entity it will be returned with ALL of it's properties in the AdditionalData bag!
|
||||
var entities = _work.Database.Fetch<dynamic, UmbracoPropertyDto, UmbracoEntity>(
|
||||
new UmbracoEntityRelator().Map, mediaSql);
|
||||
return entities;
|
||||
@@ -278,11 +393,9 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
return GetFullSqlForMedia(entitySql.Append(GetGroupBy(isContent, true, false)), filter);
|
||||
}
|
||||
|
||||
private Sql GetFullSqlForMedia(Sql entitySql, Action<Sql> filter = null)
|
||||
private Sql GetPropertySql(string nodeObjectType)
|
||||
{
|
||||
//this will add any dataNvarchar property to the output which can be added to the additional properties
|
||||
|
||||
var joinSql = new Sql()
|
||||
var sql = new Sql()
|
||||
.Select("contentNodeId, versionId, dataNvarchar, dataNtext, propertyEditorAlias, alias as propertyTypeAlias")
|
||||
.From<PropertyDataDto>()
|
||||
.InnerJoin<NodeDto>()
|
||||
@@ -291,7 +404,16 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
.On<PropertyTypeDto, PropertyDataDto>(dto => dto.Id, dto => dto.PropertyTypeId)
|
||||
.InnerJoin<DataTypeDto>()
|
||||
.On<PropertyTypeDto, DataTypeDto>(dto => dto.DataTypeId, dto => dto.DataTypeId)
|
||||
.Where("umbracoNode.nodeObjectType = @nodeObjectType", new {nodeObjectType = Constants.ObjectTypes.Media});
|
||||
.Where("umbracoNode.nodeObjectType = @nodeObjectType", new { nodeObjectType = nodeObjectType });
|
||||
|
||||
return sql;
|
||||
}
|
||||
|
||||
private Sql GetFullSqlForMedia(Sql entitySql, Action<Sql> filter = null)
|
||||
{
|
||||
//this will add any dataNvarchar property to the output which can be added to the additional properties
|
||||
|
||||
var joinSql = GetPropertySql(Constants.ObjectTypes.Media);
|
||||
|
||||
if (filter != null)
|
||||
{
|
||||
@@ -310,34 +432,52 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
.OrderBy("sortOrder, id");
|
||||
|
||||
return wrappedSql;
|
||||
}
|
||||
}
|
||||
|
||||
protected virtual Sql GetBase(bool isContent, bool isMedia, Action<Sql> customFilter)
|
||||
{
|
||||
var columns = new List<object>
|
||||
{
|
||||
"umbracoNode.id",
|
||||
"umbracoNode.trashed",
|
||||
"umbracoNode.parentID",
|
||||
"umbracoNode.nodeUser",
|
||||
"umbracoNode.level",
|
||||
"umbracoNode.path",
|
||||
"umbracoNode.sortOrder",
|
||||
"umbracoNode.uniqueID",
|
||||
"umbracoNode.text",
|
||||
"umbracoNode.nodeObjectType",
|
||||
"umbracoNode.createDate",
|
||||
"COUNT(parent.parentID) as children"
|
||||
};
|
||||
return GetBase(isContent, isMedia, customFilter, false);
|
||||
}
|
||||
|
||||
if (isContent || isMedia)
|
||||
protected virtual Sql GetBase(bool isContent, bool isMedia, Action<Sql> customFilter, bool isCount)
|
||||
{
|
||||
var columns = new List<object>();
|
||||
if (isCount)
|
||||
{
|
||||
columns.Add("published.versionId as publishedVersion");
|
||||
columns.Add("latest.versionId as newestVersion");
|
||||
columns.Add("contenttype.alias");
|
||||
columns.Add("contenttype.icon");
|
||||
columns.Add("contenttype.thumbnail");
|
||||
columns.Add("contenttype.isContainer");
|
||||
columns.Add("COUNT(*)");
|
||||
}
|
||||
else
|
||||
{
|
||||
columns.AddRange(new List<object>
|
||||
{
|
||||
"umbracoNode.id",
|
||||
"umbracoNode.trashed",
|
||||
"umbracoNode.parentID",
|
||||
"umbracoNode.nodeUser",
|
||||
"umbracoNode.level",
|
||||
"umbracoNode.path",
|
||||
"umbracoNode.sortOrder",
|
||||
"umbracoNode.uniqueID",
|
||||
"umbracoNode.text",
|
||||
"umbracoNode.nodeObjectType",
|
||||
"umbracoNode.createDate",
|
||||
"COUNT(parent.parentID) as children"
|
||||
});
|
||||
|
||||
if (isContent || isMedia)
|
||||
{
|
||||
if (isContent)
|
||||
{
|
||||
//only content has this info
|
||||
columns.Add("published.versionId as publishedVersion");
|
||||
columns.Add("document.versionId as newestVersion");
|
||||
}
|
||||
|
||||
columns.Add("contenttype.alias");
|
||||
columns.Add("contenttype.icon");
|
||||
columns.Add("contenttype.thumbnail");
|
||||
columns.Add("contenttype.isContainer");
|
||||
}
|
||||
}
|
||||
|
||||
//Creates an SQL query to return a single row for the entity
|
||||
@@ -348,17 +488,24 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
|
||||
if (isContent || isMedia)
|
||||
{
|
||||
entitySql.InnerJoin("cmsContent content").On("content.nodeId = umbracoNode.id")
|
||||
.LeftJoin("cmsContentType contenttype").On("contenttype.nodeId = content.contentType")
|
||||
.LeftJoin(
|
||||
"(SELECT nodeId, versionId FROM cmsDocument WHERE published = 1 GROUP BY nodeId, versionId) as published")
|
||||
.On("umbracoNode.id = published.nodeId")
|
||||
.LeftJoin(
|
||||
"(SELECT nodeId, versionId FROM cmsDocument WHERE newest = 1 GROUP BY nodeId, versionId) as latest")
|
||||
.On("umbracoNode.id = latest.nodeId");
|
||||
entitySql.InnerJoin("cmsContent content").On("content.nodeId = umbracoNode.id");
|
||||
|
||||
if (isContent)
|
||||
{
|
||||
//only content has this info
|
||||
entitySql
|
||||
.InnerJoin("cmsDocument document").On("document.nodeId = umbracoNode.id")
|
||||
.LeftJoin("(SELECT nodeId, versionId FROM cmsDocument WHERE published = 1) as published")
|
||||
.On("umbracoNode.id = published.nodeId");
|
||||
}
|
||||
|
||||
entitySql.LeftJoin("cmsContentType contenttype").On("contenttype.nodeId = content.contentType");
|
||||
}
|
||||
|
||||
entitySql.LeftJoin("umbracoNode parent").On("parent.parentID = umbracoNode.id");
|
||||
if (isCount == false)
|
||||
{
|
||||
entitySql.LeftJoin("umbracoNode parent").On("parent.parentID = umbracoNode.id");
|
||||
}
|
||||
|
||||
if (customFilter != null)
|
||||
{
|
||||
@@ -372,22 +519,42 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
{
|
||||
var sql = baseQuery(isContent, isMedia, filter)
|
||||
.Where("umbracoNode.nodeObjectType = @NodeObjectType", new { NodeObjectType = nodeObjectType });
|
||||
|
||||
if (isContent)
|
||||
{
|
||||
sql.Where("document.newest = 1");
|
||||
}
|
||||
|
||||
return sql;
|
||||
}
|
||||
|
||||
protected virtual Sql GetBaseWhere(Func<bool, bool, Action<Sql>, Sql> baseQuery, bool isContent, bool isMedia, int id)
|
||||
{
|
||||
var sql = baseQuery(isContent, isMedia, null)
|
||||
.Where("umbracoNode.id = @Id", new { Id = id })
|
||||
.Append(GetGroupBy(isContent, isMedia));
|
||||
.Where("umbracoNode.id = @Id", new { Id = id });
|
||||
|
||||
if (isContent)
|
||||
{
|
||||
sql.Where("document.newest = 1");
|
||||
}
|
||||
|
||||
sql.Append(GetGroupBy(isContent, isMedia));
|
||||
|
||||
return sql;
|
||||
}
|
||||
|
||||
protected virtual Sql GetBaseWhere(Func<bool, bool, Action<Sql>, Sql> baseQuery, bool isContent, bool isMedia, Guid key)
|
||||
{
|
||||
var sql = baseQuery(isContent, isMedia, null)
|
||||
.Where("umbracoNode.uniqueID = @UniqueID", new { UniqueID = key })
|
||||
.Append(GetGroupBy(isContent, isMedia));
|
||||
.Where("umbracoNode.uniqueID = @UniqueID", new { UniqueID = key });
|
||||
|
||||
if (isContent)
|
||||
{
|
||||
sql.Where("document.newest = 1");
|
||||
}
|
||||
|
||||
sql.Append(GetGroupBy(isContent, isMedia));
|
||||
|
||||
return sql;
|
||||
}
|
||||
|
||||
@@ -396,6 +563,12 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
var sql = baseQuery(isContent, isMedia, null)
|
||||
.Where("umbracoNode.id = @Id AND umbracoNode.nodeObjectType = @NodeObjectType",
|
||||
new {Id = id, NodeObjectType = nodeObjectType});
|
||||
|
||||
if (isContent)
|
||||
{
|
||||
sql.Where("document.newest = 1");
|
||||
}
|
||||
|
||||
return sql;
|
||||
}
|
||||
|
||||
@@ -404,30 +577,39 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
var sql = baseQuery(isContent, isMedia, null)
|
||||
.Where("umbracoNode.uniqueID = @UniqueID AND umbracoNode.nodeObjectType = @NodeObjectType",
|
||||
new { UniqueID = key, NodeObjectType = nodeObjectType });
|
||||
|
||||
if (isContent)
|
||||
{
|
||||
sql.Where("document.newest = 1");
|
||||
}
|
||||
|
||||
return sql;
|
||||
}
|
||||
|
||||
protected virtual Sql GetGroupBy(bool isContent, bool isMedia, bool includeSort = true)
|
||||
{
|
||||
var columns = new List<object>
|
||||
{
|
||||
"umbracoNode.id",
|
||||
"umbracoNode.trashed",
|
||||
"umbracoNode.parentID",
|
||||
"umbracoNode.nodeUser",
|
||||
"umbracoNode.level",
|
||||
"umbracoNode.path",
|
||||
"umbracoNode.sortOrder",
|
||||
"umbracoNode.uniqueID",
|
||||
"umbracoNode.text",
|
||||
"umbracoNode.nodeObjectType",
|
||||
"umbracoNode.createDate"
|
||||
};
|
||||
{
|
||||
"umbracoNode.id",
|
||||
"umbracoNode.trashed",
|
||||
"umbracoNode.parentID",
|
||||
"umbracoNode.nodeUser",
|
||||
"umbracoNode.level",
|
||||
"umbracoNode.path",
|
||||
"umbracoNode.sortOrder",
|
||||
"umbracoNode.uniqueID",
|
||||
"umbracoNode.text",
|
||||
"umbracoNode.nodeObjectType",
|
||||
"umbracoNode.createDate"
|
||||
};
|
||||
|
||||
if (isContent || isMedia)
|
||||
{
|
||||
columns.Add("published.versionId");
|
||||
columns.Add("latest.versionId");
|
||||
if (isContent)
|
||||
{
|
||||
columns.Add("published.versionId");
|
||||
columns.Add("document.versionId");
|
||||
}
|
||||
columns.Add("contenttype.alias");
|
||||
columns.Add("contenttype.icon");
|
||||
columns.Add("contenttype.thumbnail");
|
||||
|
||||
@@ -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,6 +1,7 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Umbraco.Core.Models.EntityBase;
|
||||
using Umbraco.Core.Persistence.DatabaseModelDefinitions;
|
||||
using Umbraco.Core.Persistence.Querying;
|
||||
|
||||
namespace Umbraco.Core.Persistence.Repositories
|
||||
@@ -16,6 +17,21 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
IEnumerable<IUmbracoEntity> GetByQuery(IQuery<IUmbracoEntity> query);
|
||||
IEnumerable<IUmbracoEntity> GetByQuery(IQuery<IUmbracoEntity> query, Guid objectTypeId);
|
||||
|
||||
/// <summary>
|
||||
/// Gets paged results
|
||||
/// </summary>
|
||||
/// <param name="query">Query to excute</param>
|
||||
/// <param name="objectTypeId"></param>
|
||||
/// <param name="pageIndex">Page number</param>
|
||||
/// <param name="pageSize">Page size</param>
|
||||
/// <param name="totalRecords">Total records query would return without paging</param>
|
||||
/// <param name="orderBy">Field to order by</param>
|
||||
/// <param name="orderDirection">Direction to order by</param>
|
||||
/// <param name="filter"></param>
|
||||
/// <returns>An Enumerable list of <see cref="IUmbracoEntity"/> objects</returns>
|
||||
IEnumerable<IUmbracoEntity> GetPagedResultsByQuery(IQuery<IUmbracoEntity> query, Guid objectTypeId, long pageIndex, int pageSize, out long totalRecords,
|
||||
string orderBy, Direction orderDirection, IQuery<IUmbracoEntity> filter = null);
|
||||
|
||||
/// <summary>
|
||||
/// Returns true if the entity exists
|
||||
/// </summary>
|
||||
|
||||
@@ -68,7 +68,7 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
sql.Where("umbracoNode.id in (@ids)", new { ids = ids });
|
||||
}
|
||||
|
||||
return ProcessQuery(sql);
|
||||
return ProcessQuery(sql, new PagingSqlQuery(sql));
|
||||
}
|
||||
|
||||
protected override IEnumerable<IMedia> PerformGetByQuery(IQuery<IMedia> query)
|
||||
@@ -78,7 +78,7 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
var sql = translator.Translate()
|
||||
.OrderBy<NodeDto>(x => x.SortOrder, SqlSyntax);
|
||||
|
||||
return ProcessQuery(sql);
|
||||
return ProcessQuery(sql, new PagingSqlQuery(sql));
|
||||
}
|
||||
|
||||
#endregion
|
||||
@@ -100,7 +100,7 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
|
||||
protected override Sql GetBaseQuery(bool isCount)
|
||||
{
|
||||
return GetBaseQuery(isCount ? BaseQueryType.Count : BaseQueryType.Full);
|
||||
return GetBaseQuery(isCount ? BaseQueryType.Count : BaseQueryType.FullSingle);
|
||||
}
|
||||
|
||||
protected override string GetBaseWhereClause()
|
||||
@@ -143,13 +143,24 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
var sql = GetBaseQuery(false)
|
||||
.Where(GetBaseWhereClause(), new { Id = id })
|
||||
.OrderByDescending<ContentVersionDto>(x => x.VersionDate, SqlSyntax);
|
||||
return ProcessQuery(sql, true);
|
||||
return ProcessQuery(sql, new PagingSqlQuery(sql), true);
|
||||
}
|
||||
|
||||
private IEnumerable<IMedia> ProcessQuery(Sql sql, bool withCache = false)
|
||||
/// <summary>
|
||||
/// This is the underlying method that processes most queries for this repository
|
||||
/// </summary>
|
||||
/// <param name="sqlFull">
|
||||
/// The full SQL to select all media data
|
||||
/// </param>
|
||||
/// <param name="pagingSqlQuery">
|
||||
/// The Id SQL to just return all media ids - used to process the properties for the media item
|
||||
/// </param>
|
||||
/// <param name="withCache"></param>
|
||||
/// <returns></returns>
|
||||
private IEnumerable<IMedia> ProcessQuery(Sql sqlFull, PagingSqlQuery pagingSqlQuery, bool withCache = false)
|
||||
{
|
||||
// fetch returns a list so it's ok to iterate it in this method
|
||||
var dtos = Database.Fetch<ContentVersionDto, ContentDto, NodeDto>(sql);
|
||||
var dtos = Database.Fetch<ContentVersionDto, ContentDto, NodeDto>(sqlFull);
|
||||
var content = new IMedia[dtos.Count];
|
||||
var defs = new List<DocumentDefinition>();
|
||||
|
||||
@@ -200,7 +211,7 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
}
|
||||
|
||||
// load all properties for all documents from database in 1 query
|
||||
var propertyData = GetPropertyCollection(sql, defs);
|
||||
var propertyData = GetPropertyCollection(pagingSqlQuery, defs);
|
||||
|
||||
// assign
|
||||
var dtoIndex = 0;
|
||||
@@ -257,7 +268,8 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
query = query
|
||||
.Where<NodeDto>(x => x.NodeId > baseId, SqlSyntax)
|
||||
.OrderBy<NodeDto>(x => x.NodeId, SqlSyntax);
|
||||
var xmlItems = ProcessQuery(SqlSyntax.SelectTop(query, groupSize))
|
||||
var sql = SqlSyntax.SelectTop(query, groupSize);
|
||||
var xmlItems = ProcessQuery(sql, new PagingSqlQuery(sql))
|
||||
.Select(x => new ContentXmlDto { NodeId = x.Id, Xml = serializer(x).ToString() })
|
||||
.ToList();
|
||||
|
||||
@@ -505,7 +517,7 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
|
||||
return GetPagedResultsByQuery<ContentVersionDto>(query, pageIndex, pageSize, out totalRecords,
|
||||
new Tuple<string, string>("cmsContentVersion", "contentId"),
|
||||
(sqlFull, sqlIds) => ProcessQuery(sqlFull), orderBy, orderDirection, orderBySystemField,
|
||||
(sqlFull, pagingSqlQuery) => ProcessQuery(sqlFull, pagingSqlQuery), orderBy, orderDirection, orderBySystemField,
|
||||
filterCallback);
|
||||
|
||||
}
|
||||
@@ -513,7 +525,7 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
/// <summary>
|
||||
/// Private method to create a media object from a ContentDto
|
||||
/// </summary>
|
||||
/// <param name="d"></param>
|
||||
/// <param name="dto"></param>
|
||||
/// <param name="versionId"></param>
|
||||
/// <param name="docSql"></param>
|
||||
/// <returns></returns>
|
||||
@@ -525,7 +537,7 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
|
||||
var docDef = new DocumentDefinition(dto.NodeId, versionId, media.UpdateDate, media.CreateDate, contentType);
|
||||
|
||||
var properties = GetPropertyCollection(docSql, new[] { docDef });
|
||||
var properties = GetPropertyCollection(new PagingSqlQuery(docSql), new[] { docDef });
|
||||
|
||||
media.Properties = properties[dto.NodeId];
|
||||
|
||||
|
||||
@@ -68,7 +68,7 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
sql.Where("umbracoNode.id in (@ids)", new { ids = ids });
|
||||
}
|
||||
|
||||
return ProcessQuery(sql);
|
||||
return ProcessQuery(sql, new PagingSqlQuery(sql));
|
||||
|
||||
}
|
||||
|
||||
@@ -90,7 +90,7 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
baseQuery.Append(new Sql("WHERE umbracoNode.id IN (" + sql.SQL + ")", sql.Arguments))
|
||||
.OrderBy<NodeDto>(x => x.SortOrder);
|
||||
|
||||
return ProcessQuery(baseQuery);
|
||||
return ProcessQuery(baseQuery, new PagingSqlQuery(baseQuery));
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -98,7 +98,7 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
var sql = translator.Translate()
|
||||
.OrderBy<NodeDto>(x => x.SortOrder);
|
||||
|
||||
return ProcessQuery(sql);
|
||||
return ProcessQuery(sql, new PagingSqlQuery(sql));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -129,7 +129,7 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
|
||||
protected override Sql GetBaseQuery(bool isCount)
|
||||
{
|
||||
return GetBaseQuery(isCount ? BaseQueryType.Count : BaseQueryType.Full);
|
||||
return GetBaseQuery(isCount ? BaseQueryType.Count : BaseQueryType.FullSingle);
|
||||
}
|
||||
|
||||
protected override string GetBaseWhereClause()
|
||||
@@ -385,7 +385,7 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
var sql = GetBaseQuery(false)
|
||||
.Where(GetBaseWhereClause(), new { Id = id })
|
||||
.OrderByDescending<ContentVersionDto>(x => x.VersionDate, SqlSyntax);
|
||||
return ProcessQuery(sql, true);
|
||||
return ProcessQuery(sql, new PagingSqlQuery(sql), true);
|
||||
}
|
||||
|
||||
public void RebuildXmlStructures(Func<IMember, XElement> serializer, int groupSize = 200, IEnumerable<int> contentTypeIds = null)
|
||||
@@ -408,7 +408,8 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
query = query
|
||||
.Where<NodeDto>(x => x.NodeId > baseId)
|
||||
.OrderBy<NodeDto>(x => x.NodeId, SqlSyntax);
|
||||
var xmlItems = ProcessQuery(SqlSyntax.SelectTop(query, groupSize))
|
||||
var sql = SqlSyntax.SelectTop(query, groupSize);
|
||||
var xmlItems = ProcessQuery(sql, new PagingSqlQuery(sql))
|
||||
.Select(x => new ContentXmlDto { NodeId = x.Id, Xml = serializer(x).ToString() })
|
||||
.ToList();
|
||||
|
||||
@@ -449,7 +450,7 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
var factory = new MemberFactory(memberType, NodeObjectTypeId, dto.NodeId);
|
||||
var media = factory.BuildEntity(dto);
|
||||
|
||||
var properties = GetPropertyCollection(sql, new[] { new DocumentDefinition(dto.NodeId, dto.ContentVersionDto.VersionId, media.UpdateDate, media.CreateDate, memberType) });
|
||||
var properties = GetPropertyCollection(new PagingSqlQuery(sql), new[] { new DocumentDefinition(dto.NodeId, dto.ContentVersionDto.VersionId, media.UpdateDate, media.CreateDate, memberType) });
|
||||
|
||||
media.Properties = properties[dto.NodeId];
|
||||
|
||||
@@ -540,7 +541,7 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
.OrderByDescending<ContentVersionDto>(x => x.VersionDate)
|
||||
.OrderBy<NodeDto>(x => x.SortOrder);
|
||||
|
||||
return ProcessQuery(sql);
|
||||
return ProcessQuery(sql, new PagingSqlQuery(sql));
|
||||
|
||||
}
|
||||
|
||||
@@ -601,7 +602,7 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
|
||||
return GetPagedResultsByQuery<MemberDto>(query, pageIndex, pageSize, out totalRecords,
|
||||
new Tuple<string, string>("cmsMember", "nodeId"),
|
||||
(sqlFull, sqlIds) => ProcessQuery(sqlFull), orderBy, orderDirection, orderBySystemField,
|
||||
(sqlFull, sqlIds) => ProcessQuery(sqlFull, sqlIds), orderBy, orderDirection, orderBySystemField,
|
||||
filterCallback);
|
||||
}
|
||||
|
||||
@@ -641,10 +642,21 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
return base.GetEntityPropertyNameForOrderBy(orderBy);
|
||||
}
|
||||
|
||||
private IEnumerable<IMember> ProcessQuery(Sql sql, bool withCache = false)
|
||||
/// <summary>
|
||||
/// This is the underlying method that processes most queries for this repository
|
||||
/// </summary>
|
||||
/// <param name="sqlFull">
|
||||
/// The full SQL to select all member data
|
||||
/// </param>
|
||||
/// <param name="pagingSqlQuery">
|
||||
/// The Id SQL to just return all member ids - used to process the properties for the member item
|
||||
/// </param>
|
||||
/// <param name="withCache"></param>
|
||||
/// <returns></returns>
|
||||
private IEnumerable<IMember> ProcessQuery(Sql sqlFull, PagingSqlQuery pagingSqlQuery, bool withCache = false)
|
||||
{
|
||||
// fetch returns a list so it's ok to iterate it in this method
|
||||
var dtos = Database.Fetch<MemberDto, ContentVersionDto, ContentDto, NodeDto>(sql);
|
||||
var dtos = Database.Fetch<MemberDto, ContentVersionDto, ContentDto, NodeDto>(sqlFull);
|
||||
|
||||
var content = new IMember[dtos.Count];
|
||||
var defs = new List<DocumentDefinition>();
|
||||
@@ -681,7 +693,7 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
}
|
||||
|
||||
// load all properties for all documents from database in 1 query
|
||||
var propertyData = GetPropertyCollection(sql, defs);
|
||||
var propertyData = GetPropertyCollection(pagingSqlQuery, defs);
|
||||
|
||||
// assign
|
||||
var dtoIndex = 0;
|
||||
|
||||
@@ -63,6 +63,7 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
FormatDeleteStatement("cmsContentVersion", "ContentId"),
|
||||
FormatDeleteStatement("cmsContentXml", "nodeId"),
|
||||
FormatDeleteStatement("cmsContent", "nodeId"),
|
||||
//TODO: Why is this being done? We just delete this exact data in the next line
|
||||
"UPDATE umbracoNode SET parentID = '" + RecycleBinId + "' WHERE trashed = '1' AND nodeObjectType = @NodeObjectType",
|
||||
"DELETE FROM umbracoNode WHERE trashed = '1' AND nodeObjectType = @NodeObjectType"
|
||||
};
|
||||
@@ -91,14 +92,18 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A delete statement that will delete anything in the table specified where its PK (keyName) is found in the
|
||||
/// list of umbracoNode.id that have trashed flag set
|
||||
/// </summary>
|
||||
/// <param name="tableName"></param>
|
||||
/// <param name="keyName"></param>
|
||||
/// <returns></returns>
|
||||
private string FormatDeleteStatement(string tableName, string keyName)
|
||||
{
|
||||
//This query works with sql ce and sql server:
|
||||
//DELETE FROM umbracoUser2NodeNotify WHERE umbracoUser2NodeNotify.nodeId IN
|
||||
//(SELECT nodeId FROM umbracoUser2NodeNotify as TB1 INNER JOIN umbracoNode as TB2 ON TB1.nodeId = TB2.id WHERE TB2.trashed = '1' AND TB2.nodeObjectType = 'C66BA18E-EAF3-4CFF-8A22-41B16D66A972')
|
||||
return
|
||||
string.Format(
|
||||
"DELETE FROM {0} WHERE {0}.{1} IN (SELECT TB1.{1} FROM {0} as TB1 INNER JOIN umbracoNode as TB2 ON TB1.{1} = TB2.id WHERE TB2.trashed = '1' AND TB2.nodeObjectType = @NodeObjectType)",
|
||||
"DELETE FROM {0} WHERE {0}.{1} IN (SELECT id FROM umbracoNode WHERE trashed = '1' AND nodeObjectType = @NodeObjectType)",
|
||||
tableName, keyName);
|
||||
}
|
||||
|
||||
|
||||
@@ -73,12 +73,16 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
RelationFactory factory = null;
|
||||
var relationTypeId = -1;
|
||||
|
||||
// the ToList() here is important because we are using _relationTypeRepository and we
|
||||
// cannot wait until the result is actually enumerated to do so, because that would
|
||||
// mean we kinda capture the current unit of work and reuse it after it's been disposed
|
||||
|
||||
return dtos.Select(x =>
|
||||
{
|
||||
if (relationTypeId != x.RelationType)
|
||||
factory = new RelationFactory(_relationTypeRepository.Get(relationTypeId = x.RelationType));
|
||||
return DtoToEntity(x, factory);
|
||||
});
|
||||
}).ToList();
|
||||
}
|
||||
|
||||
private static IRelation DtoToEntity(RelationDto dto, RelationFactory factory)
|
||||
|
||||
@@ -270,12 +270,11 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
|
||||
// once content has been set, "template on disk" are not "on disk" anymore
|
||||
template.Content = content;
|
||||
SetVirtualPath(template);
|
||||
|
||||
if (dto.Design == content) return;
|
||||
dto.Design = content;
|
||||
Database.Update(dto); // though... we don't care about the db value really??!!
|
||||
|
||||
SetVirtualPath(template);
|
||||
}
|
||||
|
||||
protected override void PersistDeletedItem(ITemplate entity)
|
||||
|
||||
@@ -432,7 +432,7 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
/// <exception cref="System.ArgumentNullException">orderBy</exception>
|
||||
protected IEnumerable<TEntity> GetPagedResultsByQuery<TDto>(IQuery<TEntity> query, long pageIndex, int pageSize, out long totalRecords,
|
||||
Tuple<string, string> nodeIdSelect,
|
||||
Func<Sql, Sql, IEnumerable<TEntity>> processQuery,
|
||||
Func<Sql, PagingSqlQuery<TDto>, IEnumerable<TEntity>> processQuery,
|
||||
string orderBy,
|
||||
Direction orderDirection,
|
||||
bool orderBySystemField,
|
||||
@@ -443,7 +443,7 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
// Get base query for returning IDs
|
||||
var sqlBaseIds = GetBaseQuery(BaseQueryType.Ids);
|
||||
// Get base query for returning all data
|
||||
var sqlBaseFull = GetBaseQuery(BaseQueryType.Full);
|
||||
var sqlBaseFull = GetBaseQuery(BaseQueryType.FullMultiple);
|
||||
|
||||
if (query == null) query = new Query<TEntity>();
|
||||
var translatorIds = new SqlTranslator<TEntity>(sqlBaseIds, query);
|
||||
@@ -466,7 +466,7 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
// the pageResult, then the GetAll will actually return ALL records in the db.
|
||||
if (pagedResult.Items.Any())
|
||||
{
|
||||
//Crete the inner paged query that was used above to get the paged result, we'll use that as the inner sub query
|
||||
//Create the inner paged query that was used above to get the paged result, we'll use that as the inner sub query
|
||||
var args = sqlNodeIdsWithSort.Arguments;
|
||||
string sqlStringCount, sqlStringPage;
|
||||
Database.BuildPageQueries<TDto>(pageIndex * pageSize, pageSize, sqlNodeIdsWithSort.SQL, ref args, out sqlStringCount, out sqlStringPage);
|
||||
@@ -486,8 +486,8 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
var fullQuery = GetSortedSqlForPagedResults(
|
||||
GetFilteredSqlForPagedResults(fullQueryWithPagedInnerJoin, defaultFilter),
|
||||
orderDirection, orderBy, orderBySystemField, nodeIdSelect);
|
||||
|
||||
return processQuery(fullQuery, sqlNodeIdsWithSort);
|
||||
|
||||
return processQuery(fullQuery, new PagingSqlQuery<TDto>(Database, sqlNodeIdsWithSort, pageIndex, pageSize));
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -497,18 +497,47 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the property collection for a non-paged query
|
||||
/// </summary>
|
||||
/// <param name="sql"></param>
|
||||
/// <param name="documentDefs"></param>
|
||||
/// <returns></returns>
|
||||
protected IDictionary<int, PropertyCollection> GetPropertyCollection(
|
||||
Sql docSql,
|
||||
Sql sql,
|
||||
IReadOnlyCollection<DocumentDefinition> documentDefs)
|
||||
{
|
||||
return GetPropertyCollection(new PagingSqlQuery(sql), documentDefs);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the property collection for a query
|
||||
/// </summary>
|
||||
/// <param name="pagingSqlQuery"></param>
|
||||
/// <param name="documentDefs"></param>
|
||||
/// <returns></returns>
|
||||
protected IDictionary<int, PropertyCollection> GetPropertyCollection(
|
||||
PagingSqlQuery pagingSqlQuery,
|
||||
IReadOnlyCollection<DocumentDefinition> documentDefs)
|
||||
{
|
||||
if (documentDefs.Count == 0) return new Dictionary<int, PropertyCollection>();
|
||||
|
||||
//initialize to the query passed in
|
||||
var docSql = pagingSqlQuery.PrePagedSql;
|
||||
|
||||
//we need to parse the original SQL statement and reduce the columns to just cmsContent.nodeId, cmsContentVersion.VersionId so that we can use
|
||||
// the statement to go get the property data for all of the items by using an inner join
|
||||
var parsedOriginalSql = "SELECT {0} " + docSql.SQL.Substring(docSql.SQL.IndexOf("FROM", StringComparison.Ordinal));
|
||||
//now remove everything from an Orderby clause and beyond
|
||||
if (parsedOriginalSql.InvariantContains("ORDER BY "))
|
||||
|
||||
if (pagingSqlQuery.HasPaging)
|
||||
{
|
||||
//if this is a paged query, build the paged query with the custom column substitution, then re-assign
|
||||
docSql = pagingSqlQuery.BuildPagedQuery("{0}");
|
||||
parsedOriginalSql = docSql.SQL;
|
||||
}
|
||||
else if (parsedOriginalSql.InvariantContains("ORDER BY "))
|
||||
{
|
||||
//now remove everything from an Orderby clause and beyond if this is unpaged data
|
||||
parsedOriginalSql = parsedOriginalSql.Substring(0, parsedOriginalSql.LastIndexOf("ORDER BY ", StringComparison.Ordinal));
|
||||
}
|
||||
|
||||
@@ -525,7 +554,7 @@ WHERE EXISTS(
|
||||
INNER JOIN cmsPropertyType
|
||||
ON b.datatypeNodeId = cmsPropertyType.dataTypeId
|
||||
INNER JOIN
|
||||
(" + string.Format(parsedOriginalSql, "DISTINCT cmsContent.contentType") + @") as docData
|
||||
(" + string.Format(parsedOriginalSql, "cmsContent.contentType") + @") as docData
|
||||
ON cmsPropertyType.contentTypeId = docData.contentType
|
||||
WHERE a.id = b.id)", docSql.Arguments);
|
||||
|
||||
@@ -646,28 +675,7 @@ ORDER BY contentNodeId, propertytypeid
|
||||
return result;
|
||||
|
||||
}
|
||||
|
||||
public class DocumentDefinition
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="T:System.Object"/> class.
|
||||
/// </summary>
|
||||
public DocumentDefinition(int id, Guid version, DateTime versionDate, DateTime createDate, IContentTypeComposition composition)
|
||||
{
|
||||
Id = id;
|
||||
Version = version;
|
||||
VersionDate = versionDate;
|
||||
CreateDate = createDate;
|
||||
Composition = composition;
|
||||
}
|
||||
|
||||
public int Id { get; set; }
|
||||
public Guid Version { get; set; }
|
||||
public DateTime VersionDate { get; set; }
|
||||
public DateTime CreateDate { get; set; }
|
||||
public IContentTypeComposition Composition { get; set; }
|
||||
}
|
||||
|
||||
|
||||
protected virtual string GetDatabaseFieldNameForOrderBy(string orderBy)
|
||||
{
|
||||
// Translate the passed order by field (which were originally defined for in-memory object sorting
|
||||
@@ -763,5 +771,92 @@ ORDER BY contentNodeId, propertytypeid
|
||||
/// <param name="queryType"></param>
|
||||
/// <returns></returns>
|
||||
protected abstract Sql GetBaseQuery(BaseQueryType queryType);
|
||||
|
||||
internal class DocumentDefinition
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="T:System.Object"/> class.
|
||||
/// </summary>
|
||||
public DocumentDefinition(int id, Guid version, DateTime versionDate, DateTime createDate, IContentTypeComposition composition)
|
||||
{
|
||||
Id = id;
|
||||
Version = version;
|
||||
VersionDate = versionDate;
|
||||
CreateDate = createDate;
|
||||
Composition = composition;
|
||||
}
|
||||
|
||||
public int Id { get; set; }
|
||||
public Guid Version { get; set; }
|
||||
public DateTime VersionDate { get; set; }
|
||||
public DateTime CreateDate { get; set; }
|
||||
public IContentTypeComposition Composition { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// An object representing a query that may contain paging information
|
||||
/// </summary>
|
||||
internal class PagingSqlQuery
|
||||
{
|
||||
public Sql PrePagedSql { get; private set; }
|
||||
|
||||
public PagingSqlQuery(Sql prePagedSql)
|
||||
{
|
||||
PrePagedSql = prePagedSql;
|
||||
}
|
||||
|
||||
public virtual bool HasPaging
|
||||
{
|
||||
get { return false; }
|
||||
}
|
||||
|
||||
public virtual Sql BuildPagedQuery(string selectColumns)
|
||||
{
|
||||
throw new InvalidOperationException("This query has no paging information");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// An object representing a query that contains paging information
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
internal class PagingSqlQuery<T> : PagingSqlQuery
|
||||
{
|
||||
private readonly Database _db;
|
||||
private readonly long _pageIndex;
|
||||
private readonly int _pageSize;
|
||||
|
||||
public PagingSqlQuery(Database db, Sql prePagedSql, long pageIndex, int pageSize) : base(prePagedSql)
|
||||
{
|
||||
_db = db;
|
||||
_pageIndex = pageIndex;
|
||||
_pageSize = pageSize;
|
||||
}
|
||||
|
||||
public override bool HasPaging
|
||||
{
|
||||
get { return _pageSize > 0; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a paged query based on the original query and subtitutes the selectColumns specified
|
||||
/// </summary>
|
||||
/// <param name="selectColumns"></param>
|
||||
/// <returns></returns>
|
||||
public override Sql BuildPagedQuery(string selectColumns)
|
||||
{
|
||||
if (HasPaging == false) throw new InvalidOperationException("This query has no paging information");
|
||||
|
||||
var resultSql = string.Format("SELECT {0} {1}", selectColumns, PrePagedSql.SQL.Substring(PrePagedSql.SQL.IndexOf("FROM", StringComparison.Ordinal)));
|
||||
|
||||
//this query is meant to be paged so we need to generate the paging syntax
|
||||
//Create the inner paged query that was used above to get the paged result, we'll use that as the inner sub query
|
||||
var args = PrePagedSql.Arguments;
|
||||
string sqlStringCount, sqlStringPage;
|
||||
_db.BuildPageQueries<T>(_pageIndex * _pageSize, _pageSize, resultSql, ref args, out sqlStringCount, out sqlStringPage);
|
||||
|
||||
return new Sql(sqlStringPage, args);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,23 @@
|
||||
namespace Umbraco.Core.Persistence.SqlSyntax
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Umbraco.Core.Persistence.DatabaseModelDefinitions;
|
||||
|
||||
namespace Umbraco.Core.Persistence.SqlSyntax
|
||||
{
|
||||
internal static class SqlSyntaxProviderExtensions
|
||||
{
|
||||
public static IEnumerable<DbIndexDefinition> GetDefinedIndexesDefinitions(this ISqlSyntaxProvider sql, Database db)
|
||||
{
|
||||
return sql.GetDefinedIndexes(db)
|
||||
.Select(x => new DbIndexDefinition()
|
||||
{
|
||||
TableName = x.Item1,
|
||||
IndexName = x.Item2,
|
||||
ColumnName = x.Item3,
|
||||
IsUnique = x.Item4
|
||||
}).ToArray();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the quotes tableName.columnName combo
|
||||
/// </summary>
|
||||
|
||||
@@ -15,6 +15,7 @@ namespace Umbraco.Core.PropertyEditors
|
||||
public PreValueField()
|
||||
{
|
||||
Validators = new List<IPropertyValidator>();
|
||||
Config = new Dictionary<string, object>();
|
||||
|
||||
//check for an attribute and fill the values
|
||||
var att = GetType().GetCustomAttribute<PreValueFieldAttribute>(false);
|
||||
@@ -79,5 +80,11 @@ namespace Umbraco.Core.PropertyEditors
|
||||
/// </summary>
|
||||
[JsonProperty("validation", ItemConverterType = typeof(ManifestValidatorConverter))]
|
||||
public List<IPropertyValidator> Validators { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// This allows for custom configuration to be injected into the pre-value editor
|
||||
/// </summary>
|
||||
[JsonProperty("config")]
|
||||
public IDictionary<string, object> Config { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -38,6 +38,7 @@ namespace Umbraco.Core.PropertyEditors
|
||||
IsParameterEditor = _attribute.IsParameterEditor;
|
||||
Icon = _attribute.Icon;
|
||||
Group = _attribute.Group;
|
||||
IsDeprecated = _attribute.IsDeprecated;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -90,6 +91,9 @@ namespace Umbraco.Core.PropertyEditors
|
||||
get { return CreateValueEditor(); }
|
||||
}
|
||||
|
||||
[JsonIgnore]
|
||||
public bool IsDeprecated { get; internal set; }
|
||||
|
||||
[JsonIgnore]
|
||||
IValueEditor IParameterEditor.ValueEditor
|
||||
{
|
||||
|
||||
@@ -60,6 +60,12 @@ namespace Umbraco.Core.PropertyEditors
|
||||
public string ValueType { get; set; }
|
||||
public bool IsParameterEditor { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// If set to true, this property editor will not show up in the DataType's drop down list
|
||||
/// if there is not already one of them chosen for a DataType
|
||||
/// </summary>
|
||||
public bool IsDeprecated { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// If this is is true than the editor will be displayed full width without a label
|
||||
/// </summary>
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,6 +15,7 @@ namespace Umbraco.Core.Scoping
|
||||
private bool _disposed;
|
||||
|
||||
private UmbracoDatabase _database;
|
||||
private EventMessages _messages;
|
||||
|
||||
public NoScope(ScopeProvider scopeProvider)
|
||||
{
|
||||
@@ -24,10 +25,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
|
||||
@@ -60,12 +62,42 @@ namespace Umbraco.Core.Scoping
|
||||
/// <inheritdoc />
|
||||
public EventMessages Messages
|
||||
{
|
||||
get { throw new NotSupportedException(); }
|
||||
get
|
||||
{
|
||||
EnsureNotDisposed();
|
||||
if (_messages != null) return _messages;
|
||||
|
||||
// see comments in Scope
|
||||
|
||||
var factory = ScopeLifespanMessagesFactory.Current;
|
||||
if (factory == null)
|
||||
{
|
||||
_messages = new EventMessages();
|
||||
}
|
||||
else
|
||||
{
|
||||
_messages = factory.GetFromHttpContext();
|
||||
if (_messages == null)
|
||||
factory.Set(_messages = new EventMessages());
|
||||
}
|
||||
|
||||
return _messages;
|
||||
}
|
||||
}
|
||||
|
||||
public EventMessages MessagesOrNull
|
||||
{
|
||||
get { throw new NotSupportedException(); }
|
||||
get
|
||||
{
|
||||
EnsureNotDisposed();
|
||||
|
||||
// see comments in Scope
|
||||
|
||||
if (_messages != null) return _messages;
|
||||
|
||||
var factory = ScopeLifespanMessagesFactory.Current;
|
||||
return factory == null ? null : factory.GetFromHttpContext();
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
@@ -75,7 +107,7 @@ namespace Umbraco.Core.Scoping
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Complete()
|
||||
public bool Complete()
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
@@ -100,15 +132,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,14 +16,15 @@ 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;
|
||||
|
||||
private IsolatedRuntimeCache _isolatedRuntimeCache;
|
||||
private UmbracoDatabase _database;
|
||||
private EventMessages _messages;
|
||||
private ICompletable _fscope;
|
||||
private IEventDispatcher _eventDispatcher;
|
||||
|
||||
@@ -35,15 +36,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 +77,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,43 +100,55 @@ 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;
|
||||
_messages = noScope.MessagesOrNull;
|
||||
|
||||
// make sure the NoScope can be replaced ie not in a transaction
|
||||
if (_database != null && _database.InTransaction)
|
||||
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 +159,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 +189,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; }
|
||||
|
||||
@@ -266,11 +273,27 @@ namespace Umbraco.Core.Scoping
|
||||
{
|
||||
EnsureNotDisposed();
|
||||
if (ParentScope != null) return ParentScope.Messages;
|
||||
//return _messages ?? (_messages = new EventMessages());
|
||||
|
||||
// ok, this isn't pretty, but it works
|
||||
// TODO kill the message factory and let the scope manage it all
|
||||
return ApplicationContext.Current.Services.EventMessagesFactory.Get();
|
||||
if (_messages != null) return _messages;
|
||||
|
||||
// this is ugly - in v7 for backward compatibility reasons, EventMessages need
|
||||
// to survive way longer that the scopes, and kinda resides on its own in http
|
||||
// context, but must also be in scopes for when we do async and lose http context
|
||||
// TODO refactor in v8
|
||||
|
||||
var factory = ScopeLifespanMessagesFactory.Current;
|
||||
if (factory == null)
|
||||
{
|
||||
_messages = new EventMessages();
|
||||
}
|
||||
else
|
||||
{
|
||||
_messages = factory.GetFromHttpContext();
|
||||
if (_messages == null)
|
||||
factory.Set(_messages = new EventMessages());
|
||||
}
|
||||
|
||||
return _messages;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -279,7 +302,14 @@ namespace Umbraco.Core.Scoping
|
||||
get
|
||||
{
|
||||
EnsureNotDisposed();
|
||||
return ParentScope == null ? null : ParentScope.MessagesOrNull;
|
||||
if (ParentScope != null) return ParentScope.MessagesOrNull;
|
||||
|
||||
// see comments in Messages
|
||||
|
||||
if (_messages != null) return _messages;
|
||||
|
||||
var factory = ScopeLifespanMessagesFactory.Current;
|
||||
return factory == null ? null : factory.GetFromHttpContext();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -290,15 +320,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,15 +355,26 @@ 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
|
||||
}
|
||||
|
||||
var parent = ParentScope;
|
||||
_scopeProvider.AmbientScope = parent;
|
||||
|
||||
#if DEBUG_SCOPES
|
||||
_scopeProvider.Disposed(this);
|
||||
#endif
|
||||
|
||||
var parent = ParentScope;
|
||||
_scopeProvider.AmbientScope = parent;
|
||||
|
||||
if (parent != null)
|
||||
parent.ChildCompleted(_completed);
|
||||
else
|
||||
@@ -418,7 +460,7 @@ namespace Umbraco.Core.Scoping
|
||||
}
|
||||
finally
|
||||
{
|
||||
_scopeProvider.AmbientContext = null;
|
||||
_scopeProvider.SetAmbient(null);
|
||||
}
|
||||
}
|
||||
}, () =>
|
||||
@@ -426,8 +468,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,15 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Data;
|
||||
using System.Linq;
|
||||
using System.Runtime.Remoting.Messaging;
|
||||
using System.Text;
|
||||
using System.Web;
|
||||
using Umbraco.Core.Events;
|
||||
using Umbraco.Core.Persistence;
|
||||
#if DEBUG_SCOPES
|
||||
using System.Linq;
|
||||
#endif
|
||||
|
||||
namespace Umbraco.Core.Scoping
|
||||
{
|
||||
@@ -24,17 +28,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 +48,217 @@ 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();
|
||||
if (objectKey == Guid.Empty) return null;
|
||||
|
||||
lock (StaticCallContextObjectsLock)
|
||||
{
|
||||
object callContextObject;
|
||||
if (StaticCallContextObjects.TryGetValue(objectKey, out callContextObject))
|
||||
{
|
||||
#if DEBUG_SCOPES
|
||||
Logging.LogHelper.Debug<ScopeProvider>("Got " + typeof(T).Name + " Object " + objectKey.ToString("N").Substring(0, 8));
|
||||
//Logging.LogHelper.Debug<ScopeProvider>("At:\r\n" + Head(Environment.StackTrace, 24));
|
||||
#endif
|
||||
return (T) callContextObject;
|
||||
}
|
||||
|
||||
Logging.LogHelper.Warn<ScopeProvider>("Missed " + typeof(T).Name + " Object " + objectKey.ToString("N").Substring(0, 8));
|
||||
#if DEBUG_SCOPES
|
||||
//Logging.LogHelper.Debug<ScopeProvider>("At:\r\n" + Head(Environment.StackTrace, 24));
|
||||
#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>("Remove Object " + objectKey.ToString("N").Substring(0, 8));
|
||||
//Logging.LogHelper.Debug<ScopeProvider>("At:\r\n" + Head(Environment.StackTrace, 24));
|
||||
#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));
|
||||
//Logging.LogHelper.Debug<ScopeProvider>("At:\r\n" + Head(Environment.StackTrace, 24));
|
||||
#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 +271,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 +357,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 +386,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 +397,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 +423,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 +481,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,50 +489,88 @@ 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.");
|
||||
}
|
||||
var sb = new StringBuilder();
|
||||
var s = scope;
|
||||
while (s != null)
|
||||
{
|
||||
if (sb.Length > 0) sb.Append(" < ");
|
||||
sb.Append(s.InstanceId.ToString("N").Substring(0, 8));
|
||||
var ss = s as IScopeInternal;
|
||||
s = ss == null ? null : ss.ParentScope;
|
||||
}
|
||||
Logging.LogHelper.Debug<ScopeProvider>("Register " + (context ?? "null") + " context " + sb);
|
||||
if (context == null) info.NullStack = Environment.StackTrace;
|
||||
//Logging.LogHelper.Debug<ScopeProvider>("At:\r\n" + Head(Environment.StackTrace, 16));
|
||||
info.Context = context;
|
||||
}
|
||||
}
|
||||
|
||||
private static string Head(string s, int count)
|
||||
{
|
||||
var pos = 0;
|
||||
var i = 0;
|
||||
while (i < count && pos >= 0)
|
||||
{
|
||||
pos = s.IndexOf("\r\n", pos + 1, StringComparison.OrdinalIgnoreCase);
|
||||
i++;
|
||||
}
|
||||
if (pos < 0) return s;
|
||||
return s.Substring(0, pos);
|
||||
}
|
||||
|
||||
public void Disposed(IScope scope)
|
||||
{
|
||||
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!
|
||||
|
||||
@@ -176,7 +176,7 @@ namespace Umbraco.Core.Security
|
||||
AllowRefresh = true,
|
||||
IssuedUtc = nowUtc,
|
||||
ExpiresUtc = nowUtc.AddMinutes(GlobalSettings.TimeOutInMinutes)
|
||||
}, userIdentity, rememberBrowserIdentity);
|
||||
}, userIdentity, rememberBrowserIdentity);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -189,6 +189,10 @@ namespace Umbraco.Core.Security
|
||||
}, userIdentity);
|
||||
}
|
||||
|
||||
//track the last login date
|
||||
user.LastLoginDateUtc = DateTime.UtcNow;
|
||||
await UserManager.UpdateAsync(user);
|
||||
|
||||
_logger.WriteCore(TraceEventType.Information, 0,
|
||||
string.Format(
|
||||
"Login attempt succeeded for username {0} from IP address {1}",
|
||||
|
||||
@@ -625,6 +625,12 @@ namespace Umbraco.Core.Security
|
||||
var anythingChanged = false;
|
||||
//don't assign anything if nothing has changed as this will trigger
|
||||
//the track changes of the model
|
||||
if ((user.LastLoginDate != default(DateTime) && identityUser.LastLoginDateUtc.HasValue == false)
|
||||
|| identityUser.LastLoginDateUtc.HasValue && user.LastLoginDate.ToUniversalTime() != identityUser.LastLoginDateUtc.Value)
|
||||
{
|
||||
anythingChanged = true;
|
||||
user.LastLoginDate = identityUser.LastLoginDateUtc.Value.ToLocalTime();
|
||||
}
|
||||
if (user.Name != identityUser.Name && identityUser.Name.IsNullOrWhiteSpace() == false)
|
||||
{
|
||||
anythingChanged = true;
|
||||
|
||||
@@ -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)))
|
||||
|
||||
@@ -69,7 +69,7 @@ namespace Umbraco.Core.Services
|
||||
repo.AddOrUpdate(container);
|
||||
uow.Commit();
|
||||
|
||||
uow.Events.Dispatch(SavedContentTypeContainer, this, new SaveEventArgs<EntityContainer>(container, evtMsgs));
|
||||
uow.Events.Dispatch(SavedContentTypeContainer, this, new SaveEventArgs<EntityContainer>(container, evtMsgs), "SavedContentTypeContainer");
|
||||
//TODO: Audit trail ?
|
||||
|
||||
return Attempt.Succeed(new OperationStatus<EntityContainer, OperationStatusType>(container, OperationStatusType.Success, evtMsgs));
|
||||
@@ -106,7 +106,7 @@ namespace Umbraco.Core.Services
|
||||
repo.AddOrUpdate(container);
|
||||
uow.Commit();
|
||||
|
||||
uow.Events.Dispatch(SavedMediaTypeContainer, this, new SaveEventArgs<EntityContainer>(container, evtMsgs));
|
||||
uow.Events.Dispatch(SavedMediaTypeContainer, this, new SaveEventArgs<EntityContainer>(container, evtMsgs), "SavedMediaTypeContainer");
|
||||
//TODO: Audit trail ?
|
||||
|
||||
return Attempt.Succeed(new OperationStatus<EntityContainer, OperationStatusType>(container, OperationStatusType.Success, evtMsgs));
|
||||
@@ -122,14 +122,16 @@ namespace Umbraco.Core.Services
|
||||
{
|
||||
return SaveContainer(
|
||||
SavingContentTypeContainer, SavedContentTypeContainer,
|
||||
container, Constants.ObjectTypes.DocumentTypeContainerGuid, "document type", userId);
|
||||
container, Constants.ObjectTypes.DocumentTypeContainerGuid, "document type",
|
||||
"SavedContentTypeContainer", userId);
|
||||
}
|
||||
|
||||
public Attempt<OperationStatus> SaveMediaTypeContainer(EntityContainer container, int userId = 0)
|
||||
{
|
||||
return SaveContainer(
|
||||
SavingMediaTypeContainer, SavedMediaTypeContainer,
|
||||
container, Constants.ObjectTypes.MediaTypeContainerGuid, "media type", userId);
|
||||
container, Constants.ObjectTypes.MediaTypeContainerGuid, "media type",
|
||||
"SavedMediaTypeContainer", userId);
|
||||
}
|
||||
|
||||
private Attempt<OperationStatus> SaveContainer(
|
||||
@@ -137,7 +139,9 @@ namespace Umbraco.Core.Services
|
||||
TypedEventHandler<IContentTypeService, SaveEventArgs<EntityContainer>> savedEvent,
|
||||
EntityContainer container,
|
||||
Guid containerObjectType,
|
||||
string objectTypeName, int userId)
|
||||
string objectTypeName,
|
||||
string savedEventName,
|
||||
int userId)
|
||||
{
|
||||
var evtMsgs = EventMessagesFactory.Get();
|
||||
|
||||
@@ -165,7 +169,7 @@ namespace Umbraco.Core.Services
|
||||
var repo = RepositoryFactory.CreateEntityContainerRepository(uow, containerObjectType);
|
||||
repo.AddOrUpdate(container);
|
||||
uow.Commit();
|
||||
uow.Events.Dispatch(savedEvent, this, new SaveEventArgs<EntityContainer>(container, evtMsgs));
|
||||
uow.Events.Dispatch(savedEvent, this, new SaveEventArgs<EntityContainer>(container, evtMsgs), savedEventName);
|
||||
}
|
||||
|
||||
//TODO: Audit trail ?
|
||||
@@ -756,6 +760,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 +805,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 +1217,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 +1262,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);
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@ using Umbraco.Core.Models;
|
||||
using Umbraco.Core.Models.EntityBase;
|
||||
using Umbraco.Core.Models.Rdbms;
|
||||
using Umbraco.Core.Persistence;
|
||||
using Umbraco.Core.Persistence.DatabaseModelDefinitions;
|
||||
using Umbraco.Core.Persistence.Querying;
|
||||
using Umbraco.Core.Persistence.UnitOfWork;
|
||||
|
||||
@@ -91,6 +92,7 @@ namespace Umbraco.Core.Services
|
||||
case UmbracoObjectTypes.Member:
|
||||
case UmbracoObjectTypes.DataType:
|
||||
case UmbracoObjectTypes.DocumentTypeContainer:
|
||||
uow.Commit();
|
||||
return uow.Database.ExecuteScalar<int?>(new Sql().Select("id").From<NodeDto>().Where<NodeDto>(dto => dto.UniqueId == key));
|
||||
case UmbracoObjectTypes.RecycleBin:
|
||||
case UmbracoObjectTypes.Stylesheet:
|
||||
@@ -129,6 +131,7 @@ namespace Umbraco.Core.Services
|
||||
case UmbracoObjectTypes.DocumentType:
|
||||
case UmbracoObjectTypes.Member:
|
||||
case UmbracoObjectTypes.DataType:
|
||||
uow.Commit();
|
||||
return uow.Database.ExecuteScalar<Guid?>(new Sql().Select("uniqueID").From<NodeDto>().Where<NodeDto>(dto => dto.NodeId == id));
|
||||
case UmbracoObjectTypes.RecycleBin:
|
||||
case UmbracoObjectTypes.Stylesheet:
|
||||
@@ -374,6 +377,115 @@ namespace Umbraco.Core.Services
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a paged collection of children
|
||||
/// </summary>
|
||||
/// <param name="parentId">The parent id to return children for</param>
|
||||
/// <param name="umbracoObjectType"></param>
|
||||
/// <param name="pageIndex"></param>
|
||||
/// <param name="pageSize"></param>
|
||||
/// <param name="totalRecords"></param>
|
||||
/// <param name="orderBy"></param>
|
||||
/// <param name="orderDirection"></param>
|
||||
/// <param name="filter"></param>
|
||||
/// <returns></returns>
|
||||
public IEnumerable<IUmbracoEntity> GetPagedChildren(int parentId, UmbracoObjectTypes umbracoObjectType, long pageIndex, int pageSize, out long totalRecords,
|
||||
string orderBy = "SortOrder", Direction orderDirection = Direction.Ascending, string filter = "")
|
||||
{
|
||||
var objectTypeId = umbracoObjectType.GetGuid();
|
||||
using (var uow = UowProvider.GetUnitOfWork())
|
||||
{
|
||||
var repository = RepositoryFactory.CreateEntityRepository(uow);
|
||||
var query = Query<IUmbracoEntity>.Builder.Where(x => x.ParentId == parentId && x.Trashed == false);
|
||||
|
||||
IQuery<IUmbracoEntity> filterQuery = null;
|
||||
if (filter.IsNullOrWhiteSpace() == false)
|
||||
{
|
||||
filterQuery = Query<IUmbracoEntity>.Builder.Where(x => x.Name.Contains(filter));
|
||||
}
|
||||
|
||||
var contents = repository.GetPagedResultsByQuery(query, objectTypeId, pageIndex, pageSize, out totalRecords, orderBy, orderDirection, filterQuery);
|
||||
uow.Commit();
|
||||
return contents;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a paged collection of descendants
|
||||
/// </summary>
|
||||
/// <param name="id"></param>
|
||||
/// <param name="umbracoObjectType"></param>
|
||||
/// <param name="pageIndex"></param>
|
||||
/// <param name="pageSize"></param>
|
||||
/// <param name="totalRecords"></param>
|
||||
/// <param name="orderBy"></param>
|
||||
/// <param name="orderDirection"></param>
|
||||
/// <param name="filter"></param>
|
||||
/// <returns></returns>
|
||||
public IEnumerable<IUmbracoEntity> GetPagedDescendants(int id, UmbracoObjectTypes umbracoObjectType, long pageIndex, int pageSize, out long totalRecords,
|
||||
string orderBy = "path", Direction orderDirection = Direction.Ascending, string filter = "")
|
||||
{
|
||||
var objectTypeId = umbracoObjectType.GetGuid();
|
||||
using (var uow = UowProvider.GetUnitOfWork())
|
||||
{
|
||||
var repository = RepositoryFactory.CreateEntityRepository(uow);
|
||||
|
||||
var query = Query<IUmbracoEntity>.Builder;
|
||||
//if the id is System Root, then just get all
|
||||
if (id != Constants.System.Root)
|
||||
query.Where(x => x.Path.SqlContains(string.Format(",{0},", id), TextColumnType.NVarchar));
|
||||
|
||||
IQuery<IUmbracoEntity> filterQuery = null;
|
||||
if (filter.IsNullOrWhiteSpace() == false)
|
||||
{
|
||||
filterQuery = Query<IUmbracoEntity>.Builder.Where(x => x.Name.Contains(filter));
|
||||
}
|
||||
|
||||
var contents = repository.GetPagedResultsByQuery(query, objectTypeId, pageIndex, pageSize, out totalRecords, orderBy, orderDirection, filterQuery);
|
||||
uow.Commit();
|
||||
return contents;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a paged collection of descendants from the root
|
||||
/// </summary>
|
||||
/// <param name="umbracoObjectType"></param>
|
||||
/// <param name="pageIndex"></param>
|
||||
/// <param name="pageSize"></param>
|
||||
/// <param name="totalRecords"></param>
|
||||
/// <param name="orderBy"></param>
|
||||
/// <param name="orderDirection"></param>
|
||||
/// <param name="filter"></param>
|
||||
/// <param name="includeTrashed">true/false to include trashed objects</param>
|
||||
/// <returns></returns>
|
||||
public IEnumerable<IUmbracoEntity> GetPagedDescendantsFromRoot(UmbracoObjectTypes umbracoObjectType, long pageIndex, int pageSize, out long totalRecords,
|
||||
string orderBy = "path", Direction orderDirection = Direction.Ascending, string filter = "", bool includeTrashed = true)
|
||||
{
|
||||
var objectTypeId = umbracoObjectType.GetGuid();
|
||||
using (var uow = UowProvider.GetUnitOfWork())
|
||||
{
|
||||
var repository = RepositoryFactory.CreateEntityRepository(uow);
|
||||
|
||||
var query = Query<IUmbracoEntity>.Builder;
|
||||
//don't include trashed if specfied
|
||||
if (includeTrashed == false)
|
||||
{
|
||||
query.Where(x => x.Trashed == false);
|
||||
}
|
||||
|
||||
IQuery<IUmbracoEntity> filterQuery = null;
|
||||
if (filter.IsNullOrWhiteSpace() == false)
|
||||
{
|
||||
filterQuery = Query<IUmbracoEntity>.Builder.Where(x => x.Name.Contains(filter));
|
||||
}
|
||||
|
||||
var contents = repository.GetPagedResultsByQuery(query, objectTypeId, pageIndex, pageSize, out totalRecords, orderBy, orderDirection, filterQuery);
|
||||
uow.Commit();
|
||||
return contents;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a collection of descendents by the parents Id
|
||||
/// </summary>
|
||||
|
||||
@@ -735,6 +735,26 @@ namespace Umbraco.Core.Services
|
||||
return empty.Union(files.Except(empty));
|
||||
}
|
||||
|
||||
public void CreatePartialViewFolder(string folderPath)
|
||||
{
|
||||
var uow = _fileUowProvider.GetUnitOfWork();
|
||||
using (var repository = RepositoryFactory.CreatePartialViewRepository(uow))
|
||||
{
|
||||
((PartialViewRepository)repository).AddFolder(folderPath);
|
||||
uow.Commit();
|
||||
}
|
||||
}
|
||||
|
||||
public void CreatePartialViewMacroFolder(string folderPath)
|
||||
{
|
||||
var uow = _fileUowProvider.GetUnitOfWork();
|
||||
using (var repository = RepositoryFactory.CreatePartialViewMacroRepository(uow))
|
||||
{
|
||||
((PartialViewMacroRepository)repository).AddFolder(folderPath);
|
||||
uow.Commit();
|
||||
}
|
||||
}
|
||||
|
||||
public void DeletePartialViewFolder(string folderPath)
|
||||
{
|
||||
using (var uow = _fileUowProvider.GetUnitOfWork())
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
using System.Collections.Generic;
|
||||
using Umbraco.Core.Models;
|
||||
using Umbraco.Core.Models.EntityBase;
|
||||
using Umbraco.Core.Persistence.DatabaseModelDefinitions;
|
||||
|
||||
namespace Umbraco.Core.Services
|
||||
{
|
||||
@@ -137,6 +138,51 @@ namespace Umbraco.Core.Services
|
||||
/// <returns>An enumerable list of <see cref="IUmbracoEntity"/> objects</returns>
|
||||
IEnumerable<IUmbracoEntity> GetChildren(int parentId, UmbracoObjectTypes umbracoObjectType);
|
||||
|
||||
/// <summary>
|
||||
/// Returns a paged collection of children
|
||||
/// </summary>
|
||||
/// <param name="parentId">The parent id to return children for</param>
|
||||
/// <param name="umbracoObjectType"></param>
|
||||
/// <param name="pageIndex"></param>
|
||||
/// <param name="pageSize"></param>
|
||||
/// <param name="totalRecords"></param>
|
||||
/// <param name="orderBy"></param>
|
||||
/// <param name="orderDirection"></param>
|
||||
/// <param name="filter"></param>
|
||||
/// <returns></returns>
|
||||
IEnumerable<IUmbracoEntity> GetPagedChildren(int parentId, UmbracoObjectTypes umbracoObjectType, long pageIndex, int pageSize, out long totalRecords,
|
||||
string orderBy = "SortOrder", Direction orderDirection = Direction.Ascending, string filter = "");
|
||||
|
||||
/// <summary>
|
||||
/// Returns a paged collection of descendants
|
||||
/// </summary>
|
||||
/// <param name="id"></param>
|
||||
/// <param name="umbracoObjectType"></param>
|
||||
/// <param name="pageIndex"></param>
|
||||
/// <param name="pageSize"></param>
|
||||
/// <param name="totalRecords"></param>
|
||||
/// <param name="orderBy"></param>
|
||||
/// <param name="orderDirection"></param>
|
||||
/// <param name="filter"></param>
|
||||
/// <returns></returns>
|
||||
IEnumerable<IUmbracoEntity> GetPagedDescendants(int id, UmbracoObjectTypes umbracoObjectType, long pageIndex, int pageSize, out long totalRecords,
|
||||
string orderBy = "path", Direction orderDirection = Direction.Ascending, string filter = "");
|
||||
|
||||
/// <summary>
|
||||
/// Returns a paged collection of descendants from the root
|
||||
/// </summary>
|
||||
/// <param name="umbracoObjectType"></param>
|
||||
/// <param name="pageIndex"></param>
|
||||
/// <param name="pageSize"></param>
|
||||
/// <param name="totalRecords"></param>
|
||||
/// <param name="orderBy"></param>
|
||||
/// <param name="orderDirection"></param>
|
||||
/// <param name="filter"></param>
|
||||
/// <param name="includeTrashed">true/false to include trashed objects</param>
|
||||
/// <returns></returns>
|
||||
IEnumerable<IUmbracoEntity> GetPagedDescendantsFromRoot(UmbracoObjectTypes umbracoObjectType, long pageIndex, int pageSize, out long totalRecords,
|
||||
string orderBy = "path", Direction orderDirection = Direction.Ascending, string filter = "", bool includeTrashed = true);
|
||||
|
||||
/// <summary>
|
||||
/// Gets a collection of descendents by the parents Id
|
||||
/// </summary>
|
||||
|
||||
@@ -11,6 +11,8 @@ namespace Umbraco.Core.Services
|
||||
public interface IFileService : IService
|
||||
{
|
||||
IEnumerable<string> GetPartialViewSnippetNames(params string[] filterNames);
|
||||
void CreatePartialViewFolder(string folderPath);
|
||||
void CreatePartialViewMacroFolder(string folderPath);
|
||||
void DeletePartialViewFolder(string folderPath);
|
||||
void DeletePartialViewMacroFolder(string folderPath);
|
||||
IPartialView GetPartialView(string path);
|
||||
|
||||
@@ -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())
|
||||
|
||||
@@ -1716,6 +1716,24 @@ namespace Umbraco.Core.Services
|
||||
#region Package Building
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// This method can be used to trigger the 'ImportedPackage' event when a package is installed by something else but this service.
|
||||
/// </summary>
|
||||
/// <param name="args"></param>
|
||||
internal static void OnImportedPackage(ImportPackageEventArgs<InstallationSummary> args)
|
||||
{
|
||||
ImportedPackage.RaiseEvent(args, null);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This method can be used to trigger the 'UninstalledPackage' event when a package is uninstalled by something else but this service.
|
||||
/// </summary>
|
||||
/// <param name="args"></param>
|
||||
internal static void OnUninstalledPackage(UninstallPackageEventArgs<UninstallationSummary> args)
|
||||
{
|
||||
UninstalledPackage.RaiseEvent(args, null);
|
||||
}
|
||||
|
||||
#region Event Handlers
|
||||
/// <summary>
|
||||
/// Occurs before Importing Content
|
||||
@@ -1876,10 +1894,15 @@ namespace Umbraco.Core.Services
|
||||
internal static event TypedEventHandler<IPackagingService, ImportPackageEventArgs<string>> ImportingPackage;
|
||||
|
||||
/// <summary>
|
||||
/// Occurs after a apckage is imported
|
||||
/// Occurs after a package is imported
|
||||
/// </summary>
|
||||
internal static event TypedEventHandler<IPackagingService, ImportPackageEventArgs<InstallationSummary>> ImportedPackage;
|
||||
|
||||
/// <summary>
|
||||
/// Occurs after a package is uninstalled
|
||||
/// </summary>
|
||||
internal static event TypedEventHandler<IPackagingService, UninstallPackageEventArgs<UninstallationSummary>> UninstalledPackage;
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
||||
@@ -215,7 +215,7 @@ namespace Umbraco.Core.Services
|
||||
using (var uow = UowProvider.GetUnitOfWork(commit: true))
|
||||
{
|
||||
var repository = RepositoryFactory.CreateRelationRepository(uow);
|
||||
var query = new Query<IRelation>().Where(x => x.ChildId == id || x.ParentId == id);
|
||||
var query = new Query<IRelation>().Where(x => x.ParentId == id || x.ChildId == id);
|
||||
return repository.GetByQuery(query);
|
||||
}
|
||||
}
|
||||
@@ -230,7 +230,7 @@ namespace Umbraco.Core.Services
|
||||
if (relationType == null) return Enumerable.Empty<IRelation>();
|
||||
|
||||
var relationRepo = RepositoryFactory.CreateRelationRepository(uow);
|
||||
var query = new Query<IRelation>().Where(x => (x.ChildId == id || x.ParentId == id) && x.RelationTypeId == relationType.Id);
|
||||
var query = new Query<IRelation>().Where(x => (x.ParentId == id || x.ChildId == id) && x.RelationTypeId == relationType.Id);
|
||||
return relationRepo.GetByQuery(query);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -425,7 +425,28 @@ namespace Umbraco.Core
|
||||
assemblyName.FullName.StartsWith("App_Code.") ? "App_Code" : assemblyName.Name);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// If the given <paramref name="type"/> is an array or some other collection
|
||||
/// comprised of 0 or more instances of a "subtype", get that type
|
||||
/// </summary>
|
||||
/// <param name="type">the source type</param>
|
||||
/// <returns></returns>
|
||||
internal static Type GetEnumeratedType(this Type type)
|
||||
{
|
||||
if (typeof(IEnumerable).IsAssignableFrom(type) == false)
|
||||
return null;
|
||||
|
||||
// provided by Array
|
||||
var elType = type.GetElementType();
|
||||
if (null != elType) return elType;
|
||||
|
||||
// otherwise provided by collection
|
||||
var elTypes = type.GetGenericArguments();
|
||||
if (elTypes.Length > 0) return elTypes[0];
|
||||
|
||||
// otherwise is not an 'enumerated' type
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
+55
-34
@@ -1,7 +1,9 @@
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using Umbraco.Core.Deploy;
|
||||
|
||||
namespace Umbraco.Core
|
||||
@@ -10,9 +12,10 @@ namespace Umbraco.Core
|
||||
/// Represents an entity identifier.
|
||||
/// </summary>
|
||||
/// <remarks>An Udi can be fully qualified or "closed" eg umb://document/{guid} or "open" eg umb://document.</remarks>
|
||||
[TypeConverter(typeof(UdiTypeConverter))]
|
||||
public abstract class Udi : IComparable<Udi>
|
||||
{
|
||||
private static readonly Dictionary<string, UdiType> UdiTypes = new Dictionary<string, UdiType>();
|
||||
private static readonly Lazy<Dictionary<string, UdiType>> UdiTypes;
|
||||
private static readonly ConcurrentDictionary<string, Udi> RootUdis = new ConcurrentDictionary<string, Udi>();
|
||||
internal readonly Uri UriValue; // internal for UdiRange
|
||||
|
||||
@@ -39,32 +42,50 @@ namespace Umbraco.Core
|
||||
|
||||
static Udi()
|
||||
{
|
||||
// for tests etc.
|
||||
UdiTypes[Constants.DeployEntityType.AnyGuid] = UdiType.GuidUdi;
|
||||
UdiTypes[Constants.DeployEntityType.AnyString] = UdiType.StringUdi;
|
||||
|
||||
// we don't have connectors for these...
|
||||
UdiTypes[Constants.DeployEntityType.Member] = UdiType.GuidUdi;
|
||||
UdiTypes[Constants.DeployEntityType.MemberGroup] = UdiType.GuidUdi;
|
||||
|
||||
// fixme - or inject from...?
|
||||
// there is no way we can get the "registered" service connectors, as registration
|
||||
// happens in Deploy, not in Core, and the Udi class belongs to Core - therefore, we
|
||||
// just pick every service connectors - just making sure that not two of them
|
||||
// would register the same entity type, with different udi types (would not make
|
||||
// much sense anyways).
|
||||
var connectors = PluginManager.Current.ResolveTypes<IServiceConnector>();
|
||||
foreach (var connector in connectors)
|
||||
UdiTypes = new Lazy<Dictionary<string, UdiType>>(() =>
|
||||
{
|
||||
var attrs = connector.GetCustomAttributes<UdiDefinitionAttribute>(false);
|
||||
foreach (var attr in attrs)
|
||||
var result = new Dictionary<string, UdiType>();
|
||||
|
||||
// known types:
|
||||
foreach (var fi in typeof(Constants.UdiEntityType).GetFields(BindingFlags.Public | BindingFlags.Static))
|
||||
{
|
||||
UdiType udiType;
|
||||
if (UdiTypes.TryGetValue(attr.EntityType, out udiType) && udiType != attr.UdiType)
|
||||
throw new Exception(string.Format("Entity type \"{0}\" is declared by more than one IServiceConnector, with different UdiTypes.", attr.EntityType));
|
||||
UdiTypes[attr.EntityType] = attr.UdiType;
|
||||
// IsLiteral determines if its value is written at
|
||||
// compile time and not changeable
|
||||
// IsInitOnly determine if the field can be set
|
||||
// in the body of the constructor
|
||||
// for C# a field which is readonly keyword would have both true
|
||||
// but a const field would have only IsLiteral equal to true
|
||||
if (fi.IsLiteral && fi.IsInitOnly == false)
|
||||
{
|
||||
var udiType = fi.GetCustomAttribute<Constants.UdiTypeAttribute>();
|
||||
|
||||
if (udiType == null)
|
||||
throw new InvalidOperationException("All Constants listed in UdiEntityType must be attributed with " + typeof(Constants.UdiTypeAttribute));
|
||||
result[fi.GetValue(null).ToString()] = udiType.UdiType;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Scan for unknown UDI types
|
||||
// there is no way we can get the "registered" service connectors, as registration
|
||||
// happens in Deploy, not in Core, and the Udi class belongs to Core - therefore, we
|
||||
// just pick every service connectors - just making sure that not two of them
|
||||
// would register the same entity type, with different udi types (would not make
|
||||
// much sense anyways).
|
||||
var connectors = PluginManager.Current.ResolveTypes<IServiceConnector>();
|
||||
foreach (var connector in connectors)
|
||||
{
|
||||
var attrs = connector.GetCustomAttributes<UdiDefinitionAttribute>(false);
|
||||
foreach (var attr in attrs)
|
||||
{
|
||||
UdiType udiType;
|
||||
if (result.TryGetValue(attr.EntityType, out udiType) && udiType != attr.UdiType)
|
||||
throw new Exception(string.Format("Entity type \"{0}\" is declared by more than one IServiceConnector, with different UdiTypes.", attr.EntityType));
|
||||
result[attr.EntityType] = attr.UdiType;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -105,8 +126,8 @@ namespace Umbraco.Core
|
||||
udi = null;
|
||||
Uri uri;
|
||||
|
||||
if (!Uri.IsWellFormedUriString(s, UriKind.Absolute)
|
||||
|| !Uri.TryCreate(s, UriKind.Absolute, out uri))
|
||||
if (Uri.IsWellFormedUriString(s, UriKind.Absolute) == false
|
||||
|| Uri.TryCreate(s, UriKind.Absolute, out uri) == false)
|
||||
{
|
||||
if (tryParse) return false;
|
||||
throw new FormatException(string.Format("String \"{0}\" is not a valid udi.", s));
|
||||
@@ -114,7 +135,7 @@ namespace Umbraco.Core
|
||||
|
||||
var entityType = uri.Host;
|
||||
UdiType udiType;
|
||||
if (!UdiTypes.TryGetValue(entityType, out udiType))
|
||||
if (UdiTypes.Value.TryGetValue(entityType, out udiType) == false)
|
||||
{
|
||||
if (tryParse) return false;
|
||||
throw new FormatException(string.Format("Unknown entity type \"{0}\".", entityType));
|
||||
@@ -128,7 +149,7 @@ namespace Umbraco.Core
|
||||
return true;
|
||||
}
|
||||
Guid guid;
|
||||
if (!Guid.TryParse(path, out guid))
|
||||
if (Guid.TryParse(path, out guid) == false)
|
||||
{
|
||||
if (tryParse) return false;
|
||||
throw new FormatException(string.Format("String \"{0}\" is not a valid udi.", s));
|
||||
@@ -150,7 +171,7 @@ namespace Umbraco.Core
|
||||
return RootUdis.GetOrAdd(entityType, x =>
|
||||
{
|
||||
UdiType udiType;
|
||||
if (!UdiTypes.TryGetValue(x, out udiType))
|
||||
if (UdiTypes.Value.TryGetValue(x, out udiType) == false)
|
||||
throw new ArgumentException(string.Format("Unknown entity type \"{0}\".", entityType));
|
||||
return udiType == UdiType.StringUdi
|
||||
? (Udi)new StringUdi(entityType, string.Empty)
|
||||
@@ -177,7 +198,7 @@ namespace Umbraco.Core
|
||||
public static Udi Create(string entityType, string id)
|
||||
{
|
||||
UdiType udiType;
|
||||
if (!UdiTypes.TryGetValue(entityType, out udiType))
|
||||
if (UdiTypes.Value.TryGetValue(entityType, out udiType) == false)
|
||||
throw new ArgumentException(string.Format("Unknown entity type \"{0}\".", entityType), "entityType");
|
||||
if (string.IsNullOrWhiteSpace(id))
|
||||
throw new ArgumentException("Value cannot be null or whitespace.", "id");
|
||||
@@ -196,7 +217,7 @@ namespace Umbraco.Core
|
||||
public static Udi Create(string entityType, Guid id)
|
||||
{
|
||||
UdiType udiType;
|
||||
if (!UdiTypes.TryGetValue(entityType, out udiType))
|
||||
if (UdiTypes.Value.TryGetValue(entityType, out udiType) == false)
|
||||
throw new ArgumentException(string.Format("Unknown entity type \"{0}\".", entityType), "entityType");
|
||||
if (udiType != UdiType.GuidUdi)
|
||||
throw new InvalidOperationException(string.Format("Entity type \"{0}\" does not have guid udis.", entityType));
|
||||
@@ -208,7 +229,7 @@ namespace Umbraco.Core
|
||||
internal static Udi Create(Uri uri)
|
||||
{
|
||||
UdiType udiType;
|
||||
if (!UdiTypes.TryGetValue(uri.Host, out udiType))
|
||||
if (UdiTypes.Value.TryGetValue(uri.Host, out udiType) == false)
|
||||
throw new ArgumentException(string.Format("Unknown entity type \"{0}\".", uri.Host), "uri");
|
||||
if (udiType == UdiType.GuidUdi)
|
||||
return new GuidUdi(uri);
|
||||
@@ -219,7 +240,7 @@ namespace Umbraco.Core
|
||||
|
||||
public void EnsureType(params string[] validTypes)
|
||||
{
|
||||
if (!validTypes.Contains(EntityType))
|
||||
if (validTypes.Contains(EntityType) == false)
|
||||
throw new Exception(string.Format("Unexpected entity type \"{0}\".", EntityType));
|
||||
}
|
||||
|
||||
@@ -260,7 +281,7 @@ namespace Umbraco.Core
|
||||
|
||||
public static bool operator !=(Udi udi1, Udi udi2)
|
||||
{
|
||||
return !(udi1 == udi2);
|
||||
return (udi1 == udi2) == false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+52
-13
@@ -7,52 +7,82 @@ namespace Umbraco.Core
|
||||
|
||||
public static partial class Constants
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// Defines well-known entity types.
|
||||
/// </summary>
|
||||
/// <remarks>Well-known entity types are those that Deploy already knows about,
|
||||
/// but entity types are strings and so can be extended beyond what is defined here.</remarks>
|
||||
public static class DeployEntityType
|
||||
public static class UdiEntityType
|
||||
{
|
||||
[UdiType(UdiType.Unknown)]
|
||||
public const string Unknown = "unknown";
|
||||
|
||||
// guid entity types
|
||||
|
||||
[UdiType(UdiType.GuidUdi)]
|
||||
public const string AnyGuid = "any-guid"; // that one is for tests
|
||||
|
||||
[UdiType(UdiType.GuidUdi)]
|
||||
public const string Document = "document";
|
||||
[UdiType(UdiType.GuidUdi)]
|
||||
public const string Media = "media";
|
||||
[UdiType(UdiType.GuidUdi)]
|
||||
public const string Member = "member";
|
||||
|
||||
[UdiType(UdiType.GuidUdi)]
|
||||
public const string DictionaryItem = "dictionary-item";
|
||||
[UdiType(UdiType.GuidUdi)]
|
||||
public const string Macro = "macro";
|
||||
[UdiType(UdiType.GuidUdi)]
|
||||
public const string Template = "template";
|
||||
|
||||
[UdiType(UdiType.GuidUdi)]
|
||||
public const string DocumentType = "document-type";
|
||||
[UdiType(UdiType.GuidUdi)]
|
||||
public const string DocumentTypeContainer = "document-type-container";
|
||||
[UdiType(UdiType.GuidUdi)]
|
||||
public const string MediaType = "media-type";
|
||||
[UdiType(UdiType.GuidUdi)]
|
||||
public const string MediaTypeContainer = "media-type-container";
|
||||
[UdiType(UdiType.GuidUdi)]
|
||||
public const string DataType = "data-type";
|
||||
[UdiType(UdiType.GuidUdi)]
|
||||
public const string DataTypeContainer = "data-type-container";
|
||||
[UdiType(UdiType.GuidUdi)]
|
||||
public const string MemberType = "member-type";
|
||||
[UdiType(UdiType.GuidUdi)]
|
||||
public const string MemberGroup = "member-group";
|
||||
|
||||
[UdiType(UdiType.GuidUdi)]
|
||||
public const string RelationType = "relation-type";
|
||||
|
||||
|
||||
// forms
|
||||
|
||||
[UdiType(UdiType.GuidUdi)]
|
||||
public const string FormsForm = "forms-form";
|
||||
public const string FormsWorkflow = "forms-workflow";
|
||||
public const string FormsRecord = "forms-record";
|
||||
[UdiType(UdiType.GuidUdi)]
|
||||
public const string FormsPreValue = "forms-prevalue";
|
||||
[UdiType(UdiType.GuidUdi)]
|
||||
public const string FormsDataSource = "forms-datasource";
|
||||
|
||||
// string entity types
|
||||
|
||||
[UdiType(UdiType.StringUdi)]
|
||||
public const string AnyString = "any-string"; // that one is for tests
|
||||
|
||||
[UdiType(UdiType.StringUdi)]
|
||||
public const string MediaFile = "media-file";
|
||||
[UdiType(UdiType.StringUdi)]
|
||||
public const string TemplateFile = "template-file";
|
||||
[UdiType(UdiType.StringUdi)]
|
||||
public const string Script = "script";
|
||||
[UdiType(UdiType.StringUdi)]
|
||||
public const string Stylesheet = "stylesheet";
|
||||
[UdiType(UdiType.StringUdi)]
|
||||
public const string PartialView = "partial-view";
|
||||
[UdiType(UdiType.StringUdi)]
|
||||
public const string PartialViewMacro = "partial-view-macro";
|
||||
[UdiType(UdiType.StringUdi)]
|
||||
public const string Xslt = "xslt";
|
||||
|
||||
public static string FromUmbracoObjectType(UmbracoObjectTypes umbracoObjectType)
|
||||
@@ -89,10 +119,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,16 +161,25 @@ 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));
|
||||
}
|
||||
}
|
||||
|
||||
[AttributeUsage(AttributeTargets.Field)]
|
||||
internal class UdiTypeAttribute : Attribute
|
||||
{
|
||||
public UdiType UdiType { get; private set; }
|
||||
|
||||
public UdiTypeAttribute(UdiType udiType)
|
||||
{
|
||||
UdiType = udiType;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -17,7 +17,7 @@ namespace Umbraco.Core
|
||||
public static GuidUdi GetUdi(this ITemplate entity)
|
||||
{
|
||||
if (entity == null) throw new ArgumentNullException("entity");
|
||||
return new GuidUdi(Constants.DeployEntityType.Template, entity.Key).EnsureClosed();
|
||||
return new GuidUdi(Constants.UdiEntityType.Template, entity.Key).EnsureClosed();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -28,7 +28,7 @@ namespace Umbraco.Core
|
||||
public static GuidUdi GetUdi(this IContentType entity)
|
||||
{
|
||||
if (entity == null) throw new ArgumentNullException("entity");
|
||||
return new GuidUdi(Constants.DeployEntityType.DocumentType, entity.Key).EnsureClosed();
|
||||
return new GuidUdi(Constants.UdiEntityType.DocumentType, entity.Key).EnsureClosed();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -39,7 +39,7 @@ namespace Umbraco.Core
|
||||
public static GuidUdi GetUdi(this IMediaType entity)
|
||||
{
|
||||
if (entity == null) throw new ArgumentNullException("entity");
|
||||
return new GuidUdi(Constants.DeployEntityType.MediaType, entity.Key).EnsureClosed();
|
||||
return new GuidUdi(Constants.UdiEntityType.MediaType, entity.Key).EnsureClosed();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -50,7 +50,7 @@ namespace Umbraco.Core
|
||||
public static GuidUdi GetUdi(this IMemberType entity)
|
||||
{
|
||||
if (entity == null) throw new ArgumentNullException("entity");
|
||||
return new GuidUdi(Constants.DeployEntityType.MemberType, entity.Key).EnsureClosed();
|
||||
return new GuidUdi(Constants.UdiEntityType.MemberType, entity.Key).EnsureClosed();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -61,7 +61,7 @@ namespace Umbraco.Core
|
||||
public static GuidUdi GetUdi(this IMemberGroup entity)
|
||||
{
|
||||
if (entity == null) throw new ArgumentNullException("entity");
|
||||
return new GuidUdi(Constants.DeployEntityType.MemberGroup, entity.Key).EnsureClosed();
|
||||
return new GuidUdi(Constants.UdiEntityType.MemberGroup, entity.Key).EnsureClosed();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -74,9 +74,9 @@ namespace Umbraco.Core
|
||||
if (entity == null) throw new ArgumentNullException("entity");
|
||||
|
||||
string type;
|
||||
if (entity is IContentType) type = Constants.DeployEntityType.DocumentType;
|
||||
else if (entity is IMediaType) type = Constants.DeployEntityType.MediaType;
|
||||
else if (entity is IMemberType) type = Constants.DeployEntityType.MemberType;
|
||||
if (entity is IContentType) type = Constants.UdiEntityType.DocumentType;
|
||||
else if (entity is IMediaType) type = Constants.UdiEntityType.MediaType;
|
||||
else if (entity is IMemberType) type = Constants.UdiEntityType.MemberType;
|
||||
else throw new NotSupportedException(string.Format("Composition type {0} is not supported.", entity.GetType().FullName));
|
||||
return new GuidUdi(type, entity.Key).EnsureClosed();
|
||||
}
|
||||
@@ -89,7 +89,7 @@ namespace Umbraco.Core
|
||||
public static GuidUdi GetUdi(this IDataTypeDefinition entity)
|
||||
{
|
||||
if (entity == null) throw new ArgumentNullException("entity");
|
||||
return new GuidUdi(Constants.DeployEntityType.DataType, entity.Key).EnsureClosed();
|
||||
return new GuidUdi(Constants.UdiEntityType.DataType, entity.Key).EnsureClosed();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -103,11 +103,11 @@ namespace Umbraco.Core
|
||||
|
||||
string entityType;
|
||||
if (entity.ContainedObjectType == Constants.ObjectTypes.DataTypeGuid)
|
||||
entityType = Constants.DeployEntityType.DataTypeContainer;
|
||||
entityType = Constants.UdiEntityType.DataTypeContainer;
|
||||
else if (entity.ContainedObjectType == Constants.ObjectTypes.DocumentTypeGuid)
|
||||
entityType = Constants.DeployEntityType.DocumentTypeContainer;
|
||||
entityType = Constants.UdiEntityType.DocumentTypeContainer;
|
||||
else if (entity.ContainedObjectType == Constants.ObjectTypes.MediaTypeGuid)
|
||||
entityType = Constants.DeployEntityType.MediaTypeContainer;
|
||||
entityType = Constants.UdiEntityType.MediaTypeContainer;
|
||||
else
|
||||
throw new NotSupportedException(string.Format("Contained object type {0} is not supported.", entity.ContainedObjectType));
|
||||
return new GuidUdi(entityType, entity.Key).EnsureClosed();
|
||||
@@ -121,7 +121,7 @@ namespace Umbraco.Core
|
||||
public static GuidUdi GetUdi(this IMedia entity)
|
||||
{
|
||||
if (entity == null) throw new ArgumentNullException("entity");
|
||||
return new GuidUdi(Constants.DeployEntityType.Media, entity.Key).EnsureClosed();
|
||||
return new GuidUdi(Constants.UdiEntityType.Media, entity.Key).EnsureClosed();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -132,7 +132,7 @@ namespace Umbraco.Core
|
||||
public static GuidUdi GetUdi(this IContent entity)
|
||||
{
|
||||
if (entity == null) throw new ArgumentNullException("entity");
|
||||
return new GuidUdi(Constants.DeployEntityType.Document, entity.Key).EnsureClosed();
|
||||
return new GuidUdi(Constants.UdiEntityType.Document, entity.Key).EnsureClosed();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -143,7 +143,7 @@ namespace Umbraco.Core
|
||||
public static GuidUdi GetUdi(this IMember entity)
|
||||
{
|
||||
if (entity == null) throw new ArgumentNullException("entity");
|
||||
return new GuidUdi(Constants.DeployEntityType.Member, entity.Key).EnsureClosed();
|
||||
return new GuidUdi(Constants.UdiEntityType.Member, entity.Key).EnsureClosed();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -154,7 +154,7 @@ namespace Umbraco.Core
|
||||
public static StringUdi GetUdi(this Stylesheet entity)
|
||||
{
|
||||
if (entity == null) throw new ArgumentNullException("entity");
|
||||
return new StringUdi(Constants.DeployEntityType.Stylesheet, entity.Path.TrimStart('/')).EnsureClosed();
|
||||
return new StringUdi(Constants.UdiEntityType.Stylesheet, entity.Path.TrimStart('/')).EnsureClosed();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -165,7 +165,7 @@ namespace Umbraco.Core
|
||||
public static StringUdi GetUdi(this Script entity)
|
||||
{
|
||||
if (entity == null) throw new ArgumentNullException("entity");
|
||||
return new StringUdi(Constants.DeployEntityType.Script, entity.Path.TrimStart('/')).EnsureClosed();
|
||||
return new StringUdi(Constants.UdiEntityType.Script, entity.Path.TrimStart('/')).EnsureClosed();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -176,7 +176,7 @@ namespace Umbraco.Core
|
||||
public static GuidUdi GetUdi(this IDictionaryItem entity)
|
||||
{
|
||||
if (entity == null) throw new ArgumentNullException("entity");
|
||||
return new GuidUdi(Constants.DeployEntityType.DictionaryItem, entity.Key).EnsureClosed();
|
||||
return new GuidUdi(Constants.UdiEntityType.DictionaryItem, entity.Key).EnsureClosed();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -187,7 +187,7 @@ namespace Umbraco.Core
|
||||
public static GuidUdi GetUdi(this IMacro entity)
|
||||
{
|
||||
if (entity == null) throw new ArgumentNullException("entity");
|
||||
return new GuidUdi(Constants.DeployEntityType.Macro, entity.Key).EnsureClosed();
|
||||
return new GuidUdi(Constants.UdiEntityType.Macro, entity.Key).EnsureClosed();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -198,7 +198,7 @@ namespace Umbraco.Core
|
||||
public static StringUdi GetUdi(this IPartialView entity)
|
||||
{
|
||||
if (entity == null) throw new ArgumentNullException("entity");
|
||||
return new StringUdi(Constants.DeployEntityType.PartialView, entity.Path.TrimStart('/')).EnsureClosed();
|
||||
return new StringUdi(Constants.UdiEntityType.PartialView, entity.Path.TrimStart('/')).EnsureClosed();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -209,7 +209,7 @@ namespace Umbraco.Core
|
||||
public static StringUdi GetUdi(this IXsltFile entity)
|
||||
{
|
||||
if (entity == null) throw new ArgumentNullException("entity");
|
||||
return new StringUdi(Constants.DeployEntityType.Xslt, entity.Path.TrimStart('/')).EnsureClosed();
|
||||
return new StringUdi(Constants.UdiEntityType.Xslt, entity.Path.TrimStart('/')).EnsureClosed();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -222,9 +222,9 @@ namespace Umbraco.Core
|
||||
if (entity == null) throw new ArgumentNullException("entity");
|
||||
|
||||
string type;
|
||||
if (entity is IContent) type = Constants.DeployEntityType.Document;
|
||||
else if (entity is IMedia) type = Constants.DeployEntityType.Media;
|
||||
else if (entity is IMember) type = Constants.DeployEntityType.Member;
|
||||
if (entity is IContent) type = Constants.UdiEntityType.Document;
|
||||
else if (entity is IMedia) type = Constants.UdiEntityType.Media;
|
||||
else if (entity is IMember) type = Constants.UdiEntityType.Member;
|
||||
else throw new NotSupportedException(string.Format("ContentBase type {0} is not supported.", entity.GetType().FullName));
|
||||
return new GuidUdi(type, entity.Key).EnsureClosed();
|
||||
}
|
||||
@@ -237,7 +237,7 @@ namespace Umbraco.Core
|
||||
public static GuidUdi GetUdi(this IRelationType entity)
|
||||
{
|
||||
if (entity == null) throw new ArgumentNullException("entity");
|
||||
return new GuidUdi(Constants.DeployEntityType.RelationType, entity.Key).EnsureClosed();
|
||||
return new GuidUdi(Constants.UdiEntityType.RelationType, entity.Key).EnsureClosed();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -285,18 +285,30 @@ namespace Umbraco.Core
|
||||
var member = entity as IMember;
|
||||
if (member != null) return member.GetUdi();
|
||||
|
||||
var contentBase = entity as IContentBase;
|
||||
if (contentBase != null) return contentBase.GetUdi();
|
||||
var stylesheet = entity as Stylesheet;
|
||||
if (stylesheet != null) return stylesheet.GetUdi();
|
||||
|
||||
var script = entity as Script;
|
||||
if (script != null) return script.GetUdi();
|
||||
|
||||
var dictionaryItem = entity as IDictionaryItem;
|
||||
if (dictionaryItem != null) return dictionaryItem.GetUdi();
|
||||
|
||||
var macro = entity as IMacro;
|
||||
if (macro != null) return macro.GetUdi();
|
||||
|
||||
var partialView = entity as IPartialView;
|
||||
if (partialView != null) return partialView.GetUdi();
|
||||
|
||||
var xsltFile = entity as IXsltFile;
|
||||
if (xsltFile != null) return xsltFile.GetUdi();
|
||||
|
||||
var contentBase = entity as IContentBase;
|
||||
if (contentBase != null) return contentBase.GetUdi();
|
||||
|
||||
var relationType = entity as IRelationType;
|
||||
if (relationType != null) return relationType.GetUdi();
|
||||
|
||||
var dictionaryItem = entity as IDictionaryItem;
|
||||
if (dictionaryItem != null) return dictionaryItem.GetUdi();
|
||||
|
||||
throw new NotSupportedException(string.Format("Entity type {0} is not supported.", entity.GetType().FullName));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
using System;
|
||||
using System.ComponentModel;
|
||||
using System.Globalization;
|
||||
|
||||
namespace Umbraco.Core
|
||||
{
|
||||
/// <summary>
|
||||
/// A custom type converter for UDI
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Primarily this is used so that WebApi can auto-bind a string parameter to a UDI instance
|
||||
/// </remarks>
|
||||
internal class UdiTypeConverter : TypeConverter
|
||||
{
|
||||
public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
|
||||
{
|
||||
if (sourceType == typeof(string))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
return base.CanConvertFrom(context, sourceType);
|
||||
}
|
||||
|
||||
public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
|
||||
{
|
||||
if (value is string)
|
||||
{
|
||||
Udi udi;
|
||||
if (Udi.TryParse((string)value, out udi))
|
||||
{
|
||||
return udi;
|
||||
}
|
||||
}
|
||||
return base.ConvertFrom(context, culture, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -163,6 +163,7 @@
|
||||
<Compile Include="Cache\DeepCloneRuntimeCacheProvider.cs" />
|
||||
<Compile Include="CodeAnnotations\FriendlyNameAttribute.cs" />
|
||||
<Compile Include="CodeAnnotations\UmbracoObjectTypeAttribute.cs" />
|
||||
<Compile Include="CodeAnnotations\UmbracoUdiTypeAttribute.cs" />
|
||||
<Compile Include="CodeAnnotations\UmbracoWillObsoleteAttribute.cs" />
|
||||
<Compile Include="CodeAnnotations\UmbracoExperimentalFeatureAttribute.cs" />
|
||||
<Compile Include="CodeAnnotations\UmbracoProposedPublicAttribute.cs" />
|
||||
@@ -289,7 +290,6 @@
|
||||
<Compile Include="Configuration\UmbracoSettings\WebRoutingElement.cs" />
|
||||
<Compile Include="Configuration\UmbracoVersion.cs" />
|
||||
<Compile Include="Attempt.cs" />
|
||||
<Compile Include="Constants-DeployEntityType.cs" />
|
||||
<Compile Include="Constants-DeploySelector.cs" />
|
||||
<Compile Include="Constants-Examine.cs" />
|
||||
<Compile Include="Constants-Icons.cs" />
|
||||
@@ -311,9 +311,15 @@
|
||||
<Compile Include="Deploy\ArtifactSignature.cs" />
|
||||
<Compile Include="Deploy\Difference.cs" />
|
||||
<Compile Include="Deploy\Direction.cs" />
|
||||
<Compile Include="Deploy\IFileStore.cs" />
|
||||
<Compile Include="Deploy\IFileType.cs" />
|
||||
<Compile Include="Deploy\IFileTypeCollection.cs" />
|
||||
<Compile Include="Events\EventDefinitionFilter.cs" />
|
||||
<Compile Include="Events\IDeletingMediaFilesEventArgs.cs" />
|
||||
<Compile Include="Events\ScopeEventDispatcherBase.cs" />
|
||||
<Compile Include="Events\ScopeLifespanMessagesFactory.cs" />
|
||||
<Compile Include="IHttpContextAccessor.cs" />
|
||||
<Compile Include="OrderedHashSet.cs" />
|
||||
<Compile Include="Events\UninstallPackageEventArgs.cs" />
|
||||
<Compile Include="Models\GridValue.cs" />
|
||||
<Compile Include="Deploy\IArtifact.cs" />
|
||||
<Compile Include="Deploy\IArtifactSignature.cs" />
|
||||
@@ -323,7 +329,6 @@
|
||||
<Compile Include="Deploy\IImageSourceParser.cs" />
|
||||
<Compile Include="Deploy\ILocalLinkParser.cs" />
|
||||
<Compile Include="Deploy\IMacroParser.cs" />
|
||||
<Compile Include="Deploy\IPrettyArtifact.cs" />
|
||||
<Compile Include="Deploy\IPreValueConnector.cs" />
|
||||
<Compile Include="Deploy\IServiceConnector.cs" />
|
||||
<Compile Include="Deploy\IValueConnector.cs" />
|
||||
@@ -361,7 +366,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" />
|
||||
@@ -450,6 +454,7 @@
|
||||
<Compile Include="Models\DoNotCloneAttribute.cs" />
|
||||
<Compile Include="Models\IDomain.cs" />
|
||||
<Compile Include="NamedUdiRange.cs" />
|
||||
<Compile Include="Packaging\Models\UninstallationSummary.cs" />
|
||||
<Compile Include="Persistence\BulkDataReader.cs" />
|
||||
<Compile Include="Persistence\Constants-Locks.cs" />
|
||||
<Compile Include="Persistence\DatabaseNodeLockExtensions.cs" />
|
||||
@@ -479,7 +484,10 @@
|
||||
<Compile Include="Persistence\Migrations\Upgrades\TargetVersionSevenFourZero\AddUniqueIdPropertyTypeGroupColumn.cs" />
|
||||
<Compile Include="Persistence\Migrations\Upgrades\TargetVersionSevenFourZero\RemoveParentIdPropertyTypeGroupColumn.cs" />
|
||||
<Compile Include="Persistence\Mappers\TaskTypeMapper.cs" />
|
||||
<Compile Include="Persistence\Migrations\Upgrades\TargetVersionSevenSixZero\AddIndexToCmsMemberLoginName.cs" />
|
||||
<Compile Include="Persistence\Migrations\Upgrades\TargetVersionSevenSixZero\AddIndexesToUmbracoRelationTables.cs" />
|
||||
<Compile Include="Persistence\Migrations\Upgrades\TargetVersionSevenSixZero\AddIndexToUmbracoNodePath.cs" />
|
||||
<Compile Include="Persistence\Migrations\Upgrades\TargetVersionSevenSixZero\AddIndexToUser2NodePermission.cs" />
|
||||
<Compile Include="Persistence\Migrations\Upgrades\TargetVersionSevenSixZero\AddRelationTypeUniqueIdColumn.cs" />
|
||||
<Compile Include="Persistence\Migrations\Upgrades\TargetVersionSevenSixZero\AddMacroUniqueIdColumn.cs" />
|
||||
<Compile Include="Persistence\Migrations\Upgrades\TargetVersionSevenSixZero\RemovePropertyDataIdIndex.cs" />
|
||||
@@ -558,7 +566,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" />
|
||||
@@ -1435,9 +1443,11 @@
|
||||
<Compile Include="TypeHelper.cs" />
|
||||
<Compile Include="Udi.cs" />
|
||||
<Compile Include="UdiDefinitionAttribute.cs" />
|
||||
<Compile Include="UdiEntityType.cs" />
|
||||
<Compile Include="UdiGetterExtensions.cs" />
|
||||
<Compile Include="UdiRange.cs" />
|
||||
<Compile Include="UdiType.cs" />
|
||||
<Compile Include="UdiTypeConverter.cs" />
|
||||
<Compile Include="UmbracoApplicationBase.cs" />
|
||||
<Compile Include="Constants.cs" />
|
||||
<Compile Include="Constants-Applications.cs">
|
||||
|
||||
@@ -56,5 +56,14 @@ namespace Umbraco.Tests.IO
|
||||
Assert.AreEqual(IOHelper.MapPath(SystemDirectories.WebServices, true), IOHelper.MapPath(SystemDirectories.WebServices, false));
|
||||
Assert.AreEqual(IOHelper.MapPath(SystemDirectories.Xslt, true), IOHelper.MapPath(SystemDirectories.Xslt, false));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void EnsurePathIsApplicationRootPrefixed()
|
||||
{
|
||||
//Assert
|
||||
Assert.AreEqual("~/Views/Template.cshtml", IOHelper.EnsurePathIsApplicationRootPrefixed("Views/Template.cshtml"));
|
||||
Assert.AreEqual("~/Views/Template.cshtml", IOHelper.EnsurePathIsApplicationRootPrefixed("/Views/Template.cshtml"));
|
||||
Assert.AreEqual("~/Views/Template.cshtml", IOHelper.EnsurePathIsApplicationRootPrefixed("~/Views/Template.cshtml"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -336,8 +336,9 @@ namespace Umbraco.Tests.IO
|
||||
using (var ms = new MemoryStream(Encoding.UTF8.GetBytes("foo")))
|
||||
ss.AddFile("path/to/some/dir/f1.txt", ms);
|
||||
|
||||
// file is only written to the shadow fs
|
||||
Assert.IsTrue(File.Exists(path + "/ShadowSystem/path/to/some/dir/f1.txt"));
|
||||
|
||||
Assert.IsFalse(File.Exists(path + "/ShadowTests/path/to/some/dir/f1.txt"));
|
||||
// let the shadow fs die
|
||||
}
|
||||
|
||||
@@ -376,6 +377,7 @@ namespace Umbraco.Tests.IO
|
||||
var path = IOHelper.MapPath("FileSysTests");
|
||||
var appdata = IOHelper.MapPath("App_Data");
|
||||
Directory.CreateDirectory(path);
|
||||
Directory.CreateDirectory(appdata);
|
||||
|
||||
var scopedFileSystems = false;
|
||||
var scopeProvider = MockScopeProvider(() => scopedFileSystems);
|
||||
@@ -392,47 +394,47 @@ namespace Umbraco.Tests.IO
|
||||
|
||||
// explicit shadow without scope does not work
|
||||
sw.Shadow(id = Guid.NewGuid());
|
||||
Assert.IsTrue(Directory.Exists(appdata + "/Shadow/" + id));
|
||||
Assert.IsTrue(Directory.Exists(appdata + "/TEMP/ShadowFs/" + id));
|
||||
using (var ms = new MemoryStream(Encoding.UTF8.GetBytes("foo")))
|
||||
sw.AddFile("sub/f2.txt", ms);
|
||||
Assert.IsTrue(fs.FileExists("sub/f2.txt"));
|
||||
sw.UnShadow(true);
|
||||
Assert.IsTrue(fs.FileExists("sub/f2.txt"));
|
||||
Assert.IsFalse(Directory.Exists(appdata + "/Shadow/" + id));
|
||||
Assert.IsFalse(Directory.Exists(appdata + "/TEMP/ShadowFs/" + id));
|
||||
|
||||
// shadow with scope but no complete does not complete
|
||||
scopedFileSystems = true; // pretend we have a scope
|
||||
var scope = new ShadowFileSystems(id = Guid.NewGuid(), swa);
|
||||
Assert.IsTrue(Directory.Exists(appdata + "/Shadow/" + id));
|
||||
Assert.IsTrue(Directory.Exists(appdata + "/TEMP/ShadowFs/" + id));
|
||||
using (var ms = new MemoryStream(Encoding.UTF8.GetBytes("foo")))
|
||||
sw.AddFile("sub/f3.txt", ms);
|
||||
Assert.IsFalse(fs.FileExists("sub/f3.txt"));
|
||||
Assert.AreEqual(1, Directory.GetDirectories(appdata + "/Shadow").Length);
|
||||
Assert.AreEqual(1, Directory.GetDirectories(appdata + "/TEMP/ShadowFs").Length);
|
||||
scope.Dispose();
|
||||
scopedFileSystems = false;
|
||||
Assert.IsFalse(fs.FileExists("sub/f3.txt"));
|
||||
Assert.IsFalse(Directory.Exists(appdata + "/Shadow/" + id));
|
||||
Assert.IsFalse(Directory.Exists(appdata + "/TEMP/ShadowFs/" + id));
|
||||
|
||||
// shadow with scope and complete does complete
|
||||
scopedFileSystems = true; // pretend we have a scope
|
||||
scope = new ShadowFileSystems(id = Guid.NewGuid(), swa);
|
||||
Assert.IsTrue(Directory.Exists(appdata + "/Shadow/" + id));
|
||||
Assert.IsTrue(Directory.Exists(appdata + "/TEMP/ShadowFs/" + id));
|
||||
using (var ms = new MemoryStream(Encoding.UTF8.GetBytes("foo")))
|
||||
sw.AddFile("sub/f4.txt", ms);
|
||||
Assert.IsFalse(fs.FileExists("sub/f4.txt"));
|
||||
Assert.AreEqual(1, Directory.GetDirectories(appdata + "/Shadow").Length);
|
||||
Assert.AreEqual(1, Directory.GetDirectories(appdata + "/TEMP/ShadowFs").Length);
|
||||
scope.Complete();
|
||||
scope.Dispose();
|
||||
scopedFileSystems = false;
|
||||
TestHelper.TryAssert(() => Assert.AreEqual(0, Directory.GetDirectories(appdata + "/Shadow").Length));
|
||||
TestHelper.TryAssert(() => Assert.AreEqual(0, Directory.GetDirectories(appdata + "/TEMP/ShadowFs").Length));
|
||||
Assert.IsTrue(fs.FileExists("sub/f4.txt"));
|
||||
Assert.IsFalse(Directory.Exists(appdata + "/Shadow/" + id));
|
||||
Assert.IsFalse(Directory.Exists(appdata + "/TEMP/ShadowFs/" + id));
|
||||
|
||||
// test scope for "another thread"
|
||||
|
||||
scopedFileSystems = true; // pretend we have a scope
|
||||
scope = new ShadowFileSystems(id = Guid.NewGuid(), swa);
|
||||
Assert.IsTrue(Directory.Exists(appdata + "/Shadow/" + id));
|
||||
Assert.IsTrue(Directory.Exists(appdata + "/TEMP/ShadowFs/" + id));
|
||||
using (var ms = new MemoryStream(Encoding.UTF8.GetBytes("foo")))
|
||||
sw.AddFile("sub/f5.txt", ms);
|
||||
Assert.IsFalse(fs.FileExists("sub/f5.txt"));
|
||||
@@ -448,7 +450,7 @@ namespace Umbraco.Tests.IO
|
||||
scope.Dispose();
|
||||
scopedFileSystems = false;
|
||||
Assert.IsTrue(fs.FileExists("sub/f5.txt"));
|
||||
Assert.IsFalse(Directory.Exists(appdata + "/Shadow/" + id));
|
||||
Assert.IsFalse(Directory.Exists(appdata + "/TEMP/ShadowFs/" + id));
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -473,7 +475,7 @@ namespace Umbraco.Tests.IO
|
||||
|
||||
scopedFileSystems = true; // pretend we have a scope
|
||||
var scope = new ShadowFileSystems(id = Guid.NewGuid(), swa);
|
||||
Assert.IsTrue(Directory.Exists(appdata + "/Shadow/" + id));
|
||||
Assert.IsTrue(Directory.Exists(appdata + "/TEMP/ShadowFs/" + id));
|
||||
using (var ms = new MemoryStream(Encoding.UTF8.GetBytes("foo")))
|
||||
sw.AddFile("sub/f2.txt", ms);
|
||||
Assert.IsFalse(fs.FileExists("sub/f2.txt"));
|
||||
@@ -489,7 +491,7 @@ namespace Umbraco.Tests.IO
|
||||
scope.Dispose();
|
||||
scopedFileSystems = false;
|
||||
Assert.IsTrue(fs.FileExists("sub/f2.txt"));
|
||||
TestHelper.TryAssert(() => Assert.IsFalse(Directory.Exists(appdata + "/Shadow/" + id)));
|
||||
TestHelper.TryAssert(() => Assert.IsFalse(Directory.Exists(appdata + "/TEMP/ShadowFs/" + id)));
|
||||
|
||||
string text;
|
||||
using (var s = fs.OpenFile("sub/f2.txt"))
|
||||
@@ -522,7 +524,7 @@ namespace Umbraco.Tests.IO
|
||||
|
||||
scopedFileSystems = true; // pretend we have a scope
|
||||
var scope = new ShadowFileSystems(id = Guid.NewGuid(), swa);
|
||||
Assert.IsTrue(Directory.Exists(appdata + "/Shadow/" + id));
|
||||
Assert.IsTrue(Directory.Exists(appdata + "/TEMP/ShadowFs/" + id));
|
||||
using (var ms = new MemoryStream(Encoding.UTF8.GetBytes("foo")))
|
||||
sw.AddFile("sub/f2.txt", ms);
|
||||
Assert.IsFalse(fs.FileExists("sub/f2.txt"));
|
||||
@@ -554,7 +556,7 @@ namespace Umbraco.Tests.IO
|
||||
var e = ae.InnerExceptions[0];
|
||||
Assert.IsNotNull(e.InnerException);
|
||||
Assert.IsInstanceOf<AggregateException>(e);
|
||||
ae = (AggregateException) e;
|
||||
ae = (AggregateException)e;
|
||||
|
||||
Assert.AreEqual(1, ae.InnerExceptions.Count);
|
||||
e = ae.InnerExceptions[0];
|
||||
@@ -623,5 +625,385 @@ namespace Umbraco.Tests.IO
|
||||
providerMock.Setup(x => x.AmbientScope).Returns(scopeMock.Object);
|
||||
return providerMock.Object;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Check that GetFiles will return all files on the shadow, while returning
|
||||
/// just one on each of the filesystems used by the shadow.
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void ShadowGetFiles()
|
||||
{
|
||||
// Arrange
|
||||
var path = IOHelper.MapPath("FileSysTests");
|
||||
Directory.CreateDirectory(path);
|
||||
Directory.CreateDirectory(path + "/ShadowTests");
|
||||
Directory.CreateDirectory(path + "/ShadowSystem");
|
||||
|
||||
var fs = new PhysicalFileSystem(path + "/ShadowTests/", "ignore");
|
||||
var sfs = new PhysicalFileSystem(path + "/ShadowSystem/", "ignore");
|
||||
var ss = new ShadowFileSystem(fs, sfs);
|
||||
|
||||
// Act
|
||||
File.WriteAllText(path + "/ShadowTests/f2.txt", "foo");
|
||||
using (var ms = new MemoryStream(Encoding.UTF8.GetBytes("foo")))
|
||||
ss.AddFile("f1.txt", ms);
|
||||
|
||||
// Assert
|
||||
// ensure we get 2 files from the shadow
|
||||
var getFiles = ss.GetFiles(string.Empty);
|
||||
Assert.AreEqual(2, getFiles.Count());
|
||||
|
||||
var fsFiles = fs.GetFiles(string.Empty).ToArray();
|
||||
Assert.AreEqual(1, fsFiles.Length);
|
||||
var sfsFiles = sfs.GetFiles(string.Empty).ToArray();
|
||||
Assert.AreEqual(1, sfsFiles.Length);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Check that GetFiles using the filter function with empty string will return expected results
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void ShadowGetFilesUsingEmptyFilter()
|
||||
{
|
||||
// Arrange
|
||||
var path = IOHelper.MapPath("FileSysTests");
|
||||
Directory.CreateDirectory(path);
|
||||
Directory.CreateDirectory(path + "/ShadowTests");
|
||||
Directory.CreateDirectory(path + "/ShadowSystem");
|
||||
|
||||
var fs = new PhysicalFileSystem(path + "/ShadowTests/", "ignore");
|
||||
var sfs = new PhysicalFileSystem(path + "/ShadowSystem/", "ignore");
|
||||
var ss = new ShadowFileSystem(fs, sfs);
|
||||
|
||||
// Act
|
||||
File.WriteAllText(path + "/ShadowTests/f2.txt", "foo");
|
||||
using (var ms = new MemoryStream(Encoding.UTF8.GetBytes("foo")))
|
||||
ss.AddFile("f1.txt", ms);
|
||||
|
||||
// Assert
|
||||
// ensure we get 2 files from the shadow
|
||||
var getFiles = ss.GetFiles(string.Empty);
|
||||
Assert.AreEqual(2, getFiles.Count());
|
||||
// ensure we get 0 files when using a empty filter
|
||||
var getFilesWithEmptyFilter = ss.GetFiles(string.Empty, "");
|
||||
Assert.AreEqual(0, getFilesWithEmptyFilter.Count());
|
||||
|
||||
var fsFiles = fs.GetFiles(string.Empty).ToArray();
|
||||
Assert.AreEqual(1, fsFiles.Length);
|
||||
var sfsFiles = sfs.GetFiles(string.Empty).ToArray();
|
||||
Assert.AreEqual(1, sfsFiles.Length);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Check that GetFiles using the filter function with null will return expected results
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void ShadowGetFilesUsingNullFilter()
|
||||
{
|
||||
// Arrange
|
||||
var path = IOHelper.MapPath("FileSysTests");
|
||||
Directory.CreateDirectory(path);
|
||||
Directory.CreateDirectory(path + "/ShadowTests");
|
||||
Directory.CreateDirectory(path + "/ShadowSystem");
|
||||
|
||||
var fs = new PhysicalFileSystem(path + "/ShadowTests/", "ignore");
|
||||
var sfs = new PhysicalFileSystem(path + "/ShadowSystem/", "ignore");
|
||||
var ss = new ShadowFileSystem(fs, sfs);
|
||||
|
||||
// Act
|
||||
File.WriteAllText(path + "/ShadowTests/f2.txt", "foo");
|
||||
using (var ms = new MemoryStream(Encoding.UTF8.GetBytes("foo")))
|
||||
ss.AddFile("f1.txt", ms);
|
||||
|
||||
// Assert
|
||||
// ensure we get 2 files from the shadow
|
||||
var getFiles = ss.GetFiles(string.Empty);
|
||||
Assert.AreEqual(2, getFiles.Count());
|
||||
// ensure we get 2 files when using null in filter parameter
|
||||
var getFilesWithNullFilter = ss.GetFiles(string.Empty, null);
|
||||
Assert.AreEqual(2, getFilesWithNullFilter.Count());
|
||||
|
||||
var fsFiles = fs.GetFiles(string.Empty).ToArray();
|
||||
Assert.AreEqual(1, fsFiles.Length);
|
||||
var sfsFiles = sfs.GetFiles(string.Empty).ToArray();
|
||||
Assert.AreEqual(1, sfsFiles.Length);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ShadowGetFilesUsingWildcardFilter()
|
||||
{
|
||||
// Arrange
|
||||
var path = IOHelper.MapPath("FileSysTests");
|
||||
Directory.CreateDirectory(path);
|
||||
Directory.CreateDirectory(path + "/ShadowTests");
|
||||
Directory.CreateDirectory(path + "/ShadowSystem");
|
||||
|
||||
var fs = new PhysicalFileSystem(path + "/ShadowTests/", "ignore");
|
||||
var sfs = new PhysicalFileSystem(path + "/ShadowSystem/", "ignore");
|
||||
var ss = new ShadowFileSystem(fs, sfs);
|
||||
|
||||
// Act
|
||||
File.WriteAllText(path + "/ShadowTests/f2.txt", "foo");
|
||||
File.WriteAllText(path + "/ShadowTests/f2.doc", "foo");
|
||||
using (var ms = new MemoryStream(Encoding.UTF8.GetBytes("foo")))
|
||||
ss.AddFile("f1.txt", ms);
|
||||
using (var ms = new MemoryStream(Encoding.UTF8.GetBytes("foo")))
|
||||
ss.AddFile("f1.doc", ms);
|
||||
|
||||
// Assert
|
||||
// ensure we get 4 files from the shadow
|
||||
var getFiles = ss.GetFiles(string.Empty);
|
||||
Assert.AreEqual(4, getFiles.Count());
|
||||
// ensure we get only 2 of 4 files from the shadow when using filter
|
||||
var getFilesWithWildcardFilter = ss.GetFiles(string.Empty, "*.doc");
|
||||
Assert.AreEqual(2, getFilesWithWildcardFilter.Count());
|
||||
|
||||
var fsFiles = fs.GetFiles(string.Empty).ToArray();
|
||||
Assert.AreEqual(2, fsFiles.Length);
|
||||
var sfsFiles = sfs.GetFiles(string.Empty).ToArray();
|
||||
Assert.AreEqual(2, sfsFiles.Length);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ShadowGetFilesUsingSingleCharacterFilter()
|
||||
{
|
||||
// Arrange
|
||||
var path = IOHelper.MapPath("FileSysTests");
|
||||
Directory.CreateDirectory(path);
|
||||
Directory.CreateDirectory(path + "/ShadowTests");
|
||||
Directory.CreateDirectory(path + "/ShadowSystem");
|
||||
|
||||
var fs = new PhysicalFileSystem(path + "/ShadowTests/", "ignore");
|
||||
var sfs = new PhysicalFileSystem(path + "/ShadowSystem/", "ignore");
|
||||
var ss = new ShadowFileSystem(fs, sfs);
|
||||
|
||||
// Act
|
||||
File.WriteAllText(path + "/ShadowTests/f2.txt", "foo");
|
||||
File.WriteAllText(path + "/ShadowTests/f2.doc", "foo");
|
||||
File.WriteAllText(path + "/ShadowTests/f2.docx", "foo");
|
||||
using (var ms = new MemoryStream(Encoding.UTF8.GetBytes("foo")))
|
||||
ss.AddFile("f1.txt", ms);
|
||||
using (var ms = new MemoryStream(Encoding.UTF8.GetBytes("foo")))
|
||||
ss.AddFile("f1.doc", ms);
|
||||
using (var ms = new MemoryStream(Encoding.UTF8.GetBytes("foo")))
|
||||
ss.AddFile("f1.docx", ms);
|
||||
|
||||
// Assert
|
||||
// ensure we get 6 files from the shadow
|
||||
var getFiles = ss.GetFiles(string.Empty);
|
||||
Assert.AreEqual(6, getFiles.Count());
|
||||
// ensure we get only 2 of 6 files from the shadow when using filter on shadow
|
||||
var getFilesWithWildcardSinglecharFilter = ss.GetFiles(string.Empty, "f1.d?c");
|
||||
Assert.AreEqual(1, getFilesWithWildcardSinglecharFilter.Count());
|
||||
// ensure we get only 2 of 6 files from the shadow when using filter on disk
|
||||
var getFilesWithWildcardSinglecharFilter2 = ss.GetFiles(string.Empty, "f2.d?c");
|
||||
Assert.AreEqual(1, getFilesWithWildcardSinglecharFilter2.Count());
|
||||
|
||||
var fsFiles = fs.GetFiles(string.Empty).ToArray();
|
||||
Assert.AreEqual(3, fsFiles.Length);
|
||||
var sfsFiles = sfs.GetFiles(string.Empty).ToArray();
|
||||
Assert.AreEqual(3, sfsFiles.Length);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ShadowGetFilesUsingWildcardAndSingleCharacterFilter()
|
||||
{
|
||||
// Arrange
|
||||
var path = IOHelper.MapPath("FileSysTests");
|
||||
Directory.CreateDirectory(path);
|
||||
Directory.CreateDirectory(path + "/ShadowTests");
|
||||
Directory.CreateDirectory(path + "/ShadowSystem");
|
||||
|
||||
var fs = new PhysicalFileSystem(path + "/ShadowTests/", "ignore");
|
||||
var sfs = new PhysicalFileSystem(path + "/ShadowSystem/", "ignore");
|
||||
var ss = new ShadowFileSystem(fs, sfs);
|
||||
|
||||
// Act
|
||||
File.WriteAllText(path + "/ShadowTests/f2.txt", "foo");
|
||||
File.WriteAllText(path + "/ShadowTests/f2.doc", "foo");
|
||||
File.WriteAllText(path + "/ShadowTests/f2.docx", "foo");
|
||||
using (var ms = new MemoryStream(Encoding.UTF8.GetBytes("foo")))
|
||||
ss.AddFile("f1.txt", ms);
|
||||
using (var ms = new MemoryStream(Encoding.UTF8.GetBytes("foo")))
|
||||
ss.AddFile("f1.doc", ms);
|
||||
using (var ms = new MemoryStream(Encoding.UTF8.GetBytes("foo")))
|
||||
ss.AddFile("f1.docx", ms);
|
||||
|
||||
// Assert
|
||||
// ensure we get 6 files from the shadow
|
||||
var getFiles = ss.GetFiles(string.Empty);
|
||||
Assert.AreEqual(6, getFiles.Count());
|
||||
var getFilesWithWildcardSinglecharFilter = ss.GetFiles(string.Empty, "*.d?c");
|
||||
|
||||
Assert.AreEqual(4, getFilesWithWildcardSinglecharFilter.Count());
|
||||
var getFilesWithWildcardSinglecharFilter2 = ss.GetFiles(string.Empty, "*.d?cx");
|
||||
Assert.AreEqual(2, getFilesWithWildcardSinglecharFilter2.Count());
|
||||
|
||||
var fsFiles = fs.GetFiles(string.Empty).ToArray();
|
||||
Assert.AreEqual(3, fsFiles.Length);
|
||||
var sfsFiles = sfs.GetFiles(string.Empty).ToArray();
|
||||
Assert.AreEqual(3, sfsFiles.Length);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ShadowFileSystemFilterIsAsBrokenAsRealFileSystemFilter()
|
||||
{
|
||||
// Arrange
|
||||
var path = IOHelper.MapPath("FileSysTests");
|
||||
Directory.CreateDirectory(path);
|
||||
Directory.CreateDirectory(path + "/filter");
|
||||
// create files on disk and create a "fake" list of files to verify filters against
|
||||
File.WriteAllText(path + "/filter/f1.txt", "foo");
|
||||
File.WriteAllText(path + "/filter/f1.doc", "foo");
|
||||
File.WriteAllText(path + "/filter/f1.docx", "foo");
|
||||
var files = new string[]
|
||||
{
|
||||
"f1.txt",
|
||||
"f1.doc",
|
||||
"f1.docx",
|
||||
};
|
||||
var filter1 = "";
|
||||
var filter2 = "*";
|
||||
var filter3 = "*.doc";
|
||||
var filter4 = "*.d?c";
|
||||
var filter5 = "f1.doc";
|
||||
var filter6 = "f1.d?c";
|
||||
var filter7 = "**.d?c";
|
||||
var filter8 = "f*.doc";
|
||||
|
||||
// Act & Assert
|
||||
var result1Disk = Directory.GetFiles(path + "/filter/", filter1).Select(Path.GetFileName).OrderBy(x => x).ToArray();
|
||||
var result1Fake = files.Where(x => ShadowFileSystem.FilterByRegex(x, ShadowFileSystem.FilterToRegex(filter1))).OrderBy(x => x).ToArray();
|
||||
Assert.AreEqual(result1Disk, result1Fake);
|
||||
var result2Disk = Directory.GetFiles(path + "/filter/", filter2).Select(Path.GetFileName).OrderBy(x => x).ToArray();
|
||||
var result2Fake = files.Where(x => ShadowFileSystem.FilterByRegex(x, ShadowFileSystem.FilterToRegex(filter2))).OrderBy(x => x).ToArray();
|
||||
Assert.AreEqual(result2Disk, result2Fake);
|
||||
var result3Disk = Directory.GetFiles(path + "/filter/", filter3).Select(Path.GetFileName).OrderBy(x => x).ToArray();
|
||||
var result3Fake = files.Where(x => ShadowFileSystem.FilterByRegex(x, ShadowFileSystem.FilterToRegex(filter3))).OrderBy(x => x).ToArray();
|
||||
Assert.AreEqual(result3Disk, result3Fake);
|
||||
var result4Disk = Directory.GetFiles(path + "/filter/", filter4).Select(Path.GetFileName).OrderBy(x => x).ToArray();
|
||||
var result4Fake = files.Where(x => ShadowFileSystem.FilterByRegex(x, ShadowFileSystem.FilterToRegex(filter4))).OrderBy(x => x).ToArray();
|
||||
Assert.AreEqual(result4Disk, result4Fake);
|
||||
var result5Disk = Directory.GetFiles(path + "/filter/", filter5).Select(Path.GetFileName).OrderBy(x => x).ToArray();
|
||||
var result5Fake = files.Where(x => ShadowFileSystem.FilterByRegex(x, ShadowFileSystem.FilterToRegex(filter5))).OrderBy(x => x).ToArray();
|
||||
Assert.AreEqual(result5Disk, result5Fake);
|
||||
var result6Disk = Directory.GetFiles(path + "/filter/", filter6).Select(Path.GetFileName).OrderBy(x => x).ToArray();
|
||||
var result6Fake = files.Where(x => ShadowFileSystem.FilterByRegex(x, ShadowFileSystem.FilterToRegex(filter6))).OrderBy(x => x).ToArray();
|
||||
Assert.AreEqual(result6Disk, result6Fake);
|
||||
var result7Disk = Directory.GetFiles(path + "/filter/", filter7).Select(Path.GetFileName).OrderBy(x => x).ToArray();
|
||||
var result7Fake = files.Where(x => ShadowFileSystem.FilterByRegex(x, ShadowFileSystem.FilterToRegex(filter7))).OrderBy(x => x).ToArray();
|
||||
Assert.AreEqual(result7Disk, result7Fake);
|
||||
var result8Disk = Directory.GetFiles(path + "/filter/", filter8).Select(Path.GetFileName).OrderBy(x => x).ToArray();
|
||||
var result8Fake = files.Where(x => ShadowFileSystem.FilterByRegex(x, ShadowFileSystem.FilterToRegex(filter8))).OrderBy(x => x).ToArray();
|
||||
Assert.AreEqual(result8Disk, result8Fake);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the full paths of the files on the disk.
|
||||
/// Note that this will be the *actual* path of the file, meaning a file existing on the initialized FS
|
||||
/// will be in one location, while a file written after initializing the shadow, will exist at the
|
||||
/// shadow location directory.
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void ShadowGetFullPath()
|
||||
{
|
||||
// Arrange
|
||||
var path = IOHelper.MapPath("FileSysTests");
|
||||
Directory.CreateDirectory(path);
|
||||
Directory.CreateDirectory(path + "/ShadowTests");
|
||||
Directory.CreateDirectory(path + "/ShadowSystem");
|
||||
|
||||
var fs = new PhysicalFileSystem(path + "/ShadowTests/", "ignore");
|
||||
var sfs = new PhysicalFileSystem(path + "/ShadowSystem/", "ignore");
|
||||
var ss = new ShadowFileSystem(fs, sfs);
|
||||
|
||||
// Act
|
||||
File.WriteAllText(path + "/ShadowTests/f1.txt", "foo");
|
||||
using (var ms = new MemoryStream(Encoding.UTF8.GetBytes("foo")))
|
||||
ss.AddFile("f2.txt", ms);
|
||||
|
||||
// Assert
|
||||
var f1FullPath = ss.GetFullPath("f1.txt");
|
||||
var f2FullPath = ss.GetFullPath("f2.txt");
|
||||
Assert.AreEqual(Path.Combine(path, "ShadowTests", "f1.txt"), f1FullPath);
|
||||
Assert.AreEqual(Path.Combine(path, "ShadowSystem", "f2.txt"), f2FullPath);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the path relative to the filesystem root
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This file stuff in this test is kinda irrelevant with the current implementation.
|
||||
/// We do tests that the files are written to the correct places and the relative path is returned correct,
|
||||
/// but GetRelativePath is currently really just string manipulation so files are not actually hit by the code.
|
||||
/// Leaving the file stuff in here for now in case the method becomes more clever at some point.
|
||||
/// </remarks>
|
||||
[Test]
|
||||
public void ShadowGetRelativePath()
|
||||
{
|
||||
// Arrange
|
||||
var path = IOHelper.MapPath("FileSysTests");
|
||||
Directory.CreateDirectory(path);
|
||||
Directory.CreateDirectory(path + "/ShadowTests");
|
||||
Directory.CreateDirectory(path + "/ShadowSystem");
|
||||
|
||||
var fs = new PhysicalFileSystem(path + "/ShadowTests/", "ignore");
|
||||
var sfs = new PhysicalFileSystem(path + "/ShadowSystem/", "ignore");
|
||||
var ss = new ShadowFileSystem(fs, sfs);
|
||||
|
||||
// Act
|
||||
File.WriteAllText(path + "/ShadowTests/f1.txt", "foo");
|
||||
using (var ms = new MemoryStream(Encoding.UTF8.GetBytes("foo")))
|
||||
ss.AddFile("f2.txt", ms);
|
||||
|
||||
// Assert
|
||||
var f1RelativePath = ss.GetRelativePath("f1.txt");
|
||||
var f2RelativePath = ss.GetRelativePath("f2.txt");
|
||||
Assert.AreEqual("f1.txt", f1RelativePath);
|
||||
Assert.AreEqual("f2.txt", f2RelativePath);
|
||||
Assert.IsTrue(File.Exists(Path.Combine(path, "ShadowTests", "f1.txt")));
|
||||
Assert.IsFalse(File.Exists(Path.Combine(path, "ShadowTests", "f2.txt")));
|
||||
Assert.IsTrue(File.Exists(Path.Combine(path, "ShadowSystem", "f2.txt")));
|
||||
Assert.IsFalse(File.Exists(Path.Combine(path, "ShadowSystem", "f1.txt")));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Ensure the url returned contains the path relative to the FS root,
|
||||
/// but including the rootUrl the FS was initialized with.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This file stuff in this test is kinda irrelevant with the current implementation.
|
||||
/// We do tests that the files are written to the correct places and the url is returned correct,
|
||||
/// but GetUrl is currently really just string manipulation so files are not actually hit by the code.
|
||||
/// Leaving the file stuff in here for now in case the method becomes more clever at some point.
|
||||
/// </remarks>
|
||||
[Test]
|
||||
public void ShadowGetUrl()
|
||||
{
|
||||
// Arrange
|
||||
var path = IOHelper.MapPath("FileSysTests");
|
||||
Directory.CreateDirectory(path);
|
||||
Directory.CreateDirectory(path + "/ShadowTests");
|
||||
Directory.CreateDirectory(path + "/ShadowSystem");
|
||||
|
||||
var fs = new PhysicalFileSystem(path + "/ShadowTests/", "rootUrl");
|
||||
var sfs = new PhysicalFileSystem(path + "/ShadowSystem/", "rootUrl");
|
||||
var ss = new ShadowFileSystem(fs, sfs);
|
||||
|
||||
// Act
|
||||
File.WriteAllText(path + "/ShadowTests/f1.txt", "foo");
|
||||
using (var ms = new MemoryStream(Encoding.UTF8.GetBytes("foo")))
|
||||
ss.AddFile("f2.txt", ms);
|
||||
|
||||
// Assert
|
||||
var f1Url = ss.GetUrl("f1.txt");
|
||||
var f2Url = ss.GetUrl("f2.txt");
|
||||
Assert.AreEqual("rootUrl/f1.txt", f1Url);
|
||||
Assert.AreEqual("rootUrl/f2.txt", f2Url);
|
||||
Assert.IsTrue(File.Exists(Path.Combine(path, "ShadowTests", "f1.txt")));
|
||||
Assert.IsFalse(File.Exists(Path.Combine(path, "ShadowTests", "f2.txt")));
|
||||
Assert.IsTrue(File.Exists(Path.Combine(path, "ShadowSystem", "f2.txt")));
|
||||
Assert.IsFalse(File.Exists(Path.Combine(path, "ShadowSystem", "f1.txt")));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -441,7 +441,7 @@ namespace Umbraco.Tests.Persistence.Repositories
|
||||
{
|
||||
|
||||
// Act
|
||||
var exists = repository.Exists(1034); //Content picker
|
||||
var exists = repository.Exists(1046); //Content picker
|
||||
var doesntExist = repository.Exists(-80);
|
||||
|
||||
// Assert
|
||||
|
||||
@@ -121,6 +121,19 @@ WHERE (([umbracoNode].[nodeObjectType] = @0))) x)".Replace(Environment.NewLine,
|
||||
Assert.AreEqual("CREATE UNIQUE NONCLUSTERED INDEX [IX_A] ON [TheTable] ([A])", createExpression.ToString());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void CreateIndexBuilder_SqlServer_Unique_CreatesUniqueNonClusteredIndex_Multi_Columnn()
|
||||
{
|
||||
var sqlSyntax = new SqlServerSyntaxProvider();
|
||||
var createExpression = new CreateIndexExpression(DatabaseProviders.SqlServer, new[] { DatabaseProviders.SqlServer }, sqlSyntax)
|
||||
{
|
||||
Index = { Name = "IX_AB" }
|
||||
};
|
||||
var builder = new CreateIndexBuilder(createExpression);
|
||||
builder.OnTable("TheTable").OnColumn("A").Ascending().OnColumn("B").Ascending().WithOptions().Unique();
|
||||
Assert.AreEqual("CREATE UNIQUE NONCLUSTERED INDEX [IX_AB] ON [TheTable] ([A],[B])", createExpression.ToString());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void CreateIndexBuilder_SqlServer_Clustered_CreatesClusteredIndex()
|
||||
{
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user