Compare commits

..

1 Commits

Author SHA1 Message Date
mikkelhm 4377d9294e Bump version to 7.6-beta 2017-03-17 11:51:07 +01:00
406 changed files with 7988 additions and 6087 deletions
-10
View File
@@ -1,10 +0,0 @@
root=true
[*]
end_of_line = lf
indent_style = space
indent_size = 2
trim_trailing_whitespace = true
[*.{cs,cshtml,csx,vb,vbx,vbhtml,fs,fsx,txt,ps1,sql}]
indent_size = 4
+9 -10
View File
@@ -14,13 +14,12 @@
<summary>Contains the core assemblies needed to run Umbraco Cms</summary>
<language>en-US</language>
<tags>umbraco</tags>
<dependencies>
<dependency id="log4net" version="[2.0.8,3.0.0)" />
<dependency id="Log4Net.Async" version="[2.0.4,3.0.0)" />
<dependencies>
<!--Must be this way because 2.0.5 is actually 1.2.15.0 and 2.0.6 is actually 2.0.6 !-->
<dependency id="log4net" version="[2.0.5,2.0.6)" />
<dependency id="Microsoft.AspNet.Mvc" version="[5.2.3,6.0.0)" />
<dependency id="Microsoft.AspNet.WebApi" version="[5.2.3,6.0.0)" />
<dependency id="Microsoft.AspNet.Identity.Owin" version="[2.2.1, 3.0.0)" />
<dependency id="Microsoft.AspNet.SignalR.Core" version="[2.2.1, 3.0.0)" />
<dependency id="Microsoft.Owin.Security.Cookies" version="[3.0.1, 4.0.0)" />
<dependency id="Microsoft.Owin.Security.OAuth" version="[3.0.1, 4.0.0)" />
<dependency id="Microsoft.Owin.Host.SystemWeb" version="[3.0.1, 4.0.0)" />
@@ -28,19 +27,19 @@
<dependency id="HtmlAgilityPack" version="[1.4.9.5, 2.0.0)" />
<dependency id="Lucene.Net" version="[2.9.4.1, 3.0.0.0)" />
<dependency id="SharpZipLib" version="[0.86.0, 1.0.0)" />
<dependency id="MySql.Data" version="[6.9.9, 7.0.0)" />
<dependency id="MySql.Data" version="[6.9.8, 7.0.0)" />
<dependency id="xmlrpcnet" version="[2.5.0, 3.0.0)" />
<dependency id="ClientDependency" version="[1.9.2, 2.0.0)" />
<dependency id="ClientDependency-Mvc5" version="[1.8.0.0, 2.0.0)" />
<dependency id="AutoMapper" version="[3.3.1, 4.0.0)" />
<dependency id="Newtonsoft.Json" version="[10.0.2, 11.0.0)" />
<dependency id="Examine" version="[0.1.82, 1.0.0)" />
<dependency id="ImageProcessor" version="[2.5.3, 3.0.0)" />
<dependency id="ImageProcessor.Web" version="[4.8.3, 5.0.0)" />
<dependency id="Newtonsoft.Json" version="[9.0.1, 10.0.0)" />
<dependency id="Examine" version="[0.1.81, 1.0.0)" />
<dependency id="ImageProcessor" version="[2.5.2, 3.0.0)" />
<dependency id="ImageProcessor.Web" version="[4.8.2, 5.0.0)" />
<dependency id="semver" version="[2.0.4, 3.0.0)" />
<!-- Markdown can not be updated due to: https://github.com/hey-red/markdownsharp/issues/71#issuecomment-233585487 -->
<dependency id="Markdown" version="[1.14.7, 2.0.0)" />
<dependency id="System.Threading.Tasks.Dataflow" version="[4.7.0, 5.0.0)" />
<dependency id="System.Threading.Tasks.Dataflow" version="[4.6.0, 5.0.0)" />
</dependencies>
</metadata>
<files>
+2 -2
View File
@@ -16,8 +16,8 @@
<tags>umbraco</tags>
<dependencies>
<dependency id="UmbracoCms.Core" version="[$version$]" />
<dependency id="Newtonsoft.Json" version="[10.0.2, 11.0.0)" />
<dependency id="Umbraco.ModelsBuilder" version="[3.0.7, 4.0.0)" />
<dependency id="Newtonsoft.Json" version="[9.0.1, 10.0.0)" />
<dependency id="Umbraco.ModelsBuilder" version="[3.0.5, 4.0.0)" />
<dependency id="Microsoft.AspNet.SignalR.Core" version="[2.2.1, 3.0.0)" />
<dependency id="ImageProcessor.Web.Config" version="[2.3.0, 3.0.0)" />
</dependencies>
+1 -9
View File
@@ -8,17 +8,9 @@
----------------------------------------------------
*** IMPORTANT NOTICE FOR 7.6 UPGRADES ***
Be sure to read the version specific upgrade information before proceeding:
https://our.umbraco.org/documentation/Getting-Started/Setup/Upgrading/version-specific#version-7-6-0
You will most likely need to make some changes to your web.config and you will need to be
aware of the breaking changes listed there to see if these affect your installation.
Don't forget to build!
We've done our best to transform your configuration files but in case something is not quite right: remember we
backed up your files in App_Data\NuGetBackup so you can find the original files before they were transformed.
+3 -9
View File
@@ -58,7 +58,7 @@
<httpRuntime xdt:Transform="InsertIfMissing" />
<httpRuntime maxRequestLength="51200" fcnMode="Single" xdt:Transform="SetAttributes(fcnMode,maxRequestLength)" />
<httpRuntime targetFramework="4.5" xdt:Locator="Condition(count(@targetFramework) != 1)" xdt:Transform="SetAttributes(targetFramework)" />
<membership defaultProvider="DefaultMembershipProvider" xdt:Locator="Match(defaultProvider)" xdt:Transform="Remove" />
<roleManager defaultProvider="DefaultRoleProvider" xdt:Locator="Match(defaultProvider)" xdt:Transform="Remove"/>
<profile defaultProvider="DefaultProfileProvider" xdt:Locator="Match(defaultProvider)" xdt:Transform="Remove"/>>
@@ -330,8 +330,6 @@
<dependentAssembly xdt:Locator="Condition(./_defaultNamespace:assemblyIdentity/@name='Microsoft.Owin.Security')" xdt:Transform="Remove" />
<dependentAssembly xdt:Locator="Condition(./_defaultNamespace:assemblyIdentity/@name='Microsoft.Owin.Security.Cookies')" xdt:Transform="Remove" />
<dependentAssembly xdt:Locator="Condition(./_defaultNamespace:assemblyIdentity/@name='System.Net.Http.Formatting')" xdt:Transform="Remove" />
<dependentAssembly xdt:Locator="Condition(./_defaultNamespace:assemblyIdentity/@name='Microsoft.CodeAnalysis.CSharp')" xdt:Transform="Remove" />
<dependentAssembly xdt:Locator="Condition(./_defaultNamespace:assemblyIdentity/@name='log4net')" xdt:Transform="Remove" />
</assemblyBinding>
</runtime>
@@ -339,7 +337,7 @@
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly xdt:Transform="Insert">
<assemblyIdentity name="HtmlAgilityPack" publicKeyToken="bd319b19eaf3b43a" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-1.4.9.5" newVersion="1.4.9.5" />
<bindingRedirect oldVersion="0.0.0.0-1.4.9.0" newVersion="1.4.9.0" />
</dependentAssembly>
<dependentAssembly xdt:Transform="Insert">
<assemblyIdentity name="AutoMapper" publicKeyToken="be96cd2c38ef1005" culture="neutral" />
@@ -351,7 +349,7 @@
</dependentAssembly>
<dependentAssembly xdt:Transform="Insert">
<assemblyIdentity name="Newtonsoft.Json" publicKeyToken="30ad4fe6b2a6aeed" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-10.0.0.0" newVersion="10.0.0.0" />
<bindingRedirect oldVersion="0.0.0.0-6.0.0.0" newVersion="6.0.0.0" />
</dependentAssembly>
<dependentAssembly xdt:Transform="Insert">
<assemblyIdentity name="System.Web.Mvc" publicKeyToken="31bf3856ad364e35" culture="neutral" />
@@ -385,10 +383,6 @@
<assemblyIdentity name="System.Net.Http.Formatting" publicKeyToken="31bf3856ad364e35" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-5.2.3.0" newVersion="5.2.3.0" />
</dependentAssembly>
<dependentAssembly xdt:Transform="Insert">
<assemblyIdentity name="log4net" publicKeyToken="669e0ddf0bb1aa2a" culture="neutral"/>
<bindingRedirect oldVersion="0.0.0.0-2.0.8.0" newVersion="2.0.8.0"/>
</dependentAssembly>
</assemblyBinding>
</runtime>
+7 -17
View File
@@ -34,15 +34,10 @@
xdt:Transform="SetAttributes()" />
<add application="settings" alias="templates" title="Templates" type="Umbraco.Web.Trees.TemplatesTreeController, umbraco" iconClosed="icon-folder" iconOpen="icon-folder-open" sortOrder="1"
xdt:Locator="Match(application,alias)"
xdt:Transform="SetAttributes()" />
<add application="settings" alias="partialViews" type="Umbraco.Web.Trees.PartialViewsTree, umbraco"
xdt:Locator="Match(application,alias,type)"
xdt:Transform="Remove()" />
<add application="settings" alias="partialViews" title="Partial Views" silent="false" initialize="true" iconClosed="icon-folder" iconOpen="icon-folder" type="Umbraco.Web.Trees.PartialViewsTreeController, umbraco" sortOrder="2"
xdt:Locator="Match(application,alias)"
xdt:Transform="InsertIfMissing" />
xdt:Transform="SetAttributes()" />
<add application="settings" alias="partialViews" title="Partial Views" silent="false" initialize="true" iconClosed="icon-folder" iconOpen="icon-folder" type="Umbraco.Web.Trees.PartialViewsTree, umbraco" sortOrder="2"
xdt:Locator="Match(application,alias)"
xdt:Transform="SetAttributes()" />
<add application="settings" alias="scripts" title="Scripts" type="umbraco.loadScripts, umbraco" iconClosed="icon-folder" iconOpen="icon-folder" sortOrder="4"
xdt:Locator="Match(application,alias)"
xdt:Transform="SetAttributes()" />
@@ -89,14 +84,9 @@
<add application="developer" alias="xslt" title="XSLT Files" type="umbraco.loadXslt, umbraco" iconClosed="icon-folder" iconOpen="icon-folder" sortOrder="5"
xdt:Locator="Match(application,alias)"
xdt:Transform="SetAttributes()" />
<add application="developer" alias="partialViewMacros" type="Umbraco.Web.Trees.PartialViewMacrosTree, umbraco"
xdt:Locator="Match(application,alias,type)"
xdt:Transform="Remove()" />
<add application="developer" alias="partialViewMacros" type="Umbraco.Web.Trees.PartialViewMacrosTreeController, umbraco" silent="false" initialize="true" sortOrder="6" title="Partial View Macro Files" iconClosed="icon-folder" iconOpen="icon-folder"
xdt:Locator="Match(application,alias)"
xdt:Transform="InsertIfMissing" />
<add application="developer" alias="partialViewMacros" type="Umbraco.Web.Trees.PartialViewMacrosTree, umbraco" silent="false" initialize="true" sortOrder="6" title="Partial View Macro Files" iconClosed="icon-folder" iconOpen="icon-folder"
xdt:Locator="Match(application,alias)"
xdt:Transform="SetAttributes()" />
<add application="developer" alias="python"
xdt:Locator="Match(application,alias)"
xdt:Transform="Remove" />
+1 -1
View File
@@ -1,3 +1,3 @@
# Usage: on line 2 put the release version, on line 3 put the version comment (example: beta)
7.6.0
RC3
beta
+1 -5
View File
@@ -4,7 +4,7 @@
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="Newtonsoft.Json" publicKeyToken="30ad4fe6b2a6aeed" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-10.0.0.0" newVersion="10.0.0.0" />
<bindingRedirect oldVersion="0.0.0.0-9.0.0.0" newVersion="9.0.0.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="System.Web.Mvc" publicKeyToken="31bf3856ad364e35" culture="neutral" />
@@ -30,10 +30,6 @@
<assemblyIdentity name="Microsoft.Owin.Security.Cookies" publicKeyToken="31bf3856ad364e35" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-3.0.1.0" newVersion="3.0.1.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="log4net" publicKeyToken="669e0ddf0bb1aa2a" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-2.0.8.0" newVersion="2.0.8.0" />
</dependentAssembly>
</assemblyBinding>
</runtime>
<startup><supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5" /></startup></configuration>
+1 -1
View File
@@ -12,4 +12,4 @@ using System.Resources;
[assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyFileVersion("7.6.0")]
[assembly: AssemblyInformationalVersion("7.6.0-RC3")]
[assembly: AssemblyInformationalVersion("7.6.0-beta")]
+7 -8
View File
@@ -4,14 +4,12 @@ using System.Text.RegularExpressions;
using System.Web;
using Umbraco.Core;
[assembly: PreApplicationStartMethod(typeof(BindingRedirects), "Initialize")]
namespace Umbraco.Core
{
/// <summary>
/// Manages any assembly binding redirects that cannot be done via config (i.e. unsigned --> signed assemblies)
/// Manages any assembly binding redirects that cannot be done via config
/// </summary>
public sealed class BindingRedirects
internal class BindingRedirects
{
public static void Initialize()
{
@@ -19,8 +17,8 @@ namespace Umbraco.Core
AppDomain.CurrentDomain.AssemblyResolve += CurrentDomain_AssemblyResolve;
}
private static readonly Regex Log4NetAssemblyPattern = new Regex("log4net, Version=([\\d\\.]+?), Culture=neutral, PublicKeyToken=\\w+$", RegexOptions.Compiled);
private const string Log4NetReplacement = "log4net, Version=2.0.8.0, Culture=neutral, PublicKeyToken=669e0ddf0bb1aa2a";
private static readonly Regex Log4NetAssemblyPattern = new Regex("log4net, Version=([\\d\\.]+?), Culture=neutral, PublicKeyToken=null", RegexOptions.Compiled);
private const string Log4NetReplacement = "log4net, Version=1.2.15.0, Culture=neutral, PublicKeyToken=669e0ddf0bb1aa2a";
/// <summary>
/// This is used to do an assembly binding redirect via code - normally required due to signature changes in assemblies
@@ -30,12 +28,13 @@ namespace Umbraco.Core
/// <returns></returns>
private static Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args)
{
//log4net:
//log4net:
// Use regex to match and replace
if (Log4NetAssemblyPattern.IsMatch(args.Name) && args.Name != Log4NetReplacement)
{
return Assembly.Load(Log4NetAssemblyPattern.Replace(args.Name, Log4NetReplacement));
}
//AutoMapper:
// ensure the assembly is indeed AutoMapper and that the PublicKeyToken is null before trying to Load again
// do NOT just replace this with 'return Assembly', as it will cause an infinite loop -> stackoverflow
@@ -26,9 +26,6 @@ namespace Umbraco.Core.Cache
public DeepCloneRuntimeCacheProvider(IRuntimeCacheProvider innerProvider)
{
if (innerProvider.GetType() == typeof(DeepCloneRuntimeCacheProvider))
throw new InvalidOperationException("A " + typeof(DeepCloneRuntimeCacheProvider) + " cannot wrap another instance of " + typeof(DeepCloneRuntimeCacheProvider));
InnerProvider = innerProvider;
}
@@ -108,11 +105,9 @@ namespace Umbraco.Core.Cache
var value = result.Value; // force evaluation now - this may throw if cacheItem throws, and then nothing goes into cache
if (value == null) return null; // do not store null values (backward compat)
//Clone/reset to go into the cache
return CheckCloneableAndTracksChanges(value);
}, timeout, isSliding, priority, removedCallback, dependentFiles);
//Clone/reset to go out of the cache
return CheckCloneableAndTracksChanges(cached);
}
@@ -18,14 +18,10 @@ namespace Umbraco.Core.Configuration
{
var appSettings = System.Configuration.ConfigurationManager.AppSettings;
LogUncompletedScopes = string.Equals("true", appSettings["Umbraco.CoreDebug.LogUncompletedScopes"], StringComparison.OrdinalIgnoreCase);
DumpOnTimeoutThreadAbort = string.Equals("true", appSettings["Umbraco.CoreDebug.DumpOnTimeoutThreadAbort"], StringComparison.OrdinalIgnoreCase);
}
// when true, Scope logs the stack trace for any scope that gets disposed without being completed.
// this helps troubleshooting rogue scopes that we forget to complete
public bool LogUncompletedScopes { get; private set; }
// when true, the Logger creates a minidump of w3wp in ~/App_Data/MiniDump whenever it logs
// an error due to a ThreadAbortException that is due to a timeout.
public bool DumpOnTimeoutThreadAbort { get; private set; }
}
}
@@ -183,12 +183,6 @@ namespace Umbraco.Core.Configuration.UmbracoSettings
get { return GetOptionalTextElement("EnablePropertyValueConverters", false); }
}
[ConfigurationProperty("loginBackgroundImage")]
internal InnerTextConfigurationElement<string> LoginBackgroundImage
{
get { return GetOptionalTextElement("loginBackgroundImage", string.Empty); }
}
string IContentSection.NotificationEmailAddress
{
get { return Notifications.NotificationEmailAddress; }
@@ -342,10 +336,5 @@ namespace Umbraco.Core.Configuration.UmbracoSettings
{
get { return EnablePropertyValueConverters; }
}
string IContentSection.LoginBackgroundImage
{
get { return LoginBackgroundImage; }
}
}
}
@@ -72,6 +72,5 @@ namespace Umbraco.Core.Configuration.UmbracoSettings
bool EnablePropertyValueConverters { get; }
string LoginBackgroundImage { get; }
}
}
@@ -8,7 +8,6 @@ namespace Umbraco.Core.Configuration.UmbracoSettings
Guid Id { get; }
string RepositoryUrl { get; }
string WebServiceUrl { get; }
bool HasCustomWebServiceUrl { get; }
string RestApiUrl { get; }
bool HasCustomWebServiceUrl { get; }
}
}
@@ -1,19 +0,0 @@
using System;
using System.Linq;
namespace Umbraco.Core.Configuration.UmbracoSettings
{
public static class RepositoryConfigExtensions
{
//Our package repo
private static readonly Guid RepoGuid = new Guid("65194810-1f85-11dd-bd0b-0800200c9a66");
public static IRepository GetDefault(this IRepositoriesSection repos)
{
var found = repos.Repositories.FirstOrDefault(x => x.Id == RepoGuid);
if (found == null)
throw new InvalidOperationException("No default package repository found with id " + RepoGuid);
return found;
}
}
}
@@ -38,16 +38,9 @@ namespace Umbraco.Core.Configuration.UmbracoSettings
get
{
var prop = Properties["webserviceurl"];
return (string) prop.DefaultValue != (string) this[prop];
var repoUrl = this[prop] as ConfigurationElement;
return (repoUrl != null && repoUrl.ElementInformation.IsPresent);
}
}
[ConfigurationProperty("restapiurl", DefaultValue = "https://our.umbraco.org/webapi/packages/v1")]
public string RestApiUrl
{
get { return (string)base["restapiurl"]; }
set { base["restapiurl"] = value; }
}
}
}
@@ -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 "RC3"; } }
public static string CurrentComment { get { return "beta"; } }
// 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
@@ -124,11 +124,6 @@ namespace Umbraco.Core
/// </summary>
public const string MemberGroup = "366E63B9-880F-4E13-A61C-98069B029728";
/// <summary>
/// Guid for a Member Group object.
/// </summary>
public static readonly Guid MemberGroupGuid = new Guid(MemberGroup);
/// <summary>
/// Guid for a Member Type object.
/// </summary>
+1 -2
View File
@@ -1,7 +1,6 @@
using System.Collections.Generic;
using System.Linq;
using System.Text;
using umbraco.interfaces;
using Umbraco.Core;
namespace Umbraco.Core.Deploy
@@ -9,7 +8,7 @@ namespace Umbraco.Core.Deploy
/// <summary>
/// Connects to an Umbraco service.
/// </summary>
public interface IServiceConnector : IDiscoverable
public interface IServiceConnector
{
/// <summary>
/// Gets an artifact.
-135
View File
@@ -1,135 +0,0 @@
using System;
using System.Diagnostics;
using System.IO;
using System.Runtime.InteropServices;
using Umbraco.Core.IO;
namespace Umbraco.Core.Diagnostics
{
// taken from https://blogs.msdn.microsoft.com/dondu/2010/10/24/writing-minidumps-in-c/
// and https://blogs.msdn.microsoft.com/dondu/2010/10/31/writing-minidumps-from-exceptions-in-c/
// which itself got it from http://blog.kalmbach-software.de/2008/12/13/writing-minidumps-in-c/
internal static class MiniDump
{
private static readonly object LockO = new object();
[Flags]
public enum Option : uint
{
// From dbghelp.h:
Normal = 0x00000000,
WithDataSegs = 0x00000001,
WithFullMemory = 0x00000002,
WithHandleData = 0x00000004,
FilterMemory = 0x00000008,
ScanMemory = 0x00000010,
WithUnloadedModules = 0x00000020,
WithIndirectlyReferencedMemory = 0x00000040,
FilterModulePaths = 0x00000080,
WithProcessThreadData = 0x00000100,
WithPrivateReadWriteMemory = 0x00000200,
WithoutOptionalData = 0x00000400,
WithFullMemoryInfo = 0x00000800,
WithThreadInfo = 0x00001000,
WithCodeSegs = 0x00002000,
WithoutAuxiliaryState = 0x00004000,
WithFullAuxiliaryState = 0x00008000,
WithPrivateWriteCopyMemory = 0x00010000,
IgnoreInaccessibleMemory = 0x00020000,
ValidTypeFlags = 0x0003ffff,
}
//typedef struct _MINIDUMP_EXCEPTION_INFORMATION {
// DWORD ThreadId;
// PEXCEPTION_POINTERS ExceptionPointers;
// BOOL ClientPointers;
//} MINIDUMP_EXCEPTION_INFORMATION, *PMINIDUMP_EXCEPTION_INFORMATION;
[StructLayout(LayoutKind.Sequential, Pack = 4)] // Pack=4 is important! So it works also for x64!
public struct MiniDumpExceptionInformation
{
public uint ThreadId;
public IntPtr ExceptionPointers;
[MarshalAs(UnmanagedType.Bool)]
public bool ClientPointers;
}
//BOOL
//WINAPI
//MiniDumpWriteDump(
// __in HANDLE hProcess,
// __in DWORD ProcessId,
// __in HANDLE hFile,
// __in MINIDUMP_TYPE DumpType,
// __in_opt PMINIDUMP_EXCEPTION_INFORMATION ExceptionParam,
// __in_opt PMINIDUMP_USER_STREAM_INFORMATION UserStreamParam,
// __in_opt PMINIDUMP_CALLBACK_INFORMATION CallbackParam
// );
// Overload requiring MiniDumpExceptionInformation
[DllImport("dbghelp.dll", EntryPoint = "MiniDumpWriteDump", CallingConvention = CallingConvention.StdCall, CharSet = CharSet.Unicode, ExactSpelling = true, SetLastError = true)]
private static extern bool MiniDumpWriteDump(IntPtr hProcess, uint processId, SafeHandle hFile, uint dumpType, ref MiniDumpExceptionInformation expParam, IntPtr userStreamParam, IntPtr callbackParam);
// Overload supporting MiniDumpExceptionInformation == NULL
[DllImport("dbghelp.dll", EntryPoint = "MiniDumpWriteDump", CallingConvention = CallingConvention.StdCall, CharSet = CharSet.Unicode, ExactSpelling = true, SetLastError = true)]
private static extern bool MiniDumpWriteDump(IntPtr hProcess, uint processId, SafeHandle hFile, uint dumpType, IntPtr expParam, IntPtr userStreamParam, IntPtr callbackParam);
[DllImport("kernel32.dll", EntryPoint = "GetCurrentThreadId", ExactSpelling = true)]
private static extern uint GetCurrentThreadId();
private static bool Write(SafeHandle fileHandle, Option options, bool withException = false)
{
var currentProcess = Process.GetCurrentProcess();
var currentProcessHandle = currentProcess.Handle;
var currentProcessId = (uint)currentProcess.Id;
MiniDumpExceptionInformation exp;
exp.ThreadId = GetCurrentThreadId();
exp.ClientPointers = false;
exp.ExceptionPointers = IntPtr.Zero;
if (withException)
exp.ExceptionPointers = Marshal.GetExceptionPointers();
var bRet = exp.ExceptionPointers == IntPtr.Zero
? MiniDumpWriteDump(currentProcessHandle, currentProcessId, fileHandle, (uint) options, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero)
: MiniDumpWriteDump(currentProcessHandle, currentProcessId, fileHandle, (uint) options, ref exp, IntPtr.Zero, IntPtr.Zero);
return bRet;
}
public static bool Dump(Option options = Option.WithFullMemory, bool withException = false)
{
lock (LockO)
{
// work around "stack trace is not available while minidump debugging",
// by making sure a local var (that we can inspect) contains the stack trace.
// getting the call stack before it is unwound would require a special exception
// filter everywhere in our code = not!
var stacktrace = withException ? Environment.StackTrace : string.Empty;
var filepath = IOHelper.MapPath("~/App_Data/MiniDump");
if (Directory.Exists(filepath) == false)
Directory.CreateDirectory(filepath);
var filename = Path.Combine(filepath, string.Format("{0:yyyyMMddTHHmmss}.{1}.dmp", DateTime.UtcNow, Guid.NewGuid().ToString("N").Substring(0, 4)));
using (var stream = new FileStream(filename, FileMode.Create, FileAccess.ReadWrite, FileShare.Write))
{
return Write(stream.SafeFileHandle, options, withException);
}
}
}
public static bool OkToDump()
{
lock (LockO)
{
var filepath = IOHelper.MapPath("~/App_Data/MiniDump");
if (Directory.Exists(filepath) == false) return true;
var count = Directory.GetFiles(filepath, "*.dmp").Length;
return count < 8;
}
}
}
}
+17
View File
@@ -295,5 +295,22 @@ namespace Umbraco.Core
return list1Groups.Count == list2Groups.Count
&& list1Groups.All(g => g.Count() == list2Groups[g.Key].Count());
}
///<summary>
/// Returns the items of the given enumerable as a pure enumerable.
/// <remarks>
/// When quering lists using methods such as <see cref="M:List.Where"/>, the result, despite appearing to look like and quack like an
/// <see cref="T:Enumerable{T}"/> the type is actually an instance of <see cref="T:System.Linq.Enumerable.WhereEnumerableIterator"/>
/// </remarks>
/// </summary>
///<param name="source">The item to find.</param>
///<returns>The index of the first matching item, or -1 if the item was not found.</returns>
internal static IEnumerable<T> Yield<T>(this IEnumerable<T> source)
{
foreach (var element in source)
{
yield return element;
}
}
}
}
@@ -5,10 +5,8 @@ using System.Linq;
namespace Umbraco.Core.Events
{
/// <summary>
/// An IEventDispatcher that immediately raise all events.
/// This event manager supports event cancellation and will raise the events as soon as they are tracked, it does not store tracked events
/// </summary>
/// <remarks>This means that events will be raised during the scope transaction,
/// whatever happens, and the transaction could roll back in the end.</remarks>
internal class PassThroughEventDispatcher : IEventDispatcher
{
public bool DispatchCancelable(EventHandler eventHandler, object sender, CancellableEventArgs args, string eventName = null)
@@ -3,9 +3,11 @@ using Umbraco.Core.IO;
namespace Umbraco.Core.Events
{
/// <summary>
/// An IEventDispatcher that queues events, and raise them when the scope
/// exits and has been completed.
/// 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 : ScopeEventDispatcherBase
{
public ScopeEventDispatcher()
@@ -4,15 +4,6 @@ using System.Linq;
namespace Umbraco.Core.Events
{
/// <summary>
/// An IEventDispatcher that queues events.
/// </summary>
/// <remarks>
/// <para>Can raise, or ignore, cancelable events, depending on option.</para>
/// <para>Implementations must override ScopeExitCompleted to define what
/// to do with the events when the scope exits and has been completed.</para>
/// <para>If the scope exits without being completed, events are ignored.</para>
/// </remarks>
public abstract class ScopeEventDispatcherBase : IEventDispatcher
{
private List<IEventDefinition> _events;
@@ -1,12 +0,0 @@
using System;
namespace Umbraco.Core.Exceptions
{
internal class ConnectionException : Exception
{
public ConnectionException(string message, Exception innerException) : base(message, innerException)
{
}
}
}
@@ -7,7 +7,7 @@ namespace Umbraco.Core.Exceptions
public T Operation { get; private set; }
public DataOperationException(T operation, string message)
: base(message)
:base(message)
{
Operation = operation;
}
+1 -107
View File
@@ -4,144 +4,38 @@ using System.IO;
namespace Umbraco.Core.IO
{
/// <summary>
/// Provides methods allowing the manipulation of files within an Umbraco application.
/// </summary>
public interface IFileSystem
public interface IFileSystem
{
/// <summary>
/// Gets all directories matching the given path.
/// </summary>
/// <param name="path">The path to the directories.</param>
/// <returns>
/// The <see cref="IEnumerable{String}"/> representing the matched directories.
/// </returns>
IEnumerable<string> GetDirectories(string path);
/// <summary>
/// Deletes the specified directory.
/// </summary>
/// <param name="path">The name of the directory to remove.</param>
void DeleteDirectory(string path);
/// <summary>
/// Deletes the specified directory and, if indicated, any subdirectories and files in the directory.
/// </summary>
/// <remarks>Azure blob storage has no real concept of directories so deletion is always recursive.</remarks>
/// <param name="path">The name of the directory to remove.</param>
/// <param name="recursive">Whether to remove directories, subdirectories, and files in path.</param>
void DeleteDirectory(string path, bool recursive);
/// <summary>
/// Determines whether the specified directory exists.
/// </summary>
/// <param name="path">The directory to check.</param>
/// <returns>
/// <c>True</c> if the directory exists and the user has permission to view it; otherwise <c>false</c>.
/// </returns>
bool DirectoryExists(string path);
/// <summary>
/// Adds a file to the file system.
/// </summary>
/// <param name="path">The path to the given file.</param>
/// <param name="stream">The <see cref="Stream"/> containing the file contents.</param>
void AddFile(string path, Stream stream);
/// <summary>
/// Adds a file to the file system.
/// </summary>
/// <param name="path">The path to the given file.</param>
/// <param name="stream">The <see cref="Stream"/> containing the file contents.</param>
/// <param name="overrideIfExists">Whether to override the file if it already exists.</param>
void AddFile(string path, Stream stream, bool overrideIfExists);
/// <summary>
/// Gets all files matching the given path.
/// </summary>
/// <param name="path">The path to the files.</param>
/// <returns>
/// The <see cref="IEnumerable{String}"/> representing the matched files.
/// </returns>
IEnumerable<string> GetFiles(string path);
/// <summary>
/// Gets all files matching the given path and filter.
/// </summary>
/// <param name="path">The path to the files.</param>
/// <param name="filter">A filter that allows the querying of file extension. <example>*.jpg</example></param>
/// <returns>
/// The <see cref="IEnumerable{String}"/> representing the matched files.
/// </returns>
IEnumerable<string> GetFiles(string path, string filter);
/// <summary>
/// Gets a <see cref="Stream"/> representing the file at the gieven path.
/// </summary>
/// <param name="path">The path to the file.</param>
/// <returns>
/// <see cref="Stream"/>.
/// </returns>
Stream OpenFile(string path);
/// <summary>
/// Deletes the specified file.
/// </summary>
/// <param name="path">The name of the file to remove.</param>
void DeleteFile(string path);
/// <summary>
/// Determines whether the specified file exists.
/// </summary>
/// <param name="path">The file to check.</param>
/// <returns>
/// <c>True</c> if the file exists and the user has permission to view it; otherwise <c>false</c>.
/// </returns>
bool FileExists(string path);
/// <summary>
/// Returns the application relative path to the file.
/// </summary>
/// <param name="fullPathOrUrl">The full path or url.</param>
/// <returns>
/// The <see cref="string"/> representing the relative path.
/// </returns>
string GetRelativePath(string fullPathOrUrl);
/// <summary>
/// Gets the full qualified path to the file.
/// </summary>
/// <param name="path">The file to return the full path for.</param>
/// <returns>
/// The <see cref="string"/> representing the full path.
/// </returns>
string GetFullPath(string path);
/// <summary>
/// Returns the application relative url to the file.
/// </summary>
/// <param name="path">The path to return the url for.</param>
/// <returns>
/// <see cref="string"/> representing the relative url.
/// </returns>
string GetUrl(string path);
/// <summary>
/// Gets the last modified date/time of the file, expressed as a UTC value.
/// </summary>
/// <param name="path">The path to the file.</param>
/// <returns>
/// <see cref="DateTimeOffset"/>.
/// </returns>
DateTimeOffset GetLastModified(string path);
/// <summary>
/// Gets the created date/time of the file, expressed as a UTC value.
/// </summary>
/// <param name="path">The path to the file.</param>
/// <returns>
/// <see cref="DateTimeOffset"/>.
/// </returns>
DateTimeOffset GetCreated(string path);
}
+23 -26
View File
@@ -31,7 +31,6 @@ namespace Umbraco.Core.IO
private long _folderCounter;
private bool _folderCounterInitialized;
[Obsolete("This should no longer be used, image manipulation should be done via ImageProcessor, Umbraco no longer generates '_thumb' files for media")]
private static readonly Dictionary<int, string> DefaultSizes = new Dictionary<int, string>
{
{ 100, "thumb" },
@@ -264,7 +263,7 @@ namespace Umbraco.Core.IO
var filename = Path.GetFileName(sourcepath);
var filepath = GetMediaPath(filename, content.Key, propertyType.Key);
this.CopyFile(sourcepath, filepath);
CopyThumbnails(sourcepath, filepath);
return filepath;
}
@@ -313,17 +312,29 @@ namespace Umbraco.Core.IO
}
}
/// <summary>
/// Sets a file for the FileUpload property editor and populates autofill properties
/// </summary>
/// <param name="content"></param>
/// <param name="property"></param>
/// <param name="filepath"></param>
/// <param name="filestream"></param>
// sets a file for the FileUpload property editor
// ie generates thumbnails and populates autofill properties
private void SetUploadFile(IContentBase content, Property property, string filepath, Stream filestream)
{
// will use filepath for extension, and filestream for length
_uploadAutoFillProperties.Populate(content, property.Alias, filepath, filestream);
{
// check if file is an image (and supports resizing and thumbnails etc)
var extension = Path.GetExtension(filepath);
var isImage = IsImageFile(extension);
// specific stuff for images (thumbnails etc)
if (isImage)
{
using (var image = Image.FromStream(filestream))
{
// use one image for all
GenerateThumbnails(image, filepath, property.PropertyType);
_uploadAutoFillProperties.Populate(content, property.Alias, filepath, filestream, image);
}
}
else
{
// will use filepath for extension, and filestream for length
_uploadAutoFillProperties.Populate(content, property.Alias, filepath, filestream);
}
}
#endregion
@@ -390,7 +401,6 @@ namespace Umbraco.Core.IO
// note: this does not find 'custom' thumbnails?
// will find _thumb and _big-thumb but NOT _custom?
[Obsolete("This should no longer be used, image manipulation should be done via ImageProcessor, Umbraco no longer generates '_thumb' files for media")]
public IEnumerable<string> GetThumbnails(string path)
{
var parentDirectory = Path.GetDirectoryName(path);
@@ -411,14 +421,12 @@ namespace Umbraco.Core.IO
DeleteThumbnails(path);
}
[Obsolete("This should no longer be used, image manipulation should be done via ImageProcessor, Umbraco no longer generates '_thumb' files for media")]
public void DeleteThumbnails(string path)
{
GetThumbnails(path)
.ForEach(x => base.DeleteFile(x));
}
[Obsolete("This should no longer be used, image manipulation should be done via ImageProcessor, Umbraco no longer generates '_thumb' files for media")]
public void CopyThumbnails(string sourcePath, string targetPath)
{
var targetPathBase = Path.GetDirectoryName(targetPath) ?? "";
@@ -471,7 +479,6 @@ namespace Umbraco.Core.IO
#region GenerateThumbnails
[Obsolete("This should no longer be used, image manipulation should be done via ImageProcessor, Umbraco no longer generates '_thumb' files for media")]
public IEnumerable<ResizedImage> GenerateThumbnails(
Image image,
string filepath,
@@ -493,7 +500,6 @@ namespace Umbraco.Core.IO
return GenerateThumbnails(image, filepath, additionalSizes);
}
[Obsolete("This should no longer be used, image manipulation should be done via ImageProcessor, Umbraco no longer generates '_thumb' files for media")]
public IEnumerable<ResizedImage> GenerateThumbnails(
Image image,
string filepath,
@@ -516,7 +522,6 @@ namespace Umbraco.Core.IO
.ToList(); // now
}
[Obsolete("This should no longer be used, image manipulation should be done via ImageProcessor, Umbraco no longer generates '_thumb' files for media")]
public IEnumerable<ResizedImage> GenerateThumbnails(
Stream filestream,
string filepath,
@@ -530,7 +535,6 @@ namespace Umbraco.Core.IO
}
}
[Obsolete("This should no longer be used, image manipulation should be done via ImageProcessor, Umbraco no longer generates '_thumb' files for media")]
public IEnumerable<ResizedImage> GenerateThumbnails(
Image image,
string filepath,
@@ -552,19 +556,16 @@ namespace Umbraco.Core.IO
#region GenerateResized - Generate at resized filepath derived from origin filepath
[Obsolete("This should no longer be used, image manipulation should be done via ImageProcessor, Umbraco no longer generates '_thumb' files for media")]
public ResizedImage GenerateResized(Image originImage, string originFilepath, string sizeName, int maxWidthHeight)
{
return GenerateResized(originImage, originFilepath, sizeName, maxWidthHeight, -1, -1);
}
[Obsolete("This should no longer be used, image manipulation should be done via ImageProcessor, Umbraco no longer generates '_thumb' files for media")]
public ResizedImage GenerateResized(Image originImage, string originFilepath, string sizeName, int fixedWidth, int fixedHeight)
{
return GenerateResized(originImage, originFilepath, sizeName, -1, fixedWidth, fixedHeight);
}
[Obsolete("This should no longer be used, image manipulation should be done via ImageProcessor, Umbraco no longer generates '_thumb' files for media")]
public ResizedImage GenerateResized(Image originImage, string originFilepath, string sizeName, int maxWidthHeight, int fixedWidth, int fixedHeight)
{
if (string.IsNullOrWhiteSpace(sizeName))
@@ -580,19 +581,16 @@ namespace Umbraco.Core.IO
#region GenerateResizedAt - Generate at specified resized filepath
[Obsolete("This should no longer be used, image manipulation should be done via ImageProcessor, Umbraco no longer generates '_thumb' files for media")]
public ResizedImage GenerateResizedAt(Image originImage, string resizedFilepath, int maxWidthHeight)
{
return GenerateResizedAt(originImage, resizedFilepath, maxWidthHeight, -1, -1);
}
[Obsolete("This should no longer be used, image manipulation should be done via ImageProcessor, Umbraco no longer generates '_thumb' files for media")]
public ResizedImage GenerateResizedAt(Image originImage, int fixedWidth, int fixedHeight, string resizedFilepath)
{
return GenerateResizedAt(originImage, resizedFilepath, -1, fixedWidth, fixedHeight);
}
[Obsolete("This should no longer be used, image manipulation should be done via ImageProcessor, Umbraco no longer generates '_thumb' files for media")]
public ResizedImage GenerateResizedAt(Image originImage, string resizedFilepath, int maxWidthHeight, int fixedWidth, int fixedHeight)
{
// target dimensions
@@ -701,7 +699,6 @@ namespace Umbraco.Core.IO
#region Inner classes
[Obsolete("This should no longer be used, image manipulation should be done via ImageProcessor, Umbraco no longer generates '_thumb' files for media")]
public class ResizedImage
{
public ResizedImage()
+1 -1
View File
@@ -158,7 +158,7 @@ namespace Umbraco.Core.IO
// if can seek, be safe and go back to start, else...
// hope that the stream hasn't been read already
if (stream.CanSeek)
stream.Seek(0, SeekOrigin.Begin);
stream.Seek(0, 0);
using (var destination = (Stream) File.Create(fullPath))
stream.CopyTo(destination);
+26 -59
View File
@@ -218,11 +218,11 @@ namespace Umbraco.Core.IO
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 wildcard = filter == null ? null : new WildcardExpression(filter);
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 && (wildcard == null || wildcard.IsMatch(kvp.Key))).Select(kvp => kvp.Key))
.Union(shadows.Where(kvp => kvp.Value.IsFile && kvp.Value.IsExist && FilterByRegex(kvp.Key, regexFilter)).Select(kvp => kvp.Key))
.Distinct();
}
@@ -326,65 +326,32 @@ namespace Umbraco.Core.IO
_sfs.AddFile(path, physicalPath, overrideIfExists, copy);
Nodes[normPath] = new ShadowNode(false, false);
}
// copied from System.Web.Util.Wildcard internal
internal class WildcardExpression
/// <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)
{
private readonly string _pattern;
private readonly bool _caseInsensitive;
private Regex _regex;
if (regexFilter == null) return true;
return regexFilter != string.Empty && Regex.IsMatch(input, regexFilter);
}
private static Regex metaRegex = new Regex("[\\+\\{\\\\\\[\\|\\(\\)\\.\\^\\$]");
private static Regex questRegex = new Regex("\\?");
private static Regex starRegex = new Regex("\\*");
private static Regex commaRegex = new Regex(",");
private static Regex slashRegex = new Regex("(?=/)");
private static Regex backslashRegex = new Regex("(?=[\\\\:])");
public WildcardExpression(string pattern, bool caseInsensitive = true)
{
_pattern = pattern;
_caseInsensitive = caseInsensitive;
}
private void EnsureRegex(string pattern)
{
if (_regex != null) return;
var options = RegexOptions.None;
// match right-to-left (for speed) if the pattern starts with a *
if (pattern.Length > 0 && pattern[0] == '*')
options = RegexOptions.RightToLeft | RegexOptions.Singleline;
else
options = RegexOptions.Singleline;
// case insensitivity
if (_caseInsensitive)
options |= RegexOptions.IgnoreCase | RegexOptions.CultureInvariant;
// Remove regex metacharacters
pattern = metaRegex.Replace(pattern, "\\$0");
// Replace wildcard metacharacters with regex codes
pattern = questRegex.Replace(pattern, ".");
pattern = starRegex.Replace(pattern, ".*");
pattern = commaRegex.Replace(pattern, "\\z|\\A");
// anchor the pattern at beginning and end, and return the regex
_regex = new Regex("\\A" + pattern + "\\z", options);
}
public bool IsMatch(string input)
{
EnsureRegex(_pattern);
return _regex.IsMatch(input);
}
/// <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 : "$");
}
}
}
+3 -14
View File
@@ -52,20 +52,9 @@ namespace Umbraco.Core.IO
try
{
Directory.Delete(dir, true);
// shadowPath make be path/to/dir, remove each
dir = dir.Replace("/", "\\");
var min = IOHelper.MapPath("~/App_Data/TEMP/ShadowFs").Length;
var pos = dir.LastIndexOf("\\", StringComparison.OrdinalIgnoreCase);
while (pos > min)
{
dir = dir.Substring(0, pos);
if (Directory.EnumerateFileSystemEntries(dir).Any() == false)
Directory.Delete(dir, true);
else
break;
pos = dir.LastIndexOf("\\", StringComparison.OrdinalIgnoreCase);
}
dir = dir.Substring(0, dir.Length - _shadowPath.Length - 1);
if (Directory.EnumerateFileSystemEntries(dir).Any() == false)
Directory.Delete(dir, true);
}
catch
{
@@ -16,7 +16,7 @@ namespace Umbraco.Core.Logging
/// This is an old/deprecated logger and has been superceded by ParallelForwardingAppender which is included in Umbraco and
/// also by AsyncForwardingAppender in the Log4Net.Async library.
/// </summary>
[Obsolete("This is superceded by the ParallelForwardingAppender, this will be removed in v8, do not use this")]
[Obsolete("This is superceded by the ParallelForwardingAppender, this will be removed in v8")]
[EditorBrowsable(EditorBrowsableState.Never)]
public class AsynchronousRollingFileAppender : RollingFileAppender
{
+5 -39
View File
@@ -7,8 +7,6 @@ using System.Threading;
using System.Web;
using log4net;
using log4net.Config;
using Umbraco.Core.Configuration;
using Umbraco.Core.Diagnostics;
namespace Umbraco.Core.Logging
{
@@ -59,55 +57,23 @@ namespace Umbraco.Core.Logging
internal ILog LoggerFor(object getTypeFromInstance)
{
if (getTypeFromInstance == null) throw new ArgumentNullException("getTypeFromInstance");
return LogManager.GetLogger(getTypeFromInstance.GetType());
}
public void Error(Type callingType, string message, Exception exception)
{
var logger = LogManager.GetLogger(callingType);
if (logger == null) return;
var dump = false;
if (IsTimeoutThreadAbortException(exception))
{
message += "\r\nThe thread has been aborted, because the request has timed out.";
// dump if configured, or if stacktrace contains Monitor.ReliableEnter
dump = UmbracoConfig.For.CoreDebug().DumpOnTimeoutThreadAbort || IsMonitorEnterThreadAbortException(exception);
// dump if it is ok to dump (might have a cap on number of dump...)
dump &= MiniDump.OkToDump();
}
if (dump)
{
try
{
var dumped = MiniDump.Dump(withException: true);
message += dumped
? "\r\nA minidump was created in App_Data/MiniDump"
: "\r\nFailed to create a minidump";
}
catch (Exception e)
{
message += string.Format("\r\nFailed to create a minidump ({0}: {1})", e.GetType().FullName, e.Message);
}
}
logger.Error(message, exception);
logger.Error(message, exception);
}
private static bool IsMonitorEnterThreadAbortException(Exception exception)
{
var abort = exception as ThreadAbortException;
if (abort == null) return false;
var stacktrace = abort.StackTrace;
return stacktrace.Contains("System.Threading.Monitor.ReliableEnter");
}
private static bool IsTimeoutThreadAbortException(Exception exception)
{
var abort = exception as ThreadAbortException;
@@ -139,7 +105,7 @@ namespace Umbraco.Core.Logging
if (showHttpTrace && HttpContext.Current != null)
{
HttpContext.Current.Trace.Warn(callingType.Name, string.Format(message, formatItems.Select(x => x.Invoke()).ToArray()));
}
}
var logger = LogManager.GetLogger(callingType);
if (logger == null || logger.IsWarnEnabled == false) return;
@@ -156,7 +122,7 @@ namespace Umbraco.Core.Logging
var logger = LogManager.GetLogger(callingType);
if (logger == null || logger.IsWarnEnabled == false) return;
var executedParams = formatItems.Select(x => x.Invoke()).ToArray();
logger.WarnFormat((message) + ". Exception: " + e, executedParams);
logger.WarnFormat((message) + ". Exception: " + e, executedParams);
}
/// <summary>
@@ -13,8 +13,301 @@ namespace Umbraco.Core.Logging
/// <remarks>
/// Borrowed from https://github.com/cjbhaines/Log4Net.Async - will reference Nuget packages directly in v8
/// </remarks>
[Obsolete("Use the Log4Net.Async.ParallelForwardingAppender instead this will be removed in future versions")]
public class ParallelForwardingAppender : Log4Net.Async.ParallelForwardingAppender
public class ParallelForwardingAppender : AsyncForwardingAppenderBase, IDisposable
{
#region Private Members
private const int DefaultBufferSize = 1000;
private BlockingCollection<LoggingEventContext> _loggingEvents;
private CancellationTokenSource _loggingCancelationTokenSource;
private CancellationToken _loggingCancelationToken;
private Task _loggingTask;
private Double _shutdownFlushTimeout = 2;
private TimeSpan _shutdownFlushTimespan = TimeSpan.FromSeconds(2);
private static readonly Type ThisType = typeof(ParallelForwardingAppender);
private volatile bool shutDownRequested;
private int? bufferSize = DefaultBufferSize;
#endregion Private Members
#region Properties
/// <summary>
/// Gets or sets the number of LoggingEvents that will be buffered. Set to null for unlimited.
/// </summary>
public override int? BufferSize
{
get { return bufferSize; }
set { bufferSize = value; }
}
public int BufferEntryCount
{
get
{
if (_loggingEvents == null) return 0;
return _loggingEvents.Count;
}
}
/// <summary>
/// Gets or sets the time period in which the system will wait for appenders to flush before canceling the background task.
/// </summary>
public Double ShutdownFlushTimeout
{
get
{
return _shutdownFlushTimeout;
}
set
{
_shutdownFlushTimeout = value;
}
}
protected override string InternalLoggerName
{
get
{
{
return "ParallelForwardingAppender";
}
}
}
#endregion Properties
#region Startup
public override void ActivateOptions()
{
base.ActivateOptions();
_shutdownFlushTimespan = TimeSpan.FromSeconds(_shutdownFlushTimeout);
StartForwarding();
}
private void StartForwarding()
{
if (shutDownRequested)
{
return;
}
//Create a collection which will block the thread and wait for new entries
//if the collection is empty
if (BufferSize.HasValue && BufferSize > 0)
{
_loggingEvents = new BlockingCollection<LoggingEventContext>(BufferSize.Value);
}
else
{
//No limit on the number of events.
_loggingEvents = new BlockingCollection<LoggingEventContext>();
}
//The cancellation token is used to cancel a running task gracefully.
_loggingCancelationTokenSource = new CancellationTokenSource();
_loggingCancelationToken = _loggingCancelationTokenSource.Token;
_loggingTask = new Task(SubscriberLoop, _loggingCancelationToken);
_loggingTask.Start();
}
#endregion Startup
#region Shutdown
private void CompleteSubscriberTask()
{
shutDownRequested = true;
if (_loggingEvents == null || _loggingEvents.IsAddingCompleted)
{
return;
}
//Don't allow more entries to be added.
_loggingEvents.CompleteAdding();
//Allow some time to flush
Thread.Sleep(_shutdownFlushTimespan);
if (!_loggingTask.IsCompleted && !_loggingCancelationToken.IsCancellationRequested)
{
_loggingCancelationTokenSource.Cancel();
//Wait here so that the error logging messages do not get into a random order.
//Don't pass the cancellation token because we are not interested
//in catching the OperationCanceledException that results.
_loggingTask.Wait();
}
if (!_loggingEvents.IsCompleted)
{
ForwardInternalError("The buffer was not able to be flushed before timeout occurred.", null, ThisType);
}
}
protected override void OnClose()
{
CompleteSubscriberTask();
base.OnClose();
}
#endregion Shutdown
#region Appending
protected override void Append(LoggingEvent loggingEvent)
{
if (_loggingEvents == null || _loggingEvents.IsAddingCompleted || loggingEvent == null)
{
return;
}
loggingEvent.Fix = Fix;
//In the case where blocking on a full collection, and the task is subsequently completed, the cancellation token
//will prevent the entry from attempting to add to the completed collection which would result in an exception.
_loggingEvents.Add(new LoggingEventContext(loggingEvent, HttpContext), _loggingCancelationToken);
}
protected override void Append(LoggingEvent[] loggingEvents)
{
if (_loggingEvents == null || _loggingEvents.IsAddingCompleted || loggingEvents == null)
{
return;
}
foreach (var loggingEvent in loggingEvents)
{
Append(loggingEvent);
}
}
#endregion Appending
#region Forwarding
/// <summary>
/// Iterates over a BlockingCollection containing LoggingEvents.
/// </summary>
private void SubscriberLoop()
{
Thread.CurrentThread.Name = String.Format("{0} ParallelForwardingAppender Subscriber Task", Name);
//The task will continue in a blocking loop until
//the queue is marked as adding completed, or the task is canceled.
try
{
//This call blocks until an item is available or until adding is completed
foreach (var entry in _loggingEvents.GetConsumingEnumerable(_loggingCancelationToken))
{
HttpContext = entry.HttpContext;
ForwardLoggingEvent(entry.LoggingEvent, ThisType);
}
}
catch (OperationCanceledException ex)
{
//The thread was canceled before all entries could be forwarded and the collection completed.
ForwardInternalError("Subscriber task was canceled before completion.", ex, ThisType);
//Cancellation is called in the CompleteSubscriberTask so don't call that again.
}
catch (ThreadAbortException ex)
{
//Thread abort may occur on domain unload.
ForwardInternalError("Subscriber task was aborted.", ex, ThisType);
//Cannot recover from a thread abort so complete the task.
CompleteSubscriberTask();
//The exception is swallowed because we don't want the client application
//to halt due to a logging issue.
}
catch (Exception ex)
{
//On exception, try to log the exception
ForwardInternalError("Subscriber task error in forwarding loop.", ex, ThisType);
//Any error in the loop is going to be some sort of extenuating circumstance from which we
//probably cannot recover anyway. Complete subscribing.
CompleteSubscriberTask();
}
}
#endregion Forwarding
#region IDisposable Implementation
private bool _disposed = false;
//Implement IDisposable.
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
if (!_disposed)
{
if (disposing)
{
if (_loggingTask != null)
{
if (!(_loggingTask.IsCanceled || _loggingTask.IsCompleted || _loggingTask.IsFaulted))
{
try
{
CompleteSubscriberTask();
}
catch (Exception ex)
{
LogLog.Error(ThisType, "Exception Completing Subscriber Task in Dispose Method", ex);
}
}
try
{
_loggingTask.Dispose();
}
catch (Exception ex)
{
LogLog.Error(ThisType, "Exception Disposing Logging Task", ex);
}
finally
{
_loggingTask = null;
}
}
if (_loggingEvents != null)
{
try
{
_loggingEvents.Dispose();
}
catch (Exception ex)
{
LogLog.Error(ThisType, "Exception Disposing BlockingCollection", ex);
}
finally
{
_loggingEvents = null;
}
}
if (_loggingCancelationTokenSource != null)
{
try
{
_loggingCancelationTokenSource.Dispose();
}
catch (Exception ex)
{
LogLog.Error(ThisType, "Exception Disposing CancellationTokenSource", ex);
}
finally
{
_loggingCancelationTokenSource = null;
}
}
}
_disposed = true;
}
}
// Use C# destructor syntax for finalization code.
~ParallelForwardingAppender()
{
// Simply call Dispose(false).
Dispose(false);
}
#endregion IDisposable Implementation
}
}
+2 -8
View File
@@ -1,14 +1,8 @@
using System;
using System.Collections.Generic;
using System.Collections.Generic;
namespace Umbraco.Core.Media
{
// note: because this interface is obsolete is is *not* IDiscoverable, and in case the
// PluginManager is asked to find types implementing this interface it will fall back
// to a complete scan.
[Obsolete("IImageUrlProvider is no longer used and will be removed in future versions")]
public interface IImageUrlProvider // IDiscoverable
public interface IImageUrlProvider
{
string Name { get; }
string GetImageUrlFromMedia(int mediaId, IDictionary<string, string> parameters);
+2 -9
View File
@@ -1,13 +1,6 @@
using System;
namespace Umbraco.Core.Media
namespace Umbraco.Core.Media
{
// note: because this interface is obsolete is is *not* IDiscoverable, and in case the
// PluginManager is asked to find types implementing this interface it will fall back
// to a complete scan.
[Obsolete("Thumbnails are generated by ImageProcessor, use that instead")]
public interface IThumbnailProvider // : IDiscoverable
public interface IThumbnailProvider
{
bool CanProvideThumbnail(string fileUrl);
string GetThumbnailUrl(string fileUrl);
@@ -96,7 +96,8 @@ namespace Umbraco.Core.Media
/// <param name="propertyTypeAlias">The property type alias.</param>
/// <param name="filepath">The filesystem-relative filepath, or null to clear properties.</param>
/// <param name="filestream">The stream containing the file data.</param>
public void Populate(IContentBase content, string propertyTypeAlias, string filepath, Stream filestream)
/// <param name="image">The file data as an image object.</param>
public void Populate(IContentBase content, string propertyTypeAlias, string filepath, Stream filestream, Image image = null)
{
if (content == null) throw new ArgumentNullException("content");
if (propertyTypeAlias == null) throw new ArgumentNullException("propertyTypeAlias");
@@ -109,7 +110,7 @@ namespace Umbraco.Core.Media
if (autoFillConfig == null) return; // nothing
// populate
Populate(content, autoFillConfig, filepath, filestream);
Populate(content, autoFillConfig, filepath, filestream, image);
}
/// <summary>
@@ -157,7 +158,8 @@ namespace Umbraco.Core.Media
/// <param name="autoFillConfig"></param>
/// <param name="filepath">The filesystem-relative filepath, or null to clear properties.</param>
/// <param name="filestream">The stream containing the file data.</param>
public void Populate(IContentBase content, IImagingAutoFillUploadField autoFillConfig, string filepath, Stream filestream)
/// <param name="image">The file data as an image object.</param>
public void Populate(IContentBase content, IImagingAutoFillUploadField autoFillConfig, string filepath, Stream filestream, Image image = null)
{
if (content == null) throw new ArgumentNullException("content");
if (autoFillConfig == null) throw new ArgumentNullException("autoFillConfig");
@@ -170,7 +172,11 @@ namespace Umbraco.Core.Media
else
{
var extension = (Path.GetExtension(filepath) ?? "").TrimStart('.');
var size = _mediaFileSystem.IsImageFile(extension) ? (Size?)_mediaFileSystem.GetDimensions(filestream) : null;
Size? size;
if (image == null)
size = _mediaFileSystem.IsImageFile(extension) ? (Size?) _mediaFileSystem.GetDimensions(filestream) : null;
else
size = new Size(image.Width, image.Height);
SetProperties(content, autoFillConfig, size, filestream.Length, extension);
}
}
+3 -53
View File
@@ -1,7 +1,5 @@
using System;
using System.Collections.Concurrent;
using System.Diagnostics;
using Umbraco.Core.Logging;
namespace Umbraco.Core.Models
{
@@ -86,64 +84,16 @@ namespace Umbraco.Core.Models
public string Type { get; set; }
private Type _runtimeType;
/// <summary>
/// Returns the CLR type based on it's assembly name stored in the config
/// </summary>
/// <returns></returns>
public Type GetRuntimeType()
{
if (_runtimeType != null)
return _runtimeType;
_runtimeType = TryGetType(Type);
return _runtimeType;
return _runtimeType ?? (_runtimeType = System.Type.GetType(Type));
}
/// <summary>
/// Used to try to get and cache the tree type
/// </summary>
/// <param name="type"></param>
/// <returns></returns>
internal static Type TryGetType(string type)
{
try
{
return ResolvedTypes.GetOrAdd(type, s =>
{
var result = System.Type.GetType(type);
if (result != null)
{
return result;
}
//we need to implement a bit of a hack here due to some trees being renamed and backwards compat
var parts = type.Split(new[] {','}, StringSplitOptions.RemoveEmptyEntries);
if (parts.Length == 2)
{
if (parts[1].Trim() == "umbraco" && parts[0].StartsWith("Umbraco.Web.Trees") && parts[0].EndsWith("Controller") == false)
{
//if it's one of our controllers but it's not suffixed with "Controller" then add it and try again
var tempType = parts[0] + "Controller, umbraco";
result = System.Type.GetType(tempType);
if (result != null)
{
return result;
}
}
}
throw new InvalidOperationException("Could not resolve type");
});
}
catch (InvalidOperationException)
{
//swallow, this is our own exception, couldn't find the type
return null;
}
}
private static readonly ConcurrentDictionary<string, Type> ResolvedTypes = new ConcurrentDictionary<string, Type>();
}
}
-3
View File
@@ -274,9 +274,6 @@ namespace Umbraco.Core.Models
/// </summary>
public bool HasPublishedVersion { get { return PublishedVersionGuid != default(Guid); } }
[IgnoreDataMember]
internal DateTime PublishedDate { get; set; }
/// <summary>
/// Changes the Trashed state of the content object
/// </summary>
+1 -4
View File
@@ -49,10 +49,7 @@ namespace Umbraco.Core.Models
return path
.Replace('\\', System.IO.Path.DirectorySeparatorChar)
.Replace('/', System.IO.Path.DirectorySeparatorChar);
//Don't strip the start - this was a bug fixed in 7.3, see ScriptRepositoryTests.PathTests
//.TrimStart(System.IO.Path.DirectorySeparatorChar)
//.TrimStart('/');
//.TrimStart(System.IO.Path.DirectorySeparatorChar);
}
/// <summary>
@@ -179,7 +179,7 @@ namespace Umbraco.Core.Models.PublishedContent
}
if (contentType == null)
throw new InvalidOperationException(string.Format("ContentTypeService failed to find a {0} type with alias \"{1}\".",
throw new Exception(string.Format("ContentTypeService failed to find a {0} type with alias \"{1}\".",
itemType.ToString().ToLower(), alias));
return new PublishedContentType(contentType);
@@ -19,8 +19,5 @@ namespace Umbraco.Core.Models.Rdbms
[Column("newest")]
public bool Newest { get; set; }
[Column("updateDate")]
public DateTime VersionDate { get; set; }
}
}
@@ -22,7 +22,6 @@ namespace Umbraco.Core.ObjectResolution
private readonly string _httpContextKey;
private readonly List<Type> _instanceTypes = new List<Type>();
private IEnumerable<TResolved> _sortedValues;
private readonly Func<HttpContextBase> _httpContextGetter;
private int _defaultPluginWeight = 100;
@@ -43,7 +42,12 @@ namespace Umbraco.Core.ObjectResolution
if (logger == null) throw new ArgumentNullException("logger");
CanResolveBeforeFrozen = false;
if (scope == ObjectLifetimeScope.HttpRequest)
_httpContextGetter = () => new HttpContextWrapper(HttpContext.Current);
{
if (HttpContext.Current == null)
throw new InvalidOperationException("Use alternative constructor accepting a HttpContextBase object in order to set the lifetime scope to HttpRequest when HttpContext.Current is null");
CurrentHttpContext = new HttpContextWrapper(HttpContext.Current);
}
ServiceProvider = serviceProvider;
Logger = logger;
@@ -80,7 +84,7 @@ namespace Umbraco.Core.ObjectResolution
LifetimeScope = ObjectLifetimeScope.HttpRequest;
_httpContextKey = GetType().FullName;
ServiceProvider = serviceProvider;
_httpContextGetter = () => httpContext;
CurrentHttpContext = httpContext;
_instanceTypes = new List<Type>();
InitializeAppInstances();
@@ -156,16 +160,7 @@ namespace Umbraco.Core.ObjectResolution
/// Gets or sets the <see cref="HttpContextBase"/> used to initialize this object, if any.
/// </summary>
/// <remarks>If not null, then <c>LifetimeScope</c> will be <c>ObjectLifetimeScope.HttpRequest</c>.</remarks>
protected HttpContextBase CurrentHttpContext
{
get
{
var context = _httpContextGetter == null ? null : _httpContextGetter();
if (context == null)
throw new InvalidOperationException("Cannot use this resolver with lifetime 'HttpRequest' when there is no current HttpContext. Either use the ctor accepting an HttpContextBase, or use the resolver from within a request exclusively.");
return context;
}
}
protected HttpContextBase CurrentHttpContext { get; private set; }
/// <summary>
/// Returns the service provider used to instantiate objects
@@ -201,7 +196,7 @@ namespace Umbraco.Core.ObjectResolution
/// <summary>
/// Gets or sets the default type weight.
/// </summary>
/// <remarks>Determines the weight of types that do not have a <c>WeightAttribute</c> set on
/// <remarks>Determines the weight of types that do not have a <c>WeightAttribute</c> set on
/// them, when calling <c>GetSortedValues</c>.</remarks>
protected virtual int DefaultPluginWeight
{
@@ -281,7 +276,7 @@ namespace Umbraco.Core.ObjectResolution
/// Removes a type.
/// </summary>
/// <param name="value">The type to remove.</param>
/// <exception cref="InvalidOperationException">the resolver does not support removing types, or
/// <exception cref="InvalidOperationException">the resolver does not support removing types, or
/// the type is not a valid type for the resolver.</exception>
public virtual void RemoveType(Type value)
{
@@ -301,7 +296,7 @@ namespace Umbraco.Core.ObjectResolution
/// Removes a type.
/// </summary>
/// <typeparam name="T">The type to remove.</typeparam>
/// <exception cref="InvalidOperationException">the resolver does not support removing types, or
/// <exception cref="InvalidOperationException">the resolver does not support removing types, or
/// the type is not a valid type for the resolver.</exception>
public void RemoveType<T>()
where T : TResolved
@@ -314,7 +309,7 @@ namespace Umbraco.Core.ObjectResolution
/// </summary>
/// <param name="types">The types to add.</param>
/// <remarks>The types are appended at the end of the list.</remarks>
/// <exception cref="InvalidOperationException">the resolver does not support adding types, or
/// <exception cref="InvalidOperationException">the resolver does not support adding types, or
/// a type is not a valid type for the resolver, or a type is already in the collection of types.</exception>
protected void AddTypes(IEnumerable<Type> types)
{
@@ -341,7 +336,7 @@ namespace Umbraco.Core.ObjectResolution
/// </summary>
/// <param name="value">The type to add.</param>
/// <remarks>The type is appended at the end of the list.</remarks>
/// <exception cref="InvalidOperationException">the resolver does not support adding types, or
/// <exception cref="InvalidOperationException">the resolver does not support adding types, or
/// the type is not a valid type for the resolver, or the type is already in the collection of types.</exception>
public virtual void AddType(Type value)
{
@@ -367,7 +362,7 @@ namespace Umbraco.Core.ObjectResolution
/// </summary>
/// <typeparam name="T">The type to add.</typeparam>
/// <remarks>The type is appended at the end of the list.</remarks>
/// <exception cref="InvalidOperationException">the resolver does not support adding types, or
/// <exception cref="InvalidOperationException">the resolver does not support adding types, or
/// the type is not a valid type for the resolver, or the type is already in the collection of types.</exception>
public void AddType<T>()
where T : TResolved
@@ -409,7 +404,7 @@ namespace Umbraco.Core.ObjectResolution
/// </summary>
/// <param name="index">The zero-based index at which the type should be inserted.</param>
/// <param name="value">The type to insert.</param>
/// <exception cref="InvalidOperationException">the resolver does not support inserting types, or
/// <exception cref="InvalidOperationException">the resolver does not support inserting types, or
/// the type is not a valid type for the resolver, or the type is already in the collection of types.</exception>
/// <exception cref="ArgumentOutOfRangeException"><paramref name="index"/> is out of range.</exception>
public virtual void InsertType(int index, Type value)
@@ -435,7 +430,7 @@ namespace Umbraco.Core.ObjectResolution
/// Inserts a type at the beginning of the list.
/// </summary>
/// <param name="value">The type to insert.</param>
/// <exception cref="InvalidOperationException">the resolver does not support inserting types, or
/// <exception cref="InvalidOperationException">the resolver does not support inserting types, or
/// the type is not a valid type for the resolver, or the type is already in the collection of types.</exception>
public virtual void InsertType(Type value)
{
@@ -469,7 +464,7 @@ namespace Umbraco.Core.ObjectResolution
/// </summary>
/// <param name="existingType">The existing type before which to insert.</param>
/// <param name="value">The type to insert.</param>
/// <exception cref="InvalidOperationException">the resolver does not support inserting types, or
/// <exception cref="InvalidOperationException">the resolver does not support inserting types, or
/// one of the types is not a valid type for the resolver, or the existing type is not in the collection,
/// or the new type is already in the collection of types.</exception>
public virtual void InsertTypeBefore(Type existingType, Type value)
@@ -503,7 +498,7 @@ namespace Umbraco.Core.ObjectResolution
/// </summary>
/// <typeparam name="TExisting">The existing type before which to insert.</typeparam>
/// <typeparam name="T">The type to insert.</typeparam>
/// <exception cref="InvalidOperationException">the resolver does not support inserting types, or
/// <exception cref="InvalidOperationException">the resolver does not support inserting types, or
/// one of the types is not a valid type for the resolver, or the existing type is not in the collection,
/// or the new type is already in the collection of types.</exception>
public void InsertTypeBefore<TExisting, T>()
@@ -63,6 +63,49 @@ namespace Umbraco.Core.ObjectResolution
}
}
// NOTE - the ugly code below exists only because of umbraco.BusinessLogic.Actions.Action.ReRegisterActionsAndHandlers
// which wants to re-register actions and handlers instead of properly restarting the application. Don't even think
// about using it for anything else. Also, while the backdoor is open, the resolution system is locked so nothing
// can work properly => deadlocks. Therefore, open the backdoor, do resolution changes EXCLUSIVELY, and close the door!
/// <summary>
/// Returns a disposable object that reprents dirty access to temporarily unfrozen resolution configuration.
/// </summary>
/// <remarks>
/// <para>Should not be used.</para>
/// <para>Should be used in a <c>using(Resolution.DirtyBackdoorToConfiguration) { ... }</c> mode.</para>
/// <para>Because we just lift the frozen state, and we don't actually re-freeze, the <c>Frozen</c> event does not trigger.</para>
/// </remarks>
internal static IDisposable DirtyBackdoorToConfiguration
{
get { return new DirtyBackdoor(); }
}
// keep the class here because it needs write-access to Resolution.IsFrozen
private class DirtyBackdoor : IDisposable
{
private readonly IDisposable _lock;
private readonly bool _frozen;
public DirtyBackdoor()
{
LogHelper.Debug(typeof(DirtyBackdoor), "Creating back door for resolution");
_lock = new WriteLock(ConfigurationLock);
_frozen = _isFrozen;
_isFrozen = false;
}
public void Dispose()
{
LogHelper.Debug(typeof(DirtyBackdoor), "Disposing back door for resolution");
_isFrozen = _frozen;
_lock.Dispose();
}
}
/// <summary>
/// Freezes resolution.
/// </summary>
@@ -72,9 +72,6 @@ namespace Umbraco.Core.Persistence.Factories
content.PublishedVersionGuid = publishedDto == null
? (dto.DocumentPublishedReadOnlyDto == null ? default(Guid) : dto.DocumentPublishedReadOnlyDto.VersionId)
: publishedDto.VersionId;
content.PublishedDate = publishedDto == null
? (dto.DocumentPublishedReadOnlyDto == null ? default(DateTime) : dto.DocumentPublishedReadOnlyDto.VersionDate)
: publishedDto.VersionDate;
//on initial construction we don't want to have dirty properties tracked
// http://issues.umbraco.org/issue/U4-1946
@@ -2,12 +2,11 @@
using System.Collections.Concurrent;
using System.Linq.Expressions;
using System.Reflection;
using umbraco.interfaces;
using Umbraco.Core.Persistence.SqlSyntax;
namespace Umbraco.Core.Persistence.Mappers
{
public abstract class BaseMapper : IDiscoverable
public abstract class BaseMapper
{
private readonly ISqlSyntaxProvider _sqlSyntax;
@@ -1,11 +1,9 @@
using umbraco.interfaces;
namespace Umbraco.Core.Persistence.Migrations
namespace Umbraco.Core.Persistence.Migrations
{
/// <summary>
/// Marker interface for database migrations
/// </summary>
public interface IMigration : IDiscoverable
public interface IMigration
{
void Up();
void Down();
@@ -1,5 +1,4 @@
using System;
using Umbraco.Core.Persistence.Migrations.Syntax.Alter.Column;
using Umbraco.Core.Persistence.Migrations.Syntax.Alter.Column;
using Umbraco.Core.Persistence.Migrations.Syntax.Alter.Expressions;
using Umbraco.Core.Persistence.Migrations.Syntax.Alter.Table;
using Umbraco.Core.Persistence.SqlSyntax;
@@ -26,15 +25,6 @@ namespace Umbraco.Core.Persistence.Migrations.Syntax.Alter
return new AlterTableBuilder(_context, _databaseProviders, expression);
}
/// <summary>
/// The problem with this is that only under particular circumstances is the expression added to the context
/// so you wouldn't actually know if you are using it correctly or not and chances are you are not and therefore
/// the statement won't even execute whereas using the IAlterTableSyntax to modify a column is guaranteed to add
/// the expression to the context.
/// </summary>
/// <param name="columnName"></param>
/// <returns></returns>
[Obsolete("Use the IAlterTableSyntax to modify a column instead, this will be removed in future versions")]
public IAlterColumnSyntax Column(string columnName)
{
var expression = new AlterColumnExpression(_context.CurrentDatabaseProvider, _databaseProviders, _sqlSyntax) {Column = {Name = columnName}};
@@ -1,22 +1,11 @@
using System;
using Umbraco.Core.Persistence.Migrations.Syntax.Alter.Column;
using Umbraco.Core.Persistence.Migrations.Syntax.Alter.Column;
using Umbraco.Core.Persistence.Migrations.Syntax.Alter.Table;
namespace Umbraco.Core.Persistence.Migrations.Syntax.Alter
{
public interface IAlterSyntaxBuilder : IFluentSyntax
{
IAlterTableSyntax Table(string tableName);
/// <summary>
/// The problem with this is that only under particular circumstances is the expression added to the context
/// so you wouldn't actually know if you are using it correctly or not and chances are you are not and therefore
/// the statement won't even execute whereas using the IAlterTableSyntax to modify a column is guaranteed to add
/// the expression to the context.
/// </summary>
/// <param name="columnName"></param>
/// <returns></returns>
[Obsolete("Use the IAlterTableSyntax to modify a column instead, this will be removed in future versions")]
IAlterTableSyntax Table(string tableName);
IAlterColumnSyntax Column(string columnName);
}
}
@@ -1,10 +1,11 @@
using Umbraco.Core.Logging;
using Umbraco.Core.Configuration;
using Umbraco.Core.Logging;
using Umbraco.Core.Models.Rdbms;
using Umbraco.Core.Persistence.SqlSyntax;
namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSevenSixZero
namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSevenFiveFive
{
[Migration("7.6.0", 101, Constants.System.UmbracoMigrationName)]
[Migration("7.5.5", 101, Constants.System.UmbracoMigrationName)]
public class AddLockObjects : MigrationBase
{
public AddLockObjects(ISqlSyntaxProvider sqlSyntax, ILogger logger)
@@ -1,10 +1,11 @@
using System.Linq;
using Umbraco.Core.Configuration;
using Umbraco.Core.Logging;
using Umbraco.Core.Persistence.SqlSyntax;
namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSevenSixZero
namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSevenFiveFive
{
[Migration("7.6.0", 100, Constants.System.UmbracoMigrationName)]
[Migration("7.5.5", 100, Constants.System.UmbracoMigrationName)]
public class AddLockTable : MigrationBase
{
public AddLockTable(ISqlSyntaxProvider sqlSyntax, ILogger logger)
@@ -4,7 +4,7 @@ using Umbraco.Core.Persistence.SqlSyntax;
namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSevenSixZero
{
[Migration("7.6.0", 3, Constants.System.UmbracoMigrationName)]
[Migration("7.6.0", 0, Constants.System.UmbracoMigrationName)]
public class AddIndexToCmsMemberLoginName : MigrationBase
{
public AddIndexToCmsMemberLoginName(ISqlSyntaxProvider sqlSyntax, ILogger logger)
@@ -12,43 +12,18 @@ namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSevenSixZero
{ }
public override void Up()
{
Execute.Code(database =>
{
var dbIndexes = SqlSyntax.GetDefinedIndexesDefinitions(Context.Database);
//make sure it doesn't already exist
if (dbIndexes.Any(x => x.IndexName.InvariantEquals("IX_cmsMember_LoginName")) == false)
{
//Now we need to check if we can actually d6 this because we won't be able to if there's data in there that is too long
//http://issues.umbraco.org/issue/U4-9758
var colLen = (SqlSyntax is MySqlSyntaxProvider)
? database.ExecuteScalar<int?>("select max(LENGTH(LoginName)) from cmsMember")
: database.ExecuteScalar<int?>("select max(datalength(LoginName)) from cmsMember");
if (colLen < 900 == false && colLen != null)
{
return null;
}
var dbIndexes = SqlSyntax.GetDefinedIndexesDefinitions(Context.Database);
//make sure it doesn't already exist
if (dbIndexes.Any(x => x.IndexName.InvariantEquals("IX_cmsMember_LoginName")) == false)
{
var localContext = new LocalMigrationContext(Context.CurrentDatabaseProvider, database, SqlSyntax, Logger);
//we can apply the index
localContext.Create.Index("IX_cmsMember_LoginName").OnTable("cmsMember")
.OnColumn("LoginName")
.Ascending()
.WithOptions()
.NonClustered();
return localContext.GetSql();
}
return null;
});
Create.Index("IX_cmsMember_LoginName").OnTable("cmsMember")
.OnColumn("LoginName")
.Ascending()
.WithOptions()
.NonClustered();
}
}
public override void Down()
@@ -1,50 +0,0 @@
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 NormalizeTemplateGuids : MigrationBase
{
public NormalizeTemplateGuids(ISqlSyntaxProvider sqlSyntax, ILogger logger)
: base(sqlSyntax, logger)
{ }
public override void Up()
{
Execute.Code(UpdateTemplateGuids);
}
private static string UpdateTemplateGuids(Database database)
{
// we need this migration because ppl running pre-7.6 on Cloud and Courier have templates in different
// environments having different GUIDs (Courier does not sync template GUIDs) and we need to normalize
// these GUIDs so templates with the same alias on different environments have the same GUID.
// however, if already running a prerelease version of 7.6, we do NOT want to normalize the GUIDs as quite
// probably, we are already running Deploy and the GUIDs are OK. assuming noone is running a prerelease
// of 7.6 on Courier.
// so... testing if we already have a 7.6.0 version installed. not pretty but...?
//
var version = database.FirstOrDefault<string>("SELECT version FROM umbracoMigration WHERE name=@name ORDER BY version DESC", new { name = Constants.System.UmbracoMigrationName });
if (version != null && version.StartsWith("7.6.0")) return string.Empty;
var updates = database.Query<dynamic>(@"SELECT umbracoNode.id, cmsTemplate.alias FROM umbracoNode
JOIN cmsTemplate ON umbracoNode.id=cmsTemplate.nodeId
WHERE nodeObjectType = @guid", new { guid = Constants.ObjectTypes.TemplateTypeGuid})
.Select(template => Tuple.Create((int) template.id, ("template____" + (string) template.alias).ToGuid()))
.ToList();
foreach (var update in updates)
database.Execute("UPDATE umbracoNode set uniqueId=@guid WHERE id=@id", new { guid = update.Item2, id = update.Item1 });
return string.Empty;
}
public override void Down()
{
throw new NotImplementedException();
}
}
}
@@ -1,52 +0,0 @@
using System.Linq;
using Umbraco.Core.Logging;
using Umbraco.Core.Persistence.SqlSyntax;
namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSevenSixZero
{
[Migration("7.6.0", 2, Constants.System.UmbracoMigrationName)]
public class ReduceLoginNameColumnsSize : MigrationBase
{
public ReduceLoginNameColumnsSize(ISqlSyntaxProvider sqlSyntax, ILogger logger)
: base(sqlSyntax, logger)
{ }
public override void Up()
{
//Now we need to check if we can actually d6 this because we won't be able to if there's data in there that is too long
//http://issues.umbraco.org/issue/U4-9758
Execute.Code(database =>
{
var dbIndexes = SqlSyntax.GetDefinedIndexesDefinitions(database);
var colLen = (SqlSyntax is MySqlSyntaxProvider)
? database.ExecuteScalar<int?>("select max(LENGTH(LoginName)) from cmsMember")
: database.ExecuteScalar<int?>("select max(datalength(LoginName)) from cmsMember");
if (colLen < 900 == false) return null;
var localContext = new LocalMigrationContext(Context.CurrentDatabaseProvider, database, SqlSyntax, Logger);
//if it exists we need to drop it first
if (dbIndexes.Any(x => x.IndexName.InvariantEquals("IX_cmsMember_LoginName")))
{
localContext.Delete.Index("IX_cmsMember_LoginName").OnTable("cmsMember");
}
//we can apply the col length change
localContext.Alter.Table("cmsMember")
.AlterColumn("LoginName")
.AsString(225)
.NotNullable();
return localContext.GetSql();
});
}
public override void Down()
{
}
}
}
@@ -29,7 +29,7 @@ namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSevenThreeZe
}).ToArray();
//must be non-nullable
Alter.Table("umbracoNode").AlterColumn("uniqueID").AsGuid().NotNullable();
Alter.Column("uniqueID").OnTable("umbracoNode").AsGuid().NotNullable();
//make sure it already exists
if (dbIndexes.Any(x => x.IndexName.InvariantEquals("IX_umbracoNodeUniqueID")))
@@ -513,13 +513,11 @@ namespace Umbraco.Core.Persistence.Repositories
dto.DocumentPublishedReadOnlyDto = new DocumentPublishedReadOnlyDto
{
VersionId = dto.VersionId,
VersionDate = dto.UpdateDate,
Newest = true,
NodeId = dto.NodeId,
Published = true
Published = true
};
((Content) entity).PublishedVersionGuid = dto.VersionId;
((Content) entity).PublishedDate = dto.UpdateDate;
((Content)entity).PublishedVersionGuid = dto.VersionId;
}
entity.ResetDirtyProperties();
@@ -690,26 +688,22 @@ namespace Umbraco.Core.Persistence.Repositories
dto.DocumentPublishedReadOnlyDto = new DocumentPublishedReadOnlyDto
{
VersionId = dto.VersionId,
VersionDate = dto.UpdateDate,
Newest = true,
NodeId = dto.NodeId,
Published = true
};
((Content) entity).PublishedVersionGuid = dto.VersionId;
((Content) entity).PublishedDate = dto.UpdateDate;
((Content)entity).PublishedVersionGuid = dto.VersionId;
}
else if (publishedStateChanged)
{
dto.DocumentPublishedReadOnlyDto = new DocumentPublishedReadOnlyDto
{
VersionId = default (Guid),
VersionDate = default (DateTime),
VersionId = default(Guid),
Newest = false,
NodeId = dto.NodeId,
Published = false
};
((Content) entity).PublishedVersionGuid = default(Guid);
((Content) entity).PublishedDate = default (DateTime);
((Content)entity).PublishedVersionGuid = default(Guid);
}
entity.ResetDirtyProperties();
@@ -981,7 +975,7 @@ order by umbracoNode.{2}, umbracoNode.parentID, umbracoNode.sortOrder",
}
//order by update date DESC, if there is corrupted published flags we only want the latest!
var publishedSql = new Sql(@"SELECT cmsDocument.nodeId, cmsDocument.published, cmsDocument.versionId, cmsDocument.updateDate, cmsDocument.newest
var publishedSql = new Sql(@"SELECT cmsDocument.nodeId, cmsDocument.published, cmsDocument.versionId, cmsDocument.newest
FROM cmsDocument INNER JOIN cmsContentVersion ON cmsContentVersion.VersionId = cmsDocument.versionId
WHERE cmsDocument.published = 1 AND cmsDocument.nodeId IN
(" + parsedOriginalSql + @")
@@ -101,8 +101,7 @@ namespace Umbraco.Core.Persistence.Repositories
var translator = new SqlTranslator<PropertyType>(sqlClause, query);
var sql = translator.Translate()
//must be sorted this way for the relator to work
.OrderBy<PropertyTypeGroupDto>(x => x.Id, SqlSyntax);
.OrderBy<PropertyTypeDto>(x => x.PropertyTypeGroupId, SqlSyntax);
var dtos = Database.Fetch<PropertyTypeGroupDto, PropertyTypeDto, DataTypeDto, PropertyTypeGroupDto>(new GroupPropertyTypeRelator().Map, sql);
@@ -469,8 +468,7 @@ AND umbracoNode.id <> @id",
.LeftJoin<DataTypeDto>()
.On<PropertyTypeDto, DataTypeDto>(left => left.DataTypeId, right => right.DataTypeId)
.Where<PropertyTypeGroupDto>(x => x.ContentTypeNodeId == id)
//must be sorted this way for the relator to work
.OrderBy<PropertyTypeGroupDto>(x => x.Id, SqlSyntax);
.OrderBy<PropertyTypeGroupDto>(x => x.Id);
var dtos = Database.Fetch<PropertyTypeGroupDto, PropertyTypeDto, DataTypeDto, PropertyTypeGroupDto>(new GroupPropertyTypeRelator().Map, sql);
@@ -1252,19 +1250,5 @@ WHERE cmsContentType." + aliasColumn + @" LIKE @pattern",
while (aliases.Contains(test = alias + i)) i++;
return test;
}
/// <summary>
/// Given the path of a content item, this will return true if the content item exists underneath a list view content item
/// </summary>
/// <param name="contentPath"></param>
/// <returns></returns>
public bool HasContainerInPath(string contentPath)
{
var ids = contentPath.Split(',').Select(int.Parse);
var sql = new Sql(@"SELECT COUNT(*) FROM cmsContentType
INNER JOIN cmsContent ON cmsContentType.nodeId=cmsContent.contentType
WHERE cmsContent.nodeId IN (@ids) AND cmsContentType.isContainer=@isContainer", new { ids, isContainer = true });
return Database.ExecuteScalar<int>(sql) > 0;
}
}
}
@@ -276,33 +276,47 @@ AND umbracoNode.id <> @id",
public PreValueCollection GetPreValuesCollectionByDataTypeId(int dataTypeId)
{
var collection = GetCachedPreValueCollection(dataTypeId);
return collection;
var cached = IsolatedCache.GetCacheItemsByKeySearch<PreValueCollection>(GetPrefixedCacheKey(dataTypeId));
if (cached != null && cached.Any())
{
//return from the cache, ensure it's a cloned result
return (PreValueCollection)cached.First().DeepClone();
}
return GetAndCachePreValueCollection(dataTypeId);
}
internal static string GetCacheKeyRegex(int preValueId)
{
return CacheKeys.DataTypePreValuesCacheKey + @"[-\d]+-([\d]*,)*" + preValueId + @"(?!\d)[,\d$]*";
}
/// <summary>
/// Gets a specific PreValue by its Id
/// </summary>
/// <param name="preValueId">Id of the PreValue to retrieve the value from</param>
/// <returns>PreValue as a string</returns>
public string GetPreValueAsString(int preValueId)
{
var collections = IsolatedCache.GetCacheItemsByKeySearch<PreValueCollection>(CacheKeys.DataTypePreValuesCacheKey + "_");
//We need to see if we can find the cached PreValueCollection based on the cache key above
var preValue = collections.SelectMany(x => x.FormatAsDictionary().Values).FirstOrDefault(x => x.Id == preValueId);
if (preValue != null)
return preValue.Value;
var cached = IsolatedCache.GetCacheItemsByKeyExpression<PreValueCollection>(GetCacheKeyRegex(preValueId));
if (cached != null && cached.Any())
{
//return from the cache
var collection = cached.First();
var preVal = collection.FormatAsDictionary().Single(x => x.Value.Id == preValueId);
return preVal.Value.Value;
}
var dto = Database.FirstOrDefault<DataTypePreValueDto>("WHERE id = @preValueId", new { preValueId });
//go and find the data type id for the pre val id passed in
var dto = Database.FirstOrDefault<DataTypePreValueDto>("WHERE id = @preValueId", new { preValueId = preValueId });
if (dto == null)
{
return string.Empty;
}
// go cache the collection
var preVals = GetAndCachePreValueCollection(dto.DataTypeNodeId);
var collection = GetCachedPreValueCollection(dto.DataTypeNodeId);
if (collection == null)
return string.Empty;
preValue = collection.FormatAsDictionary().Values.FirstOrDefault(x => x.Id == preValueId);
return preValue == null ? string.Empty : preValue.Value;
//return the single value for this id
var pv = preVals.FormatAsDictionary().Single(x => x.Value.Id == preValueId);
return pv.Value.Value;
}
public void AddOrUpdatePreValues(int dataTypeId, IDictionary<string, PreValue> values)
@@ -427,28 +441,40 @@ AND umbracoNode.id <> @id",
sortOrder++;
}
}
private static string GetPrefixedCacheKey(int dataTypeId)
private string GetPrefixedCacheKey(int dataTypeId)
{
return CacheKeys.DataTypePreValuesCacheKey + "_" + dataTypeId;
return CacheKeys.DataTypePreValuesCacheKey + dataTypeId + "-";
}
private PreValueCollection GetCachedPreValueCollection(int datetypeId)
private PreValueCollection GetAndCachePreValueCollection(int dataTypeId)
{
var key = GetPrefixedCacheKey(datetypeId);
return IsolatedCache.GetCacheItem<PreValueCollection>(key, () =>
{
var dtos = Database.Fetch<DataTypePreValueDto>("WHERE datatypeNodeId = @Id", new { Id = datetypeId });
var list = dtos.Select(x => new Tuple<PreValue, string, int>(new PreValue(x.Id, x.Value, x.SortOrder), x.Alias, x.SortOrder)).ToList();
var collection = PreValueConverter.ConvertToPreValuesCollection(list);
return collection;
}, TimeSpan.FromMinutes(20), isSliding: true);
//go get the data
var dtos = Database.Fetch<DataTypePreValueDto>("WHERE datatypeNodeId = @Id", new { Id = dataTypeId });
var list = dtos.Select(x => new Tuple<PreValue, string, int>(new PreValue(x.Id, x.Value, x.SortOrder), x.Alias, x.SortOrder)).ToList();
var collection = PreValueConverter.ConvertToPreValuesCollection(list);
//now create the cache key, this needs to include all pre-value ids so that we can use this cached item in the GetPreValuesAsString method
//the key will be: "UmbracoPreValDATATYPEID-CSVOFPREVALIDS
var key = GetPrefixedCacheKey(dataTypeId)
+ string.Join(",", collection.FormatAsDictionary().Select(x => x.Value.Id).ToArray());
//store into cache
IsolatedCache.InsertCacheItem(key, () => collection,
//30 mins
new TimeSpan(0, 0, 30),
//sliding is true
true);
return collection;
}
private string EnsureUniqueNodeName(string nodeName, int id = 0)
{
var sql = new Sql();
sql.Select("*")
@@ -549,7 +575,7 @@ AND umbracoNode.id <> @id",
}
//NOTE: We used to check that the Alias was unique for the given DataTypeNodeId prevalues list, BUT
// in reality there is no need to check the uniqueness of this alias because the only way that this code executes is
// in reality there is no need to check the uniqueness of this alias because the only way that this code executes is
// based on an IDictionary<string, PreValue> dictionary being passed to this repository and a dictionary
// must have unique aliases by definition, so there is no need for this additional check
@@ -569,10 +595,10 @@ AND umbracoNode.id <> @id",
{
throw new InvalidOperationException("Cannot update a pre value for a data type that has no identity");
}
//NOTE: We used to check that the Alias was unique for the given DataTypeNodeId prevalues list, BUT
// this causes issues when sorting the pre-values (http://issues.umbraco.org/issue/U4-5670) but in reality
// there is no need to check the uniqueness of this alias because the only way that this code executes is
// there is no need to check the uniqueness of this alias because the only way that this code executes is
// based on an IDictionary<string, PreValue> dictionary being passed to this repository and a dictionary
// must have unique aliases by definition, so there is no need for this additional check
@@ -587,7 +613,7 @@ AND umbracoNode.id <> @id",
Database.Update(dto);
}
}
internal static class PreValueConverter
@@ -41,7 +41,6 @@ namespace Umbraco.Core.Persistence.Repositories
{
var sql = GetBaseQuery(false)
.Where(GetBaseWhereClause(), new { Id = id })
//must be sorted this way for the relator to work
.OrderBy<DictionaryDto>(x => x.UniqueId, SqlSyntax);
var dto = Database.Fetch<DictionaryDto, LanguageTextDto, DictionaryDto>(new DictionaryLanguageTextRelator().Map, sql).FirstOrDefault();
@@ -65,9 +64,6 @@ namespace Umbraco.Core.Persistence.Repositories
sql.Where("cmsDictionary.pk in (@ids)", new { ids = ids });
}
//must be sorted this way for the relator to work
sql.OrderBy<DictionaryDto>(x => x.UniqueId, SqlSyntax);
return Database.Fetch<DictionaryDto, LanguageTextDto, DictionaryDto>(new DictionaryLanguageTextRelator().Map, sql)
.Select(dto => ConvertFromDto(dto));
}
@@ -77,7 +73,6 @@ namespace Umbraco.Core.Persistence.Repositories
var sqlClause = GetBaseQuery(false);
var translator = new SqlTranslator<IDictionaryItem>(sqlClause, query);
var sql = translator.Translate();
//must be sorted this way for the relator to work
sql.OrderBy<DictionaryDto>(x => x.UniqueId, SqlSyntax);
return Database.Fetch<DictionaryDto, LanguageTextDto, DictionaryDto>(new DictionaryLanguageTextRelator().Map, sql)
@@ -99,9 +94,9 @@ namespace Umbraco.Core.Persistence.Repositories
else
{
sql.Select("*")
.From<DictionaryDto>(SqlSyntax)
.LeftJoin<LanguageTextDto>(SqlSyntax)
.On<DictionaryDto, LanguageTextDto>(SqlSyntax, left => left.UniqueId, right => right.UniqueId);
.From<DictionaryDto>(SqlSyntax)
.LeftJoin<LanguageTextDto>(SqlSyntax)
.On<DictionaryDto, LanguageTextDto>(SqlSyntax, left => left.UniqueId, right => right.UniqueId);
}
return sql;
}
@@ -275,8 +270,6 @@ namespace Umbraco.Core.Persistence.Repositories
var translator = new SqlTranslator<IDictionaryItem>(sqlClause, Query<IDictionaryItem>.Builder);
var sql = translator.Translate();
//must be sorted this way for the relator to work
sql.OrderBy<DictionaryDto>(x => x.UniqueId, SqlSyntax);
return Database.Fetch<DictionaryDto, LanguageTextDto, DictionaryDto>(new DictionaryLanguageTextRelator().Map, sql)
@@ -304,9 +297,6 @@ namespace Umbraco.Core.Persistence.Repositories
protected override IEnumerable<DictionaryDto> PerformFetch(Sql sql)
{
//must be sorted this way for the relator to work
sql.OrderBy<DictionaryDto>(x => x.UniqueId, SqlSyntax);
return Database.Fetch<DictionaryDto, LanguageTextDto, DictionaryDto>(new DictionaryLanguageTextRelator().Map, sql);
}
@@ -359,9 +349,6 @@ namespace Umbraco.Core.Persistence.Repositories
protected override IEnumerable<DictionaryDto> PerformFetch(Sql sql)
{
//must be sorted this way for the relator to work
sql.OrderBy<DictionaryDto>(x => x.UniqueId, SqlSyntax);
return Database.Fetch<DictionaryDto, LanguageTextDto, DictionaryDto>(new DictionaryLanguageTextRelator().Map, sql);
}
@@ -8,13 +8,6 @@ namespace Umbraco.Core.Persistence.Repositories
{
public interface IContentTypeRepository : IContentTypeCompositionRepository<IContentType>
{
/// <summary>
/// Given the path of a content item, this will return true if the content item exists underneath a list view content item
/// </summary>
/// <param name="contentPath"></param>
/// <returns></returns>
bool HasContainerInPath(string contentPath);
/// <summary>
/// Gets all entities of the specified <see cref="PropertyType"/> query
/// </summary>
@@ -37,9 +37,6 @@ namespace Umbraco.Core.Persistence.Repositories
private IMacro GetBySql(Sql sql)
{
//must be sorted this way for the relator to work
sql.OrderBy<MacroDto>(x => x.Id, SqlSyntax);
var macroDto = Database.Fetch<MacroDto, MacroPropertyDto, MacroDto>(new MacroPropertyRelator().Map, sql).FirstOrDefault();
if (macroDto == null)
return null;
@@ -71,10 +68,7 @@ namespace Umbraco.Core.Persistence.Repositories
private IEnumerable<IMacro> GetAllNoIds()
{
var sql = GetBaseQuery(false)
//must be sorted this way for the relator to work
.OrderBy<MacroDto>(x => x.Id, SqlSyntax);
var sql = GetBaseQuery(false);
return ConvertFromDtos(Database.Fetch<MacroDto, MacroPropertyDto, MacroDto>(new MacroPropertyRelator().Map, sql))
.ToArray();// we don't want to re-iterate again!
}
@@ -98,9 +92,6 @@ namespace Umbraco.Core.Persistence.Repositories
var translator = new SqlTranslator<IMacro>(sqlClause, query);
var sql = translator.Translate();
//must be sorted this way for the relator to work
sql.OrderBy<MacroDto>(x => x.Id, SqlSyntax);
var dtos = Database.Fetch<MacroDto, MacroPropertyDto, MacroDto>(new MacroPropertyRelator().Map, sql);
foreach (var dto in dtos)
@@ -123,13 +114,13 @@ namespace Umbraco.Core.Persistence.Repositories
return sql;
}
private Sql GetBaseQuery()
private static Sql GetBaseQuery()
{
var sql = new Sql();
sql.Select("*")
.From<MacroDto>()
.LeftJoin<MacroPropertyDto>()
.On<MacroDto, MacroPropertyDto>(left => left.Id, right => right.Macro);
.From<MacroDto>()
.LeftJoin<MacroPropertyDto>()
.On<MacroDto, MacroPropertyDto>(left => left.Id, right => right.Macro);
return sql;
}
@@ -46,9 +46,7 @@ namespace Umbraco.Core.Persistence.Repositories
var statement = string.Join(" OR ", ids.Select(x => string.Format("umbracoNode.id='{0}'", x)));
sql.Where(statement);
}
//must be sorted this way for the relator to work
sql.OrderBy<MemberTypeReadOnlyDto>(x => x.UniqueId, SqlSyntax);
sql.OrderByDescending<NodeDto>(x => x.NodeId, SqlSyntax);
var dtos =
Database.Fetch<MemberTypeReadOnlyDto, PropertyTypeReadOnlyDto, PropertyTypeGroupReadOnlyDto, MemberTypeReadOnlyDto>(
@@ -64,8 +62,7 @@ namespace Umbraco.Core.Persistence.Repositories
var subquery = translator.Translate();
var sql = GetBaseQuery(false)
.Append(new Sql("WHERE umbracoNode.id IN (" + subquery.SQL + ")", subquery.Arguments))
//must be sorted this way for the relator to work
.OrderBy<MemberTypeReadOnlyDto>(x => x.UniqueId, SqlSyntax);
.OrderBy<NodeDto>(x => x.SortOrder, SqlSyntax);
var dtos =
Database.Fetch<MemberTypeReadOnlyDto, PropertyTypeReadOnlyDto, PropertyTypeGroupReadOnlyDto, MemberTypeReadOnlyDto>(
@@ -40,10 +40,7 @@ namespace Umbraco.Core.Persistence.Repositories
}
var factory = new PublicAccessEntryFactory();
//MUST be ordered by this GUID ID for the AccessRulesRelator to work
sql.OrderBy<AccessDto>(dto => dto.Id, SqlSyntax);
var dtos = Database.Fetch<AccessDto, AccessRuleDto, AccessDto>(new AccessRulesRelator().Map, sql);
return dtos.Select(factory.BuildEntity);
}
@@ -55,10 +52,7 @@ namespace Umbraco.Core.Persistence.Repositories
var sql = translator.Translate();
var factory = new PublicAccessEntryFactory();
//MUST be ordered by this GUID ID for the AccessRulesRelator to work
sql.OrderBy<AccessDto>(dto => dto.Id, SqlSyntax);
var dtos = Database.Fetch<AccessDto, AccessRuleDto, AccessDto>(new AccessRulesRelator().Map, sql);
return dtos.Select(factory.BuildEntity);
}
@@ -69,8 +63,9 @@ namespace Umbraco.Core.Persistence.Repositories
sql.Select("*")
.From<AccessDto>(SqlSyntax)
.LeftJoin<AccessRuleDto>(SqlSyntax)
.On<AccessDto, AccessRuleDto>(SqlSyntax, left => left.Id, right => right.AccessId);
.On<AccessDto, AccessRuleDto>(SqlSyntax, left => left.Id, right => right.AccessId)
//MUST be ordered by this GUID ID for the AccessRulesRelator to work
.OrderBy<AccessDto>(dto => dto.Id, SqlSyntax);
return sql;
}
@@ -132,11 +127,6 @@ namespace Umbraco.Core.Persistence.Repositories
Database.Update(dto);
foreach (var removedRule in entity.RemovedRules)
{
Database.Delete<AccessRuleDto>("WHERE id=@Id", new { Id = removedRule });
}
foreach (var rule in entity.Rules)
{
if (rule.HasIdentity)
@@ -162,6 +152,10 @@ namespace Umbraco.Core.Persistence.Repositories
rule.Id = rule.Key.GetHashCode();
}
}
foreach (var removedRule in entity.RemovedRules)
{
Database.Delete<AccessRuleDto>("WHERE id=@Id", new {Id = removedRule});
}
entity.ClearRemovedRules();
@@ -39,8 +39,6 @@ namespace Umbraco.Core.Persistence.Repositories
{
var sql = GetBaseQuery(false);
sql.Where(GetBaseWhereClause(), new { Id = id });
//must be sorted this way for the relator to work
sql.OrderBy<UserDto>(x => x.Id, SqlSyntax);
var dto = Database.Fetch<UserDto, User2AppDto, UserDto>(new UserSectionRelator().Map, sql).FirstOrDefault();
@@ -62,10 +60,7 @@ namespace Umbraco.Core.Persistence.Repositories
{
sql.Where("umbracoUser.id in (@ids)", new {ids = ids});
}
//must be sorted this way for the relator to work
sql.OrderBy<UserDto>(x => x.Id, SqlSyntax);
return ConvertFromDtos(Database.Fetch<UserDto, User2AppDto, UserDto>(new UserSectionRelator().Map, sql))
.ToArray(); // important so we don't iterate twice, if we don't do this we can end up with null values in cache if we were caching.
}
@@ -76,9 +71,6 @@ namespace Umbraco.Core.Persistence.Repositories
var translator = new SqlTranslator<IUser>(sqlClause, query);
var sql = translator.Translate();
//must be sorted this way for the relator to work
sql.OrderBy<UserDto>(x => x.Id, SqlSyntax);
var dtos = Database.Fetch<UserDto, User2AppDto, UserDto>(new UserSectionRelator().Map, sql)
.DistinctBy(x => x.Id);
@@ -104,13 +96,13 @@ namespace Umbraco.Core.Persistence.Repositories
return sql;
}
private Sql GetBaseQuery(string columns)
private static Sql GetBaseQuery(string columns)
{
var sql = new Sql();
sql.Select(columns)
.From<UserDto>()
.LeftJoin<User2AppDto>()
.On<UserDto, User2AppDto>(left => left.Id, right => right.UserId);
.From<UserDto>()
.LeftJoin<User2AppDto>()
.On<UserDto, User2AppDto>(left => left.Id, right => right.UserId);
return sql;
}
@@ -308,8 +300,6 @@ namespace Umbraco.Core.Persistence.Repositories
var innerSql = GetBaseQuery("umbracoUser.id");
innerSql.Where("umbracoUser2app.app = " + SqlSyntax.GetQuotedValue(sectionAlias));
sql.Where(string.Format("umbracoUser.id IN ({0})", innerSql.SQL));
//must be sorted this way for the relator to work
sql.OrderBy<UserDto>(x => x.Id, SqlSyntax);
return ConvertFromDtos(Database.Fetch<UserDto, User2AppDto, UserDto>(new UserSectionRelator().Map, sql));
}
@@ -49,12 +49,7 @@ namespace Umbraco.Core.Persistence
_cacheHelper.IsolatedRuntimeCache.CacheFactory = type =>
{
var cache = origFactory(type);
//if the result is already a DeepCloneRuntimeCacheProvider then return it, otherwise
//wrap the result with a DeepCloneRuntimeCacheProvider
return cache is DeepCloneRuntimeCacheProvider
? cache
: new DeepCloneRuntimeCacheProvider(cache);
return new DeepCloneRuntimeCacheProvider(cache);
};
}
@@ -8,6 +8,5 @@ namespace Umbraco.Core.Persistence.UnitOfWork
IScope Scope { get; }
EventMessages Messages { get; }
IEventDispatcher Events { get; }
void Flush();
}
}
@@ -160,33 +160,7 @@ namespace Umbraco.Core.Persistence.UnitOfWork
_key = Guid.NewGuid();
}
public void Flush()
{
if (_readOnly)
throw new NotSupportedException("This unit of work is read-only.");
while (_operations.Count > 0)
{
var operation = _operations.Dequeue();
switch (operation.Type)
{
case TransactionType.Insert:
operation.Repository.PersistNewItem(operation.Entity);
break;
case TransactionType.Delete:
operation.Repository.PersistDeletedItem(operation.Entity);
break;
case TransactionType.Update:
operation.Repository.PersistUpdatedItem(operation.Entity);
break;
}
}
_operations.Clear();
_key = Guid.NewGuid();
}
public object Key
public object Key
{
get { return _key; }
}
File diff suppressed because it is too large Load Diff
@@ -47,9 +47,5 @@ using System.Security.Permissions;
[assembly: InternalsVisibleTo("Umbraco.Deploy.UI")]
[assembly: InternalsVisibleTo("Umbraco.Deploy.Cloud")]
[assembly: InternalsVisibleTo("Umbraco.Forms.Core")]
[assembly: InternalsVisibleTo("Umbraco.Forms.Core.Providers")]
[assembly: InternalsVisibleTo("Umbraco.Forms.Web")]
//allow this to be mocked in our unit tests
[assembly: InternalsVisibleTo("DynamicProxyGenAssembly2")]
@@ -1,9 +1,8 @@
using System.Collections.Generic;
using umbraco.interfaces;
namespace Umbraco.Core.PropertyEditors
{
public interface IParameterEditor : IDiscoverable
public interface IParameterEditor
{
/// <summary>
/// The id of the property editor
@@ -1,5 +1,4 @@
using System;
using umbraco.interfaces;
namespace Umbraco.Core.PropertyEditors
{
@@ -8,8 +7,8 @@ namespace Umbraco.Core.PropertyEditors
/// </summary>
// todo: drop IPropertyEditorValueConverter support (when?).
[Obsolete("Use IPropertyValueConverter.")]
public interface IPropertyEditorValueConverter : IDiscoverable
{
public interface IPropertyEditorValueConverter
{
/// <summary>
/// Returns a value indicating whether this provider applies to the specified property.
/// </summary>
@@ -1,12 +1,11 @@
using umbraco.interfaces;
using Umbraco.Core.Models.PublishedContent;
using Umbraco.Core.Models.PublishedContent;
namespace Umbraco.Core.PropertyEditors
{
/// <summary>
/// Provides published content properties conversion service.
/// </summary>
public interface IPropertyValueConverter : IDiscoverable
public interface IPropertyValueConverter
{
/// <summary>
/// Gets a value indicating whether the converter supports a property type.
@@ -1,8 +1,6 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Umbraco.Core.Configuration;
using Umbraco.Core.Configuration.UmbracoSettings;
using Umbraco.Core.Logging;
using Umbraco.Core.Manifest;
using Umbraco.Core.ObjectResolution;
@@ -14,18 +12,16 @@ namespace Umbraco.Core.PropertyEditors
/// </summary>
/// <remarks>
/// This resolver will contain any parameter editors defined in manifests as well as any property editors defined in manifests
/// that have the IsParameterEditorFlag = true and any PropertyEditors found in c# that have this flag as well.
/// that have the IsParameterEditorFlag = true and any PropertyEditors found in c# that have this flag as well.
/// </remarks>
internal class ParameterEditorResolver : LazyManyObjectsResolverBase<ParameterEditorResolver, IParameterEditor>
{
private readonly ManifestBuilder _builder;
private readonly IContentSection _contentSection;
public ParameterEditorResolver(IServiceProvider serviceProvider, ILogger logger, Func<IEnumerable<Type>> typeListProducerList, ManifestBuilder builder)
: base(serviceProvider, logger, typeListProducerList, ObjectLifetimeScope.Application)
{
_builder = builder;
_contentSection = UmbracoConfig.For.UmbracoSettings().Content;
}
/// <summary>
@@ -33,51 +29,40 @@ namespace Umbraco.Core.PropertyEditors
/// </summary>
public IEnumerable<IParameterEditor> ParameterEditors
{
get { return GetParameterEditors(); }
}
public IEnumerable<IParameterEditor> GetParameterEditors(bool includeDeprecated = false)
{
// all property editors and parameter editors
// except property editors where !IsParameterEditor
var values = Values
.Where(x => x is PropertyEditor == false || ((PropertyEditor) x).IsParameterEditor);
// union all manifest parameter editors
values = values
.Union(_builder.ParameterEditors);
// union all manifest property editors where IsParameterEditor
values = values
.Union(_builder.PropertyEditors.Where(x => x.IsParameterEditor));
if (includeDeprecated == false && _contentSection.ShowDeprecatedPropertyEditors == false)
get
{
// except deprecated property editors
values = values
.Where(x => x is PropertyEditor == false || ((PropertyEditor) x).IsDeprecated == false);
//This will by default include all property editors and parameter editors but we need to filter this
//list to ensure that none of the property editors that do not have the IsParameterEditor flag set to true
//are filtered.
var filtered = Values.Select(x => x as PropertyEditor)
.WhereNotNull()
.Where(x => x.IsParameterEditor == false);
return Values
//exclude the non parameter editor c# property editors
.Except(filtered)
//include the manifest parameter editors
.Union(_builder.ParameterEditors)
//include the manifest prop editors that are parameter editors
.Union(_builder.PropertyEditors.Where(x => x.IsParameterEditor));
}
return values;
}
/// <summary>
/// Returns a property editor by alias
/// </summary>
/// <param name="alias"></param>
/// <param name="includeDeprecated"></param>
/// <returns></returns>
public IParameterEditor GetByAlias(string alias, bool includeDeprecated = false)
public IParameterEditor GetByAlias(string alias)
{
var paramEditors = GetParameterEditors(includeDeprecated).ToArray();
var found = paramEditors.SingleOrDefault(x => x.Alias == alias);
var found = ParameterEditors.SingleOrDefault(x => x.Alias == alias);
if (found != null) return found;
//couldn't find one, so try the map
var mapped = LegacyParameterEditorAliasConverter.GetNewAliasFromLegacyAlias(alias);
return mapped == null
? null
: paramEditors.SingleOrDefault(x => x.Alias == mapped);
return mapped == null
? null
: ParameterEditors.SingleOrDefault(x => x.Alias == mapped);
}
}
}
@@ -1,5 +1,4 @@
using System;
using System.Collections.Concurrent;
using System.Linq;
using Umbraco.Core.Configuration;
using Umbraco.Core.Models;
@@ -89,26 +88,13 @@ namespace Umbraco.Core.PropertyEditors.ValueConverters
/// </returns>
private bool IsRangeDataType(int dataTypeId)
{
// GetPreValuesCollectionByDataTypeId is cached at repository level;
// still, the collection is deep-cloned so this is kinda expensive,
// better to cache here + trigger refresh in DataTypeCacheRefresher
// ** This must be cached (U4-8862) **
var enableRange =
_dataTypeService.GetPreValuesCollectionByDataTypeId(dataTypeId)
.PreValuesAsDictionary.FirstOrDefault(
x => string.Equals(x.Key, "enableRange", StringComparison.InvariantCultureIgnoreCase)).Value;
return Storages.GetOrAdd(dataTypeId, id =>
{
var preValue = _dataTypeService.GetPreValuesCollectionByDataTypeId(id)
.PreValuesAsDictionary
.FirstOrDefault(x => string.Equals(x.Key, "enableRange", StringComparison.InvariantCultureIgnoreCase))
.Value;
return preValue != null && preValue.Value.TryConvertTo<bool>().Result;
});
}
private static readonly ConcurrentDictionary<int, bool> Storages = new ConcurrentDictionary<int, bool>();
internal static void ClearCaches()
{
Storages.Clear();
return enableRange != null && enableRange.Value.TryConvertTo<bool>().Result;
}
}
}
@@ -1,12 +1,9 @@
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using Umbraco.Core.Configuration;
using Umbraco.Core.Events;
using Umbraco.Core.Models;
using Umbraco.Core.Models.PublishedContent;
using Umbraco.Core.Services;
@@ -77,26 +74,18 @@ namespace Umbraco.Core.PropertyEditors.ValueConverters
/// </returns>
private bool JsonStorageType(int dataTypeId)
{
// GetPreValuesCollectionByDataTypeId is cached at repository level;
// still, the collection is deep-cloned so this is kinda expensive,
// better to cache here + trigger refresh in DataTypeCacheRefresher
// ** This must be cached (U4-8862) **
var storageType =
_dataTypeService.GetPreValuesCollectionByDataTypeId(dataTypeId)
.PreValuesAsDictionary.FirstOrDefault(
x => string.Equals(x.Key, "storageType", StringComparison.InvariantCultureIgnoreCase)).Value;
return Storages.GetOrAdd(dataTypeId, id =>
if (storageType != null && storageType.Value.InvariantEquals("Json"))
{
var preValue = _dataTypeService.GetPreValuesCollectionByDataTypeId(id)
.PreValuesAsDictionary
.FirstOrDefault(x => string.Equals(x.Key, "storageType", StringComparison.InvariantCultureIgnoreCase))
.Value;
return true;
}
return preValue != null && preValue.Value.InvariantEquals("json");
});
}
private static readonly ConcurrentDictionary<int, bool> Storages = new ConcurrentDictionary<int, bool>();
internal static void ClearCaches()
{
Storages.Clear();
return false;
}
}
}
+3 -4
View File
@@ -1,5 +1,4 @@
using System.Collections.Generic;
using System.Linq;
using Umbraco.Core.Events;
using Umbraco.Core.Models;
using Umbraco.Core.Services;
@@ -13,8 +12,7 @@ namespace Umbraco.Core.Publishing
{
public PublishStatus(IContent content, PublishStatusType statusType, EventMessages eventMessages)
: base(content, statusType, eventMessages)
{
InvalidProperties = Enumerable.Empty<Property>();
{
}
/// <summary>
@@ -22,7 +20,8 @@ namespace Umbraco.Core.Publishing
/// </summary>
public PublishStatus(IContent content, EventMessages eventMessages)
: this(content, PublishStatusType.Success, eventMessages)
{ }
{
}
public IContent ContentItem
{
@@ -51,7 +51,7 @@ namespace Umbraco.Core.Publishing
/// </summary>
/// <param name="uow"></param>
/// <param name="content"><see cref="IContent"/> to publish</param>
/// <param name="userId">Id of the User issueing the publish operation</param>
/// <param name="userId">Id of the User issueing the publish operation</param>
Attempt<PublishStatus> IPublishingStrategy2.Publish(IScopeUnitOfWork uow, IContent content, int userId)
{
var evtMsgs = _eventMessagesFactory.Get();
@@ -124,26 +124,26 @@ namespace Umbraco.Core.Publishing
/// By default this is set to true which means that it will publish any content item in the list that is completely unpublished and
/// not visible on the front-end. If set to false, this will only publish content that is live on the front-end but has new versions
/// that have yet to be published.
/// </param>
/// </param>
/// <returns></returns>
/// <remarks>
///
///
/// This method becomes complex once we start to be able to cancel events or stop publishing a content item in any way because if a
/// content item is not published then it's children shouldn't be published either. This rule will apply for the following conditions:
/// * If a document fails to be published, do not proceed to publish it's children if:
/// ** The document does not have a publish version
/// ** The document does have a published version but the includeUnpublishedDocuments = false
///
///
/// In order to do this, we will order the content by level and begin by publishing each item at that level, then proceed to the next
/// level and so on. If we detect that the above rule applies when the document publishing is cancelled we'll add it to the list of
/// level and so on. If we detect that the above rule applies when the document publishing is cancelled we'll add it to the list of
/// parentsIdsCancelled so that it's children don't get published.
///
///
/// Its important to note that all 'root' documents included in the list *will* be published regardless of the rules mentioned
/// above (unless it is invalid)!! By 'root' documents we are referring to documents in the list with the minimum value for their 'level'.
/// In most cases the 'root' documents will only be one document since under normal circumstance we only publish one document and
/// above (unless it is invalid)!! By 'root' documents we are referring to documents in the list with the minimum value for their 'level'.
/// In most cases the 'root' documents will only be one document since under normal circumstance we only publish one document and
/// its children. The reason we have to do this is because if a user is publishing a document and it's children, it is implied that
/// the user definitely wants to publish it even if it has never been published before.
///
///
/// </remarks>
IEnumerable<Attempt<PublishStatus>> IPublishingStrategy2.PublishWithChildren(IScopeUnitOfWork uow,
IEnumerable<IContent> content, int userId, bool includeUnpublishedDocuments)
@@ -157,7 +157,7 @@ namespace Umbraco.Core.Publishing
//group by levels and iterate over the sorted ascending level.
//TODO: This will cause all queries to execute, they will not be lazy but I'm not really sure being lazy actually made
// much difference because we iterate over them all anyways?? Morten?
// much difference because we iterate over them all anyways?? Morten?
// Because we're grouping I think this will execute all the queries anyways so need to fetch it all first.
var fetchedContent = content.ToArray();
@@ -174,7 +174,7 @@ namespace Umbraco.Core.Publishing
var levelGroups = fetchedContent.GroupBy(x => x.Level);
foreach (var level in levelGroups.OrderBy(x => x.Key))
{
//set the first level flag, used to ensure that all documents at the first level will
//set the first level flag, used to ensure that all documents at the first level will
//be published regardless of the rules mentioned in the remarks.
if (!firstLevel.HasValue)
{
@@ -224,10 +224,7 @@ namespace Umbraco.Core.Publishing
_logger.Info<PublishingStrategy>(
string.Format("Content '{0}' with Id '{1}' will not be published because some of it's content is not passing validation rules.",
item.Name, item.Id));
statuses.Add(Attempt.Fail(new PublishStatus(item, PublishStatusType.FailedContentInvalid, evtMsgs)
{
InvalidProperties = ((ContentBase)item).LastInvalidProperties
}));
statuses.Add(Attempt.Fail(new PublishStatus(item, PublishStatusType.FailedContentInvalid, evtMsgs)));
//Does this document apply to our rule to cancel it's children being published?
CheckCancellingOfChildPublishing(item, parentsIdsCancelled, includeUnpublishedDocuments);
@@ -299,11 +296,11 @@ namespace Umbraco.Core.Publishing
/// <param name="includeUnpublishedDocuments"></param>
/// <remarks>
/// See remarks on method: PublishWithChildrenInternal
/// </remarks>
/// </remarks>
private void CheckCancellingOfChildPublishing(IContent content, List<int> parentsIdsCancelled, bool includeUnpublishedDocuments)
{
//Does this document apply to our rule to cancel it's children being published?
//TODO: We're going back to the service layer here... not sure how to avoid this? And this will add extra overhead to
//TODO: We're going back to the service layer here... not sure how to avoid this? And this will add extra overhead to
// any document that fails to publish...
var hasPublishedVersion = ApplicationContext.Current.Services.ContentService.HasPublishedVersion(content.Id);
@@ -1,5 +1,4 @@
using System;
using System.Linq;
using Umbraco.Core.Logging;
using Umbraco.Core.Models;
using Umbraco.Core.Services;
@@ -27,16 +26,12 @@ namespace Umbraco.Core.Publishing
public int CheckPendingAndProcess()
{
var counter = 0;
var contentForRelease = _contentService.GetContentForRelease().ToArray();
if (contentForRelease.Length > 0)
LogHelper.Debug<ScheduledPublisher>(string.Format("There's {0} item(s) of content to be published", contentForRelease.Length));
foreach (var d in contentForRelease)
foreach (var d in _contentService.GetContentForRelease())
{
try
{
d.ReleaseDate = null;
var result = _contentService.SaveAndPublishWithStatus(d, (int)d.GetWriterProfile().Id);
LogHelper.Debug<ContentService>(string.Format("Result of publish attempt: {0}", result.Result.StatusType));
if (result.Success == false)
{
if (result.Exception != null)
@@ -59,11 +54,7 @@ namespace Umbraco.Core.Publishing
throw;
}
}
var contentForExpiration = _contentService.GetContentForExpiration().ToArray();
if (contentForExpiration.Length > 0)
LogHelper.Debug<ScheduledPublisher>(string.Format("There's {0} item(s) of content to be unpublished", contentForExpiration.Length));
foreach (var d in contentForExpiration)
foreach (var d in _contentService.GetContentForExpiration())
{
try
{
-1
View File
@@ -465,7 +465,6 @@ namespace Umbraco.Core.Scoping
}
finally
{
// removes the ambient context (ambient scope already gone)
_scopeProvider.SetAmbient(null);
}
}
+40 -46
View File
@@ -1,23 +1,16 @@
using System;
using System.Collections.Generic;
using System.Linq;
namespace Umbraco.Core.Scoping
{
public class ScopeContext : IInstanceIdentifiable
{
private Dictionary<string, IEnlistedObject> _enlisted;
private bool _exiting;
public void ScopeExit(bool completed)
{
if (_enlisted == null)
return;
_exiting = true;
List<Exception> exceptions = null;
foreach (var enlisted in _enlisted.Values.OrderBy(x => x.Priority))
foreach (var enlisted in Enlisted.Values)
{
try
{
@@ -30,7 +23,6 @@ namespace Umbraco.Core.Scoping
exceptions.Add(e);
}
}
if (exceptions != null)
throw new AggregateException("Exceptions were thrown by listed actions.", exceptions);
}
@@ -50,71 +42,73 @@ namespace Umbraco.Core.Scoping
private interface IEnlistedObject
{
void Execute(bool completed);
int Priority { get; }
}
private class EnlistedObject<T> : IEnlistedObject
{
private readonly Action<bool, T> _action;
public EnlistedObject(T item, Action<bool, T> action, int priority)
public EnlistedObject(T item)
{
Item = item;
}
public EnlistedObject(T item, Action<bool, T> action)
{
Item = item;
Priority = priority;
_action = action;
}
public T Item { get; private set; }
public int Priority { get; private set; }
public void Execute(bool completed)
{
_action(completed, Item);
}
}
// todo: replace with optional parameters when we can break things
/// <inheritdoc />
public T Enlist<T>(string key, Func<T> creator)
{
return Enlist(key, creator, null, 100);
}
// todo: replace with optional parameters when we can break things
public T Enlist<T>(string key, Func<T> creator, Action<bool, T> action)
{
return Enlist(key, creator, action, 100);
}
// todo: replace with optional parameters when we can break things
public void Enlist(string key, Action<bool> action)
{
Enlist<object>(key, null, (completed, item) => action(completed), 100);
}
public void Enlist(string key, Action<bool> action, int priority)
{
Enlist<object>(key, null, (completed, item) => action(completed), priority);
}
public T Enlist<T>(string key, Func<T> creator, Action<bool, T> action, int priority)
{
if (_exiting)
throw new InvalidOperationException("Cannot enlist now, context is exiting.");
var enlistedObjects = _enlisted ?? (_enlisted = new Dictionary<string, IEnlistedObject>());
IEnlistedObject enlisted;
if (enlistedObjects.TryGetValue(key, out enlisted))
if (Enlisted.TryGetValue(key, out enlisted))
{
var enlistedAs = enlisted as EnlistedObject<T>;
if (enlistedAs == null) throw new InvalidOperationException("An item with the key already exists, but with a different type.");
if (enlistedAs.Priority != priority) throw new InvalidOperationException("An item with the key already exits, but with a different priority.");
if (enlistedAs == null) throw new Exception("An item with a different type has already been enlisted with the same key.");
return enlistedAs.Item;
}
var enlistedOfT = new EnlistedObject<T>(creator == null ? default(T) : creator(), action, priority);
var enlistedOfT = new EnlistedObject<T>(creator());
Enlisted[key] = enlistedOfT;
return enlistedOfT.Item;
}
/// <inheritdoc />
public T Enlist<T>(string key, Func<T> creator, Action<bool, T> action)
{
IEnlistedObject enlisted;
if (Enlisted.TryGetValue(key, out enlisted))
{
var enlistedAs = enlisted as EnlistedObject<T>;
if (enlistedAs == null) throw new Exception("An item with a different type has already been enlisted with the same key.");
return enlistedAs.Item;
}
var enlistedOfT = new EnlistedObject<T>(creator(), action);
Enlisted[key] = enlistedOfT;
return enlistedOfT.Item;
}
/// <inheritdoc />
public void Enlist(string key, Action<bool> action)
{
IEnlistedObject enlisted;
if (Enlisted.TryGetValue(key, out enlisted))
{
var enlistedAs = enlisted as EnlistedObject<object>;
if (enlistedAs == null) throw new Exception("An item with a different type has already been enlisted with the same key.");
return;
}
var enlistedOfT = new EnlistedObject<object>(null, (completed, item) => action(completed));
Enlisted[key] = enlistedOfT;
}
}
}
@@ -80,41 +80,7 @@ namespace Umbraco.Core.Security
return false;
}
/// <summary>
/// This will return the current back office identity if the IPrincipal is the correct type
/// </summary>
/// <param name="user"></param>
/// <returns></returns>
internal static UmbracoBackOfficeIdentity GetUmbracoIdentity(this IPrincipal user)
{
//If it's already a UmbracoBackOfficeIdentity
var backOfficeIdentity = user.Identity as UmbracoBackOfficeIdentity;
if (backOfficeIdentity != null) return backOfficeIdentity;
//Check if there's more than one identity assigned and see if it's a UmbracoBackOfficeIdentity and use that
var claimsPrincipal = user as ClaimsPrincipal;
if (claimsPrincipal != null)
{
backOfficeIdentity = claimsPrincipal.Identities.OfType<UmbracoBackOfficeIdentity>().FirstOrDefault();
if (backOfficeIdentity != null) return backOfficeIdentity;
}
//Otherwise convert to a UmbracoBackOfficeIdentity if it's auth'd and has the back office session
var claimsIdentity = user.Identity as ClaimsIdentity;
if (claimsIdentity != null && claimsIdentity.IsAuthenticated && claimsIdentity.HasClaim(x => x.Type == Constants.Security.SessionIdClaimType))
{
try
{
return UmbracoBackOfficeIdentity.FromClaimsIdentity(claimsIdentity);
}
catch (InvalidOperationException)
{
}
}
return null;
}
/// <summary>
/// This will return the current back office identity.
@@ -134,8 +100,31 @@ namespace Umbraco.Core.Security
if (http.User == null) return null; //there's no user at all so no identity
//If it's already a UmbracoBackOfficeIdentity
var backOfficeIdentity = GetUmbracoIdentity(http.User);
if (backOfficeIdentity != null) return backOfficeIdentity;
var backOfficeIdentity = http.User.Identity as UmbracoBackOfficeIdentity;
if (backOfficeIdentity != null) return backOfficeIdentity;
//Check if there's more than one identity assigned and see if it's a UmbracoBackOfficeIdentity and use that
var claimsPrincipal = http.User as ClaimsPrincipal;
if (claimsPrincipal != null)
{
backOfficeIdentity = claimsPrincipal.Identities.OfType<UmbracoBackOfficeIdentity>().FirstOrDefault();
if (backOfficeIdentity != null) return backOfficeIdentity;
}
//Otherwise convert to a UmbracoBackOfficeIdentity if it's auth'd and has the back office session
var claimsIdentity = http.User.Identity as ClaimsIdentity;
if (claimsIdentity != null && claimsIdentity.IsAuthenticated && claimsIdentity.HasClaim(x => x.Type == Constants.Security.SessionIdClaimType))
{
try
{
return UmbracoBackOfficeIdentity.FromClaimsIdentity(claimsIdentity);
}
catch (InvalidOperationException ex)
{
//This will occur if the required claim types are missing which would mean something strange is going on
LogHelper.Error(typeof(AuthenticationExtensions), "The current identity cannot be converted to " + typeof(UmbracoBackOfficeIdentity), ex);
}
}
if (authenticateRequestIfNotFound == false) return null;
@@ -9,7 +9,6 @@ using System.Web.Configuration;
using System.Web.Hosting;
using System.Web.Security;
using Umbraco.Core.Logging;
using Umbraco.Core.Models;
namespace Umbraco.Core.Security
{
@@ -327,8 +326,7 @@ namespace Umbraco.Core.Security
//Special case to allow changing password without validating existing credentials
//This is used during installation only
if (AllowManuallyChangingPassword == false && ApplicationContext.Current != null
&& ApplicationContext.Current.IsConfigured == false && oldPassword == "default")
if (AllowManuallyChangingPassword == false && ApplicationContext.Current.IsConfigured == false && oldPassword == "default")
{
return PerformChangePassword(username, oldPassword, newPassword);
}
@@ -521,11 +519,7 @@ namespace Umbraco.Core.Security
public override string ResetPassword(string username, string answer)
{
var userService = ApplicationContext.Current == null ? null : ApplicationContext.Current.Services.UserService;
var canReset = this.CanResetPassword(userService);
if (canReset == false)
if (EnablePasswordReset == false)
{
throw new NotSupportedException("Password reset is not supported");
}
@@ -9,45 +9,12 @@ using System.Web;
using System.Web.Hosting;
using System.Web.Security;
using Umbraco.Core.Configuration;
using Umbraco.Core.Models;
using Umbraco.Core.Security;
using Umbraco.Core.Services;
namespace Umbraco.Core.Security
{
public static class MembershipProviderExtensions
{
/// <summary>
/// Extension method to check if a password can be reset based on a given provider and the current request (logged in user)
/// </summary>
/// <param name="provider"></param>
/// <param name="userService"></param>
/// <returns></returns>
internal static bool CanResetPassword(this MembershipProvider provider, IUserService userService)
{
if (provider == null) throw new ArgumentNullException("provider");
var canReset = provider.EnablePasswordReset;
if (userService == null) return canReset;
//we need to check for the special case in which a user is an admin - in which acse they can reset the password even if EnablePasswordReset == false
if (provider.EnablePasswordReset == false)
{
var identity = Thread.CurrentPrincipal.GetUmbracoIdentity();
if (identity != null)
{
var user = userService.GetByUsername(identity.Username);
var userIsAdmin = user.IsAdmin();
if (userIsAdmin)
{
canReset = true;
}
}
}
return canReset;
}
internal static MembershipUserCollection FindUsersByName(this MembershipProvider provider, string usernameToMatch)
{
int totalRecords = 0;
@@ -358,8 +358,9 @@ namespace Umbraco.Core.Services
{
var applicationAlias = (string)addElement.Attribute("application");
var type = (string)addElement.Attribute("type");
var clrType = ApplicationTree.TryGetType(type);
var assembly = (string)addElement.Attribute("assembly");
var clrType = Type.GetType(type);
if (clrType == null)
{
_logger.Warn<ApplicationTreeService>("The tree definition: " + addElement.ToString() + " could not be resolved to a .Net object type");
+1 -3
View File
@@ -1124,9 +1124,7 @@ namespace Umbraco.Core.Services
/// <returns>True if unpublishing succeeded, otherwise False</returns>
public bool UnPublish(IContent content, int userId = 0)
{
var attempt = ((IContentServiceOperations)this).UnPublish(content, userId);
LogHelper.Debug<ContentService>(string.Format("Result of unpublish attempt: {0}", attempt.Result.StatusType));
return attempt.Success;
return ((IContentServiceOperations)this).UnPublish(content, userId).Success;
}
/// <summary>
+28 -37
View File
@@ -19,15 +19,14 @@ namespace Umbraco.Core.Services
/// </summary>
public class ContentTypeService : ContentTypeServiceBase, IContentTypeService
{
private readonly IContentService _contentService;
private readonly IContentService _contentService;
private readonly IMediaService _mediaService;
//Support recursive locks because some of the methods that require locking call other methods that require locking.
//for example, the Move method needs to be locked but this calls the Save method which also needs to be locked.
private static readonly ReaderWriterLockSlim Locker = new ReaderWriterLockSlim(LockRecursionPolicy.SupportsRecursion);
public ContentTypeService(IDatabaseUnitOfWorkProvider provider, RepositoryFactory repositoryFactory, ILogger logger, IEventMessagesFactory eventMessagesFactory, IContentService contentService,
IMediaService mediaService)
public ContentTypeService(IDatabaseUnitOfWorkProvider provider, RepositoryFactory repositoryFactory, ILogger logger, IEventMessagesFactory eventMessagesFactory, IContentService contentService, IMediaService mediaService)
: base(provider, repositoryFactory, logger, eventMessagesFactory)
{
if (contentService == null) throw new ArgumentNullException("contentService");
@@ -211,7 +210,7 @@ namespace Umbraco.Core.Services
public IEnumerable<EntityContainer> GetMediaTypeContainers(IMediaType mediaType)
{
var ancestorIds = mediaType.Path.Split(new[] {','}, StringSplitOptions.RemoveEmptyEntries)
var ancestorIds = mediaType.Path.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries)
.Select(x =>
{
var asInt = x.TryConvertTo<int>();
@@ -298,7 +297,7 @@ namespace Umbraco.Core.Services
repo.Delete(container);
uow.Commit();
uow.Events.Dispatch(DeletedContentTypeContainer, this, new DeleteEventArgs<EntityContainer>(container, evtMsgs), "DeletedContentTypeContainer");
uow.Events.Dispatch(DeletedContentTypeContainer, this, new DeleteEventArgs<EntityContainer>(container, evtMsgs));
return OperationStatus.Success(evtMsgs);
//TODO: Audit trail ?
@@ -426,7 +425,7 @@ namespace Umbraco.Core.Services
clone.Name = name;
var compositionAliases = clone.CompositionAliases().Except(new[] {alias}).ToList();
var compositionAliases = clone.CompositionAliases().Except(new[] { alias }).ToList();
//remove all composition that is not it's current alias
foreach (var a in compositionAliases)
{
@@ -713,7 +712,7 @@ namespace Umbraco.Core.Services
var comparer = new DelegateEqualityComparer<IContentTypeComposition>((x, y) => x.Id == y.Id, x => x.Id);
var dependencies = new HashSet<IContentTypeComposition>(compositions, comparer);
var stack = new Stack<IContentTypeComposition>();
indirectReferences.ForEach(stack.Push); //Push indirect references to a stack, so we can add recursively
indirectReferences.ForEach(stack.Push);//Push indirect references to a stack, so we can add recursively
while (stack.Count > 0)
{
var indirectReference = stack.Pop();
@@ -771,8 +770,6 @@ namespace Umbraco.Core.Services
ValidateLocked(contentType); // throws if invalid
contentType.CreatorId = userId;
if (contentType.Description == string.Empty)
contentType.Description = null;
repository.AddOrUpdate(contentType);
uow.Commit();
@@ -815,8 +812,6 @@ namespace Umbraco.Core.Services
foreach (var contentType in asArray)
{
contentType.CreatorId = userId;
if (contentType.Description == string.Empty)
contentType.Description = null;
repository.AddOrUpdate(contentType);
}
@@ -1238,8 +1233,6 @@ namespace Umbraco.Core.Services
ValidateLocked(mediaType); // throws if invalid
mediaType.CreatorId = userId;
if (mediaType.Description == string.Empty)
mediaType.Description = null;
repository.AddOrUpdate(mediaType);
uow.Commit();
@@ -1281,8 +1274,6 @@ namespace Umbraco.Core.Services
foreach (var mediaType in asArray)
{
mediaType.CreatorId = userId;
if (mediaType.Description == string.Empty)
mediaType.Description = null;
repository.AddOrUpdate(mediaType);
}
@@ -1461,40 +1452,40 @@ namespace Umbraco.Core.Services
/// </summary>
public static event TypedEventHandler<IContentTypeService, DeleteEventArgs<IContentType>> DeletingContentType;
/// <summary>
/// Occurs after Delete
/// </summary>
public static event TypedEventHandler<IContentTypeService, DeleteEventArgs<IContentType>> DeletedContentType;
/// <summary>
/// Occurs after Delete
/// </summary>
public static event TypedEventHandler<IContentTypeService, DeleteEventArgs<IContentType>> DeletedContentType;
/// <summary>
/// Occurs before Delete
/// </summary>
public static event TypedEventHandler<IContentTypeService, DeleteEventArgs<IMediaType>> DeletingMediaType;
/// <summary>
/// Occurs before Delete
/// </summary>
public static event TypedEventHandler<IContentTypeService, DeleteEventArgs<IMediaType>> DeletingMediaType;
/// <summary>
/// Occurs after Delete
/// </summary>
public static event TypedEventHandler<IContentTypeService, DeleteEventArgs<IMediaType>> DeletedMediaType;
/// <summary>
/// Occurs after Delete
/// </summary>
public static event TypedEventHandler<IContentTypeService, DeleteEventArgs<IMediaType>> DeletedMediaType;
/// <summary>
/// Occurs before Save
/// </summary>
public static event TypedEventHandler<IContentTypeService, SaveEventArgs<IContentType>> SavingContentType;
public static event TypedEventHandler<IContentTypeService, SaveEventArgs<IContentType>> SavingContentType;
/// <summary>
/// Occurs after Save
/// </summary>
public static event TypedEventHandler<IContentTypeService, SaveEventArgs<IContentType>> SavedContentType;
public static event TypedEventHandler<IContentTypeService, SaveEventArgs<IContentType>> SavedContentType;
/// <summary>
/// Occurs before Save
/// </summary>
public static event TypedEventHandler<IContentTypeService, SaveEventArgs<IMediaType>> SavingMediaType;
/// <summary>
/// Occurs before Save
/// </summary>
public static event TypedEventHandler<IContentTypeService, SaveEventArgs<IMediaType>> SavingMediaType;
/// <summary>
/// Occurs after Save
/// </summary>
public static event TypedEventHandler<IContentTypeService, SaveEventArgs<IMediaType>> SavedMediaType;
/// <summary>
/// Occurs after Save
/// </summary>
public static event TypedEventHandler<IContentTypeService, SaveEventArgs<IMediaType>> SavedMediaType;
/// <summary>
/// Occurs before Move
@@ -5,7 +5,6 @@ using Umbraco.Core.Logging;
using Umbraco.Core.Models;
using Umbraco.Core.Models.EntityBase;
using Umbraco.Core.Persistence;
using Umbraco.Core.Persistence.Repositories;
using Umbraco.Core.Persistence.UnitOfWork;
namespace Umbraco.Core.Services
@@ -75,20 +74,5 @@ namespace Umbraco.Core.Services
{
return Enumerable.Empty<IContentTypeBase>();
}
/// <summary>
/// Given the path of a content item, this will return true if the content item exists underneath a list view content item
/// </summary>
/// <param name="contentPath"></param>
/// <returns></returns>
public bool HasContainerInPath(string contentPath)
{
using (var uow = UowProvider.GetUnitOfWork())
{
// can use same repo for both content and media
var repository = RepositoryFactory.CreateContentTypeRepository(uow);
return repository.HasContainerInPath(contentPath);
}
}
}
}
+86 -50
View File
@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Umbraco.Core.Cache;
using Umbraco.Core.CodeAnnotations;
using Umbraco.Core.Events;
@@ -78,18 +79,31 @@ namespace Umbraco.Core.Services
{
var result = _runtimeCache.GetCacheItem<int?>(CacheKeys.IdToKeyCacheKey + key, () =>
{
using (var uow = UowProvider.GetUnitOfWork(readOnly:true))
using (var uow = UowProvider.GetUnitOfWork())
{
var nodeObjectType = GetNodeObjectTypeGuid(umbracoObjectType);
var sql = new Sql()
.Select("id")
.From<NodeDto>()
.Where<NodeDto>(
dto =>
dto.UniqueId == key &&
dto.NodeObjectType == nodeObjectType);
return uow.Database.ExecuteScalar<int?>(sql);
switch (umbracoObjectType)
{
case UmbracoObjectTypes.Document:
case UmbracoObjectTypes.MemberType:
case UmbracoObjectTypes.Media:
case UmbracoObjectTypes.Template:
case UmbracoObjectTypes.MediaType:
case UmbracoObjectTypes.DocumentType:
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:
case UmbracoObjectTypes.MemberGroup:
case UmbracoObjectTypes.ContentItem:
case UmbracoObjectTypes.ContentItemType:
case UmbracoObjectTypes.ROOT:
case UmbracoObjectTypes.Unknown:
default:
throw new NotSupportedException();
}
}
});
return result.HasValue ? Attempt.Succeed(result.Value) : Attempt<int>.Fail();
@@ -105,32 +119,35 @@ namespace Umbraco.Core.Services
{
var result = _runtimeCache.GetCacheItem<Guid?>(CacheKeys.KeyToIdCacheKey + id, () =>
{
using (var uow = UowProvider.GetUnitOfWork(readOnly:true))
using (var uow = UowProvider.GetUnitOfWork())
{
var nodeObjectType = GetNodeObjectTypeGuid(umbracoObjectType);
var sql = new Sql()
.Select("uniqueID")
.From<NodeDto>()
.Where<NodeDto>(
dto =>
dto.NodeId == id &&
dto.NodeObjectType == nodeObjectType);
return uow.Database.ExecuteScalar<Guid?>(sql);
switch (umbracoObjectType)
{
case UmbracoObjectTypes.Document:
case UmbracoObjectTypes.MemberType:
case UmbracoObjectTypes.Media:
case UmbracoObjectTypes.Template:
case UmbracoObjectTypes.MediaType:
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:
case UmbracoObjectTypes.MemberGroup:
case UmbracoObjectTypes.ContentItem:
case UmbracoObjectTypes.ContentItemType:
case UmbracoObjectTypes.ROOT:
case UmbracoObjectTypes.Unknown:
default:
throw new NotSupportedException("Unsupported object type (" + umbracoObjectType + ").");
}
}
});
return result.HasValue ? Attempt.Succeed(result.Value) : Attempt<Guid>.Fail();
}
private static Guid GetNodeObjectTypeGuid(UmbracoObjectTypes umbracoObjectType)
{
var guid = umbracoObjectType.GetGuid();
if (guid == Guid.Empty)
throw new NotSupportedException("Unsupported object type (" + umbracoObjectType + ").");
return guid;
}
public IUmbracoEntity GetByKey(Guid key, bool loadBaseType = true)
{
if (loadBaseType)
@@ -168,10 +185,11 @@ namespace Umbraco.Core.Services
{
if (loadBaseType)
{
using (var uow = UowProvider.GetUnitOfWork(readOnly:true))
using (var uow = UowProvider.GetUnitOfWork())
{
var repository = RepositoryFactory.CreateEntityRepository(uow);
var ret = repository.Get(id);
uow.Commit();
return ret;
}
}
@@ -189,10 +207,11 @@ namespace Umbraco.Core.Services
if (loadBaseType)
{
var objectTypeId = umbracoObjectType.GetGuid();
using (var uow = UowProvider.GetUnitOfWork(readOnly:true))
using (var uow = UowProvider.GetUnitOfWork())
{
var repository = RepositoryFactory.CreateEntityRepository(uow);
var ret = repository.GetByKey(key, objectTypeId);
uow.Commit();
return ret;
}
}
@@ -222,10 +241,11 @@ namespace Umbraco.Core.Services
if (loadBaseType)
{
var objectTypeId = umbracoObjectType.GetGuid();
using (var uow = UowProvider.GetUnitOfWork(readOnly:true))
using (var uow = UowProvider.GetUnitOfWork())
{
var repository = RepositoryFactory.CreateEntityRepository(uow);
var ret = repository.Get(id, objectTypeId);
uow.Commit();
return ret;
}
}
@@ -256,10 +276,11 @@ namespace Umbraco.Core.Services
{
if (loadBaseType)
{
using (var uow = UowProvider.GetUnitOfWork(readOnly:true))
using (var uow = UowProvider.GetUnitOfWork())
{
var repository = RepositoryFactory.CreateEntityRepository(uow);
var ret = repository.Get(id);
uow.Commit();
return ret;
}
}
@@ -282,7 +303,7 @@ namespace Umbraco.Core.Services
/// <returns>An <see cref="IUmbracoEntity"/></returns>
public virtual IUmbracoEntity GetParent(int id)
{
using (var uow = UowProvider.GetUnitOfWork(readOnly:true))
using (var uow = UowProvider.GetUnitOfWork())
{
var repository = RepositoryFactory.CreateEntityRepository(uow);
var entity = repository.Get(id);
@@ -290,6 +311,7 @@ namespace Umbraco.Core.Services
if (entity.ParentId == -1 || entity.ParentId == -20 || entity.ParentId == -21)
return null;
var ret = repository.Get(entity.ParentId);
uow.Commit();
return ret;
}
}
@@ -302,7 +324,7 @@ namespace Umbraco.Core.Services
/// <returns>An <see cref="IUmbracoEntity"/></returns>
public virtual IUmbracoEntity GetParent(int id, UmbracoObjectTypes umbracoObjectType)
{
using (var uow = UowProvider.GetUnitOfWork(readOnly:true))
using (var uow = UowProvider.GetUnitOfWork())
{
var repository = RepositoryFactory.CreateEntityRepository(uow);
var entity = repository.Get(id);
@@ -312,6 +334,7 @@ namespace Umbraco.Core.Services
var objectTypeId = umbracoObjectType.GetGuid();
var ret = repository.Get(entity.ParentId, objectTypeId);
uow.Commit();
return ret;
}
}
@@ -323,12 +346,13 @@ namespace Umbraco.Core.Services
/// <returns>An enumerable list of <see cref="IUmbracoEntity"/> objects</returns>
public virtual IEnumerable<IUmbracoEntity> GetChildren(int parentId)
{
using (var uow = UowProvider.GetUnitOfWork(readOnly:true))
using (var uow = UowProvider.GetUnitOfWork())
{
var repository = RepositoryFactory.CreateEntityRepository(uow);
var query = Query<IUmbracoEntity>.Builder.Where(x => x.ParentId == parentId);
var contents = repository.GetByQuery(query);
uow.Commit();
return contents;
}
}
@@ -342,12 +366,13 @@ namespace Umbraco.Core.Services
public virtual IEnumerable<IUmbracoEntity> GetChildren(int parentId, UmbracoObjectTypes umbracoObjectType)
{
var objectTypeId = umbracoObjectType.GetGuid();
using (var uow = UowProvider.GetUnitOfWork(readOnly:true))
using (var uow = UowProvider.GetUnitOfWork())
{
var repository = RepositoryFactory.CreateEntityRepository(uow);
var query = Query<IUmbracoEntity>.Builder.Where(x => x.ParentId == parentId);
var contents = repository.GetByQuery(query, objectTypeId);
uow.Commit();
return contents;
}
}
@@ -368,7 +393,7 @@ namespace Umbraco.Core.Services
string orderBy = "SortOrder", Direction orderDirection = Direction.Ascending, string filter = "")
{
var objectTypeId = umbracoObjectType.GetGuid();
using (var uow = UowProvider.GetUnitOfWork(readOnly:true))
using (var uow = UowProvider.GetUnitOfWork())
{
var repository = RepositoryFactory.CreateEntityRepository(uow);
var query = Query<IUmbracoEntity>.Builder.Where(x => x.ParentId == parentId && x.Trashed == false);
@@ -380,6 +405,7 @@ namespace Umbraco.Core.Services
}
var contents = repository.GetPagedResultsByQuery(query, objectTypeId, pageIndex, pageSize, out totalRecords, orderBy, orderDirection, filterQuery);
uow.Commit();
return contents;
}
}
@@ -400,7 +426,7 @@ namespace Umbraco.Core.Services
string orderBy = "path", Direction orderDirection = Direction.Ascending, string filter = "")
{
var objectTypeId = umbracoObjectType.GetGuid();
using (var uow = UowProvider.GetUnitOfWork(readOnly:true))
using (var uow = UowProvider.GetUnitOfWork())
{
var repository = RepositoryFactory.CreateEntityRepository(uow);
@@ -416,6 +442,7 @@ namespace Umbraco.Core.Services
}
var contents = repository.GetPagedResultsByQuery(query, objectTypeId, pageIndex, pageSize, out totalRecords, orderBy, orderDirection, filterQuery);
uow.Commit();
return contents;
}
}
@@ -436,7 +463,7 @@ namespace Umbraco.Core.Services
string orderBy = "path", Direction orderDirection = Direction.Ascending, string filter = "", bool includeTrashed = true)
{
var objectTypeId = umbracoObjectType.GetGuid();
using (var uow = UowProvider.GetUnitOfWork(readOnly:true))
using (var uow = UowProvider.GetUnitOfWork())
{
var repository = RepositoryFactory.CreateEntityRepository(uow);
@@ -454,6 +481,7 @@ namespace Umbraco.Core.Services
}
var contents = repository.GetPagedResultsByQuery(query, objectTypeId, pageIndex, pageSize, out totalRecords, orderBy, orderDirection, filterQuery);
uow.Commit();
return contents;
}
}
@@ -465,7 +493,7 @@ namespace Umbraco.Core.Services
/// <returns>An enumerable list of <see cref="IUmbracoEntity"/> objects</returns>
public virtual IEnumerable<IUmbracoEntity> GetDescendents(int id)
{
using (var uow = UowProvider.GetUnitOfWork(readOnly:true))
using (var uow = UowProvider.GetUnitOfWork())
{
var repository = RepositoryFactory.CreateEntityRepository(uow);
var entity = repository.Get(id);
@@ -473,6 +501,7 @@ namespace Umbraco.Core.Services
var query = Query<IUmbracoEntity>.Builder.Where(x => x.Path.StartsWith(pathMatch) && x.Id != id);
var entities = repository.GetByQuery(query);
uow.Commit();
return entities;
}
}
@@ -486,13 +515,14 @@ namespace Umbraco.Core.Services
public virtual IEnumerable<IUmbracoEntity> GetDescendents(int id, UmbracoObjectTypes umbracoObjectType)
{
var objectTypeId = umbracoObjectType.GetGuid();
using (var uow = UowProvider.GetUnitOfWork(readOnly:true))
using (var uow = UowProvider.GetUnitOfWork())
{
var repository = RepositoryFactory.CreateEntityRepository(uow);
var entity = repository.Get(id);
var query = Query<IUmbracoEntity>.Builder.Where(x => x.Path.StartsWith(entity.Path) && x.Id != id);
var entities = repository.GetByQuery(query, objectTypeId);
uow.Commit();
return entities;
}
}
@@ -511,10 +541,11 @@ namespace Umbraco.Core.Services
}
var objectTypeId = umbracoObjectType.GetGuid();
using (var uow = UowProvider.GetUnitOfWork(readOnly:true))
using (var uow = UowProvider.GetUnitOfWork())
{
var repository = RepositoryFactory.CreateEntityRepository(uow);
var entities = repository.GetByQuery(_rootEntityQuery, objectTypeId);
uow.Commit();
return entities;
}
}
@@ -554,10 +585,11 @@ namespace Umbraco.Core.Services
});
var objectTypeId = umbracoObjectType.GetGuid();
using (var uow = UowProvider.GetUnitOfWork(readOnly:true))
using (var uow = UowProvider.GetUnitOfWork())
{
var repository = RepositoryFactory.CreateEntityRepository(uow);
var ret = repository.GetAll(objectTypeId, ids);
uow.Commit();
return ret;
}
}
@@ -573,10 +605,11 @@ namespace Umbraco.Core.Services
});
var objectTypeId = umbracoObjectType.GetGuid();
using (var uow = UowProvider.GetUnitOfWork(readOnly:true))
using (var uow = UowProvider.GetUnitOfWork())
{
var repository = RepositoryFactory.CreateEntityRepository(uow);
var ret = repository.GetAll(objectTypeId, keys);
uow.Commit();
return ret;
}
}
@@ -598,10 +631,11 @@ namespace Umbraco.Core.Services
("The passed in type is not supported");
});
using (var uow = UowProvider.GetUnitOfWork(readOnly:true))
using (var uow = UowProvider.GetUnitOfWork())
{
var repository = RepositoryFactory.CreateEntityRepository(uow);
var ret = repository.GetAll(objectTypeId, ids);
uow.Commit();
return ret;
}
}
@@ -687,20 +721,22 @@ namespace Umbraco.Core.Services
public bool Exists(Guid key)
{
using (var uow = UowProvider.GetUnitOfWork(readOnly:true))
using (var uow = UowProvider.GetUnitOfWork())
{
var repository = RepositoryFactory.CreateEntityRepository(uow);
var exists = repository.Exists(key);
uow.Commit();
return exists;
}
}
public bool Exists(int id)
{
using (var uow = UowProvider.GetUnitOfWork(readOnly:true))
using (var uow = UowProvider.GetUnitOfWork())
{
var repository = RepositoryFactory.CreateEntityRepository(uow);
var exists = repository.Exists(id);
uow.Commit();
return exists;
}
}
+1 -1
View File
@@ -868,7 +868,7 @@ namespace Umbraco.Core.Services
if (partialView == null)
{
uow.Commit();
return false;
return true;
}
if (uow.Events.DispatchCancelable(DeletingPartialView, this, new DeleteEventArgs<IPartialView>(partialView)))
@@ -11,13 +11,6 @@ namespace Umbraco.Core.Services
/// </summary>
public interface IContentTypeService : IService
{
/// <summary>
/// Given the path of a content item, this will return true if the content item exists underneath a list view content item
/// </summary>
/// <param name="contentPath"></param>
/// <returns></returns>
bool HasContainerInPath(string contentPath);
int CountContentTypes();
int CountMediaTypes();
+6 -1
View File
@@ -468,7 +468,12 @@ namespace Umbraco.Core.Services
/// <param name="filepath">The filesystem path to the media.</param>
void DeleteMediaFile(string filepath);
[Obsolete("This should no longer be used, thumbnail generation should be done via ImageProcessor, Umbraco no longer generates '_thumb' files for media")]
/// <summary>
/// Generates thumbnails.
/// </summary>
/// <param name="filepath">The filesystem-relative path to the original image.</param>
/// <param name="propertyType">The property type.</param>
/// <remarks>This should be obsoleted, we should not generate thumbnails.</remarks>
void GenerateThumbnails(string filepath, PropertyType propertyType);
}
}
+1 -11
View File
@@ -1,5 +1,4 @@
using System;
using System.Collections.Generic;
using System.Collections.Generic;
using System.Xml.Linq;
using Umbraco.Core.Models;
@@ -7,15 +6,6 @@ namespace Umbraco.Core.Services
{
public interface IPackagingService : IService
{
/// <summary>
/// This will fetch an Umbraco package file from the package repository and return the relative file path to the downloaded package file
/// </summary>
/// <param name="packageId"></param>
/// <param name="umbracoVersion"></param>
/// <param name="userId">The current user id performing the operation</param>
/// <returns></returns>
string FetchPackageFile(Guid packageId, Version umbracoVersion, int userId);
/// <summary>
/// Imports and saves package xml as <see cref="IContent"/>
/// </summary>
@@ -1431,7 +1431,6 @@ namespace Umbraco.Core.Services
_mediaFileSystem.DeleteFile(filepath, true);
}
[Obsolete("This should no longer be used, thumbnail generation should be done via ImageProcessor, Umbraco no longer generates '_thumb' files for media")]
public void GenerateThumbnails(string filepath, PropertyType propertyType)
{
using (var filestream = _mediaFileSystem.OpenFile(filepath))
@@ -93,8 +93,6 @@ namespace Umbraco.Core.Services
var repository = RepositoryFactory.CreateMemberTypeRepository(uow);
memberType.CreatorId = userId;
if (memberType.Description == string.Empty)
memberType.Description = null;
repository.AddOrUpdate(memberType);
uow.Commit(); // flush, so that the db contains the saved value
@@ -123,8 +121,6 @@ namespace Umbraco.Core.Services
foreach (var memberType in asArray)
{
memberType.CreatorId = userId;
if (memberType.Description == string.Empty)
memberType.Description = null;
repository.AddOrUpdate(memberType);
}
uow.Commit(); // flush, so that the db contains the saved values
@@ -1,20 +1,15 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Net.Http;
using System.Text.RegularExpressions;
using System.Web;
using System.Web.UI.WebControls;
using System.Xml.Linq;
using System.Xml.XPath;
using Newtonsoft.Json;
using Umbraco.Core.Auditing;
using Umbraco.Core.Configuration;
using Umbraco.Core.Configuration.UmbracoSettings;
using Umbraco.Core.Events;
using Umbraco.Core.Exceptions;
using Umbraco.Core.IO;
using Umbraco.Core.Logging;
using Umbraco.Core.Models;
@@ -80,61 +75,6 @@ namespace Umbraco.Core.Services
_importedContentTypes = new Dictionary<string, IContentType>();
}
/// <summary>
/// This will fetch an Umbraco package file from the package repository and return the relative file path to the downloaded package file
/// </summary>
/// <param name="packageId"></param>
/// <param name="umbracoVersion"></param>
/// /// <param name="userId">The current user id performing the operation</param>
/// <returns></returns>
public string FetchPackageFile(Guid packageId, Version umbracoVersion, int userId)
{
var packageRepo = UmbracoConfig.For.UmbracoSettings().PackageRepositories.GetDefault();
using (var httpClient = new HttpClient())
using (var uow = _uowProvider.GetUnitOfWork())
{
//includeHidden = true because we don't care if it's hidden we want to get the file regardless
var url = string.Format("{0}/{1}?version={2}&includeHidden=true&asFile=true", packageRepo.RestApiUrl, packageId, umbracoVersion.ToString(3));
byte[] bytes;
try
{
bytes = httpClient.GetByteArrayAsync(url).GetAwaiter().GetResult();
}
catch (HttpRequestException ex)
{
throw new ConnectionException("An error occuring downloading the package from " + url, ex);
}
//successfull
if (bytes.Length > 0)
{
var packagePath = IOHelper.MapPath(SystemDirectories.Packages);
// Check for package directory
if (Directory.Exists(packagePath) == false)
Directory.CreateDirectory(packagePath);
var packageFilePath = Path.Combine(packagePath, packageId + ".umb");
using (var fs1 = new FileStream(packageFilePath, FileMode.Create))
{
fs1.Write(bytes, 0, bytes.Length);
return "packages\\" + packageId + ".umb";
}
}
Audit(uow, AuditType.PackagerInstall, string.Format("Package {0} fetched from {1}", packageId, packageRepo.Id), userId, -1);
return null;
}
}
private void Audit(IScopeUnitOfWork uow, AuditType type, string message, int userId, int objectId)
{
var auditRepo = _repositoryFactory.CreateAuditRepository(uow);
auditRepo.AddOrUpdate(new AuditItem(objectId, message, type, userId));
}
#region Content
/// <summary>
+231 -124
View File
@@ -1,24 +1,32 @@
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Security;
using System.Text;
using System.Threading;
using System.Web;
using System.Web.Compilation;
using System.Web.Hosting;
using Umbraco.Core.Configuration;
using Umbraco.Core.IO;
using Umbraco.Core.Logging;
namespace Umbraco.Core
{
/// <summary>
/// A utility class to find all classes of a certain type by reflection in the current bin folder
/// of the web application.
/// </summary>
public static class TypeFinder
{
private static volatile HashSet<Assembly> _localFilteredAssemblyCache;
private static volatile HashSet<Assembly> _localFilteredAssemblyCache = null;
private static readonly object LocalFilteredAssemblyCacheLocker = new object();
/// <summary>
@@ -55,7 +63,7 @@ namespace Umbraco.Core
}
catch (InvalidOperationException e)
{
if ((e.InnerException is SecurityException) == false)
if (!(e.InnerException is SecurityException))
throw;
}
@@ -91,7 +99,7 @@ namespace Umbraco.Core
}
//if for some reason they are still no assemblies, then use the AppDomain to load in already loaded assemblies.
if (assemblies.Any() == false)
if (!assemblies.Any())
{
foreach (var a in AppDomain.CurrentDomain.GetAssemblies())
{
@@ -103,12 +111,12 @@ namespace Umbraco.Core
var fileExtensions = new[] { ".cs", ".vb" }; //only vb and cs files are supported
var appCodeFolder = new DirectoryInfo(IOHelper.MapPath(IOHelper.ResolveUrl("~/App_code")));
//check if the folder exists and if there are any files in it with the supported file extensions
if (appCodeFolder.Exists && fileExtensions.Any(x => appCodeFolder.GetFiles("*" + x).Any()))
if (appCodeFolder.Exists && (fileExtensions.Any(x => appCodeFolder.GetFiles("*" + x).Any())))
{
try
{
var appCodeAssembly = Assembly.Load("App_Code");
if (assemblies.Contains(appCodeAssembly) == false) // BuildManager will find App_Code already
if (!assemblies.Contains(appCodeAssembly)) // BuildManager will find App_Code already
assemblies.Add(appCodeAssembly);
}
catch (FileNotFoundException ex)
@@ -120,7 +128,7 @@ namespace Umbraco.Core
}
catch (InvalidOperationException e)
{
if (e.InnerException is SecurityException == false)
if (!(e.InnerException is SecurityException))
throw;
}
@@ -137,15 +145,23 @@ namespace Umbraco.Core
internal static HashSet<Assembly> GetAssembliesWithKnownExclusions(
IEnumerable<Assembly> excludeFromResults = null)
{
lock (LocalFilteredAssemblyCacheLocker)
if (_localFilteredAssemblyCache == null)
{
if (_localFilteredAssemblyCache != null)
return _localFilteredAssemblyCache;
var assemblies = GetFilteredAssemblies(excludeFromResults, KnownAssemblyExclusionFilter);
_localFilteredAssemblyCache = new HashSet<Assembly>(assemblies);
return _localFilteredAssemblyCache;
lock (LocalFilteredAssemblyCacheLocker)
{
//double check
if (_localFilteredAssemblyCache == null)
{
_localFilteredAssemblyCache = new HashSet<Assembly>();
var assemblies = GetFilteredAssemblies(excludeFromResults, KnownAssemblyExclusionFilter);
foreach (var a in assemblies)
{
_localFilteredAssemblyCache.Add(a);
}
}
}
}
return _localFilteredAssemblyCache;
}
/// <summary>
@@ -164,9 +180,9 @@ namespace Umbraco.Core
exclusionFilter = new string[] { };
return GetAllAssemblies()
.Where(x => excludeFromResults.Contains(x) == false
&& x.GlobalAssemblyCache == false
&& exclusionFilter.Any(f => x.FullName.StartsWith(f)) == false);
.Where(x => !excludeFromResults.Contains(x)
&& !x.GlobalAssemblyCache
&& !exclusionFilter.Any(f => x.FullName.StartsWith(f)));
}
/// <summary>
@@ -282,7 +298,7 @@ namespace Umbraco.Core
{
if (assemblies == null) throw new ArgumentNullException("assemblies");
return GetClassesWithBaseType(assignTypeFrom, assemblies, onlyConcreteClasses,
return GetClasses(assignTypeFrom, assemblies, onlyConcreteClasses,
//the additional filter will ensure that any found types also have the attribute applied.
t => t.GetCustomAttributes<TAttribute>(false).Any());
}
@@ -308,7 +324,7 @@ namespace Umbraco.Core
{
if (assemblies == null) throw new ArgumentNullException("assemblies");
return GetClassesWithBaseType(typeof(T), assemblies, onlyConcreteClasses);
return GetClasses(typeof(T), assemblies, onlyConcreteClasses);
}
/// <summary>
@@ -347,9 +363,105 @@ namespace Umbraco.Core
IEnumerable<Assembly> assemblies,
bool onlyConcreteClasses)
{
return GetClassesWithAttribute(attributeType, assemblies, onlyConcreteClasses);
if (assemblies == null) throw new ArgumentNullException("assemblies");
if (TypeHelper.IsTypeAssignableFrom<Attribute>(attributeType) == false)
throw new ArgumentException("The type specified: " + attributeType + " is not an Attribute type");
var foundAttributedTypes = new HashSet<Type>();
var assemblyList = assemblies.ToArray();
//find all assembly references that are referencing the attribute type's assembly since we
//should only be scanning those assemblies because any other assembly will definitely not
//contain a class that has this attribute.
var referencedAssemblies = TypeHelper.GetReferencedAssemblies(attributeType, assemblyList);
//get a list of non-referenced assemblies (we'll use this when we recurse below)
var otherAssemblies = assemblyList.Where(x => referencedAssemblies.Contains(x) == false).ToArray();
//loop through the referenced assemblies
foreach (var a in referencedAssemblies)
{
//get all types in this assembly
var allTypes = GetTypesWithFormattedException(a)
.ToArray();
var attributedTypes = new Type[] { };
try
{
//now filter the types based on the onlyConcreteClasses flag, not interfaces, not static classes but have
//the specified attribute
attributedTypes = allTypes
.Where(t => (TypeHelper.IsNonStaticClass(t)
&& (onlyConcreteClasses == false || t.IsAbstract == false))
//the type must have this attribute
&& t.GetCustomAttributes(attributeType, false).Any())
.ToArray();
}
catch (TypeLoadException ex)
{
LogHelper.Error(typeof(TypeFinder), string.Format("Could not query types on {0} assembly, this is most likely due to this assembly not being compatible with the current Umbraco version", a), ex);
continue;
}
//add the types to our list to return
foreach (var t in attributedTypes)
{
foundAttributedTypes.Add(t);
}
//get all attributes of the type being searched for
var allAttributeTypes = allTypes.Where(attributeType.IsAssignableFrom);
//now we need to include types that may be inheriting from sub classes of the attribute type being searched for
//so we will search in assemblies that reference those types too.
foreach (var subTypesInAssembly in allAttributeTypes.GroupBy(x => x.Assembly))
{
//So that we are not scanning too much, we need to group the sub types:
// * if there is more than 1 sub type in the same assembly then we should only search on the 'lowest base' type.
// * We should also not search for sub types if the type is sealed since you cannot inherit from a sealed class
// * We should not search for sub types if the type is static since you cannot inherit from them.
var subTypeList = subTypesInAssembly
.Where(t => t.IsSealed == false && TypeHelper.IsStaticClass(t) == false)
.ToArray();
var baseClassAttempt = TypeHelper.GetLowestBaseType(subTypeList);
//if there's a base class amongst the types then we'll only search for that type.
//otherwise we'll have to search for all of them.
var subTypesToSearch = new HashSet<Type>();
if (baseClassAttempt.Success)
{
subTypesToSearch.Add(baseClassAttempt.Result);
}
else
{
foreach (var t in subTypeList)
{
subTypesToSearch.Add(t);
}
}
foreach (var typeToSearch in subTypesToSearch)
{
//recursively find the types inheriting from this sub type in the other non-scanned assemblies.
var foundTypes = FindClassesWithAttribute(typeToSearch, otherAssemblies, onlyConcreteClasses);
foreach (var f in foundTypes)
{
foundAttributedTypes.Add(f);
}
}
}
}
return foundAttributedTypes;
}
/// <summary>
/// Finds the classes with attribute.
/// </summary>
@@ -373,129 +485,122 @@ namespace Umbraco.Core
return FindClassesWithAttribute<T>(GetAssembliesWithKnownExclusions());
}
#region Private methods
private static IEnumerable<Type> GetClassesWithAttribute(
Type attributeType,
IEnumerable<Assembly> assemblies,
bool onlyConcreteClasses)
{
if (typeof(Attribute).IsAssignableFrom(attributeType) == false)
throw new ArgumentException("Type " + attributeType + " is not an Attribute type.");
var candidateAssemblies = new HashSet<Assembly>(assemblies);
var attributeAssemblyIsCandidate = candidateAssemblies.Contains(attributeType.Assembly);
candidateAssemblies.Remove(attributeType.Assembly);
var types = new List<Type>();
var stack = new Stack<Assembly>();
stack.Push(attributeType.Assembly);
while (stack.Count > 0)
{
var assembly = stack.Pop();
Type[] assemblyTypes = null;
if (assembly != attributeType.Assembly || attributeAssemblyIsCandidate)
{
// get all assembly types that can be assigned to baseType
try
{
assemblyTypes = GetTypesWithFormattedException(assembly)
.ToArray(); // in try block
}
catch (TypeLoadException ex)
{
LogHelper.Error(typeof(TypeFinder), string.Format("Could not query types on {0} assembly, this is most likely due to this assembly not being compatible with the current Umbraco version", assembly), ex);
continue;
}
types.AddRange(assemblyTypes.Where(x =>
x.IsClass // only classes
&& (x.IsAbstract == false || x.IsSealed == false) // ie non-static, static is abstract and sealed
&& x.IsNestedPrivate == false // exclude nested private
&& (onlyConcreteClasses == false || x.IsAbstract == false) // exclude abstract
&& x.GetCustomAttribute<HideFromTypeFinderAttribute>() == null // exclude hidden
&& x.GetCustomAttributes(attributeType, false).Any())); // marked with the attribute
}
if (assembly != attributeType.Assembly && assemblyTypes.Where(attributeType.IsAssignableFrom).Any() == false)
continue;
foreach (var referencing in TypeHelper.GetReferencingAssemblies(assembly, candidateAssemblies))
{
candidateAssemblies.Remove(referencing);
stack.Push(referencing);
}
}
return types;
}
/// <summary>
/// Finds types that are assignable from the assignTypeFrom parameter and will scan for these types in the assembly
/// list passed in, however we will only scan assemblies that have a reference to the assignTypeFrom Type or any type
/// deriving from the base type.
/// </summary>
/// <param name="baseType"></param>
/// <param name="assignTypeFrom"></param>
/// <param name="assemblies"></param>
/// <param name="onlyConcreteClasses"></param>
/// <param name="additionalFilter">An additional filter to apply for what types will actually be included in the return value</param>
/// <returns></returns>
private static IEnumerable<Type> GetClassesWithBaseType(
Type baseType,
private static IEnumerable<Type> GetClasses(
Type assignTypeFrom,
IEnumerable<Assembly> assemblies,
bool onlyConcreteClasses,
Func<Type, bool> additionalFilter = null)
{
var candidateAssemblies = new HashSet<Assembly>(assemblies);
var baseTypeAssemblyIsCandidate = candidateAssemblies.Contains(baseType.Assembly);
candidateAssemblies.Remove(baseType.Assembly);
var types = new List<Type>();
var stack = new Stack<Assembly>();
stack.Push(baseType.Assembly);
while (stack.Count > 0)
//the default filter will always return true.
if (additionalFilter == null)
{
var assembly = stack.Pop();
// get all assembly types that can be assigned to baseType
Type[] assemblyTypes = null;
if (assembly != baseType.Assembly || baseTypeAssemblyIsCandidate)
{
try
{
assemblyTypes = GetTypesWithFormattedException(assembly)
.Where(baseType.IsAssignableFrom)
.ToArray(); // in try block
}
catch (TypeLoadException ex)
{
LogHelper.Error(typeof(TypeFinder), string.Format("Could not query types on {0} assembly, this is most likely due to this assembly not being compatible with the current Umbraco version", assembly), ex);
continue;
}
types.AddRange(assemblyTypes.Where(x =>
x.IsClass // only classes
&& (x.IsAbstract == false || x.IsSealed == false) // ie non-static, static is abstract and sealed
&& x.IsNestedPrivate == false // exclude nested private
&& (onlyConcreteClasses == false || x.IsAbstract == false) // exclude abstract
&& x.GetCustomAttribute<HideFromTypeFinderAttribute>() == null // exclude hidden
&& (additionalFilter == null || additionalFilter(x)))); // filter
}
if (assembly != baseType.Assembly && assemblyTypes.All(x => x.IsSealed))
continue;
foreach (var referencing in TypeHelper.GetReferencingAssemblies(assembly, candidateAssemblies))
{
candidateAssemblies.Remove(referencing);
stack.Push(referencing);
}
additionalFilter = type => true;
}
return types;
var foundAssignableTypes = new HashSet<Type>();
var assemblyList = assemblies.ToArray();
//find all assembly references that are referencing the current type's assembly since we
//should only be scanning those assemblies because any other assembly will definitely not
//contain sub type's of the one we're currently looking for
var referencedAssemblies = TypeHelper.GetReferencedAssemblies(assignTypeFrom, assemblyList);
//get a list of non-referenced assemblies (we'll use this when we recurse below)
var otherAssemblies = assemblyList.Where(x => referencedAssemblies.Contains(x) == false).ToArray();
//loop through the referenced assemblies
foreach (var a in referencedAssemblies)
{
//get all types in the assembly that are sub types of the current type
var allSubTypes = GetTypesWithFormattedException(a)
.Where(assignTypeFrom.IsAssignableFrom)
.ToArray();
var filteredTypes = new Type[] { };
try
{
//now filter the types based on the onlyConcreteClasses flag, not interfaces, not static classes
filteredTypes = allSubTypes
.Where(t => (TypeHelper.IsNonStaticClass(t)
//Do not include nested private classes - since we are in full trust now this will find those too!
&& t.IsNestedPrivate == false
&& (onlyConcreteClasses == false || t.IsAbstract == false)
//Do not include classes that are flagged to hide from the type finder
&& t.GetCustomAttribute<HideFromTypeFinderAttribute>() == null
&& additionalFilter(t)))
.ToArray();
}
catch (TypeLoadException ex)
{
LogHelper.Error(typeof(TypeFinder), string.Format("Could not query types on {0} assembly, this is most likely due to this assembly not being compatible with the current Umbraco version", a), ex);
continue;
}
//add the types to our list to return
foreach (var t in filteredTypes)
{
foundAssignableTypes.Add(t);
}
//now we need to include types that may be inheriting from sub classes of the type being searched for
//so we will search in assemblies that reference those types too.
foreach (var subTypesInAssembly in allSubTypes.GroupBy(x => x.Assembly))
{
//So that we are not scanning too much, we need to group the sub types:
// * if there is more than 1 sub type in the same assembly then we should only search on the 'lowest base' type.
// * We should also not search for sub types if the type is sealed since you cannot inherit from a sealed class
// * We should not search for sub types if the type is static since you cannot inherit from them.
var subTypeList = subTypesInAssembly
.Where(t => t.IsSealed == false && TypeHelper.IsStaticClass(t) == false)
.ToArray();
var baseClassAttempt = TypeHelper.GetLowestBaseType(subTypeList);
//if there's a base class amongst the types then we'll only search for that type.
//otherwise we'll have to search for all of them.
var subTypesToSearch = new HashSet<Type>();
if (baseClassAttempt.Success)
{
subTypesToSearch.Add(baseClassAttempt.Result);
}
else
{
foreach (var t in subTypeList)
{
subTypesToSearch.Add(t);
}
}
foreach (var typeToSearch in subTypesToSearch)
{
//recursively find the types inheriting from this sub type in the other non-scanned assemblies.
var foundTypes = GetClasses(typeToSearch, otherAssemblies, onlyConcreteClasses, additionalFilter);
foreach (var f in foundTypes)
{
foundAssignableTypes.Add(f);
}
}
}
}
return foundAssignableTypes;
}
internal static IEnumerable<Type> GetTypesWithFormattedException(Assembly a)
@@ -561,6 +666,7 @@ namespace Umbraco.Core
#endregion
public static Type GetTypeByName(string typeName)
{
var type = BuildManager.GetType(typeName, false);
@@ -578,5 +684,6 @@ namespace Umbraco.Core
.Select(x => x.GetType(typeName))
.FirstOrDefault(x => x != null);
}
}
}
+50 -40
View File
@@ -13,12 +13,9 @@ namespace Umbraco.Core
/// </summary>
internal static class TypeHelper
{
private static readonly ConcurrentDictionary<Tuple<Type, bool, bool, bool>, PropertyInfo[]> GetPropertiesCache
= new ConcurrentDictionary<Tuple<Type, bool, bool, bool>, PropertyInfo[]>();
private static readonly ConcurrentDictionary<Type, FieldInfo[]> GetFieldsCache
= new ConcurrentDictionary<Type, FieldInfo[]>();
private static readonly Assembly[] EmptyAssemblies = new Assembly[0];
private static readonly ConcurrentDictionary<Type, FieldInfo[]> GetFieldsCache = new ConcurrentDictionary<Type, FieldInfo[]>();
private static readonly ConcurrentDictionary<Tuple<Type, bool, bool, bool>, PropertyInfo[]> GetPropertiesCache = new ConcurrentDictionary<Tuple<Type, bool, bool, bool>, PropertyInfo[]>();
/// <summary>
/// Checks if the method is actually overriding a base method
@@ -33,40 +30,44 @@ namespace Umbraco.Core
/// <summary>
/// Find all assembly references that are referencing the assignTypeFrom Type's assembly found in the assemblyList
/// </summary>
/// <param name="assembly">The referenced assembly.</param>
/// <param name="assemblies">A list of assemblies.</param>
/// <param name="assignTypeFrom"></param>
/// <param name="assemblies"></param>
/// <returns></returns>
/// <remarks>
/// If the assembly of the assignTypeFrom Type is in the App_Code assembly, then we return nothing since things cannot
/// reference that assembly, same with the global.asax assembly.
/// </remarks>
public static Assembly[] GetReferencingAssemblies(Assembly assembly, IEnumerable<Assembly> assemblies)
public static Assembly[] GetReferencedAssemblies(Type assignTypeFrom, IEnumerable<Assembly> assemblies)
{
if (assembly.IsAppCodeAssembly() || assembly.IsGlobalAsaxAssembly())
return EmptyAssemblies;
//check if it is the app_code assembly.
//check if it is App_global.asax assembly
if (assignTypeFrom.Assembly.IsAppCodeAssembly() || assignTypeFrom.Assembly.IsGlobalAsaxAssembly())
{
return Enumerable.Empty<Assembly>().ToArray();
}
// find all assembly references that are referencing the current type's assembly since we
// should only be scanning those assemblies because any other assembly will definitely not
// contain sub type's of the one we're currently looking for
var name = assembly.GetName().Name;
return assemblies.Where(x => x == assembly || HasReference(x, name)).ToArray();
//find all assembly references that are referencing the current type's assembly since we
//should only be scanning those assemblies because any other assembly will definitely not
//contain sub type's of the one we're currently looking for
return assemblies
.Where(assembly =>
assembly == assignTypeFrom.Assembly
|| HasReferenceToAssemblyWithName(assembly, assignTypeFrom.Assembly.GetName().Name))
.ToArray();
}
/// <summary>
/// Determines if an assembly references another assembly.
/// checks if the assembly has a reference with the same name as the expected assembly name.
/// </summary>
/// <param name="assembly"></param>
/// <param name="name"></param>
/// <param name="expectedAssemblyName"></param>
/// <returns></returns>
public static bool HasReference(Assembly assembly, string name)
{
// ReSharper disable once LoopCanBeConvertedToQuery - no!
foreach (var a in assembly.GetReferencedAssemblies())
{
if (string.Equals(a.Name, name, StringComparison.Ordinal)) return true;
}
return false;
private static bool HasReferenceToAssemblyWithName(Assembly assembly, string expectedAssemblyName)
{
return assembly
.GetReferencedAssemblies()
.Select(a => a.Name)
.Contains(expectedAssemblyName, StringComparer.Ordinal);
}
/// <summary>
@@ -105,10 +106,13 @@ namespace Umbraco.Core
public static Attempt<Type> GetLowestBaseType(params Type[] types)
{
if (types.Length == 0)
{
return Attempt<Type>.Fail();
if (types.Length == 1)
}
if (types.Length == 1)
{
return Attempt.Succeed(types[0]);
}
foreach (var curr in types)
{
@@ -192,15 +196,20 @@ namespace Umbraco.Core
/// <param name="includeIndexed"></param>
/// <param name="caseSensitive"> </param>
/// <returns></returns>
public static PropertyInfo GetProperty(Type type, string name,
bool mustRead = true,
bool mustWrite = true,
public static PropertyInfo GetProperty(Type type, string name,
bool mustRead = true,
bool mustWrite = true,
bool includeIndexed = false,
bool caseSensitive = true)
{
return CachedDiscoverableProperties(type, mustRead, mustWrite, includeIndexed)
.FirstOrDefault(x => caseSensitive ? (x.Name == name) : x.Name.InvariantEquals(name));
}
return CachedDiscoverableProperties(type, mustRead, mustWrite, includeIndexed)
.FirstOrDefault(x =>
{
if (caseSensitive)
return x.Name == name;
return x.Name.InvariantEquals(name);
});
}
/// <summary>
/// Gets (and caches) <see cref="FieldInfo"/> discoverable in the current <see cref="AppDomain"/> for a given <paramref name="type"/>.
@@ -213,7 +222,7 @@ namespace Umbraco.Core
type,
x => type
.GetFields(BindingFlags.Public | BindingFlags.Instance)
.Where(y => y.IsInitOnly == false)
.Where(y => !y.IsInitOnly)
.ToArray());
}
@@ -231,12 +240,13 @@ namespace Umbraco.Core
new Tuple<Type, bool, bool, bool>(type, mustRead, mustWrite, includeIndexed),
x => type
.GetProperties(BindingFlags.Public | BindingFlags.Instance)
.Where(y => (mustRead == false || y.CanRead)
&& (mustWrite == false || y.CanWrite)
&& (includeIndexed || y.GetIndexParameters().Any() == false))
.Where(y => (!mustRead || y.CanRead)
&& (!mustWrite || y.CanWrite)
&& (includeIndexed || !y.GetIndexParameters().Any()))
.ToArray());
}
#region Match Type
//TODO: Need to determine if these methods should replace/combine/merge etc with IsTypeAssignableFrom, IsAssignableFromGeneric
@@ -327,9 +337,9 @@ namespace Umbraco.Core
// not a generic type, not a generic parameter
// so normal class or interface
// fixme structs? enums? array types?
// about primitive types, value types, etc:
// http://stackoverflow.com/questions/1827425/how-to-check-programatically-if-a-type-is-a-struct-or-a-class
// if it's a primitive type... it needs to be ==
if (implementation == contract) return true;
if (contract.IsClass && implementation.IsClass && implementation.IsSubclassOf(contract)) return true;
+15 -19
View File
@@ -53,14 +53,13 @@
<HintPath>..\packages\SharpZipLib.0.86.0\lib\20\ICSharpCode.SharpZipLib.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="ImageProcessor, Version=2.5.3.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\ImageProcessor.2.5.3\lib\net45\ImageProcessor.dll</HintPath>
<Reference Include="ImageProcessor, Version=2.5.2.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\ImageProcessor.2.5.2\lib\net45\ImageProcessor.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="log4net, Version=2.0.8.0, Culture=neutral, PublicKeyToken=669e0ddf0bb1aa2a, processorArchitecture=MSIL">
<HintPath>..\packages\log4net.2.0.8\lib\net45-full\log4net.dll</HintPath>
</Reference>
<Reference Include="Log4Net.Async, Version=2.0.4.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\Log4Net.Async.2.0.4\lib\net40\Log4Net.Async.dll</HintPath>
<Reference Include="log4net, Version=1.2.15.0, Culture=neutral, PublicKeyToken=669e0ddf0bb1aa2a, processorArchitecture=MSIL">
<HintPath>..\packages\log4net.2.0.5\lib\net45-full\log4net.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="Microsoft.AspNet.Identity.Core, Version=2.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
<HintPath>..\packages\Microsoft.AspNet.Identity.Core.2.2.1\lib\net45\Microsoft.AspNet.Identity.Core.dll</HintPath>
@@ -90,11 +89,13 @@
<HintPath>..\packages\MiniProfiler.2.1.0\lib\net40\MiniProfiler.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="MySql.Data, Version=6.9.9.0, Culture=neutral, PublicKeyToken=c5687fc88969c44d, processorArchitecture=MSIL">
<HintPath>..\packages\MySql.Data.6.9.9\lib\net45\MySql.Data.dll</HintPath>
<Reference Include="MySql.Data, Version=6.9.8.0, Culture=neutral, PublicKeyToken=c5687fc88969c44d, processorArchitecture=MSIL">
<HintPath>..\packages\MySql.Data.6.9.8\lib\net45\MySql.Data.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="Newtonsoft.Json, Version=10.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
<HintPath>..\packages\Newtonsoft.Json.10.0.2\lib\net45\Newtonsoft.Json.dll</HintPath>
<Reference Include="Newtonsoft.Json, Version=9.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
<HintPath>..\packages\Newtonsoft.Json.9.0.1\lib\net45\Newtonsoft.Json.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="Owin, Version=1.0.0.0, Culture=neutral, PublicKeyToken=f0ebd12fd5e55cc5, processorArchitecture=MSIL">
<HintPath>..\packages\Owin.1.0\lib\net40\Owin.dll</HintPath>
@@ -198,7 +199,6 @@
<Compile Include="Configuration\BaseRest\ExtensionElementCollection.cs" />
<Compile Include="Configuration\BaseRest\MethodElement.cs" />
<Compile Include="Configuration\ContentXmlStorage.cs" />
<Compile Include="Configuration\CoreDebug.cs" />
<Compile Include="Configuration\Dashboard\AccessElement.cs" />
<Compile Include="Configuration\Dashboard\AccessItem.cs" />
<Compile Include="Configuration\Dashboard\AccessType.cs" />
@@ -219,6 +219,7 @@
<Compile Include="Configuration\Dashboard\SectionElement.cs" />
<Compile Include="Configuration\Dashboard\TabCollection.cs" />
<Compile Include="Configuration\Dashboard\TabElement.cs" />
<Compile Include="Configuration\CoreDebug.cs" />
<Compile Include="Configuration\FileSystemProviderElement.cs" />
<Compile Include="Configuration\FileSystemProviderElementCollection.cs" />
<Compile Include="Configuration\FileSystemProvidersSection.cs" />
@@ -292,7 +293,6 @@
<Compile Include="Configuration\UmbracoSettings\RazorStaticMappingElement.cs" />
<Compile Include="Configuration\UmbracoSettings\RepositoriesCollection.cs" />
<Compile Include="Configuration\UmbracoSettings\RepositoriesElement.cs" />
<Compile Include="Configuration\UmbracoSettings\RepositoryConfigExtensions.cs" />
<Compile Include="Configuration\UmbracoSettings\RepositoryElement.cs" />
<Compile Include="Configuration\UmbracoSettings\RequestHandlerElement.cs" />
<Compile Include="Configuration\UmbracoSettings\ScheduledTaskElement.cs" />
@@ -323,7 +323,6 @@
<Compile Include="DateTimeExtensions.cs" />
<Compile Include="DecimalExtensions.cs" />
<Compile Include="DelegateExtensions.cs" />
<Compile Include="Diagnostics\MiniDump.cs" />
<Compile Include="Deploy\ArtifactBase.cs" />
<Compile Include="Deploy\ArtifactDependency.cs" />
<Compile Include="Deploy\ArtifactDependencyCollection.cs" />
@@ -339,7 +338,6 @@
<Compile Include="Events\IDeletingMediaFilesEventArgs.cs" />
<Compile Include="Events\ScopeEventDispatcherBase.cs" />
<Compile Include="Events\ScopeLifespanMessagesFactory.cs" />
<Compile Include="Exceptions\ConnectionException.cs" />
<Compile Include="IHttpContextAccessor.cs" />
<Compile Include="Models\PublishedContent\PublishedContentTypeConverter.cs" />
<Compile Include="OrderedHashSet.cs" />
@@ -497,8 +495,8 @@
<Compile Include="Persistence\Mappers\MigrationEntryMapper.cs" />
<Compile Include="Persistence\Migrations\LocalMigrationContext.cs" />
<Compile Include="Persistence\Migrations\Upgrades\TargetVersionFourOneZero\AddPreviewXmlTable.cs" />
<Compile Include="Persistence\Migrations\Upgrades\TargetVersionSevenSixZero\AddLockObjects.cs" />
<Compile Include="Persistence\Migrations\Upgrades\TargetVersionSevenSixZero\AddLockTable.cs" />
<Compile Include="Persistence\Migrations\Upgrades\TargetVersionSevenFiveFive\AddLockObjects.cs" />
<Compile Include="Persistence\Migrations\Upgrades\TargetVersionSevenFiveFive\AddLockTable.cs" />
<Compile Include="Persistence\Migrations\Upgrades\TargetVersionSevenFiveFive\UpdateAllowedMediaTypesAtRoot.cs" />
<Compile Include="Persistence\Migrations\Upgrades\TargetVersionSevenFiveZero\EnsureServersLockObject.cs" />
<Compile Include="Persistence\Migrations\Upgrades\TargetVersionSevenFiveZero\AddRedirectUrlTable.cs" />
@@ -516,8 +514,6 @@
<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\ReduceLoginNameColumnsSize.cs" />
<Compile Include="Persistence\Migrations\Upgrades\TargetVersionSevenSixZero\NormalizeTemplateGuids.cs" />
<Compile Include="Persistence\Migrations\Upgrades\TargetVersionSevenSixZero\RemovePropertyDataIdIndex.cs" />
<Compile Include="Persistence\Migrations\Upgrades\TargetVersionSevenSixZero\RemoveUmbracoDeployTables.cs" />
<Compile Include="Persistence\Migrations\Upgrades\TargetVersionSevenThreeTwo\EnsureMigrationsTableIdentityIsCorrect.cs" />
@@ -49,6 +49,8 @@ namespace Umbraco.Core
LogHelper.Error<UmbracoApplicationBase>(msg, exception);
};
BindingRedirects.Initialize();
//boot up the application
GetBootManager()
.Initialize()

Some files were not shown because too many files have changed in this diff Show More