diff --git a/.gitignore b/.gitignore
index 0073675d82..32e7c297db 100644
--- a/.gitignore
+++ b/.gitignore
@@ -76,6 +76,7 @@ src/Umbraco.Web.UI/[Uu]mbraco/[Jj]s/umbraco.*
src/Umbraco.Web.UI/[Uu]mbraco/[Jj]s/routes.js
src/Umbraco.Web.UI/[Uu]mbraco/[Jj]s/app.dev.js
src/Umbraco.Web.UI/[Uu]mbraco/[Jj]s/loader.js
+src/Umbraco.Web.UI/[Uu]mbraco/[Jj]s/loader.dev.js
src/Umbraco.Web.UI/[Uu]mbraco/[Jj]s/main.js
src/Umbraco.Web.UI/[Uu]mbraco/[Jj]s/app.js
@@ -129,3 +130,5 @@ src/*.boltdata/
/src/Umbraco.Web.UI/Umbraco/Js/canvasdesigner.front.js
src/umbraco.sln.ide/*
build/UmbracoCms.*/
+src/.vs/
+src/Umbraco.Web.UI/umbraco/js/install.loader.js
diff --git a/README.md b/README.md
index d4a024ddbc..5988cc2a19 100644
--- a/README.md
+++ b/README.md
@@ -7,9 +7,9 @@ The easiest way to get started is to run `build/build.bat` which will build both
If you're interested in making changes to Belle make sure to read the [Belle ReadMe file](src/Umbraco.Web.UI.Client/README.md). Note that you can always [download a nightly build](http://nightly.umbraco.org/umbraco%207.0.0/) so you don't have to build the code yourself.
-## Watch a five minute introduction video ##
+## Watch a introduction video ##
-[](http://umbraco.org/help-and-support/video-tutorials/getting-started/what-is-umbraco)
+[](https://umbraco.tv/videos/umbraco-v7/content-editor/basics/introduction/cms-explanation/)
## Umbraco - the simple, flexible and friendly ASP.NET CMS ##
diff --git a/build/Build.bat b/build/Build.bat
index f29260b274..bd18f719ad 100644
--- a/build/Build.bat
+++ b/build/Build.bat
@@ -25,9 +25,9 @@ ECHO Installing the Microsoft.Bcl.Build package before anything else, otherwise
SET nuGetFolder=%CD%\..\src\packages\
..\src\.nuget\NuGet.exe sources Remove -Name MyGetUmbracoCore >NUL
..\src\.nuget\NuGet.exe sources Add -Name MyGetUmbracoCore -Source https://www.myget.org/F/umbracocore/api/v2/ >NUL
-..\src\.nuget\NuGet.exe install ..\src\Umbraco.Web.UI\packages.config -OutputDirectory %nuGetFolder%
-..\src\.nuget\NuGet.exe install ..\src\umbraco.businesslogic\packages.config -OutputDirectory %nuGetFolder%
-..\src\.nuget\NuGet.exe install ..\src\Umbraco.Core\packages.config -OutputDirectory %nuGetFolder%
+..\src\.nuget\NuGet.exe install ..\src\Umbraco.Web.UI\packages.config -OutputDirectory %nuGetFolder% -Verbosity quiet
+..\src\.nuget\NuGet.exe install ..\src\umbraco.businesslogic\packages.config -OutputDirectory %nuGetFolder% -Verbosity quiet
+..\src\.nuget\NuGet.exe install ..\src\Umbraco.Core\packages.config -OutputDirectory %nuGetFolder% -Verbosity quiet
ECHO Removing the belle build folder and bower_components folder to make sure everything is clean as a whistle
RD ..\src\Umbraco.Web.UI.Client\build /Q /S
@@ -43,7 +43,7 @@ DEL /F /Q webpihash.txt
ECHO Making sure Git is in the path so that the build can succeed
CALL InstallGit.cmd
ECHO Performing MSBuild and producing Umbraco binaries zip files
-%windir%\Microsoft.NET\Framework\v4.0.30319\msbuild.exe "Build.proj" /p:BUILD_RELEASE=%release% /p:BUILD_COMMENT=%comment%
+%windir%\Microsoft.NET\Framework\v4.0.30319\msbuild.exe "Build.proj" /p:BUILD_RELEASE=%release% /p:BUILD_COMMENT=%comment% /verbosity:minimal
ECHO Setting node_modules folder to hidden to prevent VS13 from crashing on it while loading the websites project
attrib +h ..\src\Umbraco.Web.UI.Client\node_modules
@@ -53,11 +53,13 @@ REN .\_BuildOutput\WebApp\Views\Web.config Web.config.transform
REN .\_BuildOutput\WebApp\Xslt\Web.config Web.config.transform
ECHO Packing the NuGet release files
-..\src\.nuget\NuGet.exe Pack NuSpecs\UmbracoCms.Core.nuspec -Version %version% -Symbols
-..\src\.nuget\NuGet.exe Pack NuSpecs\UmbracoCms.nuspec -Version %version%
+..\src\.nuget\NuGet.exe Pack NuSpecs\UmbracoCms.Core.nuspec -Version %version% -Symbols -Verbosity quiet
+..\src\.nuget\NuGet.exe Pack NuSpecs\UmbracoCms.nuspec -Version %version% -Verbosity quiet
IF ERRORLEVEL 1 GOTO :showerror
+ECHO No errors were detected but you still may see some in the output, then it's time to investigate.
+ECHO You might see some warnings but that is completely normal.
GOTO :EOF
:showerror
diff --git a/build/Build.proj b/build/Build.proj
index 09303788d9..e937df15bd 100644
--- a/build/Build.proj
+++ b/build/Build.proj
@@ -68,6 +68,12 @@
.$(BUILD_RELEASE)-$(BUILD_COMMENT)
+
+ .$(BUILD_RELEASE)-$(BUILD_NIGHTLY)
+
+
+ .$(BUILD_RELEASE)-$(BUILD_COMMENT)-$(BUILD_NIGHTLY)
+ Release
@@ -91,15 +97,8 @@
-
-
-
-
-
-
-
@@ -145,14 +144,14 @@
DestinationFiles="@(WebPiFiles->'$(WebPiFolder)%(RecursiveDir)%(Filename)%(Extension)')" />
-
+
-
-
+
+
@@ -219,6 +218,7 @@
+
@@ -227,6 +227,11 @@
OverwriteReadOnlyFiles="true"
SkipUnchangedFiles="false" />
+
+
-
+
@@ -271,9 +276,11 @@
$(BUILD_RELEASE)$(BUILD_RELEASE)-$(BUILD_COMMENT)
+ $(BUILD_RELEASE)-$(BUILD_NIGHTLY)
+ $(BUILD_RELEASE)-$(BUILD_COMMENT)-$(BUILD_NIGHTLY)
-
+
+
+
+
+
+
+
+
diff --git a/build/BuildBelle.bat b/build/BuildBelle.bat
index e323abbb0d..0d58204f65 100644
--- a/build/BuildBelle.bat
+++ b/build/BuildBelle.bat
@@ -6,7 +6,7 @@ SET nuGetFolder=%CD%\..\src\packages\
ECHO Configured packages folder: %nuGetFolder%
ECHO Current folder: %CD%
-%CD%\..\src\.nuget\NuGet.exe install Npm.js -OutputDirectory %nuGetFolder%
+%CD%\..\src\.nuget\NuGet.exe install Npm.js -OutputDirectory %nuGetFolder% -Verbosity quiet
for /f "delims=" %%A in ('dir %nuGetFolder%node.js.* /b') do set "nodePath=%nuGetFolder%%%A\"
for /f "delims=" %%A in ('dir %nuGetFolder%npm.js.* /b') do set "npmPath=%nuGetFolder%%%A\tools\"
@@ -24,9 +24,9 @@ ECHO Change directory to %CD%\..\src\Umbraco.Web.UI.Client\
CD %CD%\..\src\Umbraco.Web.UI.Client\
ECHO Do npm install and the grunt build of Belle
-call npm install
-call npm install -g grunt-cli
-call npm install -g bower
+call npm install --quiet
+call npm install -g grunt-cli --quiet
+call npm install -g bower --quiet
call grunt build --buildversion=%release%
ECHO Reset path to what it was before
diff --git a/build/InstallGit.cmd b/build/InstallGit.cmd
index f10fe6c100..2bd6d7cc35 100644
--- a/build/InstallGit.cmd
+++ b/build/InstallGit.cmd
@@ -6,14 +6,14 @@ if %ERRORLEVEL%==9009 GOTO :trydefaultpath
GOTO :EOF
:trydefaultpath
-path=C:\Program Files (x86)\Git\cmd;%PATH%
+path=C:\Program Files (x86)\Git\cmd;C:\Program Files\Git\cmd;%PATH%
git.exe 2> NUL
if %ERRORLEVEL%==9009 GOTO :showerror
GOTO :EOF
:showerror
path=%oldPath%
-ECHO Git is not in your path and could not be found in C:\Program Files (x86)\Git\bin
+ECHO Git is not in your path and could not be found in C:\Program Files (x86)\Git\cmd
set /p install=" Do you want to install Git through Chocolatey [y/n]? " %=%
if %install%==y (
GOTO :installgit
diff --git a/build/NuSpecs/UmbracoCms.Core.nuspec b/build/NuSpecs/UmbracoCms.Core.nuspec
index 1bb1a78d03..6852b04510 100644
--- a/build/NuSpecs/UmbracoCms.Core.nuspec
+++ b/build/NuSpecs/UmbracoCms.Core.nuspec
@@ -15,27 +15,29 @@
en-USumbraco
-
+
-
-
-
+
+
-
+
-
+
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
diff --git a/build/NuSpecs/UmbracoCms.nuspec b/build/NuSpecs/UmbracoCms.nuspec
index 76d72935e9..61a8188804 100644
--- a/build/NuSpecs/UmbracoCms.nuspec
+++ b/build/NuSpecs/UmbracoCms.nuspec
@@ -26,7 +26,6 @@
-
@@ -41,6 +40,7 @@
+
\ No newline at end of file
diff --git a/build/NuSpecs/build/UmbracoCms.targets b/build/NuSpecs/build/UmbracoCms.targets
index 024d8af7ad..fde5b4ea81 100644
--- a/build/NuSpecs/build/UmbracoCms.targets
+++ b/build/NuSpecs/build/UmbracoCms.targets
@@ -1,6 +1,17 @@
+
+
+ $(MSBuildThisFileDirectory)..\UmbracoFiles\
+
+
+
+
+
+
+
+
$(MSBuildThisFileDirectory)..\UmbracoFiles\
@@ -47,4 +58,4 @@
-
\ No newline at end of file
+
diff --git a/build/NuSpecs/tools/Dashboard.config.install.xdt b/build/NuSpecs/tools/Dashboard.config.install.xdt
index 1f5bbb0164..197f9c1b6f 100644
--- a/build/NuSpecs/tools/Dashboard.config.install.xdt
+++ b/build/NuSpecs/tools/Dashboard.config.install.xdt
@@ -1,6 +1,6 @@
-
+
@@ -20,7 +20,7 @@
-
+
views/dashboard/developer/developerdashboardvideos.html
@@ -29,15 +29,20 @@
-
+
views/dashboard/developer/examinemanagement.html
+
+
+ views/dashboard/developer/xmldataintegrityreport.html
+
+
-
+
@@ -47,7 +52,7 @@
-
+
@@ -57,17 +62,10 @@
views/dashboard/default/startupdashboardintro.html
-
-
-
-
- views/dashboard/ChangePassword.html
-
-
-
+
diff --git a/build/NuSpecs/tools/ReadmeUpgrade.txt b/build/NuSpecs/tools/ReadmeUpgrade.txt
index 29aa41b55a..894f5e5f93 100644
--- a/build/NuSpecs/tools/ReadmeUpgrade.txt
+++ b/build/NuSpecs/tools/ReadmeUpgrade.txt
@@ -10,9 +10,6 @@
Don't forget to build!
-When upgrading your website using NuGet you should answer "No" to the questions to overwrite the Web.config
-file (and config files in the config folder).
-
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.
diff --git a/build/NuSpecs/tools/Views.Web.config.install.xdt b/build/NuSpecs/tools/Views.Web.config.install.xdt
new file mode 100644
index 0000000000..c34963f2b0
--- /dev/null
+++ b/build/NuSpecs/tools/Views.Web.config.install.xdt
@@ -0,0 +1,37 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/build/NuSpecs/tools/Web.config.install.xdt b/build/NuSpecs/tools/Web.config.install.xdt
index e6dc46aab7..cff81ba2d8 100644
--- a/build/NuSpecs/tools/Web.config.install.xdt
+++ b/build/NuSpecs/tools/Web.config.install.xdt
@@ -20,9 +20,9 @@
-
+
-
+
@@ -48,6 +48,13 @@
+
+
+
+
+
+
+ >
@@ -74,6 +81,10 @@
+
+
+
+
@@ -143,6 +154,12 @@
+
+
+
+
+
+
@@ -158,7 +175,31 @@
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/build/NuSpecs/tools/install.core.ps1 b/build/NuSpecs/tools/install.core.ps1
index 9c631001d2..c81fa419c9 100644
--- a/build/NuSpecs/tools/install.core.ps1
+++ b/build/NuSpecs/tools/install.core.ps1
@@ -56,12 +56,25 @@ if ($project) {
if(Test-Path $umbracoBinFolder\ImageProcessor.dll) { Remove-Item $umbracoBinFolder\ImageProcessor.dll -Force -Confirm:$false }
if(Test-Path $umbracoBinFolder\ImageProcessor.Web.dll) { Remove-Item $umbracoBinFolder\ImageProcessor.Web.dll -Force -Confirm:$false }
if(Test-Path $umbracoBinFolder\Lucene.Net.dll) { Remove-Item $umbracoBinFolder\Lucene.Net.dll -Force -Confirm:$false }
+ if(Test-Path $umbracoBinFolder\Microsoft.AspNet.Identity.Core.dll) { Remove-Item $umbracoBinFolder\Microsoft.AspNet.Identity.Core.dll -Force -Confirm:$false }
+ if(Test-Path $umbracoBinFolder\Microsoft.AspNet.Identity.Owin.dll) { Remove-Item $umbracoBinFolder\Microsoft.AspNet.Identity.Owin.dll -Force -Confirm:$false }
+ if(Test-Path $umbracoBinFolder\Microsoft.Owin.dll) { Remove-Item $umbracoBinFolder\Microsoft.Owin.dll -Force -Confirm:$false }
+ if(Test-Path $umbracoBinFolder\Microsoft.Owin.Host.SystemWeb.dll) { Remove-Item $umbracoBinFolder\Microsoft.Owin.Host.SystemWeb.dll -Force -Confirm:$false }
+ if(Test-Path $umbracoBinFolder\Microsoft.Owin.Security.dll) { Remove-Item $umbracoBinFolder\Microsoft.Owin.Security.dll -Force -Confirm:$false }
+ if(Test-Path $umbracoBinFolder\Microsoft.Owin.Security.Cookies.dll) { Remove-Item $umbracoBinFolder\Microsoft.Owin.Security.Cookies.dll -Force -Confirm:$false }
+ if(Test-Path $umbracoBinFolder\Microsoft.Owin.Security.OAuth.dll) { Remove-Item $umbracoBinFolder\Microsoft.Owin.Security.OAuth.dll -Force -Confirm:$false }
if(Test-Path $umbracoBinFolder\Microsoft.Web.Infrastructure.dll) { Remove-Item $umbracoBinFolder\Microsoft.Web.Infrastructure.dll -Force -Confirm:$false }
+ if(Test-Path $umbracoBinFolder\Microsoft.Web.Helpers.dll) { Remove-Item $umbracoBinFolder\Microsoft.Web.Helpers.dll -Force -Confirm:$false }
if(Test-Path $umbracoBinFolder\Microsoft.Web.Mvc.FixedDisplayModes.dll) { Remove-Item $umbracoBinFolder\Microsoft.Web.Mvc.FixedDisplayModes.dll -Force -Confirm:$false }
if(Test-Path $umbracoBinFolder\MiniProfiler.dll) { Remove-Item $umbracoBinFolder\MiniProfiler.dll -Force -Confirm:$false }
if(Test-Path $umbracoBinFolder\MySql.Data.dll) { Remove-Item $umbracoBinFolder\MySql.Data.dll -Force -Confirm:$false }
if(Test-Path $umbracoBinFolder\Newtonsoft.Json.dll) { Remove-Item $umbracoBinFolder\Newtonsoft.Json.dll -Force -Confirm:$false }
+ if(Test-Path $umbracoBinFolder\Owin.dll) { Remove-Item $umbracoBinFolder\Owin.dll -Force -Confirm:$false }
+ if(Test-Path $umbracoBinFolder\Semver.dll) { Remove-Item $umbracoBinFolder\Semver.dll -Force -Confirm:$false }
+ if(Test-Path $umbracoBinFolder\System.Net.Http.Extensions.dll) { Remove-Item $umbracoBinFolder\System.Net.Http.Extensions.dll -Force -Confirm:$false }
if(Test-Path $umbracoBinFolder\System.Net.Http.Formatting.dll) { Remove-Item $umbracoBinFolder\System.Net.Http.Formatting.dll -Force -Confirm:$false }
+ if(Test-Path $umbracoBinFolder\System.Net.Http.Primitives.dll) { Remove-Item $umbracoBinFolder\System.Net.Http.Primitives.dll -Force -Confirm:$false }
+ if(Test-Path $umbracoBinFolder\System.Web.Helpers.dll) { Remove-Item $umbracoBinFolder\System.Web.Helpers.dll -Force -Confirm:$false }
if(Test-Path $umbracoBinFolder\System.Web.Http.dll) { Remove-Item $umbracoBinFolder\System.Web.Http.dll -Force -Confirm:$false }
if(Test-Path $umbracoBinFolder\System.Web.Http.WebHost.dll) { Remove-Item $umbracoBinFolder\System.Web.Http.WebHost.dll -Force -Confirm:$false }
if(Test-Path $umbracoBinFolder\System.Web.Mvc.dll) { Remove-Item $umbracoBinFolder\System.Web.Mvc.dll -Force -Confirm:$false }
diff --git a/build/NuSpecs/tools/log4net.config.install.xdt b/build/NuSpecs/tools/log4net.config.install.xdt
new file mode 100644
index 0000000000..f4d3caf7fc
--- /dev/null
+++ b/build/NuSpecs/tools/log4net.config.install.xdt
@@ -0,0 +1,20 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/build/NuSpecs/tools/trees.config.install.xdt b/build/NuSpecs/tools/trees.config.install.xdt
index 6acc793e05..f5a807b4bf 100644
--- a/build/NuSpecs/tools/trees.config.install.xdt
+++ b/build/NuSpecs/tools/trees.config.install.xdt
@@ -1,36 +1,127 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/build/RevertToCleanInstall.bat b/build/RevertToCleanInstall.bat
index 6c391386cd..b21b33d8ff 100644
--- a/build/RevertToCleanInstall.bat
+++ b/build/RevertToCleanInstall.bat
@@ -45,9 +45,6 @@ del ..\src\Umbraco.Web.UI\UserControls\*.*
echo Removing masterpage files
del ..\src\Umbraco.Web.UI\masterpages\*.*
-echo Removing view files
-del ..\src\Umbraco.Web.UI\Views\*.*
-
echo Removing razor files
del ..\src\Umbraco.Web.UI\macroScripts\*.*
@@ -104,7 +101,9 @@ echo Removing user control files
FOR %%A IN (..\src\Umbraco.Web.UI\usercontrols\*.*) DO DEL %%A
echo Removing view files
-FOR %%A IN (..\src\Umbraco.Web.UI\Views\*.*) DO DEL %%A
+ATTRIB +H ..\src\Umbraco.Web.UI\Views\Partials\Grid\*.cshtml /S
+FOR %%A IN (..\src\Umbraco.Web.UI\Views\) DO DEL /Q /S *.cshtml -H
+ATTRIB -H ..\src\Umbraco.Web.UI\Views\Partials\Grid\*.cshtml /S
echo Removing razor files
FOR %%A IN (..\src\Umbraco.Web.UI\macroScripts\*.*) DO DEL %%A
diff --git a/build/RevertToEmptyInstall.bat b/build/RevertToEmptyInstall.bat
index d59e2cdc88..b8abe4e64e 100644
--- a/build/RevertToEmptyInstall.bat
+++ b/build/RevertToEmptyInstall.bat
@@ -54,9 +54,6 @@ del ..\src\Umbraco.Web.UI\UserControls\*.*
echo Removing masterpage files
del ..\src\Umbraco.Web.UI\masterpages\*.*
-echo Removing view files
-del ..\src\Umbraco.Web.UI\Views\*.*
-
echo Removing razor files
del ..\src\Umbraco.Web.UI\macroScripts\*.*
@@ -122,7 +119,9 @@ echo Removing user control files
FOR %%A IN (..\src\Umbraco.Web.UI\usercontrols\*.*) DO DEL %%A
echo Removing view files
-FOR %%A IN (..\src\Umbraco.Web.UI\Views\*.*) DO DEL %%A
+ATTRIB +H ..\src\Umbraco.Web.UI\Views\Partials\Grid\*.cshtml /S
+FOR %%A IN (..\src\Umbraco.Web.UI\Views\) DO DEL /Q /S *.cshtml -H
+ATTRIB -H ..\src\Umbraco.Web.UI\Views\Partials\Grid\*.cshtml /S
echo Removing razor files
FOR %%A IN (..\src\Umbraco.Web.UI\macroScripts\*.*) DO DEL %%A
@@ -151,6 +150,9 @@ rmdir "..\src\Umbraco.Web.UI\usercontrols\umbracoContour\" /S /Q
echo Start with a clean web.config
copy ..\src\Umbraco.Web.UI\web.Template.config ..\src\Umbraco.Web.UI\web.config /Y
+echo Start with a clean web.config
+copy ..\src\Umbraco.Web.UI\web.Template.config ..\src\Umbraco.Web.UI\web.config /Y
+
echo "Umbraco install reverted to clean install"
pause
exit
diff --git a/build/UmbracoVersion.txt b/build/UmbracoVersion.txt
index bba582b4dc..803b95f9af 100644
--- a/build/UmbracoVersion.txt
+++ b/build/UmbracoVersion.txt
@@ -1,2 +1,3 @@
# Usage: on line 2 put the release version, on line 3 put the version comment (example: beta)
-8.0.0
\ No newline at end of file
+8.0.0
+beta
\ No newline at end of file
diff --git a/src/NuGet.Config b/src/NuGet.Config
new file mode 100644
index 0000000000..dcccfdd5c1
--- /dev/null
+++ b/src/NuGet.Config
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/SQLCE4Umbraco/SqlCE4Umbraco.csproj.DotSettings b/src/SQLCE4Umbraco/SqlCE4Umbraco.csproj.DotSettings
new file mode 100644
index 0000000000..662f95686e
--- /dev/null
+++ b/src/SQLCE4Umbraco/SqlCE4Umbraco.csproj.DotSettings
@@ -0,0 +1,2 @@
+
+ CSharp50
\ No newline at end of file
diff --git a/src/SolutionInfo.cs b/src/SolutionInfo.cs
index 6c766f8e97..28b314750a 100644
--- a/src/SolutionInfo.cs
+++ b/src/SolutionInfo.cs
@@ -11,5 +11,5 @@ using System.Resources;
[assembly: AssemblyVersion("1.0.*")]
-[assembly: AssemblyFileVersion("7.3.0")]
-[assembly: AssemblyInformationalVersion("7.3.0")]
\ No newline at end of file
+[assembly: AssemblyFileVersion("7.4.0")]
+[assembly: AssemblyInformationalVersion("7.4.0-beta")]
\ No newline at end of file
diff --git a/src/Umbraco.Core/ActionsResolver.cs b/src/Umbraco.Core/ActionsResolver.cs
index ff34f62c60..2da95a3416 100644
--- a/src/Umbraco.Core/ActionsResolver.cs
+++ b/src/Umbraco.Core/ActionsResolver.cs
@@ -11,7 +11,7 @@ namespace Umbraco.Core
///
/// A resolver to return all IAction objects
///
- internal sealed class ActionsResolver : LazyManyObjectsResolverBase
+ public sealed class ActionsResolver : LazyManyObjectsResolverBase
{
///
/// Constructor
diff --git a/src/Umbraco.Core/ApplicationContext.cs b/src/Umbraco.Core/ApplicationContext.cs
index b303c087f7..503b7e7dd0 100644
--- a/src/Umbraco.Core/ApplicationContext.cs
+++ b/src/Umbraco.Core/ApplicationContext.cs
@@ -1,6 +1,7 @@
using System;
using System.Configuration;
using System.Threading;
+using System.Threading.Tasks;
using System.Web;
using System.Web.Caching;
using Umbraco.Core.Cache;
@@ -10,7 +11,7 @@ using Umbraco.Core.Logging;
using Umbraco.Core.ObjectResolution;
using Umbraco.Core.Profiling;
using Umbraco.Core.Services;
-
+using Umbraco.Core.Sync;
namespace Umbraco.Core
{
@@ -34,10 +35,13 @@ namespace Umbraco.Core
if (dbContext == null) throw new ArgumentNullException("dbContext");
if (serviceContext == null) throw new ArgumentNullException("serviceContext");
if (cache == null) throw new ArgumentNullException("cache");
+ if (logger == null) throw new ArgumentNullException("logger");
_databaseContext = dbContext;
_services = serviceContext;
ApplicationCache = cache;
ProfilingLogger = logger;
+
+ Init();
}
///
@@ -46,7 +50,7 @@ namespace Umbraco.Core
///
///
///
- [Obsolete("Use the other constructor specifying an ILogger instead")]
+ [Obsolete("Use the other constructor specifying a ProfilingLogger instead")]
public ApplicationContext(DatabaseContext dbContext, ServiceContext serviceContext, CacheHelper cache)
: this(dbContext, serviceContext, cache,
new ProfilingLogger(LoggerResolver.Current.Logger, ProfilerResolver.Current.Profiler))
@@ -57,9 +61,27 @@ namespace Umbraco.Core
/// Creates a basic app context
///
///
+ [Obsolete("Use the other constructor specifying a ProfilingLogger instead")]
public ApplicationContext(CacheHelper cache)
{
+ if (cache == null) throw new ArgumentNullException("cache");
ApplicationCache = cache;
+ ProfilingLogger = new ProfilingLogger(LoggerResolver.Current.Logger, ProfilerResolver.Current.Profiler);
+ Init();
+ }
+
+ ///
+ /// Creates a basic app context
+ ///
+ ///
+ ///
+ public ApplicationContext(CacheHelper cache, ProfilingLogger logger)
+ {
+ if (cache == null) throw new ArgumentNullException("cache");
+ if (logger == null) throw new ArgumentNullException("logger");
+ ApplicationCache = cache;
+ ProfilingLogger = logger;
+ Init();
}
///
@@ -75,13 +97,13 @@ namespace Umbraco.Core
///
public static ApplicationContext EnsureContext(ApplicationContext appContext, bool replaceContext)
{
- if (ApplicationContext.Current != null)
+ if (Current != null)
{
if (!replaceContext)
- return ApplicationContext.Current;
+ return Current;
}
- ApplicationContext.Current = appContext;
- return ApplicationContext.Current;
+ Current = appContext;
+ return Current;
}
///
@@ -101,14 +123,14 @@ namespace Umbraco.Core
[Obsolete("Use the other method specifying an ProfilingLogger instead")]
public static ApplicationContext EnsureContext(DatabaseContext dbContext, ServiceContext serviceContext, CacheHelper cache, bool replaceContext)
{
- if (ApplicationContext.Current != null)
+ if (Current != null)
{
if (!replaceContext)
- return ApplicationContext.Current;
+ return Current;
}
var ctx = new ApplicationContext(dbContext, serviceContext, cache);
- ApplicationContext.Current = ctx;
- return ApplicationContext.Current;
+ Current = ctx;
+ return Current;
}
///
@@ -128,14 +150,14 @@ namespace Umbraco.Core
///
public static ApplicationContext EnsureContext(DatabaseContext dbContext, ServiceContext serviceContext, CacheHelper cache, ProfilingLogger logger, bool replaceContext)
{
- if (ApplicationContext.Current != null)
+ if (Current != null)
{
if (!replaceContext)
- return ApplicationContext.Current;
+ return Current;
}
var ctx = new ApplicationContext(dbContext, serviceContext, cache, logger);
- ApplicationContext.Current = ctx;
- return ApplicationContext.Current;
+ Current = ctx;
+ return Current;
}
///
@@ -190,64 +212,117 @@ namespace Umbraco.Core
// GlobalSettings.CurrentVersion returns the hard-coded "current version"
// the system is configured if they match
// if they don't, install runs, updates web.config (presumably) and updates GlobalSettings.ConfiguredStatus
- //
- // then there is Application["umbracoNeedConfiguration"] which makes no sense... getting rid of it... SD: I have actually remove that now!
- //
+
public bool IsConfigured
{
- // todo - we should not do this - ok for now
- get
- {
- return Configured;
- }
+ get { return _configured.Value; }
}
///
- /// If the db is configured and there is a database context, but we are not 'configured' , then it means we are upgrading
+ /// If the db is configured, there is a database context and there is an umbraco schema, but we are not 'configured' , then it means we are upgrading
///
public bool IsUpgrading
{
- get { return IsConfigured == false && DatabaseContext != null && DatabaseContext.IsDatabaseConfigured; }
+ get
+ {
+ if (IsConfigured == false
+ && DatabaseContext != null
+ && DatabaseContext.IsDatabaseConfigured)
+ {
+ var schemaresult = DatabaseContext.ValidateDatabaseSchema();
+ if (schemaresult.ValidTables.Count > 0) return true;
+ }
+
+ return false;
+ }
}
///
- /// The original/first url that the web application executes
+ /// The application url.
///
///
- /// we need to set the initial url in our ApplicationContext, this is so our keep alive service works and this must
- /// exist on a global context because the keep alive service doesn't run in a web context.
- /// we are NOT going to put a lock on this because locking will slow down the application and we don't really care
- /// if two threads write to this at the exact same time during first page hit.
- /// see: http://issues.umbraco.org/issue/U4-2059
+ /// The application url is the url that should be used by services to talk to the application,
+ /// eg keep alive or scheduled publishing services. It must exist on a global context because
+ /// some of these services may not run within a web context.
+ /// The format of the application url is:
+ /// - has a scheme (http or https)
+ /// - has the SystemDirectories.Umbraco path
+ /// - does not end with a slash
+ /// It is initialized on the first request made to the server, by UmbracoModule.EnsureApplicationUrl:
+ /// - if umbracoSettings:settings/web.routing/@umbracoApplicationUrl is set, use the value (new setting)
+ /// - if umbracoSettings:settings/scheduledTasks/@baseUrl is set, use the value (backward compatibility)
+ /// - otherwise, use the url of the (first) request.
+ /// Not locking, does not matter if several threads write to this.
+ /// See also issues:
+ /// - http://issues.umbraco.org/issue/U4-2059
+ /// - http://issues.umbraco.org/issue/U4-6788
+ /// - http://issues.umbraco.org/issue/U4-5728
+ /// - http://issues.umbraco.org/issue/U4-5391
///
- internal string OriginalRequestUrl { get; set; }
+ internal string UmbracoApplicationUrl
+ {
+ get
+ {
+ ApplicationUrlHelper.EnsureApplicationUrl(this);
+ return _umbracoApplicationUrl;
+ }
+ }
- ///
- /// Checks if the version configured matches the assembly version
- ///
- private bool Configured
+ // ReSharper disable once InconsistentNaming
+ internal string _umbracoApplicationUrl;
+
+ private Lazy _configured;
+ internal MainDom MainDom { get; private set; }
+
+ private void Init()
{
- get
- {
- try
- {
- string configStatus = ConfigurationStatus;
- string currentVersion = UmbracoVersion.Current.ToString(3);
+ MainDom = new MainDom(ProfilingLogger.Logger);
+ MainDom.Acquire();
+
+ //Create the lazy value to resolve whether or not the application is 'configured'
+ _configured = new Lazy(() =>
+ {
+ try
+ {
+ var configStatus = ConfigurationStatus;
+ var currentVersion = UmbracoVersion.GetSemanticVersion();
+ var ok =
+ //we are not configured if this is null
+ string.IsNullOrWhiteSpace(configStatus) == false
+ //they must match
+ && configStatus == currentVersion;
- if (currentVersion != configStatus)
- {
- ProfilingLogger.Logger.Debug("CurrentVersion different from configStatus: '" + currentVersion + "','" + configStatus + "'");
- }
-
+ if (ok)
+ {
+ //The versions are the same in config, but are they the same in the database. We can only check this
+ // if we have a db context available, if we don't then we are not installed anyways
+ if (DatabaseContext.IsDatabaseConfigured && DatabaseContext.CanConnect)
+ {
+ var found = Services.MigrationEntryService.FindEntry(GlobalSettings.UmbracoMigrationName, UmbracoVersion.GetSemanticVersion());
+ if (found == null)
+ {
+ //we haven't executed this migration in this environment, so even though the config versions match,
+ // this db has not been updated.
+ ProfilingLogger.Logger.Debug(string.Format("The migration for version: '{0} has not been executed, there is no record in the database", currentVersion.ToSemanticString()));
+ ok = false;
+ }
+ }
+ }
+ else
+ {
+ ProfilingLogger.Logger.Debug(string.Format("CurrentVersion different from configStatus: '{0}','{1}'", currentVersion.ToSemanticString(), configStatus));
+ }
- return (configStatus == currentVersion);
- }
- catch
- {
- return false;
- }
- }
+ return ok;
+ }
+ catch (Exception ex)
+ {
+ LogHelper.Error("Error determining if application is configured, returning false", ex);
+ return false;
+ }
+
+ });
}
private string ConfigurationStatus
@@ -305,6 +380,11 @@ namespace Umbraco.Core
internal set { _services = value; }
}
+ internal ServerRole GetCurrentServerRole()
+ {
+ var registrar = ServerRegistrarResolver.Current.Registrar as IServerRegistrar2;
+ return registrar == null ? ServerRole.Unknown : registrar.GetCurrentServerRole();
+ }
private volatile bool _disposed;
private readonly ReaderWriterLockSlim _disposalLocker = new ReaderWriterLockSlim();
diff --git a/src/Umbraco.Core/ApplicationEventHandler.cs b/src/Umbraco.Core/ApplicationEventHandler.cs
index a725a08a8e..8d97baac95 100644
--- a/src/Umbraco.Core/ApplicationEventHandler.cs
+++ b/src/Umbraco.Core/ApplicationEventHandler.cs
@@ -74,7 +74,7 @@ namespace Umbraco.Core
///
private bool ShouldExecute(ApplicationContext applicationContext)
{
- if (applicationContext.IsConfigured && applicationContext.DatabaseContext.CanConnect)
+ if (applicationContext.IsConfigured && applicationContext.DatabaseContext.IsDatabaseConfigured)
{
return true;
}
@@ -84,7 +84,7 @@ namespace Umbraco.Core
return true;
}
- if (!applicationContext.DatabaseContext.CanConnect && ExecuteWhenDatabaseNotConfigured)
+ if (!applicationContext.DatabaseContext.IsDatabaseConfigured && ExecuteWhenDatabaseNotConfigured)
{
return true;
}
diff --git a/src/Umbraco.Core/AsyncLock.cs b/src/Umbraco.Core/AsyncLock.cs
index 426984ab9f..b6a3f8534e 100644
--- a/src/Umbraco.Core/AsyncLock.cs
+++ b/src/Umbraco.Core/AsyncLock.cs
@@ -1,5 +1,6 @@
using System;
using System.Runtime.ConstrainedExecution;
+using System.Runtime.InteropServices;
using System.Threading;
using System.Threading.Tasks;
@@ -11,6 +12,15 @@ namespace Umbraco.Core
// - this is NOT a reader/writer lock
// - this is NOT a recursive lock
//
+ // using a named Semaphore here and not a Mutex because mutexes have thread
+ // affinity which does not work with async situations
+ //
+ // it is important that managed code properly release the Semaphore before
+ // going down else it will maintain the lock - however note that when the
+ // whole process (w3wp.exe) goes down and all handles to the Semaphore have
+ // been closed, the Semaphore system object is destroyed - so in any case
+ // an iisreset should clean up everything
+ //
internal class AsyncLock
{
private readonly SemaphoreSlim _semaphore;
@@ -54,14 +64,14 @@ namespace Umbraco.Core
// for anonymous semaphore, use the unique releaser, else create a new one
return _semaphore != null
? _releaser // (IDisposable)new SemaphoreSlimReleaser(_semaphore)
- : (IDisposable)new NamedSemaphoreReleaser(_semaphore2);
+ : new NamedSemaphoreReleaser(_semaphore2);
}
public Task LockAsync()
{
var wait = _semaphore != null
- ? _semaphore.WaitAsync()
- : WaitOneAsync(_semaphore2);
+ ? _semaphore.WaitAsync()
+ : _semaphore2.WaitOneAsync();
return wait.IsCompleted
? _releaserTask ?? Task.FromResult(CreateReleaser()) // anonymous vs named
@@ -70,6 +80,19 @@ namespace Umbraco.Core
TaskContinuationOptions.ExecuteSynchronously, TaskScheduler.Default);
}
+ public Task LockAsync(int millisecondsTimeout)
+ {
+ var wait = _semaphore != null
+ ? _semaphore.WaitAsync(millisecondsTimeout)
+ : _semaphore2.WaitOneAsync(millisecondsTimeout);
+
+ return wait.IsCompleted
+ ? _releaserTask ?? Task.FromResult(CreateReleaser()) // anonymous vs named
+ : wait.ContinueWith((_, state) => (((AsyncLock)state).CreateReleaser()),
+ this, CancellationToken.None,
+ TaskContinuationOptions.ExecuteSynchronously, TaskScheduler.Default);
+ }
+
public IDisposable Lock()
{
if (_semaphore != null)
@@ -95,38 +118,55 @@ namespace Umbraco.Core
private class NamedSemaphoreReleaser : CriticalFinalizerObject, IDisposable
{
private readonly Semaphore _semaphore;
+ private GCHandle _handle;
internal NamedSemaphoreReleaser(Semaphore semaphore)
{
_semaphore = semaphore;
+ _handle = GCHandle.Alloc(_semaphore);
}
public void Dispose()
{
Dispose(true);
- GC.SuppressFinalize(this);
+ GC.SuppressFinalize(this); // finalize will not run
}
private void Dispose(bool disposing)
{
// critical
+ _handle.Free();
_semaphore.Release();
+ _semaphore.Dispose();
}
- // we WANT to release the semaphore because it's a system object
- // ie a critical non-managed resource - so we inherit from CriticalFinalizerObject
- // which means that the finalizer "should" run in all situations
+ // we WANT to release the semaphore because it's a system object, ie a critical
+ // non-managed resource - and if it is not released then noone else can acquire
+ // the lock - so we inherit from CriticalFinalizerObject which means that the
+ // finalizer "should" run in all situations - there is always a chance that it
+ // does not run and the semaphore remains "acquired" but then chances are the
+ // whole process (w3wp.exe...) is going down, at which point the semaphore will
+ // be destroyed by Windows.
- // however... that can fail with System.ObjectDisposedException because the
- // underlying handle was closed... because we cannot guarantee that the semaphore
- // is not gone already... unless we get a GCHandle = GCHandle.Alloc(_semaphore);
- // which should keep it around and then we free the handle?
+ // however, the semaphore is a managed object, and so when the finalizer runs it
+ // might have been finalized already, and then we get a, ObjectDisposedException
+ // in the finalizer - which is bad.
- // so... I'm not sure this is safe really...
+ // in order to prevent this we do two things
+ // - use a GCHandler to ensure the semaphore is still there when the finalizer
+ // runs, so we can actually release it
+ // - wrap the finalizer code in a try...catch to make sure it never throws
~NamedSemaphoreReleaser()
{
- Dispose(false);
+ try
+ {
+ Dispose(false);
+ }
+ catch
+ {
+ // we do NOT want the finalizer to throw - never ever
+ }
}
}
@@ -159,40 +199,5 @@ namespace Umbraco.Core
Dispose(false);
}
}
-
- // http://stackoverflow.com/questions/25382583/waiting-on-a-named-semaphore-with-waitone100-vs-waitone0-task-delay100
- // http://blog.nerdbank.net/2011/07/c-await-for-waithandle.html
- // F# has a AwaitWaitHandle method that accepts a time out... and seems pretty complex...
- // version below should be OK
-
- private static Task WaitOneAsync(WaitHandle handle)
- {
- var tcs = new TaskCompletionSource
/// true if configured; otherwise, false.
- public static bool Configured
+ [Obsolete("Do not use this, it is no longer in use and will be removed from the codebase in future versions")]
+ internal static bool Configured
{
get
{
try
{
string configStatus = ConfigurationStatus;
- string currentVersion = UmbracoVersion.Current.ToString(3);
+ string currentVersion = UmbracoVersion.GetSemanticVersion().ToSemanticString();
if (currentVersion != configStatus)
@@ -594,10 +596,7 @@ namespace Umbraco.Core.Configuration
[Obsolete("Use Umbraco.Core.Configuration.UmbracoVersion.Current instead", false)]
public static string CurrentVersion
{
- get
- {
- return UmbracoVersion.Current.ToString(3);
- }
+ get { return UmbracoVersion.GetSemanticVersion().ToSemanticString(); }
}
///
@@ -768,38 +767,31 @@ namespace Umbraco.Core.Configuration
// store references to strings to determine changes
_reservedPathsCache = GlobalSettings.ReservedPaths;
_reservedUrlsCache = GlobalSettings.ReservedUrls;
-
- string _root = SystemDirectories.Root.Trim().ToLower();
-
+
// add URLs and paths to a new list
- StartsWithContainer _newReservedList = new StartsWithContainer();
- foreach (string reservedUrl in _reservedUrlsCache.Split(new[] { "," }, StringSplitOptions.RemoveEmptyEntries))
+ var newReservedList = new HashSet();
+ foreach (var reservedUrlTrimmed in _reservedUrlsCache
+ .Split(new[] {","}, StringSplitOptions.RemoveEmptyEntries)
+ .Select(x => x.Trim().ToLowerInvariant())
+ .Where(x => x.IsNullOrWhiteSpace() == false)
+ .Select(reservedUrl => IOHelper.ResolveUrl(reservedUrl).Trim().EnsureStartsWith("/"))
+ .Where(reservedUrlTrimmed => reservedUrlTrimmed.IsNullOrWhiteSpace() == false))
{
- if (string.IsNullOrWhiteSpace(reservedUrl))
- continue;
-
-
- //resolves the url to support tilde chars
- string reservedUrlTrimmed = IOHelper.ResolveUrl(reservedUrl.Trim()).Trim().ToLower();
- if (reservedUrlTrimmed.Length > 0)
- _newReservedList.Add(reservedUrlTrimmed);
+ newReservedList.Add(reservedUrlTrimmed);
}
- foreach (string reservedPath in _reservedPathsCache.Split(new[] { "," }, StringSplitOptions.RemoveEmptyEntries))
+ foreach (var reservedPathTrimmed in _reservedPathsCache
+ .Split(new[] {","}, StringSplitOptions.RemoveEmptyEntries)
+ .Select(x => x.Trim().ToLowerInvariant())
+ .Where(x => x.IsNullOrWhiteSpace() == false)
+ .Select(reservedPath => IOHelper.ResolveUrl(reservedPath).Trim().EnsureStartsWith("/").EnsureEndsWith("/"))
+ .Where(reservedPathTrimmed => reservedPathTrimmed.IsNullOrWhiteSpace() == false))
{
- bool trimEnd = !reservedPath.EndsWith("/");
- if (string.IsNullOrWhiteSpace(reservedPath))
- continue;
-
- //resolves the url to support tilde chars
- string reservedPathTrimmed = IOHelper.ResolveUrl(reservedPath.Trim()).Trim().ToLower();
-
- if (reservedPathTrimmed.Length > 0)
- _newReservedList.Add(reservedPathTrimmed + (reservedPathTrimmed.EndsWith("/") ? "" : "/"));
+ newReservedList.Add(reservedPathTrimmed);
}
// use the new list from now on
- _reservedList = _newReservedList;
+ _reservedList = newReservedList;
}
}
}
@@ -807,107 +799,17 @@ namespace Umbraco.Core.Configuration
//The url should be cleaned up before checking:
// * If it doesn't contain an '.' in the path then we assume it is a path based URL, if that is the case we should add an trailing '/' because all of our reservedPaths use a trailing '/'
// * We shouldn't be comparing the query at all
- var pathPart = url.Split('?')[0];
- if (!pathPart.Contains(".") && !pathPart.EndsWith("/"))
+ var pathPart = url.Split(new[] {'?'}, StringSplitOptions.RemoveEmptyEntries)[0].ToLowerInvariant();
+ if (pathPart.Contains(".") == false)
{
- pathPart += "/";
+ pathPart = pathPart.EnsureEndsWith('/');
}
// return true if url starts with an element of the reserved list
- return _reservedList.StartsWith(pathPart.ToLowerInvariant());
+ return _reservedList.Any(x => pathPart.InvariantStartsWith(x));
}
- ///
- /// Structure that checks in logarithmic time
- /// if a given string starts with one of the added keys.
- ///
- private class StartsWithContainer
- {
- /// Internal sorted list of keys.
- public SortedList _list
- = new SortedList(StartsWithComparator.Instance);
-
- ///
- /// Adds the specified new key.
- ///
- /// The new key.
- public void Add(string newKey)
- {
- // if the list already contains an element that begins with newKey, return
- if (String.IsNullOrEmpty(newKey) || StartsWith(newKey))
- return;
-
- // create a new collection, so the old one can still be accessed
- SortedList newList
- = new SortedList(_list.Count + 1, StartsWithComparator.Instance);
-
- // add only keys that don't already start with newKey, others are unnecessary
- foreach (string key in _list.Keys)
- if (!key.StartsWith(newKey))
- newList.Add(key, null);
- // add the new key
- newList.Add(newKey, null);
-
- // update the list (thread safe, _list was never in incomplete state)
- _list = newList;
- }
-
- ///
- /// Checks if the given string starts with any of the added keys.
- ///
- /// The target.
- /// true if a key is found that matches the start of target
- ///
- /// Runs in O(s*log(n)), with n the number of keys and s the length of target.
- ///
- public bool StartsWith(string target)
- {
- return _list.ContainsKey(target);
- }
-
- /// Comparator that tests if a string starts with another.
- /// Not a real comparator, since it is not reflexive. (x==y does not imply y==x)
- private sealed class StartsWithComparator : IComparer
- {
- /// Default string comparer.
- private readonly static Comparer _stringComparer = Comparer.Default;
-
- /// Gets an instance of the StartsWithComparator.
- public static readonly StartsWithComparator Instance = new StartsWithComparator();
-
- ///
- /// Tests if whole begins with all characters of part.
- ///
- /// The part.
- /// The whole.
- ///
- /// Returns 0 if whole starts with part, otherwise performs standard string comparison.
- ///
- public int Compare(string part, string whole)
- {
- // let the default string comparer deal with null or when part is not smaller then whole
- if (part == null || whole == null || part.Length >= whole.Length)
- return _stringComparer.Compare(part, whole);
-
- ////ensure both have a / on the end
- //part = part.EndsWith("/") ? part : part + "/";
- //whole = whole.EndsWith("/") ? whole : whole + "/";
- //if (part.Length >= whole.Length)
- // return _stringComparer.Compare(part, whole);
-
- // loop through all characters that part and whole have in common
- int pos = 0;
- bool match;
- do
- {
- match = (part[pos] == whole[pos]);
- } while (match && ++pos < part.Length);
-
- // return result of last comparison
- return match ? 0 : (part[pos] < whole[pos] ? -1 : 1);
- }
- }
- }
+
}
diff --git a/src/Umbraco.Core/Configuration/Grid/GridConfig.cs b/src/Umbraco.Core/Configuration/Grid/GridConfig.cs
new file mode 100644
index 0000000000..334124a4e5
--- /dev/null
+++ b/src/Umbraco.Core/Configuration/Grid/GridConfig.cs
@@ -0,0 +1,16 @@
+using System.IO;
+using Umbraco.Core.Cache;
+using Umbraco.Core.Logging;
+
+namespace Umbraco.Core.Configuration.Grid
+{
+ class GridConfig : IGridConfig
+ {
+ public GridConfig(ILogger logger, IRuntimeCacheProvider runtimeCache, DirectoryInfo appPlugins, DirectoryInfo configFolder, bool isDebug)
+ {
+ EditorsConfig = new GridEditorsConfig(logger, runtimeCache, appPlugins, configFolder, isDebug);
+ }
+
+ public IGridEditorsConfig EditorsConfig { get; private set; }
+ }
+}
\ No newline at end of file
diff --git a/src/Umbraco.Core/Configuration/Grid/GridEditorsConfig.cs b/src/Umbraco.Core/Configuration/Grid/GridEditorsConfig.cs
new file mode 100644
index 0000000000..389c620637
--- /dev/null
+++ b/src/Umbraco.Core/Configuration/Grid/GridEditorsConfig.cs
@@ -0,0 +1,78 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using Newtonsoft.Json.Linq;
+using Umbraco.Core.Cache;
+using Umbraco.Core.Logging;
+using Umbraco.Core.Manifest;
+using Umbraco.Core.PropertyEditors;
+
+namespace Umbraco.Core.Configuration.Grid
+{
+ class GridEditorsConfig : IGridEditorsConfig
+ {
+ private readonly ILogger _logger;
+ private readonly IRuntimeCacheProvider _runtimeCache;
+ private readonly DirectoryInfo _appPlugins;
+ private readonly DirectoryInfo _configFolder;
+ private readonly bool _isDebug;
+
+ public GridEditorsConfig(ILogger logger, IRuntimeCacheProvider runtimeCache, DirectoryInfo appPlugins, DirectoryInfo configFolder, bool isDebug)
+ {
+ _logger = logger;
+ _runtimeCache = runtimeCache;
+ _appPlugins = appPlugins;
+ _configFolder = configFolder;
+ _isDebug = isDebug;
+ }
+
+ public IEnumerable Editors
+ {
+ get
+ {
+ Func> getResult = () =>
+ {
+ var editors = new List();
+ var gridConfig = Path.Combine(_configFolder.FullName, "grid.editors.config.js");
+ if (File.Exists(gridConfig))
+ {
+ try
+ {
+ var arr = JArray.Parse(File.ReadAllText(gridConfig));
+ //ensure the contents parse correctly to objects
+ var parsed = ManifestParser.GetGridEditors(arr);
+ editors.AddRange(parsed);
+ }
+ catch (Exception ex)
+ {
+ _logger.Error("Could not parse the contents of grid.editors.config.js into a JSON array", ex);
+ }
+ }
+
+ var parser = new ManifestParser(_appPlugins, _runtimeCache);
+ var builder = new ManifestBuilder(_runtimeCache, parser);
+ foreach (var gridEditor in builder.GridEditors)
+ {
+ //no duplicates! (based on alias)
+ if (editors.Contains(gridEditor) == false)
+ {
+ editors.Add(gridEditor);
+ }
+ }
+ return editors;
+ };
+
+ //cache the result if debugging is disabled
+ var result = _isDebug
+ ? getResult()
+ : _runtimeCache.GetCacheItem>(
+ typeof(GridEditorsConfig) + "Editors",
+ () => getResult(),
+ TimeSpan.FromMinutes(10));
+
+ return result;
+ }
+
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Umbraco.Core/Configuration/Grid/IGridConfig.cs b/src/Umbraco.Core/Configuration/Grid/IGridConfig.cs
new file mode 100644
index 0000000000..a1170c136e
--- /dev/null
+++ b/src/Umbraco.Core/Configuration/Grid/IGridConfig.cs
@@ -0,0 +1,14 @@
+using System;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Umbraco.Core.Configuration.Grid
+{
+ public interface IGridConfig
+ {
+
+ IGridEditorsConfig EditorsConfig { get; }
+
+ }
+}
diff --git a/src/Umbraco.Core/Configuration/Grid/IGridEditorConfig.cs b/src/Umbraco.Core/Configuration/Grid/IGridEditorConfig.cs
new file mode 100644
index 0000000000..0eedd5e035
--- /dev/null
+++ b/src/Umbraco.Core/Configuration/Grid/IGridEditorConfig.cs
@@ -0,0 +1,15 @@
+using System.Collections.Generic;
+using Newtonsoft.Json.Linq;
+
+namespace Umbraco.Core.Configuration.Grid
+{
+ public interface IGridEditorConfig
+ {
+ string Name { get; }
+ string Alias { get; }
+ string View { get; }
+ string Render { get; }
+ string Icon { get; }
+ IDictionary Config { get; }
+ }
+}
\ No newline at end of file
diff --git a/src/Umbraco.Core/Configuration/Grid/IGridEditorsConfig.cs b/src/Umbraco.Core/Configuration/Grid/IGridEditorsConfig.cs
new file mode 100644
index 0000000000..64fb1a831f
--- /dev/null
+++ b/src/Umbraco.Core/Configuration/Grid/IGridEditorsConfig.cs
@@ -0,0 +1,9 @@
+using System.Collections.Generic;
+
+namespace Umbraco.Core.Configuration.Grid
+{
+ public interface IGridEditorsConfig
+ {
+ IEnumerable Editors { get; }
+ }
+}
\ No newline at end of file
diff --git a/src/Umbraco.Core/Configuration/UmbracoConfig.cs b/src/Umbraco.Core/Configuration/UmbracoConfig.cs
index 1be7b5aadf..3cd9bab776 100644
--- a/src/Umbraco.Core/Configuration/UmbracoConfig.cs
+++ b/src/Umbraco.Core/Configuration/UmbracoConfig.cs
@@ -1,6 +1,9 @@
using System;
using System.Configuration;
+using System.IO;
+using Umbraco.Core.Cache;
using Umbraco.Core.Configuration.Dashboard;
+using Umbraco.Core.Configuration.Grid;
using Umbraco.Core.Configuration.UmbracoSettings;
using Umbraco.Core.Logging;
@@ -53,6 +56,7 @@ namespace Umbraco.Core.Configuration
private IDashboardSection _dashboardSection;
private IUmbracoSettingsSection _umbracoSettings;
+ private IGridConfig _gridConfig;
///
/// Gets the IDashboardSection
@@ -103,6 +107,28 @@ namespace Umbraco.Core.Configuration
}
+ }
+
+ ///
+ /// Only for testing
+ ///
+ ///
+ public void SetGridConfig(IGridConfig value)
+ {
+ _gridConfig = value;
+ }
+
+ ///
+ /// Gets the IGridConfig
+ ///
+ public IGridConfig GridConfig(ILogger logger, IRuntimeCacheProvider runtimeCache, DirectoryInfo appPlugins, DirectoryInfo configFolder, bool isDebug)
+ {
+ if (_gridConfig == null)
+ {
+ _gridConfig = new GridConfig(logger, runtimeCache, appPlugins, configFolder, isDebug);
+ }
+
+ return _gridConfig;
//TODO: Add other configurations here !
}
diff --git a/src/Umbraco.Core/Configuration/UmbracoSettings/IWebRoutingSection.cs b/src/Umbraco.Core/Configuration/UmbracoSettings/IWebRoutingSection.cs
index f3d42b6904..2998fc2f78 100644
--- a/src/Umbraco.Core/Configuration/UmbracoSettings/IWebRoutingSection.cs
+++ b/src/Umbraco.Core/Configuration/UmbracoSettings/IWebRoutingSection.cs
@@ -11,6 +11,8 @@
bool DisableFindContentByIdPath { get; }
string UrlProviderMode { get; }
+
+ string UmbracoApplicationUrl { get; }
}
}
\ No newline at end of file
diff --git a/src/Umbraco.Core/Configuration/UmbracoSettings/WebRoutingElement.cs b/src/Umbraco.Core/Configuration/UmbracoSettings/WebRoutingElement.cs
index f5b71eb2c7..1ed9bc034c 100644
--- a/src/Umbraco.Core/Configuration/UmbracoSettings/WebRoutingElement.cs
+++ b/src/Umbraco.Core/Configuration/UmbracoSettings/WebRoutingElement.cs
@@ -30,8 +30,13 @@ namespace Umbraco.Core.Configuration.UmbracoSettings
[ConfigurationProperty("urlProviderMode", DefaultValue = "AutoLegacy")]
public string UrlProviderMode
{
- get { return (string)base["urlProviderMode"]; }
+ get { return (string) base["urlProviderMode"]; }
}
+ [ConfigurationProperty("umbracoApplicationUrl", DefaultValue = null)]
+ public string UmbracoApplicationUrl
+ {
+ get { return (string)base["umbracoApplicationUrl"]; }
+ }
}
}
\ No newline at end of file
diff --git a/src/Umbraco.Core/Configuration/UmbracoVersion.cs b/src/Umbraco.Core/Configuration/UmbracoVersion.cs
index d79b2d6123..ef33e7a966 100644
--- a/src/Umbraco.Core/Configuration/UmbracoVersion.cs
+++ b/src/Umbraco.Core/Configuration/UmbracoVersion.cs
@@ -1,5 +1,6 @@
using System;
using System.Reflection;
+using Semver;
namespace Umbraco.Core.Configuration
{
@@ -23,10 +24,20 @@ namespace Umbraco.Core.Configuration
/// Gets the version comment (like beta or RC).
///
/// The version comment.
- public static string CurrentComment { get { return ""; } }
+ 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
public static string AssemblyVersion { get { return new AssemblyName(typeof(ActionsResolver).Assembly.FullName).Version.ToString(); } }
+
+ public static SemVersion GetSemanticVersion()
+ {
+ return new SemVersion(
+ Current.Major,
+ Current.Minor,
+ Current.Build,
+ CurrentComment.IsNullOrWhiteSpace() ? null : CurrentComment,
+ Current.Revision > 0 ? Current.Revision.ToInvariantString() : null);
+ }
}
}
\ No newline at end of file
diff --git a/src/Umbraco.Core/Constants-Applications.cs b/src/Umbraco.Core/Constants-Applications.cs
index f03108f4ac..12f7076fc4 100644
--- a/src/Umbraco.Core/Constants-Applications.cs
+++ b/src/Umbraco.Core/Constants-Applications.cs
@@ -43,7 +43,7 @@
public const string Users = "users";
///
- /// Application alias for the users section.
+ /// Application alias for the forms section.
///
public const string Forms = "forms";
}
@@ -59,7 +59,7 @@
public const string Content = "content";
///
- /// alias for the media tree.
+ /// alias for the member tree.
///
public const string Members = "member";
@@ -71,7 +71,7 @@
///
/// alias for the datatype tree.
///
- public const string DataTypes = "datatype";
+ public const string DataTypes = "dataTypes";
///
/// alias for the dictionary tree.
@@ -80,6 +80,22 @@
public const string Stylesheets = "stylesheets";
+ ///
+ /// alias for the document type tree.
+ ///
+ public const string DocumentTypes = "documentTypes";
+
+ ///
+ /// alias for the media type tree.
+ ///
+ public const string MediaTypes = "mediaTypes";
+
+
+ ///
+ /// alias for the member type tree.
+ ///
+ public const string MemberTypes = "memberTypes";
+
///
/// alias for the template tree.
///
diff --git a/src/Umbraco.Core/Constants-Conventions.cs b/src/Umbraco.Core/Constants-Conventions.cs
index c1d5bd35e3..7e2bb88964 100644
--- a/src/Umbraco.Core/Constants-Conventions.cs
+++ b/src/Umbraco.Core/Constants-Conventions.cs
@@ -27,6 +27,7 @@ namespace Umbraco.Core
///
/// The root id for all top level dictionary items
///
+ [Obsolete("There is no dictionary root item id anymore, it is simply null")]
public const string DictionaryItemRootId = "41c7638d-f529-4bff-853e-59a0c2fb1bde";
}
@@ -205,7 +206,7 @@ namespace Umbraco.Core
///
internal const string StandardPropertiesGroupName = "Membership";
- internal static Dictionary GetStandardPropertyTypeStubs()
+ public static Dictionary GetStandardPropertyTypeStubs()
{
return new Dictionary
{
diff --git a/src/Umbraco.Core/Constants-Icons.cs b/src/Umbraco.Core/Constants-Icons.cs
new file mode 100644
index 0000000000..a0928919f0
--- /dev/null
+++ b/src/Umbraco.Core/Constants-Icons.cs
@@ -0,0 +1,53 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Umbraco.Core
+{
+ public static partial class Constants
+ {
+ public static class Icons
+ {
+
+
+ ///
+ /// System contenttype icon
+ ///
+ public const string ContentType = "icon-arrangement";
+
+ ///
+ /// System datatype icon
+ ///
+ public const string DataType = "icon-autofill";
+
+ ///
+ /// System property editor icon
+ ///
+ public const string PropertyEditor = "icon-autofill";
+
+ ///
+ /// System macro icon
+ ///
+ public const string Macro = "icon-settings-alt";
+
+ ///
+ /// System member icon
+ ///
+ public const string Member = "icon-user";
+
+ ///
+ /// System member icon
+ ///
+ public const string MemberType = "icon-users";
+
+
+ ///
+ /// System member icon
+ ///
+ public const string Template = "icon-layout";
+
+ }
+ }
+}
diff --git a/src/Umbraco.Core/Constants-ObjectTypes.cs b/src/Umbraco.Core/Constants-ObjectTypes.cs
index 81bd66928d..560cd4b306 100644
--- a/src/Umbraco.Core/Constants-ObjectTypes.cs
+++ b/src/Umbraco.Core/Constants-ObjectTypes.cs
@@ -2,91 +2,148 @@
namespace Umbraco.Core
{
- public static partial class Constants
- {
- ///
- /// Defines the identifiers for Umbraco object types as constants for easy centralized access/management.
- ///
- public static class ObjectTypes
- {
- ///
- /// Guid for a Content Item object.
- ///
- public const string ContentItem = "10E2B09F-C28B-476D-B77A-AA686435E44A";
+ public static partial class Constants
+ {
+ ///
+ /// Defines the identifiers for Umbraco object types as constants for easy centralized access/management.
+ ///
+ public static class ObjectTypes
+ {
+ ///
+ /// Guid for a data type container
+ ///
+ public const string DataTypeContainer = "521231E3-8B37-469C-9F9D-51AFC91FEB7B";
- ///
- /// Guid for a Content Item Type object.
- ///
- public const string ContentItemType = "7A333C54-6F43-40A4-86A2-18688DC7E532";
+ ///
+ /// Guid for a data type container
+ ///
+ public static readonly Guid DataTypeContainerGuid = new Guid(DataTypeContainer);
- ///
- /// Guid for the Content Recycle Bin.
- ///
- public const string ContentRecycleBin = "01BB7FF2-24DC-4C0C-95A2-C24EF72BBAC8";
+ ///
+ /// Guid for a doc type container
+ ///
+ public const string DocumentTypeContainer = "2F7A2769-6B0B-4468-90DD-AF42D64F7F16";
- ///
- /// Guid for a DataType object.
- ///
- public const string DataType = "30A2A501-1978-4DDB-A57B-F7EFED43BA3C";
+ ///
+ /// Guid for a doc type container
+ ///
+ public static readonly Guid DocumentTypeContainerGuid = new Guid(DocumentTypeContainer);
- ///
- /// Guid for a Document object.
- ///
- public const string Document = "C66BA18E-EAF3-4CFF-8A22-41B16D66A972";
+ ///
+ /// Guid for a doc type container
+ ///
+ public const string MediaTypeContainer = "42AEF799-B288-4744-9B10-BE144B73CDC4";
- ///
- /// Guid for a Document Type object.
- ///
- public const string DocumentType = "A2CB7800-F571-4787-9638-BC48539A0EFB";
+ ///
+ /// Guid for a doc type container
+ ///
+ public static readonly Guid MediaTypeContainerGuid = new Guid(MediaTypeContainer);
- ///
- /// Guid for a Media object.
- ///
- public const string Media = "B796F64C-1F99-4FFB-B886-4BF4BC011A9C";
+ ///
+ /// Guid for a Content Item object.
+ ///
+ public const string ContentItem = "10E2B09F-C28B-476D-B77A-AA686435E44A";
- ///
- /// Guid for the Media Recycle Bin.
- ///
- public const string MediaRecycleBin = "CF3D8E34-1C1C-41e9-AE56-878B57B32113";
+ ///
+ /// Guid for a Content Item Type object.
+ ///
+ public const string ContentItemType = "7A333C54-6F43-40A4-86A2-18688DC7E532";
- ///
- /// Guid for a Media Type object.
- ///
- public const string MediaType = "4EA4382B-2F5A-4C2B-9587-AE9B3CF3602E";
+ ///
+ /// Guid for the Content Recycle Bin.
+ ///
+ public const string ContentRecycleBin = "01BB7FF2-24DC-4C0C-95A2-C24EF72BBAC8";
- ///
- /// Guid for a Member object.
- ///
- public const string Member = "39EB0F98-B348-42A1-8662-E7EB18487560";
+ ///
+ /// Guid for a DataType object.
+ ///
+ public const string DataType = "30A2A501-1978-4DDB-A57B-F7EFED43BA3C";
- ///
- /// Guid for a Member Group object.
- ///
- public const string MemberGroup = "366E63B9-880F-4E13-A61C-98069B029728";
+ ///
+ /// Guid for a DataType object.
+ ///
+ public static readonly Guid DataTypeGuid = new Guid(DataType);
- ///
- /// Guid for a Member Type object.
- ///
- public const string MemberType = "9B5416FB-E72F-45A9-A07B-5A9A2709CE43";
+ ///
+ /// Guid for a Document object.
+ ///
+ public const string Document = "C66BA18E-EAF3-4CFF-8A22-41B16D66A972";
- ///
- /// Guid for a Stylesheet object.
- ///
+ ///
+ /// Guid for a Document Type object.
+ ///
+ public const string DocumentType = "A2CB7800-F571-4787-9638-BC48539A0EFB";
+
+ ///
+ /// Guid for a Document Type object.
+ ///
+ public static readonly Guid DocumentTypeGuid = new Guid(DocumentType);
+
+ ///
+ /// Guid for a Media object.
+ ///
+ public const string Media = "B796F64C-1F99-4FFB-B886-4BF4BC011A9C";
+
+ ///
+ /// Guid for the Media Recycle Bin.
+ ///
+ public const string MediaRecycleBin = "CF3D8E34-1C1C-41e9-AE56-878B57B32113";
+
+ ///
+ /// Guid for a Media Type object.
+ ///
+ public const string MediaType = "4EA4382B-2F5A-4C2B-9587-AE9B3CF3602E";
+
+ ///
+ /// Guid for a Media Type object.
+ ///
+ public static readonly Guid MediaTypeGuid = new Guid(MediaType);
+
+ ///
+ /// Guid for a Member object.
+ ///
+ public const string Member = "39EB0F98-B348-42A1-8662-E7EB18487560";
+
+ ///
+ /// Guid for a Member Group object.
+ ///
+ public const string MemberGroup = "366E63B9-880F-4E13-A61C-98069B029728";
+
+ ///
+ /// Guid for a Member Type object.
+ ///
+ public const string MemberType = "9B5416FB-E72F-45A9-A07B-5A9A2709CE43";
+
+ ///
+ /// Guid for a Member Type object.
+ ///
+ public static readonly Guid MemberTypeGuid = new Guid(MemberType);
+
+ ///
+ /// Guid for a Stylesheet object.
+ ///
[Obsolete("This no longer exists in the database")]
public const string Stylesheet = "9F68DA4F-A3A8-44C2-8226-DCBD125E4840";
[Obsolete("This no longer exists in the database")]
internal const string StylesheetProperty = "5555da4f-a123-42b2-4488-dcdfb25e4111";
- ///
- /// Guid for the System Root.
- ///
- public const string SystemRoot = "EA7D8624-4CFE-4578-A871-24AA946BF34D";
+ ///
+ /// Guid for the System Root.
+ ///
+ public const string SystemRoot = "EA7D8624-4CFE-4578-A871-24AA946BF34D";
- ///
- /// Guid for a Template object.
- ///
- public const string Template = "6FBDE604-4178-42CE-A10B-8A2600A2F07D";
- }
- }
+ ///
+ /// Guid for a Template object.
+ ///
+ public const string Template = "6FBDE604-4178-42CE-A10B-8A2600A2F07D";
+
+ ///
+ /// Guid for a Lock object.
+ ///
+ public const string LockObject = "87A9F1FF-B1E4-4A25-BABB-465A4A47EC41";
+
+
+ }
+ }
}
\ No newline at end of file
diff --git a/src/Umbraco.Core/Constants-PropertyEditors.cs b/src/Umbraco.Core/Constants-PropertyEditors.cs
index a719e845b1..2f7d247b36 100644
--- a/src/Umbraco.Core/Constants-PropertyEditors.cs
+++ b/src/Umbraco.Core/Constants-PropertyEditors.cs
@@ -158,6 +158,11 @@ namespace Umbraco.Core
///
public const string IntegerAlias = "Umbraco.Integer";
+ ///
+ /// Alias for the Decimal datatype.
+ ///
+ public const string DecimalAlias = "Umbraco.Decimal";
+
///
/// Alias for the listview datatype.
///
@@ -310,13 +315,13 @@ namespace Umbraco.Core
public const string TextboxAlias = "Umbraco.Textbox";
///
- /// Guid for the Textbox multiple datatype.
+ /// Guid for the Textarea datatype.
///
[Obsolete("GUIDs are no longer used to reference Property Editors, use the Alias constant instead. This will be removed in future versions")]
public const string TextboxMultiple = "67DB8357-EF57-493E-91AC-936D305E0F2A";
///
- /// Alias for the Textbox multiple datatype.
+ /// Alias for the Textarea datatype.
///
public const string TextboxMultipleAlias = "Umbraco.TextboxMultiple";
diff --git a/src/Umbraco.Core/Constants-PropertyTypeGroups.cs b/src/Umbraco.Core/Constants-PropertyTypeGroups.cs
new file mode 100644
index 0000000000..5dabe90029
--- /dev/null
+++ b/src/Umbraco.Core/Constants-PropertyTypeGroups.cs
@@ -0,0 +1,31 @@
+namespace Umbraco.Core
+{
+ public static partial class Constants
+ {
+ ///
+ /// Defines the identifiers for property-type groups conventions that are used within the Umbraco core.
+ ///
+ public static class PropertyTypeGroups
+ {
+ ///
+ /// Guid for a Image PropertyTypeGroup object.
+ ///
+ public const string Image = "79ED4D07-254A-42CF-8FA9-EBE1C116A596";
+
+ ///
+ /// Guid for a File PropertyTypeGroup object.
+ ///
+ public const string File = "50899F9C-023A-4466-B623-ABA9049885FE";
+
+ ///
+ /// Guid for a Image PropertyTypeGroup object.
+ ///
+ public const string Contents = "79995FA2-63EE-453C-A29B-2E66F324CDBE";
+
+ ///
+ /// Guid for a Image PropertyTypeGroup object.
+ ///
+ public const string Membership = "0756729D-D665-46E3-B84A-37ACEAA614F8";
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Umbraco.Core/Constants-System.cs b/src/Umbraco.Core/Constants-System.cs
index b72afa265f..82e3a1ff3f 100644
--- a/src/Umbraco.Core/Constants-System.cs
+++ b/src/Umbraco.Core/Constants-System.cs
@@ -26,6 +26,8 @@
public const int DefaultMediaListViewDataTypeId = -96;
public const int DefaultMembersListViewDataTypeId = -97;
+ // identifiers for lock objects
+ public const int ServersLock = -331;
}
}
}
\ No newline at end of file
diff --git a/src/Umbraco.Core/Constants-Web.cs b/src/Umbraco.Core/Constants-Web.cs
index 0d7c2f41da..60fba0ae40 100644
--- a/src/Umbraco.Core/Constants-Web.cs
+++ b/src/Umbraco.Core/Constants-Web.cs
@@ -1,4 +1,7 @@
-namespace Umbraco.Core
+using System;
+using System.ComponentModel;
+
+namespace Umbraco.Core
{
public static partial class Constants
{
@@ -15,6 +18,8 @@
///
/// The auth cookie name
///
+ [Obsolete("DO NOT USE THIS, USE ISecuritySection.AuthCookieName, this will be removed in future versions")]
+ [EditorBrowsable(EditorBrowsableState.Never)]
public const string AuthCookieName = "UMB_UCONTEXT";
}
@@ -26,6 +31,7 @@
public const string BackOfficeExternalAuthenticationType = "UmbracoExternalCookie";
public const string BackOfficeExternalCookieName = "UMB_EXTLOGIN";
public const string BackOfficeTokenAuthenticationType = "UmbracoBackOfficeToken";
+ public const string BackOfficeTwoFactorAuthenticationType = "UmbracoTwoFactorCookie";
///
/// The prefix used for external identity providers for their authentication type
diff --git a/src/Umbraco.Core/CoreBootManager.cs b/src/Umbraco.Core/CoreBootManager.cs
index 990e8d471e..2c8ffe4f7f 100644
--- a/src/Umbraco.Core/CoreBootManager.cs
+++ b/src/Umbraco.Core/CoreBootManager.cs
@@ -3,10 +3,13 @@ using System.IO;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
+using System.Threading;
using AutoMapper;
using Umbraco.Core.Cache;
using Umbraco.Core.Configuration;
using Umbraco.Core.Configuration.UmbracoSettings;
+using Umbraco.Core.Events;
+using Umbraco.Core.Exceptions;
using Umbraco.Core.IO;
using Umbraco.Core.LightInject;
using Umbraco.Core.Logging;
@@ -51,8 +54,8 @@ namespace Umbraco.Core
private bool _isStarted = false;
private bool _isComplete = false;
private readonly UmbracoApplicationBase _umbracoApplication;
-
protected ApplicationContext ApplicationContext { get; private set; }
+ protected CacheHelper ApplicationCache { get; private set; }
protected UmbracoApplicationBase UmbracoApplication
{
@@ -72,27 +75,32 @@ namespace Umbraco.Core
_umbracoApplication = umbracoApplication;
}
+ internal CoreBootManager(UmbracoApplicationBase umbracoApplication, ProfilingLogger logger)
+ {
+ if (umbracoApplication == null) throw new ArgumentNullException("umbracoApplication");
+ if (logger == null) throw new ArgumentNullException("logger");
+ _umbracoApplication = umbracoApplication;
+ ProfilingLogger = logger;
+ }
+
public virtual IBootManager Initialize()
{
if (_isInitialized)
throw new InvalidOperationException("The boot manager has already been initialized");
- //Create logger/profiler, and their resolvers, these are special resolvers that can be resolved before frozen so we can start logging
- LoggerResolver.Current = new LoggerResolver(_umbracoApplication.Logger) { CanResolveBeforeFrozen = true };
- var profiler = CreateProfiler();
- ProfilerResolver.Current = new ProfilerResolver(profiler) {CanResolveBeforeFrozen = true};
- ProfilingLogger = new ProfilingLogger(_umbracoApplication.Logger, profiler);
+ InitializeLoggerResolver();
+ InitializeProfilerResolver();
- _timer = ProfilingLogger.DebugDuration("Umbraco application starting", "Umbraco application startup complete");
+ ProfilingLogger = ProfilingLogger?? new ProfilingLogger(LoggerResolver.Current.Logger, ProfilerResolver.Current.Profiler);
- //create the plugin manager
- //TODO: this is currently a singleton but it would be better if it weren't. Unfortunately the only way to get
- // rid of this singleton would be to put it into IoC and then use the ServiceLocator pattern.
+ _timer = ProfilingLogger.TraceDuration(
+ string.Format("Umbraco {0} application starting on {1}", UmbracoVersion.GetSemanticVersion().ToSemanticString(), NetworkHelper.MachineName),
+ "Umbraco application startup complete");
_cacheHelper = CreateApplicationCache();
ServiceProvider = new ActivatorServiceProvider();
PluginManager.Current = PluginManager = new PluginManager(ServiceProvider, _cacheHelper.RuntimeCache, ProfilingLogger, true);
- //build up core IoC servoces
+ ApplicationCache = CreateApplicationCache();
ConfigureCoreServices(Container);
//set the singleton resolved from the core container
@@ -118,6 +126,7 @@ namespace Umbraco.Core
InitializeModelMappers();
//now we need to call the initialize methods
+ //TODO: Make sure to try/catch the OnApplicationInitialized!!
Parallel.ForEach(_appStartupEvtContainer.GetAllInstances(), x => x.OnApplicationInitialized(UmbracoApplication, ApplicationContext));
_isInitialized = true;
@@ -258,6 +267,7 @@ namespace Umbraco.Core
if (_isStarted)
throw new InvalidOperationException("The boot manager has already been initialized");
+ //TODO: Make sure to try/catch the OnApplicationInitialized!!
//call OnApplicationStarting of each application events handler
Parallel.ForEach(_appStartupEvtContainer.GetAllInstances(), x => x.OnApplicationStarting(UmbracoApplication, ApplicationContext));
@@ -280,12 +290,24 @@ namespace Umbraco.Core
{
if (_isComplete)
throw new InvalidOperationException("The boot manager has already been completed");
-
+
FreezeResolution();
+ //Here we need to make sure the db can be connected to
+ EnsureDatabaseConnection();
+
+
+ //This is a special case for the user service, we need to tell it if it's an upgrade, if so we need to ensure that
+ // exceptions are bubbled up if a user is attempted to be persisted during an upgrade (i.e. when they auth to login)
+ ((UserService) ApplicationContext.Services.UserService).IsUpgrading = true;
+
+
+
//call OnApplicationStarting of each application events handler
+ //TODO: Make sure to try/catch the OnApplicationInitialized!!
Parallel.ForEach(_appStartupEvtContainer.GetAllInstances(), x => x.OnApplicationStarted(UmbracoApplication, ApplicationContext));
+
//end the current scope which was created to intantiate all of the startup handlers
_appStartupEvtContainer.EndCurrentScope();
@@ -302,6 +324,36 @@ namespace Umbraco.Core
//stop the timer and log the output
_timer.Dispose();
return this;
+ }
+
+ ///
+ /// We cannot continue if the db cannot be connected to
+ ///
+ private void EnsureDatabaseConnection()
+ {
+ if (ApplicationContext.IsConfigured == false) return;
+ if (ApplicationContext.DatabaseContext.IsDatabaseConfigured == false) return;
+
+ //try now
+ if (ApplicationContext.DatabaseContext.CanConnect)
+ return;
+
+ var currentTry = 0;
+ while (currentTry < 5)
+ {
+ //first wait, then retry
+ Thread.Sleep(1000);
+
+ if (ApplicationContext.DatabaseContext.CanConnect)
+ break;
+
+ currentTry++;
+ }
+
+ if (currentTry == 5)
+ {
+ throw new UmbracoStartupFailedException("Umbraco cannot start. A connection string is configured but the Umbraco cannot connect to the database.");
+ }
}
///
@@ -336,17 +388,34 @@ namespace Umbraco.Core
new Lazy(() => typeof (DelimitedManifestValueValidator)),
new Lazy(() => typeof (EmailValidator)),
new Lazy(() => typeof (IntegerValidator)),
+ new Lazy(() => typeof (DecimalValidator)),
});
- //by default we'll use the standard configuration based sync
- ServerRegistrarResolver.Current = new ServerRegistrarResolver(Container, typeof(ConfigServerRegistrar));
+ //by default we'll use the db server registrar unless the developer has the legacy
+ // dist calls enabled, in which case we'll use the config server registrar
+ if (UmbracoConfig.For.UmbracoSettings().DistributedCall.Enabled)
+ {
+ ServerRegistrarResolver.Current = new ServerRegistrarResolver(new ConfigServerRegistrar());
+ }
+ else
+ {
+ ServerRegistrarResolver.Current = new ServerRegistrarResolver(
+ new DatabaseServerRegistrar(
+ new Lazy(() => ApplicationContext.Services.ServerRegistrationService),
+ new DatabaseServerRegistrarOptions()));
+ }
+
- //by default (outside of the web) we'll use the default server messenger without
- //supplying a username/password, this will automatically disable distributed calls
- // .. we'll override this in the WebBootManager
- ServerMessengerResolver.Current = new ServerMessengerResolver(Container, typeof (WebServiceServerMessenger));
+ //by default we'll use the database server messenger with default options (no callbacks),
+ // this will be overridden in the web startup
+ ServerMessengerResolver.Current = new ServerMessengerResolver(
+ new DatabaseServerMessenger(ApplicationContext, true, new DatabaseServerMessengerOptions()));
+ MappingResolver.Current = new MappingResolver(
+ ServiceProvider, ProfilingLogger.Logger,
+ () => PluginManager.ResolveAssignedMapperTypes());
+
//RepositoryResolver.Current = new RepositoryResolver(
// new RepositoryFactory(ApplicationCache));
diff --git a/src/Umbraco.Core/DatabaseContext.cs b/src/Umbraco.Core/DatabaseContext.cs
index 741c4f9746..17b274d4ba 100644
--- a/src/Umbraco.Core/DatabaseContext.cs
+++ b/src/Umbraco.Core/DatabaseContext.cs
@@ -6,6 +6,7 @@ using System.Linq;
using System.Web;
using System.Web.Configuration;
using System.Xml.Linq;
+using Semver;
using Umbraco.Core.Configuration;
using Umbraco.Core.IO;
using Umbraco.Core.Logging;
@@ -13,6 +14,7 @@ using Umbraco.Core.Persistence;
using Umbraco.Core.Persistence.Migrations;
using Umbraco.Core.Persistence.Migrations.Initial;
using Umbraco.Core.Persistence.SqlSyntax;
+using Umbraco.Core.Services;
namespace Umbraco.Core
{
@@ -28,8 +30,6 @@ namespace Umbraco.Core
private readonly ILogger _logger;
private readonly SqlSyntaxProviders _syntaxProviders;
private bool _configured;
- private bool _canConnect;
- private volatile bool _connectCheck = false;
private readonly object _locker = new object();
private string _connectionString;
private string _providerName;
@@ -78,7 +78,7 @@ namespace Umbraco.Core
/// This should not be used for CRUD operations or queries against the
/// standard Umbraco tables! Use the Public services for that.
///
- public UmbracoDatabase Database
+ public virtual UmbracoDatabase Database
{
get { return _factory.CreateDatabase(); }
}
@@ -86,7 +86,7 @@ namespace Umbraco.Core
///
/// Boolean indicating whether the database has been configured
///
- public bool IsDatabaseConfigured
+ public virtual bool IsDatabaseConfigured
{
get { return _configured; }
}
@@ -94,33 +94,21 @@ namespace Umbraco.Core
///
/// Determines if the db can be connected to
///
- public bool CanConnect
+ public virtual bool CanConnect
{
get
{
if (IsDatabaseConfigured == false) return false;
-
- //double check lock so that it is only checked once and is fast
- if (_connectCheck == false)
- {
- lock (_locker)
- {
- if (_canConnect == false)
- {
- _canConnect = DbConnectionExtensions.IsConnectionAvailable(ConnectionString, DatabaseProvider);
- _connectCheck = true;
- }
- }
- }
-
- return _canConnect;
+ var canConnect = DbConnectionExtensions.IsConnectionAvailable(ConnectionString, DatabaseProvider);
+ LogHelper.Info("CanConnect = " + canConnect);
+ return canConnect;
}
}
///
/// Gets the configured umbraco db connection string.
///
- public string ConnectionString
+ public virtual string ConnectionString
{
get { return _connectionString; }
}
@@ -152,7 +140,7 @@ namespace Umbraco.Core
///
/// Returns the Type of DatabaseProvider used
///
- public DatabaseProviders DatabaseProvider
+ public virtual DatabaseProviders DatabaseProvider
{
get
{
@@ -527,7 +515,7 @@ namespace Umbraco.Core
return _result;
}
- internal Result CreateDatabaseSchemaAndData()
+ internal Result CreateDatabaseSchemaAndData(ApplicationContext applicationContext)
{
try
{
@@ -564,13 +552,15 @@ namespace Umbraco.Core
message = GetResultMessageForMySql();
var schemaResult = ValidateDatabaseSchema();
- var installedVersion = schemaResult.DetermineInstalledVersion();
+
+ var installedSchemaVersion = schemaResult.DetermineInstalledVersion();
//If Configuration Status is empty and the determined version is "empty" its a new install - otherwise upgrade the existing
- if (string.IsNullOrEmpty(GlobalSettings.ConfigurationStatus) && installedVersion.Equals(new Version(0, 0, 0)))
+ if (string.IsNullOrEmpty(GlobalSettings.ConfigurationStatus) && installedSchemaVersion.Equals(new Version(0, 0, 0)))
{
- var schemaHelper = new DatabaseSchemaHelper(database, _logger, SqlSyntax);
- schemaHelper.CreateDatabaseSchema();
+ var helper = new DatabaseSchemaHelper(database, _logger, SqlSyntax);
+ helper.CreateDatabaseSchema(true, applicationContext);
+
message = message + "
Installation completed!
";
//now that everything is done, we need to determine the version of SQL server that is executing
@@ -599,7 +589,7 @@ namespace Umbraco.Core
/// This assumes all of the previous checks are done!
///
///
- internal Result UpgradeSchemaAndData(IMigrationResolver migrationResolver)
+ internal Result UpgradeSchemaAndData(IMigrationEntryService migrationEntryService)
{
try
{
@@ -618,16 +608,55 @@ namespace Umbraco.Core
var message = GetResultMessageForMySql();
var schemaResult = ValidateDatabaseSchema();
- var installedVersion = schemaResult.DetermineInstalledVersion();
+
+ var installedSchemaVersion = new SemVersion(schemaResult.DetermineInstalledVersion());
+
+ var installedMigrationVersion = new SemVersion(0);
+ //we cannot check the migrations table if it doesn't exist, this will occur when upgrading to 7.3
+ if (schemaResult.ValidTables.Any(x => x.InvariantEquals("umbracoMigration")))
+ {
+ installedMigrationVersion = schemaResult.DetermineInstalledVersionByMigrations(migrationEntryService);
+ }
+
+ var targetVersion = UmbracoVersion.Current;
+ //In some cases - like upgrading from 7.2.6 -> 7.3, there will be no migration information in the database and therefore it will
+ // return a version of 0.0.0 and we don't necessarily want to run all migrations from 0 -> 7.3, so we'll just ensure that the
+ // migrations are run for the target version
+ if (installedMigrationVersion == new SemVersion(new Version(0, 0, 0)) && installedSchemaVersion > new SemVersion(new Version(0, 0, 0)))
+ {
+ //set the installedMigrationVersion to be one less than the target so the latest migrations are guaranteed to execute
+ installedMigrationVersion = new SemVersion(targetVersion.SubtractRevision());
+ }
+
+ //Figure out what our current installed version is. If the web.config doesn't have a version listed, then we'll use the minimum
+ // version detected between the schema installed and the migrations listed in the migration table.
+ // If there is a version in the web.config, we'll take the minimum between the listed migration in the db and what
+ // is declared in the web.config.
+
+ var currentInstalledVersion = string.IsNullOrEmpty(GlobalSettings.ConfigurationStatus)
+ //Take the minimum version between the detected schema version and the installed migration version
+ ? new[] {installedSchemaVersion, installedMigrationVersion}.Min()
+ //Take the minimum version between the installed migration version and the version specified in the config
+ : new[] { SemVersion.Parse(GlobalSettings.ConfigurationStatus), installedMigrationVersion }.Min();
+
+ //Ok, another edge case here. If the current version is a pre-release,
+ // then we want to ensure all migrations for the current release are executed.
+ if (currentInstalledVersion.Prerelease.IsNullOrWhiteSpace() == false)
+ {
+ currentInstalledVersion = new SemVersion(currentInstalledVersion.GetVersion().SubtractRevision());
+ }
+
//DO the upgrade!
- var currentVersion = string.IsNullOrEmpty(GlobalSettings.ConfigurationStatus)
- ? installedVersion
- : new Version(GlobalSettings.ConfigurationStatus);
- var targetVersion = UmbracoVersion.Current;
- var runner = new MigrationRunner(migrationResolver, _logger, currentVersion, targetVersion, GlobalSettings.UmbracoMigrationName);
- var upgraded = runner.Execute(database, DatabaseProvider, SqlSyntax, true);
+ var runner = new MigrationRunner(migrationEntryService, _logger, currentInstalledVersion, UmbracoVersion.GetSemanticVersion(), GlobalSettings.UmbracoMigrationName);
+
+
+ if (upgraded == false)
+ {
+ throw new ApplicationException("Upgrading failed, either an error occurred during the upgrade process or an event canceled the upgrade process, see log for full details");
+ }
+
message = message + "
Upgrade completed!
";
//now that everything is done, we need to determine the version of SQL server that is executing
diff --git a/src/Umbraco.Core/DelegateExtensions.cs b/src/Umbraco.Core/DelegateExtensions.cs
new file mode 100644
index 0000000000..78450448e2
--- /dev/null
+++ b/src/Umbraco.Core/DelegateExtensions.cs
@@ -0,0 +1,44 @@
+using System;
+using System.Diagnostics;
+using System.Threading;
+
+namespace Umbraco.Core
+{
+ public static class DelegateExtensions
+ {
+ public static Attempt RetryUntilSuccessOrTimeout(this Func> task, TimeSpan timeout, TimeSpan pause)
+ {
+ if (pause.TotalMilliseconds < 0)
+ {
+ throw new ArgumentException("pause must be >= 0 milliseconds");
+ }
+ var stopwatch = Stopwatch.StartNew();
+ do
+ {
+ var result = task();
+ if (result) { return result; }
+ Thread.Sleep((int)pause.TotalMilliseconds);
+ }
+ while (stopwatch.Elapsed < timeout);
+ return Attempt.Fail();
+ }
+
+ public static Attempt RetryUntilSuccessOrMaxAttempts(this Func> task, int totalAttempts, TimeSpan pause)
+ {
+ if (pause.TotalMilliseconds < 0)
+ {
+ throw new ArgumentException("pause must be >= 0 milliseconds");
+ }
+ int attempts = 0;
+ do
+ {
+ attempts++;
+ var result = task(attempts);
+ if (result) { return result; }
+ Thread.Sleep((int)pause.TotalMilliseconds);
+ }
+ while (attempts < totalAttempts);
+ return Attempt.Fail();
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Umbraco.Core/Dictionary/CultureDictionaryFactoryResolver.cs b/src/Umbraco.Core/Dictionary/CultureDictionaryFactoryResolver.cs
index 8d744c90a6..cf0227d2a3 100644
--- a/src/Umbraco.Core/Dictionary/CultureDictionaryFactoryResolver.cs
+++ b/src/Umbraco.Core/Dictionary/CultureDictionaryFactoryResolver.cs
@@ -1,5 +1,6 @@
using System;
using System.Linq.Expressions;
+using System.ComponentModel;
using Umbraco.Core.LightInject;
using Umbraco.Core.ObjectResolution;
@@ -36,13 +37,13 @@ namespace Umbraco.Core.Dictionary
}
///
- /// Can be used by developers at runtime to set their ICultureDictionaryFactory at app startup
- ///
- ///
- public void SetContentStore(ICultureDictionaryFactory factory)
- {
- Value = factory;
- }
+ /// Can be used by developers at runtime to set their ICultureDictionaryFactory at app startup
+ ///
+ ///
+ public void SetDictionaryFactory(ICultureDictionaryFactory factory)
+ {
+ Value = factory;
+ }
///
/// Returns the ICultureDictionaryFactory
diff --git a/src/Umbraco.Core/Dictionary/ICultureDictionary.cs b/src/Umbraco.Core/Dictionary/ICultureDictionary.cs
index 57a739dbc6..8617d7440e 100644
--- a/src/Umbraco.Core/Dictionary/ICultureDictionary.cs
+++ b/src/Umbraco.Core/Dictionary/ICultureDictionary.cs
@@ -1,4 +1,5 @@
using System;
+using System.Collections.Generic;
using System.Globalization;
namespace Umbraco.Core.Dictionary
@@ -19,5 +20,12 @@ namespace Umbraco.Core.Dictionary
/// Returns the current culture
///
CultureInfo Culture { get; }
+
+ ///
+ /// Returns the child dictionary entries for a given key
+ ///
+ ///
+ ///
+ IDictionary GetChildren(string key);
}
}
\ No newline at end of file
diff --git a/src/Umbraco.Core/DisposableObject.cs b/src/Umbraco.Core/DisposableObject.cs
index f054b53c98..516a9712e5 100644
--- a/src/Umbraco.Core/DisposableObject.cs
+++ b/src/Umbraco.Core/DisposableObject.cs
@@ -4,69 +4,58 @@ using System.Threading;
namespace Umbraco.Core
{
///
- /// Abstract implementation of logic commonly required to safely handle disposable unmanaged resources.
+ /// Abstract implementation of IDisposable.
///
+ ///
+ /// Can also be used as a pattern for when inheriting is not possible.
+ ///
+ /// See also: https://msdn.microsoft.com/en-us/library/b1yfkh5e%28v=vs.110%29.aspx
+ /// See also: https://lostechies.com/chrispatterson/2012/11/29/idisposable-done-right/
+ ///
+ /// Note: if an object's ctor throws, it will never be disposed, and so if that ctor
+ /// has allocated disposable objects, it should take care of disposing them.
+ ///
public abstract class DisposableObject : IDisposable
{
private bool _disposed;
- private readonly ReaderWriterLockSlim _disposalLocker = new ReaderWriterLockSlim();
+ private readonly object _locko = new object();
- ///
- /// Gets a value indicating whether this instance is disposed.
- ///
- ///
- /// true if this instance is disposed; otherwise, false.
- ///
- public bool IsDisposed
- {
- get { return _disposed; }
- }
+ // gets a value indicating whether this instance is disposed.
+ // for internal tests only (not thread safe)
+ //TODO make this internal + rename "Disposed" when we can break compatibility
+ public bool IsDisposed { get { return _disposed; } }
- ///
- /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
- ///
- /// 2
+ // implements IDisposable
public void Dispose()
{
Dispose(true);
-
- // Use SupressFinalize in case a subclass of this type implements a finalizer.
GC.SuppressFinalize(this);
}
+ // finalizer
~DisposableObject()
{
- // Run dispose but let the class know it was due to the finalizer running.
Dispose(false);
}
- protected virtual void Dispose(bool disposing)
+ //TODO make this private, non-virtual when we can break compatibility
+ protected virtual void Dispose(bool disposing)
{
- // Only operate if we haven't already disposed
- if (IsDisposed || !disposing) return;
+ lock (_locko)
+ {
+ if (_disposed) return;
+ _disposed = true;
+ }
- using (new WriteLock(_disposalLocker))
- {
- // Check again now we're inside the lock
- if (IsDisposed) return;
+ DisposeUnmanagedResources();
- // Call to actually release resources. This method is only
- // kept separate so that the entire disposal logic can be used as a VS snippet
- DisposeResources();
-
- // Indicate that the instance has been disposed.
- _disposed = true;
- }
+ if (disposing)
+ DisposeResources();
}
- ///
- /// Handles the disposal of resources. Derived from abstract class which handles common required locking logic.
- ///
protected abstract void DisposeResources();
protected virtual void DisposeUnmanagedResources()
- {
-
- }
+ { }
}
}
\ No newline at end of file
diff --git a/src/Umbraco.Core/EnumerableExtensions.cs b/src/Umbraco.Core/EnumerableExtensions.cs
index 79b48af3ab..58d4d453b7 100644
--- a/src/Umbraco.Core/EnumerableExtensions.cs
+++ b/src/Umbraco.Core/EnumerableExtensions.cs
@@ -101,6 +101,7 @@ namespace Umbraco.Core
/// The select child.
/// Item type
/// list of TItem
+ [Obsolete("Do not use, use SelectRecursive instead which has far less potential of re-iterating an iterator which may cause significantly more SQL queries")]
public static IEnumerable FlattenList(this IEnumerable e, Func> f)
{
return e.SelectMany(c => f(c).FlattenList(f)).Concat(e);
diff --git a/src/Umbraco.Core/Events/CancellableEventArgs.cs b/src/Umbraco.Core/Events/CancellableEventArgs.cs
index 80e69ae353..506ba1c22e 100644
--- a/src/Umbraco.Core/Events/CancellableEventArgs.cs
+++ b/src/Umbraco.Core/Events/CancellableEventArgs.cs
@@ -3,7 +3,7 @@ using System.Security.Permissions;
namespace Umbraco.Core.Events
{
- ///
+ ///
/// Event args for that can support cancellation
///
[HostProtection(SecurityAction.LinkDemand, SharedState = true)]
@@ -11,15 +11,30 @@ namespace Umbraco.Core.Events
{
private bool _cancel;
- public CancellableEventArgs(bool canCancel)
+ public CancellableEventArgs(bool canCancel, EventMessages eventMessages)
+ {
+ if (eventMessages == null) throw new ArgumentNullException("eventMessages");
+ CanCancel = canCancel;
+ Messages = eventMessages;
+ }
+
+ public CancellableEventArgs(bool canCancel)
{
CanCancel = canCancel;
- }
+ //create a standalone messages
+ Messages = new EventMessages();
+ }
- public CancellableEventArgs()
+ public CancellableEventArgs(EventMessages eventMessages)
+ : this(true, eventMessages)
+ {
+ }
+
+ public CancellableEventArgs()
: this(true)
{
}
+
///
/// Flag to determine if this instance will support being cancellable
///
@@ -32,7 +47,7 @@ namespace Umbraco.Core.Events
{
get
{
- if (!CanCancel)
+ if (CanCancel == false)
{
throw new InvalidOperationException("This event argument class does not support cancelling.");
}
@@ -40,12 +55,28 @@ namespace Umbraco.Core.Events
}
set
{
- if (!CanCancel)
+ if (CanCancel == false)
{
throw new InvalidOperationException("This event argument class does not support cancelling.");
}
_cancel = value;
}
}
- }
+
+ ///
+ /// if this instance supports cancellation, this will set Cancel to true with an affiliated cancellation message
+ ///
+ ///
+ public void CancelOperation(EventMessage cancelationMessage)
+ {
+ Cancel = true;
+ cancelationMessage.IsDefaultEventMessage = true;
+ Messages.Add(cancelationMessage);
+ }
+
+ ///
+ /// Returns the EventMessages object which is used to add messages to the message collection for this event
+ ///
+ public EventMessages Messages { get; private set; }
+ }
}
\ No newline at end of file
diff --git a/src/Umbraco.Core/Events/CancellableObjectEventArgs.cs b/src/Umbraco.Core/Events/CancellableObjectEventArgs.cs
index a51da5652e..726d32d7b0 100644
--- a/src/Umbraco.Core/Events/CancellableObjectEventArgs.cs
+++ b/src/Umbraco.Core/Events/CancellableObjectEventArgs.cs
@@ -12,7 +12,18 @@ namespace Umbraco.Core.Events
public class CancellableObjectEventArgs : CancellableEventArgs
{
- public CancellableObjectEventArgs(T eventObject, bool canCancel)
+ public CancellableObjectEventArgs(T eventObject, bool canCancel, EventMessages eventMessages)
+ : base(canCancel, eventMessages)
+ {
+ EventObject = eventObject;
+ }
+
+ public CancellableObjectEventArgs(T eventObject, EventMessages eventMessages)
+ : this(eventObject, true, eventMessages)
+ {
+ }
+
+ public CancellableObjectEventArgs(T eventObject, bool canCancel)
: base(canCancel)
{
EventObject = eventObject;
diff --git a/src/Umbraco.Core/Events/DeleteEventArgs.cs b/src/Umbraco.Core/Events/DeleteEventArgs.cs
index 4edee846cf..1025066bcc 100644
--- a/src/Umbraco.Core/Events/DeleteEventArgs.cs
+++ b/src/Umbraco.Core/Events/DeleteEventArgs.cs
@@ -4,12 +4,56 @@ namespace Umbraco.Core.Events
{
public class DeleteEventArgs : CancellableObjectEventArgs>
{
- ///
- /// Constructor accepting multiple entities that are used in the delete operation
- ///
- ///
- ///
- public DeleteEventArgs(IEnumerable eventObject, bool canCancel) : base(eventObject, canCancel)
+ ///
+ /// Constructor accepting multiple entities that are used in the delete operation
+ ///
+ ///
+ ///
+ ///
+ public DeleteEventArgs(IEnumerable eventObject, bool canCancel, EventMessages eventMessages) : base(eventObject, canCancel, eventMessages)
+ {
+ MediaFilesToDelete = new List();
+ }
+
+ ///
+ /// Constructor accepting multiple entities that are used in the delete operation
+ ///
+ ///
+ ///
+ public DeleteEventArgs(IEnumerable eventObject, EventMessages eventMessages) : base(eventObject, eventMessages)
+ {
+ MediaFilesToDelete = new List();
+ }
+
+ ///
+ /// Constructor accepting a single entity instance
+ ///
+ ///
+ ///
+ public DeleteEventArgs(TEntity eventObject, EventMessages eventMessages)
+ : base(new List { eventObject }, eventMessages)
+ {
+ MediaFilesToDelete = new List();
+ }
+
+ ///
+ /// Constructor accepting a single entity instance
+ ///
+ ///
+ ///
+ ///
+ public DeleteEventArgs(TEntity eventObject, bool canCancel, EventMessages eventMessages)
+ : base(new List { eventObject }, canCancel, eventMessages)
+ {
+ MediaFilesToDelete = new List();
+ }
+
+ ///
+ /// Constructor accepting multiple entities that are used in the delete operation
+ ///
+ ///
+ ///
+ public DeleteEventArgs(IEnumerable eventObject, bool canCancel) : base(eventObject, canCancel)
{
MediaFilesToDelete = new List();
}
@@ -60,7 +104,13 @@ namespace Umbraco.Core.Events
public class DeleteEventArgs : CancellableEventArgs
{
- public DeleteEventArgs(int id, bool canCancel)
+ public DeleteEventArgs(int id, bool canCancel, EventMessages eventMessages)
+ : base(canCancel, eventMessages)
+ {
+ Id = id;
+ }
+
+ public DeleteEventArgs(int id, bool canCancel)
: base(canCancel)
{
Id = id;
diff --git a/src/Umbraco.Core/Events/EventExtensions.cs b/src/Umbraco.Core/Events/EventExtensions.cs
index 99cf145fe0..8c645aead6 100644
--- a/src/Umbraco.Core/Events/EventExtensions.cs
+++ b/src/Umbraco.Core/Events/EventExtensions.cs
@@ -1,4 +1,5 @@
using System;
+using System.Linq;
namespace Umbraco.Core.Events
{
@@ -7,16 +8,16 @@ namespace Umbraco.Core.Events
///
public static class EventExtensions
{
- ///
- /// Raises the event and returns a boolean value indicating if the event was cancelled
- ///
- ///
- ///
- ///
- ///
- ///
- ///
- public static bool IsRaisedEventCancelled(
+ ///
+ /// Raises the event and returns a boolean value indicating if the event was cancelled
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ public static bool IsRaisedEventCancelled(
this TypedEventHandler eventHandler,
TArgs args,
TSender sender)
@@ -27,16 +28,16 @@ namespace Umbraco.Core.Events
return args.Cancel;
}
-
- ///
- /// Raises the event
- ///
- ///
- ///
- ///
- ///
- ///
- public static void RaiseEvent(
+
+ ///
+ /// Raises the event
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ public static void RaiseEvent(
this TypedEventHandler eventHandler,
TArgs args,
TSender sender)
diff --git a/src/Umbraco.Core/Events/EventMessage.cs b/src/Umbraco.Core/Events/EventMessage.cs
new file mode 100644
index 0000000000..473f955d45
--- /dev/null
+++ b/src/Umbraco.Core/Events/EventMessage.cs
@@ -0,0 +1,27 @@
+namespace Umbraco.Core.Events
+{
+ ///
+ /// An event message
+ ///
+ public sealed class EventMessage
+ {
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public EventMessage(string category, string message, EventMessageType messageType = EventMessageType.Default)
+ {
+ Category = category;
+ Message = message;
+ MessageType = messageType;
+ }
+
+ public string Category { get; private set; }
+ public string Message { get; private set; }
+ public EventMessageType MessageType { get; private set; }
+
+ ///
+ /// This is used to track if this message should be used as a default message so that Umbraco doesn't also append it's own default messages
+ ///
+ internal bool IsDefaultEventMessage { get; set; }
+ }
+}
\ No newline at end of file
diff --git a/src/Umbraco.Core/Events/EventMessages.cs b/src/Umbraco.Core/Events/EventMessages.cs
new file mode 100644
index 0000000000..2900f3d471
--- /dev/null
+++ b/src/Umbraco.Core/Events/EventMessages.cs
@@ -0,0 +1,32 @@
+using System.Collections.Generic;
+
+namespace Umbraco.Core.Events
+{
+ ///
+ /// Event messages collection
+ ///
+ public sealed class EventMessages : DisposableObject
+ {
+ private readonly List _msgs = new List();
+
+ public void Add(EventMessage msg)
+ {
+ _msgs.Add(msg);
+ }
+
+ public int Count
+ {
+ get { return _msgs.Count; }
+ }
+
+ public IEnumerable GetAll()
+ {
+ return _msgs;
+ }
+
+ protected override void DisposeResources()
+ {
+ _msgs.Clear();
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Umbraco.Core/Events/IEventMessagesFactory.cs b/src/Umbraco.Core/Events/IEventMessagesFactory.cs
new file mode 100644
index 0000000000..cb2391186d
--- /dev/null
+++ b/src/Umbraco.Core/Events/IEventMessagesFactory.cs
@@ -0,0 +1,12 @@
+using System.Collections.Generic;
+
+namespace Umbraco.Core.Events
+{
+ ///
+ /// Event messages factory
+ ///
+ public interface IEventMessagesFactory
+ {
+ EventMessages Get();
+ }
+}
\ No newline at end of file
diff --git a/src/Umbraco.Core/Events/MessageType.cs b/src/Umbraco.Core/Events/MessageType.cs
new file mode 100644
index 0000000000..6299a0e7d7
--- /dev/null
+++ b/src/Umbraco.Core/Events/MessageType.cs
@@ -0,0 +1,14 @@
+namespace Umbraco.Core.Events
+{
+ ///
+ /// The type of event message
+ ///
+ public enum EventMessageType
+ {
+ Default = 0,
+ Info = 1,
+ Error = 2,
+ Success = 3,
+ Warning = 4
+ }
+}
\ No newline at end of file
diff --git a/src/Umbraco.Core/Events/MigrationEventArgs.cs b/src/Umbraco.Core/Events/MigrationEventArgs.cs
index c6da480999..7eed61484b 100644
--- a/src/Umbraco.Core/Events/MigrationEventArgs.cs
+++ b/src/Umbraco.Core/Events/MigrationEventArgs.cs
@@ -1,5 +1,7 @@
using System;
using System.Collections.Generic;
+using System.ComponentModel;
+using Semver;
using Umbraco.Core.Persistence.Migrations;
namespace Umbraco.Core.Events
@@ -13,11 +15,20 @@ namespace Umbraco.Core.Events
///
///
///
+ public MigrationEventArgs(IList eventObject, SemVersion configuredVersion, SemVersion targetVersion, bool canCancel)
+ : base(eventObject, canCancel)
+ {
+ ConfiguredSemVersion = configuredVersion;
+ TargetSemVersion = targetVersion;
+ }
+
+ [Obsolete("Use constructor accepting UmbracoVersion instances instead")]
+ [EditorBrowsable(EditorBrowsableState.Never)]
public MigrationEventArgs(IList eventObject, Version configuredVersion, Version targetVersion, bool canCancel)
: base(eventObject, canCancel)
{
- ConfiguredVersion = configuredVersion;
- TargetVersion = targetVersion;
+ ConfiguredSemVersion = new SemVersion(configuredVersion);
+ TargetSemVersion = new SemVersion(targetVersion);
}
///
@@ -28,12 +39,12 @@ namespace Umbraco.Core.Events
///
///
///
- internal MigrationEventArgs(IList eventObject, MigrationContext migrationContext, Version configuredVersion, Version targetVersion, bool canCancel)
+ internal MigrationEventArgs(IList eventObject, MigrationContext migrationContext, SemVersion configuredVersion, SemVersion targetVersion, bool canCancel)
: base(eventObject, canCancel)
{
MigrationContext = migrationContext;
- ConfiguredVersion = configuredVersion;
- TargetVersion = targetVersion;
+ ConfiguredSemVersion = configuredVersion;
+ TargetSemVersion = targetVersion;
}
///
@@ -42,11 +53,20 @@ namespace Umbraco.Core.Events
///
///
///
+ public MigrationEventArgs(IList eventObject, SemVersion configuredVersion, SemVersion targetVersion)
+ : base(eventObject)
+ {
+ ConfiguredSemVersion = configuredVersion;
+ TargetSemVersion = targetVersion;
+ }
+
+ [Obsolete("Use constructor accepting UmbracoVersion instances instead")]
+ [EditorBrowsable(EditorBrowsableState.Never)]
public MigrationEventArgs(IList eventObject, Version configuredVersion, Version targetVersion)
: base(eventObject)
{
- ConfiguredVersion = configuredVersion;
- TargetVersion = targetVersion;
+ ConfiguredSemVersion = new SemVersion(configuredVersion);
+ TargetSemVersion = new SemVersion(targetVersion);
}
///
@@ -57,9 +77,23 @@ namespace Umbraco.Core.Events
get { return EventObject; }
}
- public Version ConfiguredVersion { get; private set; }
+ [Obsolete("Use ConfiguredSemVersion instead")]
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public Version ConfiguredVersion
+ {
+ get { return ConfiguredSemVersion.GetVersion(); }
+ }
- public Version TargetVersion { get; private set; }
+ [Obsolete("Use TargetUmbracoVersion instead")]
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public Version TargetVersion
+ {
+ get { return TargetSemVersion.GetVersion(); }
+ }
+
+ public SemVersion ConfiguredSemVersion { get; private set; }
+
+ public SemVersion TargetSemVersion { get; private set; }
internal MigrationContext MigrationContext { get; private set; }
}
diff --git a/src/Umbraco.Core/Events/MoveEventArgs.cs b/src/Umbraco.Core/Events/MoveEventArgs.cs
index 107629ff19..0f0a5183a9 100644
--- a/src/Umbraco.Core/Events/MoveEventArgs.cs
+++ b/src/Umbraco.Core/Events/MoveEventArgs.cs
@@ -4,22 +4,51 @@ using System.Linq;
namespace Umbraco.Core.Events
{
- public class MoveEventInfo
- {
- public MoveEventInfo(TEntity entity, string originalPath, int newParentId)
- {
- Entity = entity;
- OriginalPath = originalPath;
- NewParentId = newParentId;
- }
-
- public TEntity Entity { get; set; }
- public string OriginalPath { get; set; }
- public int NewParentId { get; set; }
- }
-
public class MoveEventArgs : CancellableObjectEventArgs
{
+ ///
+ /// Constructor accepting a collection of MoveEventInfo objects
+ ///
+ ///
+ ///
+ ///
+ /// A colleciton of MoveEventInfo objects that exposes all entities that have been moved during a single move operation
+ ///
+ public MoveEventArgs(bool canCancel, EventMessages eventMessages, params MoveEventInfo[] moveInfo)
+ : base(default(TEntity), canCancel, eventMessages)
+ {
+ if (moveInfo.FirstOrDefault() == null)
+ {
+ throw new ArgumentException("moveInfo argument must contain at least one item");
+ }
+
+ MoveInfoCollection = moveInfo;
+ //assign the legacy props
+ EventObject = moveInfo.First().Entity;
+ ParentId = moveInfo.First().NewParentId;
+ }
+
+ ///
+ /// Constructor accepting a collection of MoveEventInfo objects
+ ///
+ ///
+ ///
+ /// A colleciton of MoveEventInfo objects that exposes all entities that have been moved during a single move operation
+ ///
+ public MoveEventArgs(EventMessages eventMessages, params MoveEventInfo[] moveInfo)
+ : base(default(TEntity), eventMessages)
+ {
+ if (moveInfo.FirstOrDefault() == null)
+ {
+ throw new ArgumentException("moveInfo argument must contain at least one item");
+ }
+
+ MoveInfoCollection = moveInfo;
+ //assign the legacy props
+ EventObject = moveInfo.First().Entity;
+ ParentId = moveInfo.First().NewParentId;
+ }
+
///
/// Constructor accepting a collection of MoveEventInfo objects
///
diff --git a/src/Umbraco.Core/Events/MoveEventInfo.cs b/src/Umbraco.Core/Events/MoveEventInfo.cs
new file mode 100644
index 0000000000..a74db7f36e
--- /dev/null
+++ b/src/Umbraco.Core/Events/MoveEventInfo.cs
@@ -0,0 +1,16 @@
+namespace Umbraco.Core.Events
+{
+ public class MoveEventInfo
+ {
+ public MoveEventInfo(TEntity entity, string originalPath, int newParentId)
+ {
+ Entity = entity;
+ OriginalPath = originalPath;
+ NewParentId = newParentId;
+ }
+
+ public TEntity Entity { get; set; }
+ public string OriginalPath { get; set; }
+ public int NewParentId { get; set; }
+ }
+}
\ No newline at end of file
diff --git a/src/Umbraco.Core/Events/NewEventArgs.cs b/src/Umbraco.Core/Events/NewEventArgs.cs
index 8e6cd64db1..acfd64e60d 100644
--- a/src/Umbraco.Core/Events/NewEventArgs.cs
+++ b/src/Umbraco.Core/Events/NewEventArgs.cs
@@ -4,7 +4,39 @@ namespace Umbraco.Core.Events
{
public class NewEventArgs : CancellableObjectEventArgs
{
- public NewEventArgs(TEntity eventObject, bool canCancel, string @alias, int parentId) : base(eventObject, canCancel)
+
+
+ public NewEventArgs(TEntity eventObject, bool canCancel, string @alias, int parentId, EventMessages eventMessages)
+ : base(eventObject, canCancel, eventMessages)
+ {
+ Alias = alias;
+ ParentId = parentId;
+ }
+
+ public NewEventArgs(TEntity eventObject, bool canCancel, string @alias, TEntity parent, EventMessages eventMessages)
+ : base(eventObject, canCancel, eventMessages)
+ {
+ Alias = alias;
+ Parent = parent;
+ }
+
+ public NewEventArgs(TEntity eventObject, string @alias, int parentId, EventMessages eventMessages)
+ : base(eventObject, eventMessages)
+ {
+ Alias = alias;
+ ParentId = parentId;
+ }
+
+ public NewEventArgs(TEntity eventObject, string @alias, TEntity parent, EventMessages eventMessages)
+ : base(eventObject, eventMessages)
+ {
+ Alias = alias;
+ Parent = parent;
+ }
+
+
+
+ public NewEventArgs(TEntity eventObject, bool canCancel, string @alias, int parentId) : base(eventObject, canCancel)
{
Alias = alias;
ParentId = parentId;
diff --git a/src/Umbraco.Core/Events/PublishEventArgs.cs b/src/Umbraco.Core/Events/PublishEventArgs.cs
index 2355e250ef..a791781617 100644
--- a/src/Umbraco.Core/Events/PublishEventArgs.cs
+++ b/src/Umbraco.Core/Events/PublishEventArgs.cs
@@ -4,6 +4,52 @@ namespace Umbraco.Core.Events
{
public class PublishEventArgs : CancellableObjectEventArgs>
{
+ ///
+ /// Constructor accepting multiple entities that are used in the publish operation
+ ///
+ ///
+ ///
+ ///
+ ///
+ public PublishEventArgs(IEnumerable eventObject, bool canCancel, bool isAllPublished, EventMessages eventMessages)
+ : base(eventObject, canCancel, eventMessages)
+ {
+ IsAllRepublished = isAllPublished;
+ }
+
+ ///
+ /// Constructor accepting multiple entities that are used in the publish operation
+ ///
+ ///
+ ///
+ public PublishEventArgs(IEnumerable eventObject, EventMessages eventMessages)
+ : base(eventObject, eventMessages)
+ {
+ }
+
+ ///
+ /// Constructor accepting a single entity instance
+ ///
+ ///
+ ///
+ public PublishEventArgs(TEntity eventObject, EventMessages eventMessages)
+ : base(new List { eventObject }, eventMessages)
+ {
+ }
+
+ ///
+ /// Constructor accepting a single entity instance
+ ///
+ ///
+ ///
+ ///
+ ///
+ public PublishEventArgs(TEntity eventObject, bool canCancel, bool isAllPublished, EventMessages eventMessages)
+ : base(new List { eventObject }, canCancel, eventMessages)
+ {
+ IsAllRepublished = isAllPublished;
+ }
+
///
/// Constructor accepting multiple entities that are used in the publish operation
///
diff --git a/src/Umbraco.Core/Events/SaveEventArgs.cs b/src/Umbraco.Core/Events/SaveEventArgs.cs
index d3ac8b22e5..b84e28285e 100644
--- a/src/Umbraco.Core/Events/SaveEventArgs.cs
+++ b/src/Umbraco.Core/Events/SaveEventArgs.cs
@@ -4,12 +4,55 @@ namespace Umbraco.Core.Events
{
public class SaveEventArgs : CancellableObjectEventArgs>
{
- ///
- /// Constructor accepting multiple entities that are used in the saving operation
- ///
- ///
- ///
- public SaveEventArgs(IEnumerable eventObject, bool canCancel)
+ ///
+ /// Constructor accepting multiple entities that are used in the saving operation
+ ///
+ ///
+ ///
+ ///
+ public SaveEventArgs(IEnumerable eventObject, bool canCancel, EventMessages eventMessages)
+ : base(eventObject, canCancel, eventMessages)
+ {
+ }
+
+ ///
+ /// Constructor accepting multiple entities that are used in the saving operation
+ ///
+ ///
+ ///
+ public SaveEventArgs(IEnumerable eventObject, EventMessages eventMessages)
+ : base(eventObject, eventMessages)
+ {
+ }
+
+ ///
+ /// Constructor accepting a single entity instance
+ ///
+ ///
+ ///
+ public SaveEventArgs(TEntity eventObject, EventMessages eventMessages)
+ : base(new List { eventObject }, eventMessages)
+ {
+ }
+
+ ///
+ /// Constructor accepting a single entity instance
+ ///
+ ///
+ ///
+ ///
+ public SaveEventArgs(TEntity eventObject, bool canCancel, EventMessages eventMessages)
+ : base(new List { eventObject }, canCancel, eventMessages)
+ {
+ }
+
+
+ ///
+ /// Constructor accepting multiple entities that are used in the saving operation
+ ///
+ ///
+ ///
+ public SaveEventArgs(IEnumerable eventObject, bool canCancel)
: base(eventObject, canCancel)
{
}
diff --git a/src/Umbraco.Core/Events/TransientMessagesFactory.cs b/src/Umbraco.Core/Events/TransientMessagesFactory.cs
new file mode 100644
index 0000000000..5cd291a37f
--- /dev/null
+++ b/src/Umbraco.Core/Events/TransientMessagesFactory.cs
@@ -0,0 +1,13 @@
+namespace Umbraco.Core.Events
+{
+ ///
+ /// A simple/default transient messages factory
+ ///
+ internal class TransientMessagesFactory : IEventMessagesFactory
+ {
+ public EventMessages Get()
+ {
+ return new EventMessages();
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Umbraco.Core/Exceptions/DataOperationException.cs b/src/Umbraco.Core/Exceptions/DataOperationException.cs
new file mode 100644
index 0000000000..9a66e6a5be
--- /dev/null
+++ b/src/Umbraco.Core/Exceptions/DataOperationException.cs
@@ -0,0 +1,21 @@
+using System;
+
+namespace Umbraco.Core.Exceptions
+{
+ internal class DataOperationException : Exception
+ {
+ public T Operation { get; private set; }
+
+ public DataOperationException(T operation, string message)
+ :base(message)
+ {
+ Operation = operation;
+ }
+
+ public DataOperationException(T operation)
+ : base("Data operation exception: " + operation)
+ {
+ Operation = operation;
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Umbraco.Core/Exceptions/InvalidCompositionException.cs b/src/Umbraco.Core/Exceptions/InvalidCompositionException.cs
index fcf7a44677..bb9becf058 100644
--- a/src/Umbraco.Core/Exceptions/InvalidCompositionException.cs
+++ b/src/Umbraco.Core/Exceptions/InvalidCompositionException.cs
@@ -3,22 +3,41 @@
namespace Umbraco.Core.Exceptions
{
public class InvalidCompositionException : Exception
- {
- public string ContentTypeAlias { get; set; }
+ {
+ public InvalidCompositionException(string contentTypeAlias, string addedCompositionAlias, string[] propertyTypeAliass)
+ {
+ ContentTypeAlias = contentTypeAlias;
+ AddedCompositionAlias = addedCompositionAlias;
+ PropertyTypeAliases = propertyTypeAliass;
+ }
- public string AddedCompositionAlias { get; set; }
+ public InvalidCompositionException(string contentTypeAlias, string[] propertyTypeAliass)
+ {
+ ContentTypeAlias = contentTypeAlias;
+ PropertyTypeAliases = propertyTypeAliass;
+ }
- public string PropertyTypeAlias { get; set; }
+ public string ContentTypeAlias { get; private set; }
+
+ public string AddedCompositionAlias { get; private set; }
+
+ public string[] PropertyTypeAliases { get; private set; }
public override string Message
{
get
{
- return string.Format(
- "InvalidCompositionException - ContentType with alias '{0}' was added as a Compsition to ContentType with alias '{1}', " +
- "but there was a conflict on the PropertyType alias '{2}'. " +
+ return AddedCompositionAlias.IsNullOrWhiteSpace()
+ ? string.Format(
+ "ContentType with alias '{0}' has an invalid composition " +
+ "and there was a conflict on the following PropertyTypes: '{1}'. " +
"PropertyTypes must have a unique alias across all Compositions in order to compose a valid ContentType Composition.",
- AddedCompositionAlias, ContentTypeAlias, PropertyTypeAlias);
+ ContentTypeAlias, string.Join(", ", PropertyTypeAliases))
+ : string.Format(
+ "ContentType with alias '{0}' was added as a Composition to ContentType with alias '{1}', " +
+ "but there was a conflict on the following PropertyTypes: '{2}'. " +
+ "PropertyTypes must have a unique alias across all Compositions in order to compose a valid ContentType Composition.",
+ AddedCompositionAlias, ContentTypeAlias, string.Join(", ", PropertyTypeAliases));
}
}
}
diff --git a/src/Umbraco.Core/Exceptions/UmbracoStartupFailedException.cs b/src/Umbraco.Core/Exceptions/UmbracoStartupFailedException.cs
new file mode 100644
index 0000000000..d27d38de9a
--- /dev/null
+++ b/src/Umbraco.Core/Exceptions/UmbracoStartupFailedException.cs
@@ -0,0 +1,18 @@
+using System;
+
+namespace Umbraco.Core.Exceptions
+{
+ ///
+ /// An exception that is thrown if the umbraco application cannnot boot
+ ///
+ public class UmbracoStartupFailedException : Exception
+ {
+ ///
+ /// Initializes a new instance of the class with a specified error message.
+ ///
+ /// The message that describes the error.
+ public UmbracoStartupFailedException(string message) : base(message)
+ {
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Umbraco.Web.UI/xslt/Web.config b/src/Umbraco.Core/FileResources/BlockingWeb.config
similarity index 96%
rename from src/Umbraco.Web.UI/xslt/Web.config
rename to src/Umbraco.Core/FileResources/BlockingWeb.config
index fd6e3a816a..80182af9a1 100644
--- a/src/Umbraco.Web.UI/xslt/Web.config
+++ b/src/Umbraco.Core/FileResources/BlockingWeb.config
@@ -1,18 +1,18 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/Umbraco.Core/FileResources/Files.Designer.cs b/src/Umbraco.Core/FileResources/Files.Designer.cs
new file mode 100644
index 0000000000..456dae221f
--- /dev/null
+++ b/src/Umbraco.Core/FileResources/Files.Designer.cs
@@ -0,0 +1,86 @@
+//------------------------------------------------------------------------------
+//
+// This code was generated by a tool.
+// Runtime Version:4.0.30319.0
+//
+// Changes to this file may cause incorrect behavior and will be lost if
+// the code is regenerated.
+//
+//------------------------------------------------------------------------------
+
+namespace Umbraco.Core.FileResources {
+ using System;
+
+
+ ///
+ /// A strongly-typed resource class, for looking up localized strings, etc.
+ ///
+ // This class was auto-generated by the StronglyTypedResourceBuilder
+ // class via a tool like ResGen or Visual Studio.
+ // To add or remove a member, edit your .ResX file then rerun ResGen
+ // with the /str option, or rebuild your VS project.
+ [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
+ internal class Files {
+
+ private static global::System.Resources.ResourceManager resourceMan;
+
+ private static global::System.Globalization.CultureInfo resourceCulture;
+
+ [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
+ internal Files() {
+ }
+
+ ///
+ /// Returns the cached ResourceManager instance used by this class.
+ ///
+ [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
+ internal static global::System.Resources.ResourceManager ResourceManager {
+ get {
+ if (object.ReferenceEquals(resourceMan, null)) {
+ global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Umbraco.Core.FileResources.Files", typeof(Files).Assembly);
+ resourceMan = temp;
+ }
+ return resourceMan;
+ }
+ }
+
+ ///
+ /// Overrides the current thread's CurrentUICulture property for all
+ /// resource lookups using this strongly typed resource class.
+ ///
+ [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
+ internal static global::System.Globalization.CultureInfo Culture {
+ get {
+ return resourceCulture;
+ }
+ set {
+ resourceCulture = value;
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to <?xml version="1.0"?>
+ ///
+ ///<!-- Blocks public downloading of anything in this folder and sub folders -->
+ ///
+ ///<configuration>
+ /// <system.web>
+ /// <httpHandlers>
+ /// <add path="*" verb="*" type="System.Web.HttpNotFoundHandler"/>
+ /// </httpHandlers>
+ /// </system.web>
+ /// <system.webServer>
+ /// <validation validateIntegratedModeConfiguration="false" />
+ /// <handlers>
+ /// <remove name="BlockViewHandler"/>
+ /// <add name="BlockViewHandler" path="*" verb="*" preCondition="integratedMode" type="System.Web.H [rest of string was truncated]";.
+ ///
+ internal static string BlockingWebConfig {
+ get {
+ return ResourceManager.GetString("BlockingWebConfig", resourceCulture);
+ }
+ }
+ }
+}
diff --git a/src/Umbraco.Core/FileResources/Files.resx b/src/Umbraco.Core/FileResources/Files.resx
new file mode 100644
index 0000000000..823aacefb7
--- /dev/null
+++ b/src/Umbraco.Core/FileResources/Files.resx
@@ -0,0 +1,124 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ text/microsoft-resx
+
+
+ 2.0
+
+
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+
+ blockingweb.config;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;Windows-1252
+
+
\ No newline at end of file
diff --git a/src/Umbraco.Core/IO/IOHelper.cs b/src/Umbraco.Core/IO/IOHelper.cs
index dc5e278970..286acf0285 100644
--- a/src/Umbraco.Core/IO/IOHelper.cs
+++ b/src/Umbraco.Core/IO/IOHelper.cs
@@ -6,6 +6,7 @@ using System.IO;
using System.Configuration;
using System.Web;
using System.Text.RegularExpressions;
+using System.Web.Hosting;
using Umbraco.Core.Configuration;
using Umbraco.Core.Logging;
@@ -51,7 +52,23 @@ namespace Umbraco.Core.IO
return VirtualPathUtility.ToAbsolute(virtualPath, SystemDirectories.Root);
}
- [Obsolete("Use Umbraco.Web.Templates.TemplateUtilities.ResolveUrlsFromTextString instead, this method on this class will be removed in future versions")]
+ public static Attempt TryResolveUrl(string virtualPath)
+ {
+ try
+ {
+ if (virtualPath.StartsWith("~"))
+ return Attempt.Succeed(virtualPath.Replace("~", SystemDirectories.Root).Replace("//", "/"));
+ if (Uri.IsWellFormedUriString(virtualPath, UriKind.Absolute))
+ return Attempt.Succeed(virtualPath);
+ return Attempt.Succeed(VirtualPathUtility.ToAbsolute(virtualPath, SystemDirectories.Root));
+ }
+ catch (Exception ex)
+ {
+ return Attempt.Fail(virtualPath, ex);
+ }
+ }
+
+ [Obsolete("Use Umbraco.Web.Templates.TemplateUtilities.ResolveUrlsFromTextString instead, this method on this class will be removed in future versions")]
internal static string ResolveUrlsFromTextString(string text)
{
if (UmbracoConfig.For.UmbracoSettings().Content.ResolveUrlsFromTextString)
@@ -67,7 +84,7 @@ namespace Umbraco.Core.IO
if (tag.Groups[1].Success)
url = tag.Groups[1].Value;
- if (string.IsNullOrEmpty(url) == false)
+ if (String.IsNullOrEmpty(url) == false)
{
string resolvedUrl = (url.Substring(0, 1) == "/") ? ResolveUrl(url.Substring(1)) : ResolveUrl(url);
text = text.Replace(url, resolvedUrl);
@@ -92,10 +109,10 @@ namespace Umbraco.Core.IO
if (useHttpContext && HttpContext.Current != null)
{
//string retval;
- if (string.IsNullOrEmpty(path) == false && (path.StartsWith("~") || path.StartsWith(SystemDirectories.Root)))
- return System.Web.Hosting.HostingEnvironment.MapPath(path);
+ if (String.IsNullOrEmpty(path) == false && (path.StartsWith("~") || path.StartsWith(SystemDirectories.Root)))
+ return HostingEnvironment.MapPath(path);
else
- return System.Web.Hosting.HostingEnvironment.MapPath("~/" + path.TrimStart('/'));
+ return HostingEnvironment.MapPath("~/" + path.TrimStart('/'));
}
var root = GetRootDirectorySafe();
@@ -115,7 +132,7 @@ namespace Umbraco.Core.IO
{
string retval = ConfigurationManager.AppSettings[settingsKey];
- if (string.IsNullOrEmpty(retval))
+ if (String.IsNullOrEmpty(retval))
retval = standardPath;
return retval.TrimEnd('/');
@@ -135,12 +152,7 @@ namespace Umbraco.Core.IO
/// A value indicating whether the filepath is valid.
internal static bool VerifyEditPath(string filePath, string validDir)
{
- if (filePath.StartsWith(MapPath(SystemDirectories.Root)) == false)
- filePath = MapPath(filePath);
- if (validDir.StartsWith(MapPath(SystemDirectories.Root)) == false)
- validDir = MapPath(validDir);
-
- return filePath.StartsWith(validDir);
+ return VerifyEditPath(filePath, new[] { validDir });
}
///
@@ -165,15 +177,31 @@ namespace Umbraco.Core.IO
/// A value indicating whether the filepath is valid.
internal static bool VerifyEditPath(string filePath, IEnumerable validDirs)
{
+ // this is called from ScriptRepository, PartialViewRepository, etc.
+ // filePath is the fullPath (rooted, filesystem path, can be trusted)
+ // validDirs are virtual paths (eg ~/Views)
+ //
+ // except that for templates, filePath actually is a virtual path
+
+ //TODO
+ // what's below is dirty, there are too many ways to get the root dir, etc.
+ // not going to fix everything today
+
+ var mappedRoot = MapPath(SystemDirectories.Root);
+ if (filePath.StartsWith(mappedRoot) == false)
+ filePath = MapPath(filePath);
+
+ // yes we can (see above)
+ //// don't trust what we get, it may contain relative segments
+ //filePath = Path.GetFullPath(filePath);
+
foreach (var dir in validDirs)
{
var validDir = dir;
- if (filePath.StartsWith(MapPath(SystemDirectories.Root)) == false)
- filePath = MapPath(filePath);
- if (validDir.StartsWith(MapPath(SystemDirectories.Root)) == false)
+ if (validDir.StartsWith(mappedRoot) == false)
validDir = MapPath(validDir);
- if (filePath.StartsWith(validDir))
+ if (PathStartsWith(filePath, validDir, Path.DirectorySeparatorChar))
return true;
}
@@ -202,11 +230,8 @@ namespace Umbraco.Core.IO
/// A value indicating whether the filepath is valid.
internal static bool VerifyFileExtension(string filePath, List validFileExtensions)
{
- if (filePath.StartsWith(MapPath(SystemDirectories.Root)) == false)
- filePath = MapPath(filePath);
- var f = new FileInfo(filePath);
-
- return validFileExtensions.Contains(f.Extension.Substring(1));
+ var ext = Path.GetExtension(filePath);
+ return ext != null && validFileExtensions.Contains(ext.TrimStart('.'));
}
///
@@ -223,6 +248,16 @@ namespace Umbraco.Core.IO
return true;
}
+ public static bool PathStartsWith(string path, string root, char separator)
+ {
+ // either it is identical to root,
+ // or it is root + separator + anything
+
+ if (path.StartsWith(root, StringComparison.OrdinalIgnoreCase) == false) return false;
+ if (path.Length == root.Length) return true;
+ if (path.Length < root.Length) return false;
+ return path[root.Length] == separator;
+ }
///
/// Returns the path to the root of the application, by getting the path to where the assembly where this
@@ -232,7 +267,7 @@ namespace Umbraco.Core.IO
///
internal static string GetRootDirectorySafe()
{
- if (string.IsNullOrEmpty(_rootDir) == false)
+ if (String.IsNullOrEmpty(_rootDir) == false)
{
return _rootDir;
}
@@ -241,7 +276,7 @@ namespace Umbraco.Core.IO
var uri = new Uri(codeBase);
var path = uri.LocalPath;
var baseDirectory = Path.GetDirectoryName(path);
- if (string.IsNullOrEmpty(baseDirectory))
+ if (String.IsNullOrEmpty(baseDirectory))
throw new Exception("No root directory could be resolved. Please ensure that your Umbraco solution is correctly configured.");
_rootDir = baseDirectory.Contains("bin")
@@ -253,8 +288,8 @@ namespace Umbraco.Core.IO
internal static string GetRootDirectoryBinFolder()
{
- string binFolder = string.Empty;
- if (string.IsNullOrEmpty(_rootDir))
+ string binFolder = String.Empty;
+ if (String.IsNullOrEmpty(_rootDir))
{
binFolder = Assembly.GetExecutingAssembly().GetAssemblyFile().Directory.FullName;
return binFolder;
@@ -298,5 +333,25 @@ namespace Umbraco.Core.IO
// use string extensions
return filePath.ToSafeFileName();
}
+
+ public static void EnsurePathExists(string path)
+ {
+ var absolutePath = IOHelper.MapPath(path);
+ if (Directory.Exists(absolutePath) == false)
+ Directory.CreateDirectory(absolutePath);
+ }
+
+ public static void EnsureFileExists(string path, string contents)
+ {
+ var absolutePath = IOHelper.MapPath(path);
+ if (File.Exists(absolutePath) == false)
+ {
+ using (var writer = File.CreateText(absolutePath))
+ {
+ writer.Write(contents);
+ }
+ }
+
+ }
}
}
diff --git a/src/Umbraco.Core/IO/PhysicalFileSystem.cs b/src/Umbraco.Core/IO/PhysicalFileSystem.cs
index 13df315960..47daff932d 100644
--- a/src/Umbraco.Core/IO/PhysicalFileSystem.cs
+++ b/src/Umbraco.Core/IO/PhysicalFileSystem.cs
@@ -1,7 +1,6 @@
using System;
using System.Collections.Generic;
-using System.Globalization;
-using System.IO;
+using System.IO;
using System.Linq;
using Umbraco.Core.Logging;
@@ -9,7 +8,12 @@ namespace Umbraco.Core.IO
{
public class PhysicalFileSystem : IFileSystem
{
- internal string RootPath { get; private set; }
+ // the rooted, filesystem path, using directory separator chars, NOT ending with a separator
+ // eg "c:" or "c:\path\to\site" or "\\server\path"
+ private readonly string _rootPath;
+
+ // the ??? url, using url separator chars, NOT ending with a separator
+ // eg "" (?) or "/Scripts" or ???
private readonly string _rootUrl;
public PhysicalFileSystem(string virtualRoot)
@@ -18,8 +22,13 @@ namespace Umbraco.Core.IO
if (virtualRoot.StartsWith("~/") == false)
throw new ArgumentException("The virtualRoot argument must be a virtual path and start with '~/'");
- RootPath = IOHelper.MapPath(virtualRoot);
+ _rootPath = IOHelper.MapPath(virtualRoot);
+ _rootPath = EnsureDirectorySeparatorChar(_rootPath);
+ _rootPath = _rootPath.TrimEnd(Path.DirectorySeparatorChar);
+
_rootUrl = IOHelper.ResolveUrl(virtualRoot);
+ _rootUrl = EnsureUrlSeparatorChar(_rootUrl);
+ _rootUrl = _rootUrl.TrimEnd('/');
}
public PhysicalFileSystem(string rootPath, string rootUrl)
@@ -33,18 +42,31 @@ namespace Umbraco.Core.IO
if (rootPath.StartsWith("~/"))
throw new ArgumentException("The rootPath argument cannot be a virtual path and cannot start with '~/'");
- RootPath = rootPath;
- _rootUrl = rootUrl;
+ // rootPath should be... rooted, as in, it's a root path!
+ // but the test suite App.config cannot really "root" anything so we'll have to do it here
+
+ //var localRoot = AppDomain.CurrentDomain.BaseDirectory;
+ var localRoot = IOHelper.GetRootDirectorySafe();
+ if (Path.IsPathRooted(rootPath) == false)
+ {
+ rootPath = Path.Combine(localRoot, rootPath);
+ }
+
+ rootPath = EnsureDirectorySeparatorChar(rootPath);
+ rootUrl = EnsureUrlSeparatorChar(rootUrl);
+
+ _rootPath = rootPath.TrimEnd(Path.DirectorySeparatorChar);
+ _rootUrl = rootUrl.TrimEnd('/');
}
public IEnumerable GetDirectories(string path)
{
- path = EnsureTrailingSeparator(GetFullPath(path));
+ var fullPath = GetFullPath(path);
try
{
- if (Directory.Exists(path))
- return Directory.EnumerateDirectories(path).Select(GetRelativePath);
+ if (Directory.Exists(fullPath))
+ return Directory.EnumerateDirectories(fullPath).Select(GetRelativePath);
}
catch (UnauthorizedAccessException ex)
{
@@ -65,12 +87,13 @@ namespace Umbraco.Core.IO
public void DeleteDirectory(string path, bool recursive)
{
- if (DirectoryExists(path) == false)
+ var fullPath = GetFullPath(path);
+ if (Directory.Exists(fullPath) == false)
return;
try
{
- Directory.Delete(GetFullPath(path), recursive);
+ Directory.Delete(fullPath, recursive);
}
catch (DirectoryNotFoundException ex)
{
@@ -80,7 +103,8 @@ namespace Umbraco.Core.IO
public bool DirectoryExists(string path)
{
- return Directory.Exists(GetFullPath(path));
+ var fullPath = GetFullPath(path);
+ return Directory.Exists(fullPath);
}
public void AddFile(string path, Stream stream)
@@ -90,17 +114,17 @@ namespace Umbraco.Core.IO
public void AddFile(string path, Stream stream, bool overrideIfExists)
{
- var fsRelativePath = GetRelativePath(path);
+ var fullPath = GetFullPath(path);
+ var exists = File.Exists(fullPath);
+ if (exists && overrideIfExists == false)
+ throw new InvalidOperationException(string.Format("A file at path '{0}' already exists", path));
- var exists = FileExists(fsRelativePath);
- if (exists && overrideIfExists == false) throw new InvalidOperationException(string.Format("A file at path '{0}' already exists", path));
-
- EnsureDirectory(Path.GetDirectoryName(fsRelativePath));
+ Directory.CreateDirectory(Path.GetDirectoryName(fullPath)); // ensure it exists
if (stream.CanSeek)
stream.Seek(0, 0);
- using (var destination = (Stream)File.Create(GetFullPath(fsRelativePath)))
+ using (var destination = (Stream)File.Create(fullPath))
stream.CopyTo(destination);
}
@@ -111,9 +135,7 @@ namespace Umbraco.Core.IO
public IEnumerable GetFiles(string path, string filter)
{
- var fsRelativePath = GetRelativePath(path);
-
- var fullPath = EnsureTrailingSeparator(GetFullPath(fsRelativePath));
+ var fullPath = GetFullPath(path);
try
{
@@ -140,12 +162,13 @@ namespace Umbraco.Core.IO
public void DeleteFile(string path)
{
- if (!FileExists(path))
+ var fullPath = GetFullPath(path);
+ if (File.Exists(fullPath) == false)
return;
try
{
- File.Delete(GetFullPath(path));
+ File.Delete(fullPath);
}
catch (FileNotFoundException ex)
{
@@ -155,39 +178,86 @@ namespace Umbraco.Core.IO
public bool FileExists(string path)
{
- return File.Exists(GetFullPath(path));
+ var fullpath = GetFullPath(path);
+ return File.Exists(fullpath);
}
+ // beware, many things depend on how the GetRelative/AbsolutePath methods work!
+
+ ///
+ /// Gets the relative path.
+ ///
+ /// The full path or url.
+ /// The path, relative to this filesystem's root.
+ ///
+ /// The relative path is relative to this filesystem's root, not starting with any
+ /// directory separator. If input was recognized as a url (path), then output uses url (path) separator
+ /// chars.
+ ///
public string GetRelativePath(string fullPathOrUrl)
{
- var relativePath = fullPathOrUrl
- .TrimStart(_rootUrl)
- .Replace('/', Path.DirectorySeparatorChar)
- .TrimStart(RootPath)
- .TrimStart(Path.DirectorySeparatorChar);
+ // test url
+ var path = fullPathOrUrl.Replace('\\', '/'); // ensure url separator char
- return relativePath;
+ if (IOHelper.PathStartsWith(path, _rootUrl, '/')) // if it starts with the root url...
+ return path.Substring(_rootUrl.Length) // strip it
+ .TrimStart('/'); // it's relative
+
+ // test path
+ path = EnsureDirectorySeparatorChar(fullPathOrUrl);
+
+ if (IOHelper.PathStartsWith(path, _rootPath, Path.DirectorySeparatorChar)) // if it starts with the root path
+ return path.Substring(_rootPath.Length) // strip it
+ .TrimStart(Path.DirectorySeparatorChar); // it's relative
+
+ // unchanged - including separators
+ return fullPathOrUrl;
}
+ ///
+ /// Gets the full path.
+ ///
+ /// The full or relative path.
+ /// The full path.
+ ///
+ /// On the physical filesystem, the full path is the rooted (ie non-relative), safe (ie within this
+ /// filesystem's root) path. All separators are converted to Path.DirectorySeparatorChar.
+ ///
public string GetFullPath(string path)
{
- //if the path starts with a '/' then it's most likely not a FS relative path which is required so convert it
- if (path.StartsWith("/"))
- {
- path = GetRelativePath(path);
- }
+ // normalize
+ var opath = path;
+ path = EnsureDirectorySeparatorChar(path);
- return !path.StartsWith(RootPath)
- ? Path.Combine(RootPath, path)
- : path;
+ // not sure what we are doing here - so if input starts with a (back) slash,
+ // we assume it's not a FS relative path and we try to convert it... but it
+ // really makes little sense?
+ if (path.StartsWith(Path.DirectorySeparatorChar.ToString()))
+ path = GetRelativePath(path);
+
+ // if already a full path, return
+ if (IOHelper.PathStartsWith(path, _rootPath, Path.DirectorySeparatorChar))
+ return path;
+
+ // else combine and sanitize, ie GetFullPath will take care of any relative
+ // segments in path, eg '../../foo.tmp' - it may throw a SecurityException
+ // if the combined path reaches illegal parts of the filesystem
+ var fpath = Path.Combine(_rootPath, path);
+ fpath = Path.GetFullPath(fpath);
+
+ // at that point, path is within legal parts of the filesystem, ie we have
+ // permissions to reach that path, but it may nevertheless be outside of
+ // our root path, due to relative segments, so better check
+ if (IOHelper.PathStartsWith(fpath, _rootPath, Path.DirectorySeparatorChar))
+ return fpath;
+
+ throw new FileSecurityException("File '" + opath + "' is outside this filesystem's root.");
}
public string GetUrl(string path)
{
- return _rootUrl.TrimEnd("/") + "/" + path
- .TrimStart(Path.DirectorySeparatorChar)
- .Replace(Path.DirectorySeparatorChar, '/')
- .TrimEnd("/");
+ path = EnsureUrlSeparatorChar(path).Trim('/');
+ return _rootUrl + "/" + path;
}
public DateTimeOffset GetLastModified(string path)
@@ -214,9 +284,19 @@ namespace Umbraco.Core.IO
protected string EnsureTrailingSeparator(string path)
{
- if (!path.EndsWith(Path.DirectorySeparatorChar.ToString(CultureInfo.InvariantCulture), StringComparison.Ordinal))
- path = path + Path.DirectorySeparatorChar;
+ return path.EnsureEndsWith(Path.DirectorySeparatorChar);
+ }
+ protected string EnsureDirectorySeparatorChar(string path)
+ {
+ path = path.Replace('/', Path.DirectorySeparatorChar);
+ path = path.Replace('\\', Path.DirectorySeparatorChar);
+ return path;
+ }
+
+ protected string EnsureUrlSeparatorChar(string path)
+ {
+ path = path.Replace('\\', '/');
return path;
}
diff --git a/src/Umbraco.Core/IO/SystemDirectories.cs b/src/Umbraco.Core/IO/SystemDirectories.cs
index 14997b7de7..38bc358e94 100644
--- a/src/Umbraco.Core/IO/SystemDirectories.cs
+++ b/src/Umbraco.Core/IO/SystemDirectories.cs
@@ -132,7 +132,7 @@ namespace Umbraco.Core.IO
{
get
{
- return IOHelper.ReturnPath("umbracoWebservicesPath", "~/umbraco/webservices");
+ return IOHelper.ReturnPath("umbracoWebservicesPath", Umbraco.EnsureEndsWith("/") + "webservices");
}
}
diff --git a/src/Umbraco.Core/IO/UmbracoMediaFile.cs b/src/Umbraco.Core/IO/UmbracoMediaFile.cs
index b8fe310f54..d708fcb804 100644
--- a/src/Umbraco.Core/IO/UmbracoMediaFile.cs
+++ b/src/Umbraco.Core/IO/UmbracoMediaFile.cs
@@ -199,8 +199,8 @@ namespace Umbraco.Core.IO
using (var image = Image.FromStream(fs))
{
var fileNameThumb = string.IsNullOrWhiteSpace(fileNameAddition)
- ? string.Format("{0}_UMBRACOSYSTHUMBNAIL.jpg", Path.Substring(0, Path.LastIndexOf(".", StringComparison.Ordinal)))
- : string.Format("{0}_{1}.jpg", Path.Substring(0, Path.LastIndexOf(".", StringComparison.Ordinal)), fileNameAddition);
+ ? string.Format("{0}_UMBRACOSYSTHUMBNAIL." + Extension, Path.Substring(0, Path.LastIndexOf(".", StringComparison.Ordinal)))
+ : string.Format("{0}_{1}." + Extension, Path.Substring(0, Path.LastIndexOf(".", StringComparison.Ordinal)), fileNameAddition);
var thumbnail = maxWidthHeight == -1
? ImageHelper.GenerateThumbnail(image, width, height, fileNameThumb, Extension, _fs)
diff --git a/src/Umbraco.Core/Logging/AppDomainTokenFormatter.cs b/src/Umbraco.Core/Logging/AppDomainTokenConverter.cs
similarity index 100%
rename from src/Umbraco.Core/Logging/AppDomainTokenFormatter.cs
rename to src/Umbraco.Core/Logging/AppDomainTokenConverter.cs
diff --git a/src/Umbraco.Core/Logging/AsyncForwardingAppenderBase.cs b/src/Umbraco.Core/Logging/AsyncForwardingAppenderBase.cs
new file mode 100644
index 0000000000..74a1de81f4
--- /dev/null
+++ b/src/Umbraco.Core/Logging/AsyncForwardingAppenderBase.cs
@@ -0,0 +1,105 @@
+using System;
+using log4net.Appender;
+using log4net.Core;
+using log4net.Util;
+
+namespace Umbraco.Core.Logging
+{
+ ///
+ /// Based on https://github.com/cjbhaines/Log4Net.Async
+ ///
+ public abstract class AsyncForwardingAppenderBase : ForwardingAppender
+ {
+ #region Private Members
+
+ private const FixFlags DefaultFixFlags = FixFlags.Partial;
+ private FixFlags _fixFlags = DefaultFixFlags;
+ private LoggingEventHelper _loggingEventHelper;
+
+ #endregion Private Members
+
+ #region Properties
+
+ public FixFlags Fix
+ {
+ get { return _fixFlags; }
+ set { SetFixFlags(value); }
+ }
+
+ ///
+ /// The logger name that will be used for logging internal errors.
+ ///
+ protected abstract string InternalLoggerName { get; }
+
+ public abstract int? BufferSize { get; set; }
+
+ #endregion Properties
+
+ public override void ActivateOptions()
+ {
+ base.ActivateOptions();
+ _loggingEventHelper = new LoggingEventHelper(InternalLoggerName, DefaultFixFlags);
+ InitializeAppenders();
+ }
+
+ #region Appender Management
+
+ public override void AddAppender(IAppender newAppender)
+ {
+ base.AddAppender(newAppender);
+ SetAppenderFixFlags(newAppender);
+ }
+
+ private void SetFixFlags(FixFlags newFixFlags)
+ {
+ if (newFixFlags != _fixFlags)
+ {
+ _loggingEventHelper.Fix = newFixFlags;
+ _fixFlags = newFixFlags;
+ InitializeAppenders();
+ }
+ }
+
+ private void InitializeAppenders()
+ {
+ foreach (var appender in Appenders)
+ {
+ SetAppenderFixFlags(appender);
+ }
+ }
+
+ private void SetAppenderFixFlags(IAppender appender)
+ {
+ var bufferingAppender = appender as BufferingAppenderSkeleton;
+ if (bufferingAppender != null)
+ {
+ bufferingAppender.Fix = Fix;
+ }
+ }
+
+ #endregion Appender Management
+
+ #region Forwarding
+
+ protected void ForwardInternalError(string message, Exception exception, Type thisType)
+ {
+ LogLog.Error(thisType, message, exception);
+ var loggingEvent = _loggingEventHelper.CreateLoggingEvent(Level.Error, message, exception);
+ ForwardLoggingEvent(loggingEvent, thisType);
+ }
+
+ protected void ForwardLoggingEvent(LoggingEvent loggingEvent, Type thisType)
+ {
+ try
+ {
+ base.Append(loggingEvent);
+ }
+ catch (Exception exception)
+ {
+ LogLog.Error(thisType, "Unable to forward logging event", exception);
+ }
+ }
+
+ #endregion Forwarding
+ }
+}
\ No newline at end of file
diff --git a/src/Umbraco.Core/Logging/AsynchronousRollingFileAppender.cs b/src/Umbraco.Core/Logging/AsynchronousRollingFileAppender.cs
index 56d04c8426..cb58ebbfaa 100644
--- a/src/Umbraco.Core/Logging/AsynchronousRollingFileAppender.cs
+++ b/src/Umbraco.Core/Logging/AsynchronousRollingFileAppender.cs
@@ -1,276 +1,276 @@
+using log4net.Core;
+using log4net.Util;
using System;
+using System.Runtime.Remoting.Messaging;
using System.Security.Principal;
using System.Threading;
using System.Threading.Tasks;
using log4net.Appender;
-using log4net.Core;
-using log4net.Util;
namespace Umbraco.Core.Logging
{
- ///
- /// Based on code by Chris Haines http://cjbhaines.wordpress.com/2012/02/13/asynchronous-log4net-appenders/
+ ///
+ /// Based on https://github.com/cjbhaines/Log4Net.Async
+ /// which is based on code by Chris Haines http://cjbhaines.wordpress.com/2012/02/13/asynchronous-log4net-appenders/
///
public class AsynchronousRollingFileAppender : RollingFileAppender
{
- private readonly ManualResetEvent _manualResetEvent;
- private int _bufferOverflowCounter;
- private bool _forceStop;
- private bool _hasFinished;
- private DateTime _lastLoggedBufferOverflow;
- private bool _logBufferOverflow;
- private RingBuffer _pendingAppends;
- private int _queueSizeLimit = 1000;
- private bool _shuttingDown;
+ private RingBuffer pendingAppends;
+ private readonly ManualResetEvent manualResetEvent;
+ private bool shuttingDown;
+ private bool hasFinished;
+ private bool forceStop;
+ private bool logBufferOverflow;
+ private int bufferOverflowCounter;
+ private DateTime lastLoggedBufferOverflow;
+ private int queueSizeLimit = 1000;
+ public int QueueSizeLimit
+ {
+ get
+ {
+ return queueSizeLimit;
+ }
+ set
+ {
+ queueSizeLimit = value;
+ }
+ }
- public AsynchronousRollingFileAppender()
- {
- _manualResetEvent = new ManualResetEvent(false);
- }
+ public AsynchronousRollingFileAppender()
+ {
+ manualResetEvent = new ManualResetEvent(false);
+ }
- public int QueueSizeLimit
- {
- get { return _queueSizeLimit; }
- set { _queueSizeLimit = value; }
- }
+ public override void ActivateOptions()
+ {
+ base.ActivateOptions();
+ pendingAppends = new RingBuffer(QueueSizeLimit);
+ pendingAppends.BufferOverflow += OnBufferOverflow;
+ StartAppendTask();
+ }
- public override void ActivateOptions()
- {
- base.ActivateOptions();
- _pendingAppends = new RingBuffer(QueueSizeLimit);
- _pendingAppends.BufferOverflow += OnBufferOverflow;
- StartAppendTask();
- }
+ protected override void Append(LoggingEvent[] loggingEvents)
+ {
+ Array.ForEach(loggingEvents, Append);
+ }
- protected override void Append(LoggingEvent[] loggingEvents)
- {
- Array.ForEach(loggingEvents, Append);
- }
+ protected override void Append(LoggingEvent loggingEvent)
+ {
+ if (FilterEvent(loggingEvent))
+ {
+ pendingAppends.Enqueue(loggingEvent);
+ }
+ }
- protected override void Append(LoggingEvent loggingEvent)
- {
- if (FilterEvent(loggingEvent))
- {
- _pendingAppends.Enqueue(loggingEvent);
- }
- }
+ protected override void OnClose()
+ {
+ shuttingDown = true;
+ manualResetEvent.WaitOne(TimeSpan.FromSeconds(5));
- protected override void OnClose()
- {
- _shuttingDown = true;
- _manualResetEvent.WaitOne(TimeSpan.FromSeconds(5));
+ if (!hasFinished)
+ {
+ forceStop = true;
+ base.Append(new LoggingEvent(new LoggingEventData
+ {
+ Level = Level.Error,
+ Message = "Unable to clear out the AsyncRollingFileAppender buffer in the allotted time, forcing a shutdown",
+ TimeStamp = DateTime.UtcNow,
+ Identity = "",
+ ExceptionString = "",
+ UserName = WindowsIdentity.GetCurrent() != null ? WindowsIdentity.GetCurrent().Name : "",
+ Domain = AppDomain.CurrentDomain.FriendlyName,
+ ThreadName = Thread.CurrentThread.ManagedThreadId.ToString(),
+ LocationInfo = new LocationInfo(this.GetType().Name, "OnClose", "AsyncRollingFileAppender.cs", "75"),
+ LoggerName = this.GetType().FullName,
+ Properties = new PropertiesDictionary(),
+ })
+ );
+ }
- if (!_hasFinished)
- {
- _forceStop = true;
- var windowsIdentity = WindowsIdentity.GetCurrent();
+ base.OnClose();
+ }
- var logEvent = new LoggingEvent(new LoggingEventData
- {
- Level = global::log4net.Core.Level.Error,
- Message =
- "Unable to clear out the AsynchronousRollingFileAppender buffer in the allotted time, forcing a shutdown",
- TimeStamp = DateTime.UtcNow,
- Identity = "",
- ExceptionString = "",
- UserName = windowsIdentity != null ? windowsIdentity.Name : "",
- Domain = AppDomain.CurrentDomain.FriendlyName,
- ThreadName = Thread.CurrentThread.ManagedThreadId.ToString(),
- LocationInfo =
- new LocationInfo(this.GetType().Name, "OnClose", "AsynchronousRollingFileAppender.cs", "59"),
- LoggerName = this.GetType().FullName,
- Properties = new PropertiesDictionary(),
- });
+ private void StartAppendTask()
+ {
+ if (!shuttingDown)
+ {
+ Task appendTask = new Task(AppendLoggingEvents, TaskCreationOptions.LongRunning);
+ appendTask.LogErrors(LogAppenderError).ContinueWith(x => StartAppendTask()).LogErrors(LogAppenderError);
+ appendTask.Start();
+ }
+ }
- if (this.DateTimeStrategy != null)
- {
- base.Append(logEvent);
- }
- }
+ private void LogAppenderError(string logMessage, Exception exception)
+ {
+ base.Append(new LoggingEvent(new LoggingEventData
+ {
+ Level = Level.Error,
+ Message = "Appender exception: " + logMessage,
+ TimeStamp = DateTime.UtcNow,
+ Identity = "",
+ ExceptionString = exception.ToString(),
+ UserName = WindowsIdentity.GetCurrent() != null ? WindowsIdentity.GetCurrent().Name : "",
+ Domain = AppDomain.CurrentDomain.FriendlyName,
+ ThreadName = Thread.CurrentThread.ManagedThreadId.ToString(),
+ LocationInfo = new LocationInfo(this.GetType().Name, "LogAppenderError", "AsyncRollingFileAppender.cs", "152"),
+ LoggerName = this.GetType().FullName,
+ Properties = new PropertiesDictionary(),
+ }));
+ }
- base.OnClose();
- }
+ private void AppendLoggingEvents()
+ {
+ LoggingEvent loggingEventToAppend;
+ while (!shuttingDown)
+ {
+ if (logBufferOverflow)
+ {
+ LogBufferOverflowError();
+ logBufferOverflow = false;
+ bufferOverflowCounter = 0;
+ lastLoggedBufferOverflow = DateTime.UtcNow;
+ }
- private void StartAppendTask()
- {
- if (!_shuttingDown)
- {
- Task appendTask = new Task(AppendLoggingEvents, TaskCreationOptions.LongRunning);
- appendTask.LogErrors(LogAppenderError).ContinueWith(x => StartAppendTask()).LogErrors(LogAppenderError);
- appendTask.Start();
- }
- }
+ while (!pendingAppends.TryDequeue(out loggingEventToAppend))
+ {
+ Thread.Sleep(10);
+ if (shuttingDown)
+ {
+ break;
+ }
+ }
+ if (loggingEventToAppend == null)
+ {
+ continue;
+ }
- private void LogAppenderError(string logMessage, Exception exception)
- {
- var windowsIdentity = WindowsIdentity.GetCurrent();
- base.Append(new LoggingEvent(new LoggingEventData
- {
- Level = Level.Error,
- Message = "Appender exception: " + logMessage,
- TimeStamp = DateTime.UtcNow,
- Identity = "",
- ExceptionString = exception.ToString(),
- UserName = windowsIdentity != null ? windowsIdentity.Name : "",
- Domain = AppDomain.CurrentDomain.FriendlyName,
- ThreadName = Thread.CurrentThread.ManagedThreadId.ToString(),
- LocationInfo =
- new LocationInfo(this.GetType().Name,
- "LogAppenderError",
- "AsynchronousRollingFileAppender.cs",
- "100"),
- LoggerName = this.GetType().FullName,
- Properties = new PropertiesDictionary(),
- }));
- }
+ try
+ {
+ base.Append(loggingEventToAppend);
+ }
+ catch
+ {
+ }
+ }
- private void AppendLoggingEvents()
- {
- LoggingEvent loggingEventToAppend;
- while (!_shuttingDown)
- {
- if (_logBufferOverflow)
- {
- LogBufferOverflowError();
- _logBufferOverflow = false;
- _bufferOverflowCounter = 0;
- _lastLoggedBufferOverflow = DateTime.UtcNow;
- }
+ while (pendingAppends.TryDequeue(out loggingEventToAppend) && !forceStop)
+ {
+ try
+ {
+ base.Append(loggingEventToAppend);
+ }
+ catch
+ {
+ }
+ }
+ hasFinished = true;
+ manualResetEvent.Set();
+ }
- while (!_pendingAppends.TryDequeue(out loggingEventToAppend))
- {
- Thread.Sleep(10);
- if (_shuttingDown)
- {
- break;
- }
- }
- if (loggingEventToAppend == null)
- {
- continue;
- }
+ private void LogBufferOverflowError()
+ {
+ base.Append(new LoggingEvent(new LoggingEventData
+ {
+ Level = Level.Error,
+ Message = string.Format("Buffer overflow. {0} logging events have been lost in the last 30 seconds. [QueueSizeLimit: {1}]", bufferOverflowCounter, QueueSizeLimit),
+ TimeStamp = DateTime.UtcNow,
+ Identity = "",
+ ExceptionString = "",
+ UserName = WindowsIdentity.GetCurrent() != null ? WindowsIdentity.GetCurrent().Name : "",
+ Domain = AppDomain.CurrentDomain.FriendlyName,
+ ThreadName = Thread.CurrentThread.ManagedThreadId.ToString(),
+ LocationInfo = new LocationInfo(this.GetType().Name, "LogBufferOverflowError", "AsyncRollingFileAppender.cs", "152"),
+ LoggerName = this.GetType().FullName,
+ Properties = new PropertiesDictionary(),
+ }));
+ }
- try
- {
- base.Append(loggingEventToAppend);
- }
- catch
- {
- }
- }
+ private void OnBufferOverflow(object sender, EventArgs eventArgs)
+ {
+ bufferOverflowCounter++;
+ if (logBufferOverflow == false)
+ {
+ if (lastLoggedBufferOverflow < DateTime.UtcNow.AddSeconds(-30))
+ {
+ logBufferOverflow = true;
+ }
+ }
+ }
+ }
- while (_pendingAppends.TryDequeue(out loggingEventToAppend) && !_forceStop)
- {
- try
- {
- base.Append(loggingEventToAppend);
- }
- catch
- {
- }
- }
- _hasFinished = true;
- _manualResetEvent.Set();
- }
+ internal interface IQueue
+ {
+ void Enqueue(T item);
+ bool TryDequeue(out T ret);
+ }
- private void LogBufferOverflowError()
- {
- var windowsIdentity = WindowsIdentity.GetCurrent();
- base.Append(new LoggingEvent(new LoggingEventData
- {
- Level = Level.Error,
- Message =
- string.Format(
- "Buffer overflow. {0} logging events have been lost in the last 30 seconds. [QueueSizeLimit: {1}]",
- _bufferOverflowCounter,
- QueueSizeLimit),
- TimeStamp = DateTime.UtcNow,
- Identity = "",
- ExceptionString = "",
- UserName = windowsIdentity != null ? windowsIdentity.Name : "",
- Domain = AppDomain.CurrentDomain.FriendlyName,
- ThreadName = Thread.CurrentThread.ManagedThreadId.ToString(),
- LocationInfo =
- new LocationInfo(this.GetType().Name,
- "LogBufferOverflowError",
- "AsynchronousRollingFileAppender.cs",
- "172"),
- LoggerName = this.GetType().FullName,
- Properties = new PropertiesDictionary(),
- }));
- }
+ internal class RingBuffer : IQueue
+ {
+ private readonly object lockObject = new object();
+ private readonly T[] buffer;
+ private readonly int size;
+ private int readIndex = 0;
+ private int writeIndex = 0;
+ private bool bufferFull = false;
- private void OnBufferOverflow(object sender, EventArgs eventArgs)
- {
- _bufferOverflowCounter++;
- if (_logBufferOverflow == false)
- {
- if (_lastLoggedBufferOverflow < DateTime.UtcNow.AddSeconds(-30))
- {
- _logBufferOverflow = true;
- }
- }
- }
+ public int Size { get { return size; } }
- private class RingBuffer
- {
- private readonly object _lockObject = new object();
- private readonly T[] _buffer;
- private readonly int _size;
- private int _readIndex = 0;
- private int _writeIndex = 0;
- private bool _bufferFull = false;
+ public event Action BufferOverflow;
- public event Action BufferOverflow;
+ public RingBuffer(int size)
+ {
+ this.size = size;
+ buffer = new T[size];
+ }
- public RingBuffer(int size)
- {
- this._size = size;
- _buffer = new T[size];
- }
+ public void Enqueue(T item)
+ {
+ var bufferWasFull = false;
+ lock (lockObject)
+ {
+ buffer[writeIndex] = item;
+ writeIndex = (++writeIndex) % size;
+ if (bufferFull)
+ {
+ bufferWasFull = true;
+ readIndex = writeIndex;
+ }
+ else if (writeIndex == readIndex)
+ {
+ bufferFull = true;
+ }
+ }
- public void Enqueue(T item)
- {
- lock (_lockObject)
- {
- _buffer[_writeIndex] = item;
- _writeIndex = (++_writeIndex) % _size;
- if (_bufferFull)
- {
- if (BufferOverflow != null)
- {
- BufferOverflow(this, EventArgs.Empty);
- }
- _readIndex = _writeIndex;
- }
- else if (_writeIndex == _readIndex)
- {
- _bufferFull = true;
- }
- }
- }
+ if (bufferWasFull)
+ {
+ if (BufferOverflow != null)
+ {
+ BufferOverflow(this, EventArgs.Empty);
+ }
+ }
+ }
- public bool TryDequeue(out T ret)
- {
- if (_readIndex == _writeIndex && !_bufferFull)
- {
- ret = default(T);
- return false;
- }
- lock (_lockObject)
- {
- if (_readIndex == _writeIndex && !_bufferFull)
- {
- ret = default(T);
- return false;
- }
+ public bool TryDequeue(out T ret)
+ {
+ if (readIndex == writeIndex && !bufferFull)
+ {
+ ret = default(T);
+ return false;
+ }
+ lock (lockObject)
+ {
+ if (readIndex == writeIndex && !bufferFull)
+ {
+ ret = default(T);
+ return false;
+ }
- ret = _buffer[_readIndex];
- _readIndex = (++_readIndex) % _size;
- _bufferFull = false;
- return true;
- }
- }
- }
- }
+ ret = buffer[readIndex];
+ buffer[readIndex] = default(T);
+ readIndex = (++readIndex) % size;
+ bufferFull = false;
+ return true;
+ }
+ }
+ }
}
\ No newline at end of file
diff --git a/src/Umbraco.Core/Logging/Logger.cs b/src/Umbraco.Core/Logging/Logger.cs
index 58f696d82b..ae8bb60fcd 100644
--- a/src/Umbraco.Core/Logging/Logger.cs
+++ b/src/Umbraco.Core/Logging/Logger.cs
@@ -1,4 +1,5 @@
using System;
+using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Threading;
@@ -12,15 +13,20 @@ namespace Umbraco.Core.Logging
/// Used for logging
///
public class Logger : ILogger
- {
+ {
+
public Logger(FileInfo log4NetConfigFile)
+ :this()
{
XmlConfigurator.Configure(log4NetConfigFile);
}
private Logger()
{
-
+ //Add custom global properties to the log4net context that we can use in our logging output
+
+ log4net.GlobalContext.Properties["processId"] = Process.GetCurrentProcess().Id;
+ log4net.GlobalContext.Properties["appDomainId"] = AppDomain.CurrentDomain.Id;
}
///
@@ -53,31 +59,19 @@ namespace Umbraco.Core.Logging
return LogManager.GetLogger(getTypeFromInstance.GetType());
}
-
- ///
- /// Useful if the logger itself is running on another thread
- ///
- ///
- ///
- private string PrefixThreadId(string generateMessageFormat)
- {
- return "[Thread " + Thread.CurrentThread.ManagedThreadId + "] " + generateMessageFormat;
- }
-
+
public void Error(Type callingType, string message, Exception exception)
{
var logger = LogManager.GetLogger(callingType);
if (logger != null)
- logger.Error(PrefixThreadId(message), exception);
+ logger.Error((message), exception);
}
-
-
public void Warn(Type callingType, string message, params Func[] formatItems)
{
var logger = LogManager.GetLogger(callingType);
if (logger == null || logger.IsWarnEnabled == false) return;
- logger.WarnFormat(PrefixThreadId(message), formatItems.Select(x => x.Invoke()).ToArray());
+ logger.WarnFormat((message), formatItems.Select(x => x.Invoke()).ToArray());
}
public void Warn(Type callingType, string message, bool showHttpTrace, params Func[] formatItems)
@@ -92,7 +86,7 @@ namespace Umbraco.Core.Logging
var logger = LogManager.GetLogger(callingType);
if (logger == null || logger.IsWarnEnabled == false) return;
- logger.WarnFormat(PrefixThreadId(message), formatItems.Select(x => x.Invoke()).ToArray());
+ logger.WarnFormat((message), formatItems.Select(x => x.Invoke()).ToArray());
}
@@ -105,7 +99,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(PrefixThreadId(message) + ". Exception: " + e, executedParams);
+ logger.WarnFormat((message) + ". Exception: " + e, executedParams);
}
///
@@ -117,7 +111,7 @@ namespace Umbraco.Core.Logging
{
var logger = LogManager.GetLogger(callingType);
if (logger == null || logger.IsInfoEnabled == false) return;
- logger.Info(PrefixThreadId(generateMessage.Invoke()));
+ logger.Info((generateMessage.Invoke()));
}
///
@@ -131,7 +125,7 @@ namespace Umbraco.Core.Logging
var logger = LogManager.GetLogger(type);
if (logger == null || logger.IsInfoEnabled == false) return;
var executedParams = formatItems.Select(x => x.Invoke()).ToArray();
- logger.InfoFormat(PrefixThreadId(generateMessageFormat), executedParams);
+ logger.InfoFormat((generateMessageFormat), executedParams);
}
@@ -144,7 +138,7 @@ namespace Umbraco.Core.Logging
{
var logger = LogManager.GetLogger(callingType);
if (logger == null || logger.IsDebugEnabled == false) return;
- logger.Debug(PrefixThreadId(generateMessage.Invoke()));
+ logger.Debug((generateMessage.Invoke()));
}
///
@@ -158,7 +152,7 @@ namespace Umbraco.Core.Logging
var logger = LogManager.GetLogger(type);
if (logger == null || logger.IsDebugEnabled == false) return;
var executedParams = formatItems.Select(x => x.Invoke()).ToArray();
- logger.DebugFormat(PrefixThreadId(generateMessageFormat), executedParams);
+ logger.DebugFormat((generateMessageFormat), executedParams);
}
diff --git a/src/Umbraco.Core/Logging/LoggingEventContext.cs b/src/Umbraco.Core/Logging/LoggingEventContext.cs
new file mode 100644
index 0000000000..159af4266b
--- /dev/null
+++ b/src/Umbraco.Core/Logging/LoggingEventContext.cs
@@ -0,0 +1,17 @@
+using log4net.Core;
+
+namespace Umbraco.Core.Logging
+{
+ ///
+ /// Based on https://github.com/cjbhaines/Log4Net.Async
+ ///
+ internal class LoggingEventContext
+ {
+ public LoggingEventContext(LoggingEvent loggingEvent)
+ {
+ LoggingEvent = loggingEvent;
+ }
+
+ public LoggingEvent LoggingEvent { get; set; }
+ }
+}
\ No newline at end of file
diff --git a/src/Umbraco.Core/Logging/LoggingEventHelper.cs b/src/Umbraco.Core/Logging/LoggingEventHelper.cs
new file mode 100644
index 0000000000..c788e115f2
--- /dev/null
+++ b/src/Umbraco.Core/Logging/LoggingEventHelper.cs
@@ -0,0 +1,31 @@
+using System;
+using log4net.Core;
+
+namespace Umbraco.Core.Logging
+{
+ ///
+ /// Based on https://github.com/cjbhaines/Log4Net.Async
+ ///
+ internal class LoggingEventHelper
+ {
+ // needs to be a seperate class so that location is determined correctly by log4net when required
+
+ private static readonly Type HelperType = typeof(LoggingEventHelper);
+ private readonly string loggerName;
+
+ public FixFlags Fix { get; set; }
+
+ public LoggingEventHelper(string loggerName, FixFlags fix)
+ {
+ this.loggerName = loggerName;
+ Fix = fix;
+ }
+
+ public LoggingEvent CreateLoggingEvent(Level level, string message, Exception exception)
+ {
+ var loggingEvent = new LoggingEvent(HelperType, null, loggerName, level, message, exception);
+ loggingEvent.Fix = Fix;
+ return loggingEvent;
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Umbraco.Core/Logging/ParallelForwardingAppender.cs b/src/Umbraco.Core/Logging/ParallelForwardingAppender.cs
new file mode 100644
index 0000000000..cf7efdb4c4
--- /dev/null
+++ b/src/Umbraco.Core/Logging/ParallelForwardingAppender.cs
@@ -0,0 +1,307 @@
+using System;
+using System.Collections.Concurrent;
+using System.Threading;
+using System.Threading.Tasks;
+using log4net.Core;
+using log4net.Util;
+
+namespace Umbraco.Core.Logging
+{
+ ///
+ /// An asynchronous appender based on
+ ///
+ ///
+ /// Based on https://github.com/cjbhaines/Log4Net.Async
+ ///
+ public class ParallelForwardingAppender : AsyncForwardingAppenderBase, IDisposable
+ {
+ #region Private Members
+
+ private const int DefaultBufferSize = 1000;
+ private BlockingCollection _loggingEvents;
+ private CancellationTokenSource _loggingCancelationTokenSource;
+ private CancellationToken _loggingCancelationToken;
+ private Task _loggingTask;
+ private Double _shutdownFlushTimeout = 1;
+ private TimeSpan _shutdownFlushTimespan = TimeSpan.FromSeconds(1);
+ private static readonly Type ThisType = typeof(ParallelForwardingAppender);
+ private volatile bool _shutDownRequested;
+ private int? _bufferSize = DefaultBufferSize;
+
+ #endregion Private Members
+
+ #region Properties
+
+ ///
+ /// Gets or sets the number of LoggingEvents that will be buffered. Set to null for unlimited.
+ ///
+ public override int? BufferSize
+ {
+ get { return _bufferSize; }
+ set { _bufferSize = value; }
+ }
+
+ public int BufferEntryCount
+ {
+ get
+ {
+ if (_loggingEvents == null) return 0;
+ return _loggingEvents.Count;
+ }
+ }
+
+ ///
+ /// Gets or sets the time period in which the system will wait for appenders to flush before canceling the background task.
+ ///
+ 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(BufferSize.Value);
+ }
+ else
+ {
+ //No limit on the number of events.
+ _loggingEvents = new BlockingCollection();
+ }
+ //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), _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
+
+ ///
+ /// Iterates over a BlockingCollection containing LoggingEvents.
+ ///
+ 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))
+ {
+ 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
+ }
+}
\ No newline at end of file
diff --git a/src/Umbraco.Core/Macros/MacroTagParser.cs b/src/Umbraco.Core/Macros/MacroTagParser.cs
index f731eda56e..66f8a8a568 100644
--- a/src/Umbraco.Core/Macros/MacroTagParser.cs
+++ b/src/Umbraco.Core/Macros/MacroTagParser.cs
@@ -11,8 +11,12 @@ namespace Umbraco.Core.Macros
///
internal class MacroTagParser
{
- private static readonly Regex MacroRteContent = new Regex(@"()", RegexOptions.Compiled | RegexOptions.IgnoreCase | RegexOptions.Singleline);
- private static readonly Regex MacroPersistedFormat = new Regex(@"(<\?UMBRACO_MACRO macroAlias=[""']([^""\'\n\r]+?)[""'].+?)(?:/>|>.*?\?UMBRACO_MACRO>)", RegexOptions.Compiled | RegexOptions.IgnoreCase | RegexOptions.Singleline);
+ private static readonly Regex MacroRteContent = new Regex(@"()",
+ RegexOptions.Compiled | RegexOptions.IgnoreCase | RegexOptions.CultureInvariant | RegexOptions.Singleline);
+
+ private static readonly Regex MacroPersistedFormat =
+ new Regex(@"(<\?UMBRACO_MACRO (?:.+?)??macroAlias=[""']([^""\'\n\r]+?)[""'].+?)(?:/>|>.*?\?UMBRACO_MACRO>)",
+ RegexOptions.Compiled | RegexOptions.IgnoreCase | RegexOptions.CultureInvariant | RegexOptions.Singleline);
///
/// This formats the persisted string to something useful for the rte so that the macro renders properly since we
diff --git a/src/Umbraco.Core/MainDom.cs b/src/Umbraco.Core/MainDom.cs
new file mode 100644
index 0000000000..6f4a539194
--- /dev/null
+++ b/src/Umbraco.Core/MainDom.cs
@@ -0,0 +1,189 @@
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.IO.MemoryMappedFiles;
+using System.Text;
+using System.Threading;
+using System.Threading.Tasks;
+using System.Web.Hosting;
+using Umbraco.Core.Logging;
+using Umbraco.Core.ObjectResolution;
+
+namespace Umbraco.Core
+{
+ // represents the main domain
+ class MainDom : IRegisteredObject
+ {
+ #region Vars
+
+ private readonly ILogger _logger;
+
+ // our own lock for local consistency
+ private readonly object _locko = new object();
+
+ // async lock representing the main domain lock
+ private readonly AsyncLock _asyncLock;
+ private IDisposable _asyncLocker;
+
+ // event wait handle used to notify current main domain that it should
+ // release the lock because a new domain wants to be the main domain
+ private readonly EventWaitHandle _signal;
+
+ // indicates whether...
+ private volatile bool _isMainDom; // we are the main domain
+ private volatile bool _signaled; // we have been signaled
+
+ // actions to run before releasing the main domain
+ private readonly SortedList _callbacks = new SortedList();
+
+ private const int LockTimeoutMilliseconds = 90000; // (1.5 * 60 * 1000) == 1 min 30 seconds
+
+ #endregion
+
+ #region Ctor
+
+ // initializes a new instance of MainDom
+ public MainDom(ILogger logger)
+ {
+ _logger = logger;
+
+ var appId = string.Empty;
+ // HostingEnvironment.ApplicationID is null in unit tests, making ReplaceNonAlphanumericChars fail
+ if (HostingEnvironment.ApplicationID != null)
+ appId = HostingEnvironment.ApplicationID.ReplaceNonAlphanumericChars(string.Empty);
+
+ var lockName = "UMBRACO-" + appId + "-MAINDOM-LCK";
+ _asyncLock = new AsyncLock(lockName);
+
+ var eventName = "UMBRACO-" + appId + "-MAINDOM-EVT";
+ _signal = new EventWaitHandle(false, EventResetMode.AutoReset, eventName);
+ }
+
+ #endregion
+
+ // register a main domain consumer
+ public bool Register(Action release, int weight = 100)
+ {
+ return Register(null, release, weight);
+ }
+
+ // register a main domain consumer
+ public bool Register(Action install, Action release, int weight = 100)
+ {
+ lock (_locko)
+ {
+ if (_signaled) return false;
+ if (install != null)
+ install();
+ if (release != null)
+ _callbacks.Add(weight, release);
+ return true;
+ }
+ }
+
+ // handles the signal requesting that the main domain is released
+ private void OnSignal(string source)
+ {
+ // once signaled, we stop waiting, but then there is the hosting environment
+ // so we have to make sure that we only enter that method once
+
+ lock (_locko)
+ {
+ _logger.Debug("Signaled" + (_signaled ? " (again)" : "") + " (" + source + ").");
+ if (_signaled) return;
+ if (_isMainDom == false) return; // probably not needed
+ _signaled = true;
+ }
+
+ try
+ {
+ _logger.Debug("Stopping...");
+ foreach (var callback in _callbacks.Values)
+ {
+ try
+ {
+ callback(); // no timeout on callbacks
+ }
+ catch (Exception e)
+ {
+ _logger.Error("Error while running callback, remaining callbacks will not run.", e);
+ throw;
+ }
+
+ }
+ _logger.Debug("Stopped.");
+ }
+ finally
+ {
+ // in any case...
+ _isMainDom = false;
+ _asyncLocker.Dispose();
+ _logger.Debug("Released MainDom.");
+ }
+ }
+
+ // acquires the main domain
+ public bool Acquire()
+ {
+ lock (_locko) // we don't want the hosting environment to interfere by signaling
+ {
+ // if signaled, too late to acquire, give up
+ // the handler is not installed so that would be the hosting environment
+ if (_signaled)
+ {
+ _logger.Debug("Cannot acquire MainDom (signaled).");
+ return false;
+ }
+
+ _logger.Debug("Acquiring MainDom...");
+
+ // signal other instances that we want the lock, then wait one the lock,
+ // which may timeout, and this is accepted - see comments below
+
+ // signal, then wait for the lock, then make sure the event is
+ // resetted (maybe there was noone listening..)
+ _signal.Set();
+
+ // if more than 1 instance reach that point, one will get the lock
+ // and the other one will timeout, which is accepted
+
+ _asyncLocker = _asyncLock.Lock(LockTimeoutMilliseconds);
+ _isMainDom = true;
+
+ // we need to reset the event, because otherwise we would end up
+ // signaling ourselves and commiting suicide immediately.
+ // only 1 instance can reach that point, but other instances may
+ // have started and be trying to get the lock - they will timeout,
+ // which is accepted
+
+ _signal.Reset();
+ _signal.WaitOneAsync()
+ .ContinueWith(_ => OnSignal("signal"));
+
+ HostingEnvironment.RegisterObject(this);
+
+ _logger.Debug("Acquired MainDom.");
+ return true;
+ }
+ }
+
+ // gets a value indicating whether we are the main domain
+ public bool IsMainDom
+ {
+ get { return _isMainDom; }
+ }
+
+ // IRegisteredObject
+ public void Stop(bool immediate)
+ {
+ try
+ {
+ OnSignal("environment"); // will run once
+ }
+ finally
+ {
+ HostingEnvironment.UnregisterObject(this);
+ }
+ }
+ }
+}
diff --git a/src/Umbraco.Core/Manifest/ManifestParser.cs b/src/Umbraco.Core/Manifest/ManifestParser.cs
index c8c421aea8..7e7d9fe2b6 100644
--- a/src/Umbraco.Core/Manifest/ManifestParser.cs
+++ b/src/Umbraco.Core/Manifest/ManifestParser.cs
@@ -2,6 +2,7 @@
using System.Collections.Generic;
using System.IO;
using System.Linq;
+using System.Text;
using System.Text.RegularExpressions;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
@@ -100,11 +101,15 @@ namespace Umbraco.Core.Manifest
if (depth < 1)
{
- var dirs = currDir.GetDirectories();
var result = new List();
- foreach (var d in dirs)
+ if (currDir.Exists)
{
- result.AddRange(GetAllManifestFileContents(d));
+ var dirs = currDir.GetDirectories();
+
+ foreach (var d in dirs)
+ {
+ result.AddRange(GetAllManifestFileContents(d));
+ }
}
return result;
}
@@ -140,11 +145,22 @@ namespace Umbraco.Core.Manifest
{
var result = new List();
foreach (var m in manifestFileContents)
- {
- if (m.IsNullOrWhiteSpace()) continue;
+ {
+ var manifestContent = m;
+
+ if (manifestContent.IsNullOrWhiteSpace()) continue;
+
+ // Strip byte object marker, JSON.NET does not like it
+ var preamble = Encoding.UTF8.GetString(Encoding.UTF8.GetPreamble());
+
+ // Strangely StartsWith(preamble) would always return true
+ if (manifestContent.Substring(0, 1) == preamble)
+ manifestContent = manifestContent.Remove(0, preamble.Length);
+
+ if (manifestContent.IsNullOrWhiteSpace()) continue;
//remove any comments first
- var replaced = CommentsSurround.Replace(m, match => " ");
+ var replaced = CommentsSurround.Replace(manifestContent, match => " ");
replaced = CommentsLine.Replace(replaced, match => "");
JObject deserialized;
diff --git a/src/Umbraco.Core/Media/Exif/ExifFileTypeDescriptor.cs b/src/Umbraco.Core/Media/Exif/ExifFileTypeDescriptor.cs
index 04c9c0ded7..00fd9a11c0 100644
--- a/src/Umbraco.Core/Media/Exif/ExifFileTypeDescriptor.cs
+++ b/src/Umbraco.Core/Media/Exif/ExifFileTypeDescriptor.cs
@@ -34,7 +34,7 @@ namespace Umbraco.Core.Media.Exif
}
///
- /// Expands ExifProperty objects contained in an ExifFile as seperate properties.
+ /// Expands ExifProperty objects contained in an ExifFile as separate properties.
///
internal sealed class ExifFileTypeDescriptor : CustomTypeDescriptor
{
diff --git a/src/Umbraco.Core/Media/ImageHelper.cs b/src/Umbraco.Core/Media/ImageHelper.cs
index 5842bb67bb..99fc278e61 100644
--- a/src/Umbraco.Core/Media/ImageHelper.cs
+++ b/src/Umbraco.Core/Media/ImageHelper.cs
@@ -59,7 +59,7 @@ namespace Umbraco.Core.Media
var fileHeight = image.Height;
return new Size(fileWidth, fileHeight);
}
-
+
}
public static string GetMimeType(this Image image)
@@ -79,22 +79,22 @@ namespace Umbraco.Core.Media
///
///
internal static IEnumerable GenerateMediaThumbnails(
- IFileSystem fs,
- string fileName,
- string extension,
+ IFileSystem fs,
+ string fileName,
+ string extension,
Image originalImage,
IEnumerable additionalThumbSizes)
{
var result = new List();
- var allSizesDictionary = new Dictionary {{100,"thumb"}, {500,"big-thumb"}};
-
+ var allSizesDictionary = new Dictionary { { 100, "thumb" }, { 500, "big-thumb" } };
+
//combine the static dictionary with the additional sizes with only unique values
var allSizes = allSizesDictionary.Select(kv => kv.Key)
.Union(additionalThumbSizes.Where(x => x > 0).Distinct());
- var sizesDictionary = allSizes.ToDictionary(s => s, s => allSizesDictionary.ContainsKey(s) ? allSizesDictionary[s]: "");
+ var sizesDictionary = allSizes.ToDictionary(s => s, s => allSizesDictionary.ContainsKey(s) ? allSizesDictionary[s] : "");
foreach (var s in sizesDictionary)
{
@@ -121,9 +121,9 @@ namespace Umbraco.Core.Media
///
private static ResizedImage Resize(IFileSystem fileSystem, string path, string extension, int maxWidthHeight, string fileNameAddition, Image originalImage)
{
- var fileNameThumb = String.IsNullOrEmpty(fileNameAddition)
- ? string.Format("{0}_UMBRACOSYSTHUMBNAIL.jpg", path.Substring(0, path.LastIndexOf(".")))
- : string.Format("{0}_{1}.jpg", path.Substring(0, path.LastIndexOf(".")), fileNameAddition);
+ var fileNameThumb = string.IsNullOrWhiteSpace(fileNameAddition)
+ ? string.Format("{0}_UMBRACOSYSTHUMBNAIL." + extension, path.Substring(0, path.LastIndexOf(".", StringComparison.Ordinal)))
+ : string.Format("{0}_{1}." + extension, path.Substring(0, path.LastIndexOf(".", StringComparison.Ordinal)), fileNameAddition);
var thumb = GenerateThumbnail(
originalImage,
@@ -190,9 +190,9 @@ namespace Umbraco.Core.Media
//use best quality
g.InterpolationMode = InterpolationMode.HighQualityBicubic;
}
-
- g.SmoothingMode = SmoothingMode.HighQuality;
+
+ g.SmoothingMode = SmoothingMode.HighQuality;
g.PixelOffsetMode = PixelOffsetMode.HighQuality;
g.CompositingQuality = CompositingQuality.HighQuality;
@@ -202,10 +202,29 @@ namespace Umbraco.Core.Media
// Copy metadata
var imageEncoders = ImageCodecInfo.GetImageEncoders();
-
- var codec = extension.ToLower() == "png" || extension.ToLower() == "gif"
- ? imageEncoders.Single(t => t.MimeType.Equals("image/png"))
- : imageEncoders.Single(t => t.MimeType.Equals("image/jpeg"));
+ ImageCodecInfo codec;
+ switch (extension.ToLower())
+ {
+ case "png":
+ codec = imageEncoders.Single(t => t.MimeType.Equals("image/png"));
+ break;
+ case "gif":
+ codec = imageEncoders.Single(t => t.MimeType.Equals("image/gif"));
+ break;
+ case "tif":
+ case "tiff":
+ codec = imageEncoders.Single(t => t.MimeType.Equals("image/tiff"));
+ break;
+ case "bmp":
+ codec = imageEncoders.Single(t => t.MimeType.Equals("image/bmp"));
+ break;
+ // TODO: this is dirty, defaulting to jpg but the return value of this thing is used all over the
+ // place so left it here, but it needs to not set a codec if it doesn't know which one to pick
+ // Note: when fixing this: both .jpg and .jpeg should be handled as extensions
+ default:
+ codec = imageEncoders.Single(t => t.MimeType.Equals("image/jpeg"));
+ break;
+ }
// Set compresion ratio to 90%
var ep = new EncoderParameters();
@@ -213,12 +232,14 @@ namespace Umbraco.Core.Media
// Save the new image using the dimensions of the image
var predictableThumbnailName = thumbnailFileName.Replace("UMBRACOSYSTHUMBNAIL", maxWidthHeight.ToString(CultureInfo.InvariantCulture));
+ var predictableThumbnailNameJpg = predictableThumbnailName.Substring(0, predictableThumbnailName.LastIndexOf(".", StringComparison.Ordinal)) + ".jpg";
using (var ms = new MemoryStream())
{
bp.Save(ms, codec, ep);
ms.Seek(0, 0);
fs.AddFile(predictableThumbnailName, ms);
+ fs.AddFile(predictableThumbnailNameJpg, ms);
}
// TODO: Remove this, this is ONLY here for backwards compatibility but it is essentially completely unusable see U4-5385
diff --git a/src/Umbraco.Core/Models/Content.cs b/src/Umbraco.Core/Models/Content.cs
index 9b95fe5475..2238260611 100644
--- a/src/Umbraco.Core/Models/Content.cs
+++ b/src/Umbraco.Core/Models/Content.cs
@@ -95,13 +95,7 @@ namespace Umbraco.Core.Models
[DataMember]
public virtual ITemplate Template
{
- get
- {
- if (_template == null)
- return _contentType.DefaultTemplate;
-
- return _template;
- }
+ get { return _template; }
set
{
SetPropertyValueAndDetectChanges(o =>
diff --git a/src/Umbraco.Core/Models/ContentExtensions.cs b/src/Umbraco.Core/Models/ContentExtensions.cs
index 88b6308c29..e91996e32a 100644
--- a/src/Umbraco.Core/Models/ContentExtensions.cs
+++ b/src/Umbraco.Core/Models/ContentExtensions.cs
@@ -560,8 +560,7 @@ namespace Umbraco.Core.Models
//Additional thumbnails configured as prevalues on the DataType
if (thumbnailSizes != null)
{
- var sep = (thumbnailSizes.Contains("") == false && thumbnailSizes.Contains(",")) ? ',' : ';';
- foreach (var thumb in thumbnailSizes.Split(sep))
+ foreach (var thumb in thumbnailSizes.Split(new[] { ";", "," }, StringSplitOptions.RemoveEmptyEntries))
{
int thumbSize;
if (thumb != "" && int.TryParse(thumb, out thumbSize))
diff --git a/src/Umbraco.Core/Models/ContentTypeBase.cs b/src/Umbraco.Core/Models/ContentTypeBase.cs
index bee9ed9b6e..a83defb7b5 100644
--- a/src/Umbraco.Core/Models/ContentTypeBase.cs
+++ b/src/Umbraco.Core/Models/ContentTypeBase.cs
@@ -26,7 +26,7 @@ namespace Umbraco.Core.Models
private string _alias;
private string _description;
private int _sortOrder;
- private string _icon = "folder.png";
+ private string _icon = "icon-folder";
private string _thumbnail = "folder.png";
private int _creatorId;
private bool _allowedAsRoot;
@@ -373,43 +373,35 @@ namespace Umbraco.Core.Models
{
_propertyGroups = value;
_propertyGroups.CollectionChanged += PropertyGroupsChanged;
+ PropertyGroupsChanged(_propertyGroups, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
}
}
///
- /// List of PropertyTypes available on this ContentType.
- /// This list aggregates PropertyTypes across the PropertyGroups.
+ /// Gets all property types, across all property groups.
///
- ///
- ///
- /// The setter is used purely to set the property types that DO NOT belong to a group!
- ///
- /// Marked as DoNotClone because the result of this property is not the natural result of the data, it is
- /// a union of data so when auto-cloning if the setter is used it will be setting the unnatural result of the
- /// data. We manually clone this instead.
- ///
[IgnoreDataMember]
[DoNotClone]
public virtual IEnumerable PropertyTypes
{
get
{
- var types = _propertyTypes.Union(PropertyGroups.SelectMany(x => x.PropertyTypes));
- return types;
- }
- internal set
- {
- _propertyTypes = new PropertyTypeCollection(value);
- _propertyTypes.CollectionChanged += PropertyTypesChanged;
+ return _propertyTypes.Union(PropertyGroups.SelectMany(x => x.PropertyTypes));
}
}
///
- /// Returns the property type collection containing types that are non-groups - used for tests
+ /// Gets or sets the property types that are not in a group.
///
- internal IEnumerable NonGroupedPropertyTypes
+ public IEnumerable NoGroupPropertyTypes
{
get { return _propertyTypes; }
+ set
+ {
+ _propertyTypes = new PropertyTypeCollection(value);
+ _propertyTypes.CollectionChanged += PropertyTypesChanged;
+ PropertyTypesChanged(_propertyTypes, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
+ }
}
///
@@ -460,6 +452,11 @@ namespace Umbraco.Core.Models
/// Returns True if PropertyType was added, otherwise False
public bool AddPropertyType(PropertyType propertyType)
{
+ if (propertyType.HasIdentity == false)
+ {
+ propertyType.Key = Guid.NewGuid();
+ }
+
if (PropertyTypeExists(propertyType.Alias) == false)
{
_propertyTypes.Add(propertyType);
@@ -475,24 +472,37 @@ namespace Umbraco.Core.Models
/// Alias of the PropertyType to move
/// Name of the PropertyGroup to move the PropertyType to
///
+ /// If is null then the property is moved back to
+ /// "generic properties" ie does not have a tab anymore.
public bool MovePropertyType(string propertyTypeAlias, string propertyGroupName)
{
- if (PropertyTypes.Any(x => x.Alias == propertyTypeAlias) == false || PropertyGroups.Any(x => x.Name == propertyGroupName) == false)
- return false;
+ // note: not dealing with alias casing at all here?
- var propertyType = PropertyTypes.First(x => x.Alias == propertyTypeAlias);
- //The PropertyType already belongs to a PropertyGroup, so we have to remove the PropertyType from that group
- if (PropertyGroups.Any(x => x.PropertyTypes.Any(y => y.Alias == propertyTypeAlias)))
- {
- var oldPropertyGroup = PropertyGroups.First(x => x.PropertyTypes.Any(y => y.Alias == propertyTypeAlias));
- oldPropertyGroup.PropertyTypes.RemoveItem(propertyTypeAlias);
- }
+ // get property, ensure it exists
+ var propertyType = PropertyTypes.FirstOrDefault(x => x.Alias == propertyTypeAlias);
+ if (propertyType == null) return false;
+ // get new group, if required, and ensure it exists
+ var newPropertyGroup = propertyGroupName == null
+ ? null
+ : PropertyGroups.FirstOrDefault(x => x.Name == propertyGroupName);
+ if (propertyGroupName != null && newPropertyGroup == null) return false;
+
+ // get old group
+ var oldPropertyGroup = PropertyGroups.FirstOrDefault(x =>
+ x.PropertyTypes.Any(y => y.Alias == propertyTypeAlias));
+
+ // reset PropertyGroupId, which will be re-evaluated when the content type
+ // is saved - what is important is group.PropertyTypes - see code in
+ // ContentTypeBaseRepository.PersistUpdatedBaseContentType
propertyType.PropertyGroupId = new Lazy(() => default(int));
- propertyType.ResetDirtyProperties();
+ propertyType.ResetDirtyProperties(); // PropertyGroupId must not be dirty
- var propertyGroup = PropertyGroups.First(x => x.Name == propertyGroupName);
- propertyGroup.PropertyTypes.Add(propertyType);
+ // remove from old group, if any - add to new group, if any
+ if (oldPropertyGroup != null)
+ oldPropertyGroup.PropertyTypes.RemoveItem(propertyTypeAlias);
+ if (newPropertyGroup != null)
+ newPropertyGroup.PropertyTypes.Add(propertyType);
return true;
}
@@ -528,6 +538,18 @@ namespace Umbraco.Core.Models
/// Name of the to remove
public void RemovePropertyGroup(string propertyGroupName)
{
+ // if no group exists with that name, do nothing
+ var group = PropertyGroups[propertyGroupName];
+ if (group == null) return;
+
+ // re-assign the group's properties to no group
+ foreach (var property in group.PropertyTypes)
+ {
+ property.PropertyGroupId = new Lazy(() => 0);
+ _propertyTypes.Add(property);
+ }
+
+ // actually remove the group
PropertyGroups.RemoveItem(propertyGroupName);
OnPropertyChanged(PropertyGroupCollectionSelector);
}
diff --git a/src/Umbraco.Core/Models/ContentTypeCompositionBase.cs b/src/Umbraco.Core/Models/ContentTypeCompositionBase.cs
index 1fd1496742..5ac21885d7 100644
--- a/src/Umbraco.Core/Models/ContentTypeCompositionBase.cs
+++ b/src/Umbraco.Core/Models/ContentTypeCompositionBase.cs
@@ -37,16 +37,21 @@ namespace Umbraco.Core.Models
x => x.ContentTypeComposition);
///
- /// List of ContentTypes that make up a composition of PropertyGroups and PropertyTypes for the current ContentType
+ /// Gets or sets the content types that compose this content type.
///
[DataMember]
public IEnumerable ContentTypeComposition
{
get { return _contentTypeComposition; }
+ set
+ {
+ _contentTypeComposition = value.ToList();
+ OnPropertyChanged(ContentTypeCompositionSelector);
+ }
}
///
- /// Returns a list of objects from the composition
+ /// Gets the property groups for the entire composition.
///
[IgnoreDataMember]
public IEnumerable CompositionPropertyGroups
@@ -59,7 +64,7 @@ namespace Umbraco.Core.Models
}
///
- /// Returns a list of objects from the composition
+ /// Gets the property types for the entire composition.
///
[IgnoreDataMember]
public IEnumerable CompositionPropertyTypes
@@ -72,10 +77,10 @@ namespace Umbraco.Core.Models
}
///
- /// Adds a new ContentType to the list of composite ContentTypes
+ /// Adds a content type to the composition.
///
- /// to add
- /// True if ContentType was added, otherwise returns False
+ /// The content type to add.
+ /// True if the content type was added, otherwise false.
public bool AddContentType(IContentTypeComposition contentType)
{
if (contentType.ContentTypeComposition.Any(x => x.CompositionAliases().Any(ContentTypeCompositionExists)))
@@ -94,13 +99,7 @@ namespace Umbraco.Core.Models
.Select(p => p.Alias)).ToList();
if (conflictingPropertyTypeAliases.Any())
- throw new InvalidCompositionException
- {
- AddedCompositionAlias = contentType.Alias,
- ContentTypeAlias = Alias,
- PropertyTypeAlias =
- string.Join(", ", conflictingPropertyTypeAliases)
- };
+ throw new InvalidCompositionException(Alias, contentType.Alias, conflictingPropertyTypeAliases.ToArray());
_contentTypeComposition.Add(contentType);
OnPropertyChanged(ContentTypeCompositionSelector);
@@ -110,10 +109,10 @@ namespace Umbraco.Core.Models
}
///
- /// Removes a ContentType with the supplied alias from the the list of composite ContentTypes
+ /// Removes a content type with a specified alias from the composition.
///
- /// Alias of a
- /// True if ContentType was removed, otherwise returns False
+ /// The alias of the content type to remove.
+ /// True if the content type was removed, otherwise false.
public bool RemoveContentType(string alias)
{
if (ContentTypeCompositionExists(alias))
@@ -163,31 +162,44 @@ namespace Umbraco.Core.Models
///
/// Adds a PropertyGroup.
- /// This method will also check if a group already exists with the same name and link it to the parent.
///
/// Name of the PropertyGroup to add
/// Returns True if a PropertyGroup with the passed in name was added, otherwise False
public override bool AddPropertyGroup(string groupName)
{
- if (PropertyGroups.Any(x => x.Name == groupName))
- return false;
+ return AddAndReturnPropertyGroup(groupName) != null;
+ }
- var propertyGroup = new PropertyGroup {Name = groupName, SortOrder = 0};
+ private PropertyGroup AddAndReturnPropertyGroup(string name)
+ {
+ // ensure we don't have it already
+ if (PropertyGroups.Any(x => x.Name == name))
+ return null;
- if (CompositionPropertyGroups.Any(x => x.Name == groupName))
+ // create the new group
+ var group = new PropertyGroup { Name = name, SortOrder = 0 };
+
+ // check if it is inherited - there might be more than 1 but we want the 1st, to
+ // reuse its sort order - if there are more than 1 and they have different sort
+ // orders... there isn't much we can do anyways
+ var inheritGroup = CompositionPropertyGroups.FirstOrDefault(x => x.Name == name);
+ if (inheritGroup == null)
{
- var firstGroup = CompositionPropertyGroups.First(x => x.Name == groupName && x.ParentId.HasValue == false);
- propertyGroup.SetLazyParentId(new Lazy(() => firstGroup.Id));
+ // no, just local, set sort order
+ var lastGroup = PropertyGroups.LastOrDefault();
+ if (lastGroup != null)
+ group.SortOrder = lastGroup.SortOrder + 1;
+ }
+ else
+ {
+ // yes, inherited, re-use sort order
+ group.SortOrder = inheritGroup.SortOrder;
}
- if (PropertyGroups.Any())
- {
- var last = PropertyGroups.Last();
- propertyGroup.SortOrder = last.SortOrder + 1;
- }
+ // add
+ PropertyGroups.Add(group);
- PropertyGroups.Add(propertyGroup);
- return true;
+ return group;
}
///
@@ -198,34 +210,25 @@ namespace Umbraco.Core.Models
/// Returns True if PropertyType was added, otherwise False
public override bool AddPropertyType(PropertyType propertyType, string propertyGroupName)
{
- if (PropertyTypeExists(propertyType.Alias) == false)
- {
- if (PropertyGroups.Contains(propertyGroupName))
- {
- propertyType.PropertyGroupId = new Lazy(() => PropertyGroups[propertyGroupName].Id);
- PropertyGroups[propertyGroupName].PropertyTypes.Add(propertyType);
- }
- else
- {
- //If the PropertyGroup doesn't already exist we create a new one
- var propertyTypes = new List { propertyType };
- var propertyGroup = new PropertyGroup(new PropertyTypeCollection(propertyTypes)) { Name = propertyGroupName, SortOrder = 1 };
- //and check if its an inherited PropertyGroup, which exists in the composition
- if (CompositionPropertyGroups.Any(x => x.Name == propertyGroupName))
- {
- var parentPropertyGroup = CompositionPropertyGroups.First(x => x.Name == propertyGroupName && x.ParentId.HasValue == false);
- propertyGroup.SortOrder = parentPropertyGroup.SortOrder;
- //propertyGroup.ParentId = parentPropertyGroup.Id;
- propertyGroup.SetLazyParentId(new Lazy(() => parentPropertyGroup.Id));
- }
+ if (propertyType.HasIdentity == false)
+ propertyType.Key = Guid.NewGuid();
- PropertyGroups.Add(propertyGroup);
- }
+ // ensure no duplicate alias - over all composition properties
+ if (PropertyTypeExists(propertyType.Alias))
+ return false;
- return true;
- }
+ // get and ensure a group local to this content type
+ var group = PropertyGroups.Contains(propertyGroupName)
+ ? PropertyGroups[propertyGroupName]
+ : AddAndReturnPropertyGroup(propertyGroupName);
+ if (group == null)
+ return false;
- return false;
+ // add property to group
+ propertyType.PropertyGroupId = new Lazy(() => group.Id);
+ group.PropertyTypes.Add(propertyType);
+
+ return true;
}
///
diff --git a/src/Umbraco.Core/Models/ContentTypeExtensions.cs b/src/Umbraco.Core/Models/ContentTypeExtensions.cs
index df8c09b3d1..a47b430979 100644
--- a/src/Umbraco.Core/Models/ContentTypeExtensions.cs
+++ b/src/Umbraco.Core/Models/ContentTypeExtensions.cs
@@ -15,7 +15,7 @@ namespace Umbraco.Core.Models
{
var contentTypeService = ApplicationContext.Current.Services.ContentTypeService;
var descendants = contentTypeService.GetContentTypeChildren(contentType.Id)
- .FlattenList(type => contentTypeService.GetContentTypeChildren(type.Id));
+ .SelectRecursive(type => contentTypeService.GetContentTypeChildren(type.Id));
return descendants;
}
diff --git a/src/Umbraco.Core/Models/DataTypeDatabaseType.cs b/src/Umbraco.Core/Models/DataTypeDatabaseType.cs
index e45cdd1a73..1db8ac65cb 100644
--- a/src/Umbraco.Core/Models/DataTypeDatabaseType.cs
+++ b/src/Umbraco.Core/Models/DataTypeDatabaseType.cs
@@ -21,6 +21,8 @@ namespace Umbraco.Core.Models
[EnumMember]
Integer,
[EnumMember]
- Date
+ Date,
+ [EnumMember]
+ Decimal
}
}
\ No newline at end of file
diff --git a/src/Umbraco.Core/Models/DataTypeDefinition.cs b/src/Umbraco.Core/Models/DataTypeDefinition.cs
index 2fddbad6cb..5cf3976613 100644
--- a/src/Umbraco.Core/Models/DataTypeDefinition.cs
+++ b/src/Umbraco.Core/Models/DataTypeDefinition.cs
@@ -32,6 +32,7 @@ namespace Umbraco.Core.Models
private string _propertyEditorAlias;
private DataTypeDatabaseType _databaseType;
+
public DataTypeDefinition(int parentId, string propertyEditorAlias)
{
_parentId = parentId;
@@ -40,6 +41,14 @@ namespace Umbraco.Core.Models
_additionalData = new Dictionary();
}
+ public DataTypeDefinition(string propertyEditorAlias)
+ {
+ _parentId = -1;
+ _propertyEditorAlias = propertyEditorAlias;
+
+ _additionalData = new Dictionary();
+ }
+
private static readonly PropertyInfo NameSelector = ExpressionHelper.GetPropertyInfo(x => x.Name);
private static readonly PropertyInfo ParentIdSelector = ExpressionHelper.GetPropertyInfo(x => x.ParentId);
private static readonly PropertyInfo SortOrderSelector = ExpressionHelper.GetPropertyInfo(x => x.SortOrder);
@@ -220,11 +229,6 @@ namespace Umbraco.Core.Models
get { return _additionalData; }
}
- ///
- /// Some entities may expose additional data that other's might not, this custom data will be available in this collection
- ///
- public IDictionary AdditionalData { get; private set; }
-
internal override void AddingEntity()
{
base.AddingEntity();
@@ -233,4 +237,4 @@ namespace Umbraco.Core.Models
Key = Guid.NewGuid();
}
}
-}
+}
diff --git a/src/Umbraco.Core/Models/DeepCloneHelper.cs b/src/Umbraco.Core/Models/DeepCloneHelper.cs
index 0e0e260c06..7523555c24 100644
--- a/src/Umbraco.Core/Models/DeepCloneHelper.cs
+++ b/src/Umbraco.Core/Models/DeepCloneHelper.cs
@@ -78,6 +78,12 @@ namespace Umbraco.Core.Models
}
else
{
+ //skip instead of trying to create instance of abstract or interface
+ if (propertyInfo.PropertyType.IsAbstract || propertyInfo.PropertyType.IsInterface)
+ {
+ continue;
+ }
+
//its a custom IEnumerable, we'll try to create it
try
{
diff --git a/src/Umbraco.Core/Models/DictionaryItem.cs b/src/Umbraco.Core/Models/DictionaryItem.cs
index e09c370053..7e59726c80 100644
--- a/src/Umbraco.Core/Models/DictionaryItem.cs
+++ b/src/Umbraco.Core/Models/DictionaryItem.cs
@@ -14,22 +14,22 @@ namespace Umbraco.Core.Models
[DataContract(IsReference = true)]
public class DictionaryItem : Entity, IDictionaryItem
{
- private Guid _parentId;
+ private Guid? _parentId;
private string _itemKey;
private IEnumerable _translations;
public DictionaryItem(string itemKey)
- : this(new Guid(Constants.Conventions.Localization.DictionaryItemRootId), itemKey)
+ : this(null, itemKey)
{}
- public DictionaryItem(Guid parentId, string itemKey)
+ public DictionaryItem(Guid? parentId, string itemKey)
{
_parentId = parentId;
_itemKey = itemKey;
_translations = new List();
}
- private static readonly PropertyInfo ParentIdSelector = ExpressionHelper.GetPropertyInfo(x => x.ParentId);
+ private static readonly PropertyInfo ParentIdSelector = ExpressionHelper.GetPropertyInfo(x => x.ParentId);
private static readonly PropertyInfo ItemKeySelector = ExpressionHelper.GetPropertyInfo(x => x.ItemKey);
private static readonly PropertyInfo TranslationsSelector = ExpressionHelper.GetPropertyInfo>(x => x.Translations);
@@ -37,7 +37,7 @@ namespace Umbraco.Core.Models
/// Gets or Sets the Parent Id of the Dictionary Item
///
[DataMember]
- public Guid ParentId
+ public Guid? ParentId
{
get { return _parentId; }
set
@@ -95,11 +95,7 @@ namespace Umbraco.Core.Models
{
base.AddingEntity();
- Key = Guid.NewGuid();
-
- //If ParentId is not set we should default to the root parent id
- if(ParentId == Guid.Empty)
- _parentId = new Guid(Constants.Conventions.Localization.DictionaryItemRootId);
+ Key = Guid.NewGuid();
}
}
diff --git a/src/Umbraco.Core/Models/EntityBase/Entity.cs b/src/Umbraco.Core/Models/EntityBase/Entity.cs
index 660d6674ca..eeacb771a9 100644
--- a/src/Umbraco.Core/Models/EntityBase/Entity.cs
+++ b/src/Umbraco.Core/Models/EntityBase/Entity.cs
@@ -101,6 +101,7 @@ namespace Umbraco.Core.Models.EntityBase
/// the new api, which also needs to take effect in the legacy api.
///
[IgnoreDataMember]
+ [Obsolete("Anytime there's a cancellable method it needs to return an Attempt so we know the outcome instead of this hack, not all services have been updated to use this though yet.")]
internal bool WasCancelled
{
get { return _wasCancelled; }
diff --git a/src/Umbraco.Core/Models/EntityContainer.cs b/src/Umbraco.Core/Models/EntityContainer.cs
new file mode 100644
index 0000000000..92965bf238
--- /dev/null
+++ b/src/Umbraco.Core/Models/EntityContainer.cs
@@ -0,0 +1,93 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+
+namespace Umbraco.Core.Models
+{
+ ///
+ /// Represents a folder for organizing entities such as content types and data types.
+ ///
+ public sealed class EntityContainer : UmbracoEntity
+ {
+ private readonly Guid _containedObjectType;
+
+ private static readonly Dictionary ObjectTypeMap = new Dictionary
+ {
+ { Constants.ObjectTypes.DataTypeGuid, Constants.ObjectTypes.DataTypeContainerGuid },
+ { Constants.ObjectTypes.DocumentTypeGuid, Constants.ObjectTypes.DocumentTypeContainerGuid },
+ { Constants.ObjectTypes.MediaTypeGuid, Constants.ObjectTypes.MediaTypeContainerGuid }
+ };
+
+ ///
+ /// Initializes a new instance of an class.
+ ///
+ public EntityContainer(Guid containedObjectType)
+ {
+ if (ObjectTypeMap.ContainsKey(containedObjectType) == false)
+ throw new ArgumentException("Not a contained object type.", "containedObjectType");
+ _containedObjectType = containedObjectType;
+
+ ParentId = -1;
+ Path = "-1";
+ Level = 0;
+ SortOrder = 0;
+ }
+
+ ///
+ /// Initializes a new instance of an class.
+ ///
+ internal EntityContainer(int id, Guid uniqueId, int parentId, string path, int level, int sortOrder, Guid containedObjectType, string name, int userId)
+ : this(containedObjectType)
+ {
+ Id = id;
+ Key = uniqueId;
+ ParentId = parentId;
+ Name = name;
+ Path = path;
+ Level = level;
+ SortOrder = sortOrder;
+ CreatorId = userId;
+ }
+
+ ///
+ /// Gets or sets the node object type of the contained objects.
+ ///
+ public Guid ContainedObjectType
+ {
+ get { return _containedObjectType; }
+ }
+
+ ///
+ /// Gets the node object type of the container objects.
+ ///
+ public Guid ContainerObjectType
+ {
+ get { return ObjectTypeMap[_containedObjectType]; }
+ }
+
+ ///
+ /// Gets the container object type corresponding to a contained object type.
+ ///
+ /// The contained object type.
+ /// The object type of containers containing objects of the contained object type.
+ public static Guid GetContainerObjectType(Guid containedObjectType)
+ {
+ if (ObjectTypeMap.ContainsKey(containedObjectType) == false)
+ throw new ArgumentException("Not a contained object type.", "containedObjectType");
+ return ObjectTypeMap[containedObjectType];
+ }
+
+ ///
+ /// Gets the contained object type corresponding to a container object type.
+ ///
+ /// The container object type.
+ /// The object type of objects that containers of the container object type can contain.
+ public static Guid GetContainedObjectType(Guid containerObjectType)
+ {
+ var contained = ObjectTypeMap.FirstOrDefault(x => x.Value == containerObjectType).Key;
+ if (contained == null)
+ throw new ArgumentException("Not a container object type.", "containerObjectType");
+ return contained;
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Umbraco.Core/Models/File.cs b/src/Umbraco.Core/Models/File.cs
index ae4aac6a1c..8ead6da5f8 100644
--- a/src/Umbraco.Core/Models/File.cs
+++ b/src/Umbraco.Core/Models/File.cs
@@ -2,6 +2,8 @@
using System.IO;
using System.Reflection;
using System.Runtime.Serialization;
+using System.Text;
+using Umbraco.Core.IO;
using Umbraco.Core.Models.EntityBase;
namespace Umbraco.Core.Models
@@ -15,12 +17,20 @@ namespace Umbraco.Core.Models
{
private string _path;
private string _originalPath;
- private string _content = string.Empty; //initialize to empty string, not null
- protected File(string path)
+ // initialize to string.Empty so that it is possible to save a new file,
+ // should use the lazyContent ctor to set it to null when loading existing.
+ // cannot simply use HasIdentity as some classes (eg Script) override it
+ // in a weird way.
+ private string _content;
+ internal Func GetFileContent { get; set; }
+
+ protected File(string path, Func getFileContent = null)
{
- _path = path;
+ _path = SanitizePath(path);
_originalPath = _path;
+ GetFileContent = getFileContent;
+ _content = getFileContent != null ? null : string.Empty;
}
private static readonly PropertyInfo ContentSelector = ExpressionHelper.GetPropertyInfo(x => x.Content);
@@ -28,6 +38,14 @@ namespace Umbraco.Core.Models
private string _alias;
private string _name;
+ private static string SanitizePath(string path)
+ {
+ return path
+ .Replace('\\', System.IO.Path.DirectorySeparatorChar)
+ .Replace('/', System.IO.Path.DirectorySeparatorChar);
+ //.TrimStart(System.IO.Path.DirectorySeparatorChar);
+ }
+
///
/// Gets or sets the Name of the File including extension
///
@@ -71,7 +89,7 @@ namespace Umbraco.Core.Models
SetPropertyValueAndDetectChanges(o =>
{
- _path = value;
+ _path = SanitizePath(value);
return _path;
}, _path, PathSelector);
}
@@ -96,15 +114,26 @@ namespace Umbraco.Core.Models
///
/// Gets or sets the Content of a File
///
+ /// Marked as DoNotClone, because it should be lazy-reloaded from disk.
[DataMember]
+ [DoNotClone]
public virtual string Content
{
- get { return _content; }
+ get
+ {
+ if (_content != null)
+ return _content;
+
+ // else, must lazy-load, and ensure it's not null
+ if (GetFileContent != null)
+ _content = GetFileContent(this);
+ return _content ?? (_content = string.Empty);
+ }
set
{
SetPropertyValueAndDetectChanges(o =>
{
- _content = value;
+ _content = value ?? string.Empty; // cannot set to null
return _content;
}, _content, ContentSelector);
}
@@ -121,17 +150,32 @@ namespace Umbraco.Core.Models
return true;
}
+ // this exists so that class that manage name and alias differently, eg Template,
+ // can implement their own cloning - (though really, not sure it's even needed)
+ protected virtual void DeepCloneNameAndAlias(File clone)
+ {
+ // set fields that have a lazy value, by forcing evaluation of the lazy
+ clone._name = Name;
+ clone._alias = Alias;
+ }
+
public override object DeepClone()
{
- var clone = (File)base.DeepClone();
- //turn off change tracking
+ var clone = (File) base.DeepClone();
+
+ // clear fields that were memberwise-cloned and that we don't want to clone
+ clone._content = null;
+
+ // turn off change tracking
clone.DisableChangeTracking();
- //need to manually assign since they are readonly properties
- clone._alias = Alias;
- clone._name = Name;
- //this shouldn't really be needed since we're not tracking
+
+ // ...
+ DeepCloneNameAndAlias(clone);
+
+ // this shouldn't really be needed since we're not tracking
clone.ResetDirtyProperties(false);
- //re-enable tracking
+
+ // re-enable tracking
clone.EnableChangeTracking();
return clone;
diff --git a/src/Umbraco.Core/Models/IContentTypeBase.cs b/src/Umbraco.Core/Models/IContentTypeBase.cs
index 89fca20b8e..80e62f50cf 100644
--- a/src/Umbraco.Core/Models/IContentTypeBase.cs
+++ b/src/Umbraco.Core/Models/IContentTypeBase.cs
@@ -54,10 +54,15 @@ namespace Umbraco.Core.Models
PropertyGroupCollection PropertyGroups { get; set; }
///
- /// Gets an enumerable list of Property Types aggregated for all groups
+ /// Gets all property types, across all property groups.
///
IEnumerable PropertyTypes { get; }
+ ///
+ /// Gets or sets the property types that are not in a group.
+ ///
+ IEnumerable NoGroupPropertyTypes { get; set; }
+
///
/// Removes a PropertyType from the current ContentType
///
diff --git a/src/Umbraco.Core/Models/IContentTypeComposition.cs b/src/Umbraco.Core/Models/IContentTypeComposition.cs
index 8b5049f236..ab7e068fdd 100644
--- a/src/Umbraco.Core/Models/IContentTypeComposition.cs
+++ b/src/Umbraco.Core/Models/IContentTypeComposition.cs
@@ -8,17 +8,17 @@ namespace Umbraco.Core.Models
public interface IContentTypeComposition : IContentTypeBase
{
///
- /// Gets a list of ContentTypes that make up a composition of PropertyGroups and PropertyTypes for the current ContentType
+ /// Gets or sets the content types that compose this content type.
///
- IEnumerable ContentTypeComposition { get; }
+ IEnumerable ContentTypeComposition { get; set; }
///
- /// Gets a list of objects from the composition
+ /// Gets the property groups for the entire composition.
///
IEnumerable CompositionPropertyGroups { get; }
///
- /// Gets a list of objects from the composition
+ /// Gets the property types for the entire composition.
///
IEnumerable CompositionPropertyTypes { get; }
diff --git a/src/Umbraco.Core/Models/IDictionaryItem.cs b/src/Umbraco.Core/Models/IDictionaryItem.cs
index e10a80e831..ed88acfbec 100644
--- a/src/Umbraco.Core/Models/IDictionaryItem.cs
+++ b/src/Umbraco.Core/Models/IDictionaryItem.cs
@@ -12,7 +12,7 @@ namespace Umbraco.Core.Models
/// Gets or Sets the Parent Id of the Dictionary Item
///
[DataMember]
- Guid ParentId { get; set; }
+ Guid? ParentId { get; set; }
///
/// Gets or sets the Key for the Dictionary Item
diff --git a/src/Umbraco.Core/Models/IDomain.cs b/src/Umbraco.Core/Models/IDomain.cs
index 9fb3e8019d..b9f6bc65ec 100644
--- a/src/Umbraco.Core/Models/IDomain.cs
+++ b/src/Umbraco.Core/Models/IDomain.cs
@@ -4,9 +4,14 @@ namespace Umbraco.Core.Models
{
public interface IDomain : IAggregateRoot, IRememberBeingDirty, ICanBeDirty
{
- ILanguage Language { get; set; }
+ int? LanguageId { get; set; }
string DomainName { get; set; }
- IContent RootContent { get; set; }
+ int? RootContentId { get; set; }
bool IsWildcard { get; }
+
+ ///
+ /// Readonly value of the language ISO code for the domain
+ ///
+ string LanguageIsoCode { get; }
}
}
\ No newline at end of file
diff --git a/src/Umbraco.Core/Models/IMigrationEntry.cs b/src/Umbraco.Core/Models/IMigrationEntry.cs
new file mode 100644
index 0000000000..65bb7bc1f3
--- /dev/null
+++ b/src/Umbraco.Core/Models/IMigrationEntry.cs
@@ -0,0 +1,12 @@
+using System;
+using Semver;
+using Umbraco.Core.Models.EntityBase;
+
+namespace Umbraco.Core.Models
+{
+ public interface IMigrationEntry : IAggregateRoot, IRememberBeingDirty
+ {
+ string MigrationName { get; set; }
+ SemVersion Version { get; set; }
+ }
+}
\ No newline at end of file
diff --git a/src/Umbraco.Core/Models/IPublishedContentWithKey.cs b/src/Umbraco.Core/Models/IPublishedContentWithKey.cs
new file mode 100644
index 0000000000..b0e71221b2
--- /dev/null
+++ b/src/Umbraco.Core/Models/IPublishedContentWithKey.cs
@@ -0,0 +1,14 @@
+using System;
+
+namespace Umbraco.Core.Models
+{
+ ///
+ /// Represents a cached content with a GUID key.
+ ///
+ /// This is temporary, because we cannot add the Key property to IPublishedContent without
+ /// breaking backward compatibility. With v8, it will be merged into IPublishedContent.
+ public interface IPublishedContentWithKey : IPublishedContent
+ {
+ Guid Key { get; }
+ }
+}
diff --git a/src/Umbraco.Core/Models/IServerRegistration.cs b/src/Umbraco.Core/Models/IServerRegistration.cs
new file mode 100644
index 0000000000..8053ea0a6e
--- /dev/null
+++ b/src/Umbraco.Core/Models/IServerRegistration.cs
@@ -0,0 +1,36 @@
+using System;
+using Umbraco.Core.Models.EntityBase;
+using Umbraco.Core.Sync;
+
+namespace Umbraco.Core.Models
+{
+ public interface IServerRegistration : IServerAddress, IAggregateRoot, IRememberBeingDirty
+ {
+ ///
+ /// Gets or sets the server unique identity.
+ ///
+ string ServerIdentity { get; set; }
+
+ new string ServerAddress { get; set; }
+
+ ///
+ /// Gets or sets a value indicating whether the server is active.
+ ///
+ bool IsActive { get; set; }
+
+ ///
+ /// Gets or sets a value indicating whether the server is master.
+ ///
+ bool IsMaster { get; set; }
+
+ ///
+ /// Gets the date and time the registration was created.
+ ///
+ DateTime Registered { get; set; }
+
+ ///
+ /// Gets the date and time the registration was last accessed.
+ ///
+ DateTime Accessed { get; set; }
+ }
+}
\ No newline at end of file
diff --git a/src/Umbraco.Core/Models/Identity/BackOfficeIdentityUser.cs b/src/Umbraco.Core/Models/Identity/BackOfficeIdentityUser.cs
index 1523cf9040..0d803c26e5 100644
--- a/src/Umbraco.Core/Models/Identity/BackOfficeIdentityUser.cs
+++ b/src/Umbraco.Core/Models/Identity/BackOfficeIdentityUser.cs
@@ -12,6 +12,13 @@ namespace Umbraco.Core.Models.Identity
public class BackOfficeIdentityUser : IdentityUser, IdentityUserClaim>
{
+ public BackOfficeIdentityUser()
+ {
+ StartMediaId = -1;
+ StartContentId = -1;
+ Culture = Configuration.GlobalSettings.DefaultUILanguage;
+ }
+
public virtual async Task GenerateUserIdentityAsync(BackOfficeUserManager manager)
{
// NOTE the authenticationType must match the umbraco one
@@ -31,6 +38,30 @@ namespace Umbraco.Core.Models.Identity
public string UserTypeAlias { get; set; }
+ ///
+ /// Lockout is always enabled
+ ///
+ public override bool LockoutEnabled
+ {
+ get { return true; }
+ set
+ {
+ //do nothing
+ }
+ }
+
+ ///
+ /// Based on the user's lockout end date, this will determine if they are locked out
+ ///
+ internal bool IsLockedOut
+ {
+ get
+ {
+ var isLocked = (LockoutEndDateUtc.HasValue && LockoutEndDateUtc.Value.ToLocalTime() >= DateTime.Now);
+ return isLocked;
+ }
+ }
+
///
/// Overridden to make the retrieval lazy
///
diff --git a/src/Umbraco.Core/Models/Identity/IdentityModelMappings.cs b/src/Umbraco.Core/Models/Identity/IdentityModelMappings.cs
index def71a8982..0dc95a8987 100644
--- a/src/Umbraco.Core/Models/Identity/IdentityModelMappings.cs
+++ b/src/Umbraco.Core/Models/Identity/IdentityModelMappings.cs
@@ -4,6 +4,7 @@ using AutoMapper;
using Umbraco.Core.Models.Mapping;
using Umbraco.Core.Models.Membership;
+using Umbraco.Core.Security;
namespace Umbraco.Core.Models.Identity
{
@@ -14,8 +15,7 @@ namespace Umbraco.Core.Models.Identity
config.CreateMap()
.ForMember(user => user.Email, expression => expression.MapFrom(user => user.Email))
.ForMember(user => user.Id, expression => expression.MapFrom(user => user.Id))
- .ForMember(user => user.LockoutEnabled, expression => expression.MapFrom(user => user.IsLockedOut))
- .ForMember(user => user.LockoutEndDateUtc, expression => expression.UseValue(DateTime.MaxValue.ToUniversalTime()))
+ .ForMember(user => user.LockoutEndDateUtc, expression => expression.MapFrom(user => user.IsLockedOut ? DateTime.MaxValue.ToUniversalTime() : (DateTime?)null))
.ForMember(user => user.UserName, expression => expression.MapFrom(user => user.Username))
.ForMember(user => user.PasswordHash, expression => expression.MapFrom(user => GetPasswordHash(user.RawPasswordValue)))
.ForMember(user => user.Culture, expression => expression.MapFrom(user => user.GetUserCulture(applicationContext.Services.TextService)))
@@ -23,7 +23,20 @@ namespace Umbraco.Core.Models.Identity
.ForMember(user => user.StartMediaId, expression => expression.MapFrom(user => user.StartMediaId))
.ForMember(user => user.StartContentId, expression => expression.MapFrom(user => user.StartContentId))
.ForMember(user => user.UserTypeAlias, expression => expression.MapFrom(user => user.UserType.Alias))
+ .ForMember(user => user.AccessFailedCount, expression => expression.MapFrom(user => user.FailedPasswordAttempts))
.ForMember(user => user.AllowedSections, expression => expression.MapFrom(user => user.AllowedSections.ToArray()));
+
+ config.CreateMap()
+ .ConstructUsing((BackOfficeIdentityUser user) => new UserData(Guid.NewGuid().ToString("N"))) //this is the 'session id'
+ .ForMember(detail => detail.Id, opt => opt.MapFrom(user => user.Id))
+ .ForMember(detail => detail.AllowedApplications, opt => opt.MapFrom(user => user.AllowedSections))
+ .ForMember(detail => detail.RealName, opt => opt.MapFrom(user => user.Name))
+ .ForMember(detail => detail.Roles, opt => opt.MapFrom(user => new[] { user.UserTypeAlias }))
+ .ForMember(detail => detail.StartContentNode, opt => opt.MapFrom(user => user.StartContentId))
+ .ForMember(detail => detail.StartMediaNode, opt => opt.MapFrom(user => user.StartMediaId))
+ .ForMember(detail => detail.Username, opt => opt.MapFrom(user => user.UserName))
+ .ForMember(detail => detail.Culture, opt => opt.MapFrom(user => user.Culture))
+ .ForMember(detail => detail.SessionId, opt => opt.MapFrom(user => user.SecurityStamp.IsNullOrWhiteSpace() ? Guid.NewGuid().ToString("N") : user.SecurityStamp));
}
private string GetPasswordHash(string storedPass)
diff --git a/src/Umbraco.Core/Models/Language.cs b/src/Umbraco.Core/Models/Language.cs
index ef8ebbf931..b23bbfb52a 100644
--- a/src/Umbraco.Core/Models/Language.cs
+++ b/src/Umbraco.Core/Models/Language.cs
@@ -64,7 +64,7 @@ namespace Umbraco.Core.Models
[IgnoreDataMember]
public CultureInfo CultureInfo
{
- get { return CultureInfo.CreateSpecificCulture(IsoCode); }
+ get { return CultureInfo.GetCultureInfo(IsoCode); }
}
}
}
\ No newline at end of file
diff --git a/src/Umbraco.Core/Models/Macro.cs b/src/Umbraco.Core/Models/Macro.cs
index fcc23ed858..a15639f886 100644
--- a/src/Umbraco.Core/Models/Macro.cs
+++ b/src/Umbraco.Core/Models/Macro.cs
@@ -18,7 +18,7 @@ namespace Umbraco.Core.Models
///
[Serializable]
[DataContract(IsReference = true)]
- internal class Macro : Entity, IMacro
+ public class Macro : Entity, IMacro
{
public Macro()
{
diff --git a/src/Umbraco.Core/Models/Mapping/MappingExpressionExtensions.cs b/src/Umbraco.Core/Models/Mapping/MappingExpressionExtensions.cs
new file mode 100644
index 0000000000..570e51dbc3
--- /dev/null
+++ b/src/Umbraco.Core/Models/Mapping/MappingExpressionExtensions.cs
@@ -0,0 +1,20 @@
+using AutoMapper;
+
+namespace Umbraco.Core.Models.Mapping
+{
+ internal static class MappingExpressionExtensions
+ {
+ ///
+ /// Ignores all unmapped members by default - Use with caution!
+ ///
+ ///
+ ///
+ ///
+ ///
+ public static IMappingExpression IgnoreAllUnmapped(this IMappingExpression expression)
+ {
+ expression.ForAllMembers(opt => opt.Ignore());
+ return expression;
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Umbraco.Core/Models/Member.cs b/src/Umbraco.Core/Models/Member.cs
index d416c846ef..7788fadf75 100644
--- a/src/Umbraco.Core/Models/Member.cs
+++ b/src/Umbraco.Core/Models/Member.cs
@@ -280,7 +280,7 @@ namespace Umbraco.Core.Models
//This is the default value if the prop is not found
true);
if (a.Success == false) return a.Result;
-
+ if (Properties[Constants.Conventions.Member.IsApproved].Value == null) return true;
var tryConvert = Properties[Constants.Conventions.Member.IsApproved].Value.TryConvertTo();
if (tryConvert.Success)
{
@@ -313,7 +313,7 @@ namespace Umbraco.Core.Models
{
var a = WarnIfPropertyTypeNotFoundOnGet(Constants.Conventions.Member.IsLockedOut, "IsLockedOut", false);
if (a.Success == false) return a.Result;
-
+ if (Properties[Constants.Conventions.Member.IsLockedOut].Value == null) return false;
var tryConvert = Properties[Constants.Conventions.Member.IsLockedOut].Value.TryConvertTo();
if (tryConvert.Success)
{
@@ -346,7 +346,7 @@ namespace Umbraco.Core.Models
{
var a = WarnIfPropertyTypeNotFoundOnGet(Constants.Conventions.Member.LastLoginDate, "LastLoginDate", default(DateTime));
if (a.Success == false) return a.Result;
-
+ if (Properties[Constants.Conventions.Member.LastLoginDate].Value == null) return default(DateTime);
var tryConvert = Properties[Constants.Conventions.Member.LastLoginDate].Value.TryConvertTo();
if (tryConvert.Success)
{
@@ -379,7 +379,7 @@ namespace Umbraco.Core.Models
{
var a = WarnIfPropertyTypeNotFoundOnGet(Constants.Conventions.Member.LastPasswordChangeDate, "LastPasswordChangeDate", default(DateTime));
if (a.Success == false) return a.Result;
-
+ if (Properties[Constants.Conventions.Member.LastPasswordChangeDate].Value == null) return default(DateTime);
var tryConvert = Properties[Constants.Conventions.Member.LastPasswordChangeDate].Value.TryConvertTo();
if (tryConvert.Success)
{
@@ -412,7 +412,7 @@ namespace Umbraco.Core.Models
{
var a = WarnIfPropertyTypeNotFoundOnGet(Constants.Conventions.Member.LastLockoutDate, "LastLockoutDate", default(DateTime));
if (a.Success == false) return a.Result;
-
+ if (Properties[Constants.Conventions.Member.LastLockoutDate].Value == null) return default(DateTime);
var tryConvert = Properties[Constants.Conventions.Member.LastLockoutDate].Value.TryConvertTo();
if (tryConvert.Success)
{
@@ -446,7 +446,7 @@ namespace Umbraco.Core.Models
{
var a = WarnIfPropertyTypeNotFoundOnGet(Constants.Conventions.Member.FailedPasswordAttempts, "FailedPasswordAttempts", 0);
if (a.Success == false) return a.Result;
-
+ if (Properties[Constants.Conventions.Member.FailedPasswordAttempts].Value == null) return default(int);
var tryConvert = Properties[Constants.Conventions.Member.FailedPasswordAttempts].Value.TryConvertTo();
if (tryConvert.Success)
{
diff --git a/src/Umbraco.Core/Models/MigrationEntry.cs b/src/Umbraco.Core/Models/MigrationEntry.cs
new file mode 100644
index 0000000000..e756e92629
--- /dev/null
+++ b/src/Umbraco.Core/Models/MigrationEntry.cs
@@ -0,0 +1,53 @@
+using System;
+using System.Reflection;
+using Semver;
+using Umbraco.Core.Models.EntityBase;
+
+namespace Umbraco.Core.Models
+{
+ public class MigrationEntry : Entity, IMigrationEntry
+ {
+ public MigrationEntry()
+ {
+ }
+
+ public MigrationEntry(int id, DateTime createDate, string migrationName, SemVersion version)
+ {
+ Id = id;
+ CreateDate = createDate;
+ _migrationName = migrationName;
+ _version = version;
+ }
+
+ private static readonly PropertyInfo NameSelector = ExpressionHelper.GetPropertyInfo(x => x.MigrationName);
+ private static readonly PropertyInfo VersionSelector = ExpressionHelper.GetPropertyInfo(x => x.Version);
+ private string _migrationName;
+ private SemVersion _version;
+
+ public string MigrationName
+ {
+ get { return _migrationName; }
+ set
+ {
+ SetPropertyValueAndDetectChanges(o =>
+ {
+ _migrationName = value;
+ return _migrationName;
+ }, _migrationName, NameSelector);
+ }
+ }
+
+ public SemVersion Version
+ {
+ get { return _version; }
+ set
+ {
+ SetPropertyValueAndDetectChanges(o =>
+ {
+ _version = value;
+ return _version;
+ }, _version, VersionSelector);
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Umbraco.Core/Models/PartialView.cs b/src/Umbraco.Core/Models/PartialView.cs
index d483de4176..75914820f0 100644
--- a/src/Umbraco.Core/Models/PartialView.cs
+++ b/src/Umbraco.Core/Models/PartialView.cs
@@ -1,9 +1,6 @@
using System;
-using System.Collections.Generic;
-using System.Linq;
using System.Runtime.Serialization;
-using System.Text.RegularExpressions;
-using Umbraco.Core.IO;
+using Umbraco.Core.Services;
namespace Umbraco.Core.Models
{
@@ -15,12 +12,14 @@ namespace Umbraco.Core.Models
[DataContract(IsReference = true)]
public class PartialView : File, IPartialView
{
-
public PartialView(string path)
- : base(path)
- {
- base.Path = path;
- }
+ : this(path, null)
+ { }
+ internal PartialView(string path, Func getFileContent)
+ : base(path, getFileContent)
+ { }
+
+ internal PartialViewType ViewType { get; set; }
}
}
\ No newline at end of file
diff --git a/src/Umbraco.Core/Models/PartialViewType.cs b/src/Umbraco.Core/Models/PartialViewType.cs
new file mode 100644
index 0000000000..2b45448271
--- /dev/null
+++ b/src/Umbraco.Core/Models/PartialViewType.cs
@@ -0,0 +1,9 @@
+namespace Umbraco.Core.Models
+{
+ internal enum PartialViewType : byte
+ {
+ Unknown = 0, // default
+ PartialView = 1,
+ PartialViewMacro = 2
+ }
+}
diff --git a/src/Umbraco.Core/Models/Property.cs b/src/Umbraco.Core/Models/Property.cs
index b618f84341..d7c2eb92a8 100644
--- a/src/Umbraco.Core/Models/Property.cs
+++ b/src/Umbraco.Core/Models/Property.cs
@@ -137,9 +137,23 @@ namespace Umbraco.Core.Models
new DelegateEqualityComparer(
(o, o1) =>
{
- //Custom comparer for enumerable if it is enumerable
if (o == null && o1 == null) return true;
+
+ //custom comparer for strings.
+ if (o is string || o1 is string)
+ {
+ //if one is null and another is empty then they are the same
+ if ((o as string).IsNullOrWhiteSpace() && (o1 as string).IsNullOrWhiteSpace())
+ {
+ return true;
+ }
+ if (o == null || o1 == null) return false;
+ return o.Equals(o1);
+ }
+
if (o == null || o1 == null) return false;
+
+ //Custom comparer for enumerable if it is enumerable
var enum1 = o as IEnumerable;
var enum2 = o1 as IEnumerable;
if (enum1 != null && enum2 != null)
diff --git a/src/Umbraco.Core/Models/PropertyGroup.cs b/src/Umbraco.Core/Models/PropertyGroup.cs
index 94e46e1ed1..de88012c0e 100644
--- a/src/Umbraco.Core/Models/PropertyGroup.cs
+++ b/src/Umbraco.Core/Models/PropertyGroup.cs
@@ -1,6 +1,7 @@
using System;
using System.Collections.Specialized;
using System.Diagnostics;
+using System.Linq;
using System.Reflection;
using System.Runtime.Serialization;
using Umbraco.Core.Models.EntityBase;
@@ -16,7 +17,6 @@ namespace Umbraco.Core.Models
public class PropertyGroup : Entity, IEquatable
{
private string _name;
- private Lazy _parentId;
private int _sortOrder;
private PropertyTypeCollection _propertyTypes;
@@ -30,7 +30,6 @@ namespace Umbraco.Core.Models
}
private static readonly PropertyInfo NameSelector = ExpressionHelper.GetPropertyInfo(x => x.Name);
- private static readonly PropertyInfo ParentIdSelector = ExpressionHelper.GetPropertyInfo(x => x.ParentId);
private static readonly PropertyInfo SortOrderSelector = ExpressionHelper.GetPropertyInfo(x => x.SortOrder);
private readonly static PropertyInfo PropertyTypeCollectionSelector = ExpressionHelper.GetPropertyInfo(x => x.PropertyTypes);
void PropertyTypesChanged(object sender, NotifyCollectionChangedEventArgs e)
@@ -55,29 +54,6 @@ namespace Umbraco.Core.Models
}
}
- ///
- /// Gets or sets the Id of the Parent PropertyGroup.
- ///
- ///
- /// A Parent PropertyGroup corresponds to an inherited PropertyGroup from a composition.
- /// If a PropertyType is inserted into an inherited group then a new group will be created with an Id reference to the parent.
- ///
- [DataMember]
- public int? ParentId
- {
- get
- {
- if (_parentId == null)
- return default(int?);
- return _parentId.Value;
- }
- set
- {
- _parentId = new Lazy(() => value);
- OnPropertyChanged(ParentIdSelector);
- }
- }
-
///
/// Gets or sets the Sort Order of the Group
///
@@ -105,19 +81,17 @@ namespace Umbraco.Core.Models
set
{
_propertyTypes = value;
+
+ //since we're adding this collection to this group, we need to ensure that all the lazy values are set.
+ foreach (var propertyType in _propertyTypes)
+ {
+ propertyType.PropertyGroupId = new Lazy(() => this.Id);
+ }
+
_propertyTypes.CollectionChanged += PropertyTypesChanged;
}
}
- ///
- /// Sets the ParentId from the lazy integer id
- ///
- /// Id of the Parent
- internal void SetLazyParentId(Lazy id)
- {
- _parentId = id;
- }
-
public bool Equals(PropertyGroup other)
{
if (base.Equals(other)) return true;
diff --git a/src/Umbraco.Core/Models/PropertyType.cs b/src/Umbraco.Core/Models/PropertyType.cs
index e9d4451314..7f22b65c8c 100644
--- a/src/Umbraco.Core/Models/PropertyType.cs
+++ b/src/Umbraco.Core/Models/PropertyType.cs
@@ -9,6 +9,7 @@ using Umbraco.Core.Strings;
namespace Umbraco.Core.Models
{
+
///
/// Defines the type of a object
///
@@ -32,6 +33,8 @@ namespace Umbraco.Core.Models
public PropertyType(IDataTypeDefinition dataTypeDefinition)
{
+ if (dataTypeDefinition == null) throw new ArgumentNullException("dataTypeDefinition");
+
if(dataTypeDefinition.HasIdentity)
_dataTypeDefinitionId = dataTypeDefinition.Id;
@@ -401,6 +404,9 @@ namespace Umbraco.Core.Models
if (DataTypeDatabaseType == DataTypeDatabaseType.Integer && type == typeof(int))
return true;
+ if (DataTypeDatabaseType == DataTypeDatabaseType.Decimal && type == typeof(decimal))
+ return true;
+
if (DataTypeDatabaseType == DataTypeDatabaseType.Date && type == typeof(DateTime))
return true;
diff --git a/src/Umbraco.Core/Models/PublishedContent/PublishedContentExtended.cs b/src/Umbraco.Core/Models/PublishedContent/PublishedContentExtended.cs
index 8c0eeef86f..fef066e0b1 100644
--- a/src/Umbraco.Core/Models/PublishedContent/PublishedContentExtended.cs
+++ b/src/Umbraco.Core/Models/PublishedContent/PublishedContentExtended.cs
@@ -62,9 +62,20 @@ namespace Umbraco.Core.Models.PublishedContent
// model and therefore returned the original content unchanged.
var model = content.CreateModel();
- var extended = model == content // == means the factory did not create a model
- ? new PublishedContentExtended(content) // so we have to extend
- : model; // else we can use what the factory returned
+ IPublishedContent extended;
+ if (model == content) // == means the factory did not create a model
+ {
+ // so we have to extend
+ var contentWithKey = content as IPublishedContentWithKey;
+ extended = contentWithKey == null
+ ? new PublishedContentExtended(content)
+ : new PublishedContentWithKeyExtended(contentWithKey);
+ }
+ else
+ {
+ // else we can use what the factory returned
+ extended = model;
+ }
// so extended should always implement IPublishedContentExtended, however if
// by mistake the factory returned a different object that does not implement
diff --git a/src/Umbraco.Core/Models/PublishedContent/PublishedContentWithKeyExtended.cs b/src/Umbraco.Core/Models/PublishedContent/PublishedContentWithKeyExtended.cs
new file mode 100644
index 0000000000..492fd79796
--- /dev/null
+++ b/src/Umbraco.Core/Models/PublishedContent/PublishedContentWithKeyExtended.cs
@@ -0,0 +1,14 @@
+using System;
+
+namespace Umbraco.Core.Models.PublishedContent
+{
+ public class PublishedContentWithKeyExtended : PublishedContentExtended, IPublishedContentWithKey
+ {
+ // protected for models, internal for PublishedContentExtended static Extend method
+ protected internal PublishedContentWithKeyExtended(IPublishedContentWithKey content)
+ : base(content)
+ { }
+
+ public Guid Key { get { return ((IPublishedContentWithKey) Content).Key; } }
+ }
+}
diff --git a/src/Umbraco.Core/Models/PublishedContent/PublishedContentWithKeyModel.cs b/src/Umbraco.Core/Models/PublishedContent/PublishedContentWithKeyModel.cs
new file mode 100644
index 0000000000..4761a52617
--- /dev/null
+++ b/src/Umbraco.Core/Models/PublishedContent/PublishedContentWithKeyModel.cs
@@ -0,0 +1,13 @@
+using System;
+
+namespace Umbraco.Core.Models.PublishedContent
+{
+ public abstract class PublishedContentWithKeyModel : PublishedContentModel, IPublishedContentWithKey
+ {
+ protected PublishedContentWithKeyModel(IPublishedContentWithKey content)
+ : base (content)
+ { }
+
+ public Guid Key { get { return ((IPublishedContentWithKey) Content).Key; } }
+ }
+}
diff --git a/src/Umbraco.Core/Models/PublishedContent/PublishedContentWithKeyWrapped.cs b/src/Umbraco.Core/Models/PublishedContent/PublishedContentWithKeyWrapped.cs
new file mode 100644
index 0000000000..35d7dd6f1f
--- /dev/null
+++ b/src/Umbraco.Core/Models/PublishedContent/PublishedContentWithKeyWrapped.cs
@@ -0,0 +1,17 @@
+using System;
+
+namespace Umbraco.Core.Models.PublishedContent
+{
+ ///
+ /// Provides an abstract base class for IPublishedContentWithKey implementations that
+ /// wrap and extend another IPublishedContentWithKey.
+ ///
+ public class PublishedContentWithKeyWrapped : PublishedContentWrapped, IPublishedContentWithKey
+ {
+ protected PublishedContentWithKeyWrapped(IPublishedContentWithKey content)
+ : base(content)
+ { }
+
+ public virtual Guid Key { get { return ((IPublishedContentWithKey) Content).Key; } }
+ }
+}
diff --git a/src/Umbraco.Core/Models/Rdbms/AccessDto.cs b/src/Umbraco.Core/Models/Rdbms/AccessDto.cs
index ba2cb6d767..37b1dbddd8 100644
--- a/src/Umbraco.Core/Models/Rdbms/AccessDto.cs
+++ b/src/Umbraco.Core/Models/Rdbms/AccessDto.cs
@@ -4,6 +4,7 @@ using System.Collections.Generic;
using System.Security.AccessControl;
using Umbraco.Core.Persistence;
using Umbraco.Core.Persistence.DatabaseAnnotations;
+using Umbraco.Core.Persistence.DatabaseModelDefinitions;
namespace Umbraco.Core.Models.Rdbms
{
@@ -30,11 +31,11 @@ namespace Umbraco.Core.Models.Rdbms
public int NoAccessNodeId { get; set; }
[Column("createDate")]
- [Constraint(Default = "getdate()")]
+ [Constraint(Default = SystemMethods.CurrentDateTime)]
public DateTime CreateDate { get; set; }
[Column("updateDate")]
- [Constraint(Default = "getdate()")]
+ [Constraint(Default = SystemMethods.CurrentDateTime)]
public DateTime UpdateDate { get; set; }
[ResultColumn]
diff --git a/src/Umbraco.Core/Models/Rdbms/AccessRuleDto.cs b/src/Umbraco.Core/Models/Rdbms/AccessRuleDto.cs
index a98dfb9450..78e3444e56 100644
--- a/src/Umbraco.Core/Models/Rdbms/AccessRuleDto.cs
+++ b/src/Umbraco.Core/Models/Rdbms/AccessRuleDto.cs
@@ -1,6 +1,7 @@
using System;
using Umbraco.Core.Persistence;
using Umbraco.Core.Persistence.DatabaseAnnotations;
+using Umbraco.Core.Persistence.DatabaseModelDefinitions;
namespace Umbraco.Core.Models.Rdbms
{
@@ -25,11 +26,11 @@ namespace Umbraco.Core.Models.Rdbms
public string RuleType { get; set; }
[Column("createDate")]
- [Constraint(Default = "getdate()")]
+ [Constraint(Default = SystemMethods.CurrentDateTime)]
public DateTime CreateDate { get; set; }
[Column("updateDate")]
- [Constraint(Default = "getdate()")]
+ [Constraint(Default = SystemMethods.CurrentDateTime)]
public DateTime UpdateDate { get; set; }
}
}
\ No newline at end of file
diff --git a/src/Umbraco.Core/Models/Rdbms/DocumentTypeDto.cs b/src/Umbraco.Core/Models/Rdbms/ContentTypeTemplateDto.cs
similarity index 93%
rename from src/Umbraco.Core/Models/Rdbms/DocumentTypeDto.cs
rename to src/Umbraco.Core/Models/Rdbms/ContentTypeTemplateDto.cs
index e3fdfb9810..88ef02ea90 100644
--- a/src/Umbraco.Core/Models/Rdbms/DocumentTypeDto.cs
+++ b/src/Umbraco.Core/Models/Rdbms/ContentTypeTemplateDto.cs
@@ -1,28 +1,28 @@
-using Umbraco.Core.Persistence;
-using Umbraco.Core.Persistence.DatabaseAnnotations;
-
-namespace Umbraco.Core.Models.Rdbms
-{
- [TableName("cmsDocumentType")]
- [PrimaryKey("contentTypeNodeId", autoIncrement = false)]
- [ExplicitColumns]
- internal class DocumentTypeDto
- {
- [Column("contentTypeNodeId")]
- [PrimaryKeyColumn(AutoIncrement = false, Name = "PK_cmsDocumentType", OnColumns = "contentTypeNodeId, templateNodeId")]
- [ForeignKey(typeof(ContentTypeDto), Column = "nodeId")]
- [ForeignKey(typeof(NodeDto))]
- public int ContentTypeNodeId { get; set; }
-
- [Column("templateNodeId")]
- [ForeignKey(typeof(TemplateDto), Column = "nodeId")]
- public int TemplateNodeId { get; set; }
-
- [Column("IsDefault")]
- [Constraint(Default = "0")]
- public bool IsDefault { get; set; }
-
- [ResultColumn]
- public ContentTypeDto ContentTypeDto { get; set; }
- }
+using Umbraco.Core.Persistence;
+using Umbraco.Core.Persistence.DatabaseAnnotations;
+
+namespace Umbraco.Core.Models.Rdbms
+{
+ [TableName("cmsDocumentType")]
+ [PrimaryKey("contentTypeNodeId", autoIncrement = false)]
+ [ExplicitColumns]
+ internal class ContentTypeTemplateDto
+ {
+ [Column("contentTypeNodeId")]
+ [PrimaryKeyColumn(AutoIncrement = false, Name = "PK_cmsDocumentType", OnColumns = "contentTypeNodeId, templateNodeId")]
+ [ForeignKey(typeof(ContentTypeDto), Column = "nodeId")]
+ [ForeignKey(typeof(NodeDto))]
+ public int ContentTypeNodeId { get; set; }
+
+ [Column("templateNodeId")]
+ [ForeignKey(typeof(TemplateDto), Column = "nodeId")]
+ public int TemplateNodeId { get; set; }
+
+ [Column("IsDefault")]
+ [Constraint(Default = "0")]
+ public bool IsDefault { get; set; }
+
+ [ResultColumn]
+ public ContentTypeDto ContentTypeDto { get; set; }
+ }
}
\ No newline at end of file
diff --git a/src/Umbraco.Core/Models/Rdbms/ContentVersionDto.cs b/src/Umbraco.Core/Models/Rdbms/ContentVersionDto.cs
index 9b1fa02dae..3bd85cccf6 100644
--- a/src/Umbraco.Core/Models/Rdbms/ContentVersionDto.cs
+++ b/src/Umbraco.Core/Models/Rdbms/ContentVersionDto.cs
@@ -1,6 +1,7 @@
using System;
using Umbraco.Core.Persistence;
using Umbraco.Core.Persistence.DatabaseAnnotations;
+using Umbraco.Core.Persistence.DatabaseModelDefinitions;
namespace Umbraco.Core.Models.Rdbms
{
@@ -23,7 +24,7 @@ namespace Umbraco.Core.Models.Rdbms
public Guid VersionId { get; set; }
[Column("VersionDate")]
- [Constraint(Default = "getdate()")]
+ [Constraint(Default = SystemMethods.CurrentDateTime)]
public DateTime VersionDate { get; set; }
[ResultColumn]
diff --git a/src/Umbraco.Core/Models/Rdbms/DictionaryDto.cs b/src/Umbraco.Core/Models/Rdbms/DictionaryDto.cs
index 18fd16b658..712d1937a9 100644
--- a/src/Umbraco.Core/Models/Rdbms/DictionaryDto.cs
+++ b/src/Umbraco.Core/Models/Rdbms/DictionaryDto.cs
@@ -19,7 +19,9 @@ namespace Umbraco.Core.Models.Rdbms
public Guid UniqueId { get; set; }
[Column("parent")]
- public Guid Parent { get; set; }
+ [NullSetting(NullSetting = NullSettings.Null)]
+ [ForeignKey(typeof(DictionaryDto), Column = "id")]
+ public Guid? Parent { get; set; }
[Column("key")]
[Length(1000)]
diff --git a/src/Umbraco.Core/Models/Rdbms/DocumentDto.cs b/src/Umbraco.Core/Models/Rdbms/DocumentDto.cs
index de087667b2..88e04087c9 100644
--- a/src/Umbraco.Core/Models/Rdbms/DocumentDto.cs
+++ b/src/Umbraco.Core/Models/Rdbms/DocumentDto.cs
@@ -1,6 +1,7 @@
using System;
using Umbraco.Core.Persistence;
using Umbraco.Core.Persistence.DatabaseAnnotations;
+using Umbraco.Core.Persistence.DatabaseModelDefinitions;
namespace Umbraco.Core.Models.Rdbms
{
@@ -38,7 +39,7 @@ namespace Umbraco.Core.Models.Rdbms
public DateTime? ExpiresDate { get; set; }
[Column("updateDate")]
- [Constraint(Default = "getdate()")]
+ [Constraint(Default = SystemMethods.CurrentDateTime)]
public DateTime UpdateDate { get; set; }
[Column("templateId")]
diff --git a/src/Umbraco.Core/Models/Rdbms/DomainDto.cs b/src/Umbraco.Core/Models/Rdbms/DomainDto.cs
index bd0bbc859c..e43c1bdeae 100644
--- a/src/Umbraco.Core/Models/Rdbms/DomainDto.cs
+++ b/src/Umbraco.Core/Models/Rdbms/DomainDto.cs
@@ -23,5 +23,11 @@ namespace Umbraco.Core.Models.Rdbms
[Column("domainName")]
public string DomainName { get; set; }
+
+ ///
+ /// Used for a result on the query to get the associated language for a domain if there is one
+ ///
+ [ResultColumn("languageISOCode")]
+ public string IsoCode { get; set; }
}
}
\ No newline at end of file
diff --git a/src/Umbraco.Core/Models/Rdbms/ExternalLoginDto.cs b/src/Umbraco.Core/Models/Rdbms/ExternalLoginDto.cs
index 652c9df714..803c25fdfc 100644
--- a/src/Umbraco.Core/Models/Rdbms/ExternalLoginDto.cs
+++ b/src/Umbraco.Core/Models/Rdbms/ExternalLoginDto.cs
@@ -1,6 +1,7 @@
using System;
using Umbraco.Core.Persistence;
using Umbraco.Core.Persistence.DatabaseAnnotations;
+using Umbraco.Core.Persistence.DatabaseModelDefinitions;
namespace Umbraco.Core.Models.Rdbms
{
@@ -27,7 +28,7 @@ namespace Umbraco.Core.Models.Rdbms
public string ProviderKey { get; set; }
[Column("createDate")]
- [Constraint(Default = "getdate()")]
+ [Constraint(Default = SystemMethods.CurrentDateTime)]
public DateTime CreateDate { get; set; }
diff --git a/src/Umbraco.Core/Models/Rdbms/LanguageTextDto.cs b/src/Umbraco.Core/Models/Rdbms/LanguageTextDto.cs
index d062df4eae..87329fbd4c 100644
--- a/src/Umbraco.Core/Models/Rdbms/LanguageTextDto.cs
+++ b/src/Umbraco.Core/Models/Rdbms/LanguageTextDto.cs
@@ -14,6 +14,7 @@ namespace Umbraco.Core.Models.Rdbms
public int PrimaryKey { get; set; }
[Column("languageId")]
+ [ForeignKey(typeof(LanguageDto), Column = "id")]
public int LanguageId { get; set; }
[Column("UniqueId")]
diff --git a/src/Umbraco.Core/Models/Rdbms/LogDto.cs b/src/Umbraco.Core/Models/Rdbms/LogDto.cs
index 3c2a6a61a5..be67b5873a 100644
--- a/src/Umbraco.Core/Models/Rdbms/LogDto.cs
+++ b/src/Umbraco.Core/Models/Rdbms/LogDto.cs
@@ -1,6 +1,7 @@
using System;
using Umbraco.Core.Persistence;
using Umbraco.Core.Persistence.DatabaseAnnotations;
+using Umbraco.Core.Persistence.DatabaseModelDefinitions;
namespace Umbraco.Core.Models.Rdbms
{
@@ -21,7 +22,7 @@ namespace Umbraco.Core.Models.Rdbms
public int NodeId { get; set; }
[Column("Datestamp")]
- [Constraint(Default = "getdate()")]
+ [Constraint(Default = SystemMethods.CurrentDateTime)]
public DateTime Datestamp { get; set; }
[Column("logHeader")]
diff --git a/src/Umbraco.Core/Models/Rdbms/MigrationDto.cs b/src/Umbraco.Core/Models/Rdbms/MigrationDto.cs
new file mode 100644
index 0000000000..1a76895e90
--- /dev/null
+++ b/src/Umbraco.Core/Models/Rdbms/MigrationDto.cs
@@ -0,0 +1,30 @@
+using System;
+using Umbraco.Core.Persistence;
+using Umbraco.Core.Persistence.DatabaseAnnotations;
+using Umbraco.Core.Persistence.DatabaseModelDefinitions;
+
+namespace Umbraco.Core.Models.Rdbms
+{
+ [TableName("umbracoMigration")]
+ [PrimaryKey("id")]
+ [ExplicitColumns]
+ internal class MigrationDto
+ {
+ [Column("id")]
+ [PrimaryKeyColumn(AutoIncrement = true, IdentitySeed = 100)]
+ public int Id { get; set; }
+
+ [Column("name")]
+ [Length(255)]
+ [Index(IndexTypes.UniqueNonClustered, ForColumns = "name,version", Name = "IX_umbracoMigration")]
+ public string Name { get; set; }
+
+ [Column("createDate")]
+ [Constraint(Default = SystemMethods.CurrentDateTime)]
+ public DateTime CreateDate { get; set; }
+
+ [Column("version")]
+ [Length(50)]
+ public string Version { get; set; }
+ }
+}
\ No newline at end of file
diff --git a/src/Umbraco.Core/Models/Rdbms/NodeDto.cs b/src/Umbraco.Core/Models/Rdbms/NodeDto.cs
index 33c1a73059..7003c58e77 100644
--- a/src/Umbraco.Core/Models/Rdbms/NodeDto.cs
+++ b/src/Umbraco.Core/Models/Rdbms/NodeDto.cs
@@ -1,6 +1,7 @@
using System;
using Umbraco.Core.Persistence;
using Umbraco.Core.Persistence.DatabaseAnnotations;
+using Umbraco.Core.Persistence.DatabaseModelDefinitions;
namespace Umbraco.Core.Models.Rdbms
{
@@ -9,6 +10,12 @@ namespace Umbraco.Core.Models.Rdbms
[ExplicitColumns]
internal class NodeDto
{
+ public NodeDto()
+ {
+ //By default, always generate a new guid
+ UniqueId = Guid.NewGuid();
+ }
+
public const int NodeIdSeed = 1050;
[Column("id")]
@@ -42,6 +49,7 @@ namespace Umbraco.Core.Models.Rdbms
[Column("uniqueID")]
[NullSetting(NullSetting = NullSettings.NotNull)]
[Index(IndexTypes.UniqueNonClustered, Name = "IX_umbracoNodeUniqueID")]
+ [Constraint(Default = SystemMethods.NewGuid)]
public Guid UniqueId { get; set; }
[Column("text")]
@@ -54,7 +62,7 @@ namespace Umbraco.Core.Models.Rdbms
public Guid? NodeObjectType { get; set; }
[Column("createDate")]
- [Constraint(Default = "getdate()")]
+ [Constraint(Default = SystemMethods.CurrentDateTime)]
public DateTime CreateDate { get; set; }
}
}
\ No newline at end of file
diff --git a/src/Umbraco.Core/Models/Rdbms/PropertyDataDto.cs b/src/Umbraco.Core/Models/Rdbms/PropertyDataDto.cs
index 4e59f0275b..63e3104d12 100644
--- a/src/Umbraco.Core/Models/Rdbms/PropertyDataDto.cs
+++ b/src/Umbraco.Core/Models/Rdbms/PropertyDataDto.cs
@@ -33,6 +33,10 @@ namespace Umbraco.Core.Models.Rdbms
[NullSetting(NullSetting = NullSettings.Null)]
public int? Integer { get; set; }
+ [Column("dataDecimal")]
+ [NullSetting(NullSetting = NullSettings.Null)]
+ public decimal? Decimal { get; set; }
+
[Column("dataDate")]
[NullSetting(NullSetting = NullSettings.Null)]
public DateTime? Date { get; set; }
@@ -55,22 +59,27 @@ namespace Umbraco.Core.Models.Rdbms
{
get
{
- if(Integer.HasValue)
+ if (Integer.HasValue)
{
return Integer.Value;
}
+
+ if (Decimal.HasValue)
+ {
+ return Decimal.Value;
+ }
- if(Date.HasValue)
+ if (Date.HasValue)
{
return Date.Value;
}
- if(string.IsNullOrEmpty(VarChar) == false)
+ if (string.IsNullOrEmpty(VarChar) == false)
{
return VarChar;
}
- if(string.IsNullOrEmpty(Text) == false)
+ if (string.IsNullOrEmpty(Text) == false)
{
return Text;
}
diff --git a/src/Umbraco.Core/Models/Rdbms/PropertyTypeDto.cs b/src/Umbraco.Core/Models/Rdbms/PropertyTypeDto.cs
index f2ca705e8b..74a6d34289 100644
--- a/src/Umbraco.Core/Models/Rdbms/PropertyTypeDto.cs
+++ b/src/Umbraco.Core/Models/Rdbms/PropertyTypeDto.cs
@@ -1,5 +1,7 @@
-using Umbraco.Core.Persistence;
+using System;
+using Umbraco.Core.Persistence;
using Umbraco.Core.Persistence.DatabaseAnnotations;
+using Umbraco.Core.Persistence.DatabaseModelDefinitions;
namespace Umbraco.Core.Models.Rdbms
{
@@ -8,6 +10,12 @@ namespace Umbraco.Core.Models.Rdbms
[ExplicitColumns]
internal class PropertyTypeDto
{
+ public PropertyTypeDto()
+ {
+ //by default always create a new guid
+ UniqueId = Guid.NewGuid();
+ }
+
[Column("id")]
[PrimaryKeyColumn(IdentitySeed = 50)]
public int Id { get; set; }
@@ -51,5 +59,11 @@ namespace Umbraco.Core.Models.Rdbms
[ResultColumn]
public DataTypeDto DataTypeDto { get; set; }
+
+ [Column("UniqueID")]
+ [NullSetting(NullSetting = NullSettings.NotNull)]
+ [Constraint(Default = SystemMethods.NewGuid)]
+ [Index(IndexTypes.UniqueNonClustered, Name = "IX_cmsPropertyTypeUniqueID")]
+ public Guid UniqueId { get; set; }
}
}
\ No newline at end of file
diff --git a/src/Umbraco.Core/Models/Rdbms/PropertyTypeGroupDto.cs b/src/Umbraco.Core/Models/Rdbms/PropertyTypeGroupDto.cs
index 51cb9872fe..42abd9ed49 100644
--- a/src/Umbraco.Core/Models/Rdbms/PropertyTypeGroupDto.cs
+++ b/src/Umbraco.Core/Models/Rdbms/PropertyTypeGroupDto.cs
@@ -1,6 +1,8 @@
-using System.Collections.Generic;
+using System;
+using System.Collections.Generic;
using Umbraco.Core.Persistence;
using Umbraco.Core.Persistence.DatabaseAnnotations;
+using Umbraco.Core.Persistence.DatabaseModelDefinitions;
namespace Umbraco.Core.Models.Rdbms
{
@@ -9,16 +11,16 @@ namespace Umbraco.Core.Models.Rdbms
[ExplicitColumns]
internal class PropertyTypeGroupDto
{
+ public PropertyTypeGroupDto()
+ {
+ //by default always create a new guid
+ UniqueId = Guid.NewGuid();
+ }
+
[Column("id")]
[PrimaryKeyColumn(IdentitySeed = 12)]
public int Id { get; set; }
- [Column("parentGroupId")]
- [NullSetting(NullSetting = NullSettings.Null)]
- //[Constraint(Default = "NULL")]
- [ForeignKey(typeof(PropertyTypeGroupDto))]
- public int? ParentGroupId { get; set; }
-
[Column("contenttypeNodeId")]
[ForeignKey(typeof(ContentTypeDto), Column = "nodeId")]
public int ContentTypeNodeId { get; set; }
@@ -31,5 +33,11 @@ namespace Umbraco.Core.Models.Rdbms
[ResultColumn]
public List PropertyTypeDtos { get; set; }
+
+ [Column("uniqueID")]
+ [NullSetting(NullSetting = NullSettings.NotNull)]
+ [Constraint(Default = SystemMethods.NewGuid)]
+ [Index(IndexTypes.UniqueNonClustered, Name = "IX_cmsPropertyTypeGroupUniqueID")]
+ public Guid UniqueId { get; set; }
}
}
\ No newline at end of file
diff --git a/src/Umbraco.Core/Models/Rdbms/PropertyTypeGroupReadOnlyDto.cs b/src/Umbraco.Core/Models/Rdbms/PropertyTypeGroupReadOnlyDto.cs
index 8dcc4af29c..beebef9eeb 100644
--- a/src/Umbraco.Core/Models/Rdbms/PropertyTypeGroupReadOnlyDto.cs
+++ b/src/Umbraco.Core/Models/Rdbms/PropertyTypeGroupReadOnlyDto.cs
@@ -10,9 +10,6 @@ namespace Umbraco.Core.Models.Rdbms
[Column("PropertyTypeGroupId")]
public int? Id { get; set; }
- [Column("parentGroupId")]
- public int? ParentGroupId { get; set; }
-
[Column("PropertyGroupName")]
public string Text { get; set; }
diff --git a/src/Umbraco.Core/Models/Rdbms/RelationDto.cs b/src/Umbraco.Core/Models/Rdbms/RelationDto.cs
index e204e42040..368904a5cb 100644
--- a/src/Umbraco.Core/Models/Rdbms/RelationDto.cs
+++ b/src/Umbraco.Core/Models/Rdbms/RelationDto.cs
@@ -1,6 +1,7 @@
using System;
using Umbraco.Core.Persistence;
using Umbraco.Core.Persistence.DatabaseAnnotations;
+using Umbraco.Core.Persistence.DatabaseModelDefinitions;
namespace Umbraco.Core.Models.Rdbms
{
@@ -26,7 +27,7 @@ namespace Umbraco.Core.Models.Rdbms
public int RelationType { get; set; }
[Column("datetime")]
- [Constraint(Default = "getdate()")]
+ [Constraint(Default = SystemMethods.CurrentDateTime)]
public DateTime Datetime { get; set; }
[Column("comment")]
diff --git a/src/Umbraco.Core/Models/Rdbms/ServerRegistrationDto.cs b/src/Umbraco.Core/Models/Rdbms/ServerRegistrationDto.cs
index b7bdf265ce..2a3751c083 100644
--- a/src/Umbraco.Core/Models/Rdbms/ServerRegistrationDto.cs
+++ b/src/Umbraco.Core/Models/Rdbms/ServerRegistrationDto.cs
@@ -1,6 +1,7 @@
using System;
using Umbraco.Core.Persistence;
using Umbraco.Core.Persistence.DatabaseAnnotations;
+using Umbraco.Core.Persistence.DatabaseModelDefinitions;
namespace Umbraco.Core.Models.Rdbms
{
@@ -23,7 +24,7 @@ namespace Umbraco.Core.Models.Rdbms
public string ServerIdentity { get; set; }
[Column("registeredDate")]
- [Constraint(Default = "getdate()")]
+ [Constraint(Default = SystemMethods.CurrentDateTime)]
public DateTime DateRegistered { get; set; }
[Column("lastNotifiedDate")]
@@ -33,6 +34,7 @@ namespace Umbraco.Core.Models.Rdbms
[Index(IndexTypes.NonClustered)]
public bool IsActive { get; set; }
-
+ [Column("isMaster")]
+ public bool IsMaster { get; set; }
}
}
\ No newline at end of file
diff --git a/src/Umbraco.Core/Models/Rdbms/TaskDto.cs b/src/Umbraco.Core/Models/Rdbms/TaskDto.cs
index 75e4d25cb7..e27f7c0a93 100644
--- a/src/Umbraco.Core/Models/Rdbms/TaskDto.cs
+++ b/src/Umbraco.Core/Models/Rdbms/TaskDto.cs
@@ -1,6 +1,7 @@
using System;
using Umbraco.Core.Persistence;
using Umbraco.Core.Persistence.DatabaseAnnotations;
+using Umbraco.Core.Persistence.DatabaseModelDefinitions;
namespace Umbraco.Core.Models.Rdbms
{
@@ -34,7 +35,7 @@ namespace Umbraco.Core.Models.Rdbms
public int UserId { get; set; }
[Column("DateTime")]
- [Constraint(Default = "getdate()")]
+ [Constraint(Default = SystemMethods.CurrentDateTime)]
public DateTime DateTime { get; set; }
[Column("Comment")]
diff --git a/src/Umbraco.Core/Models/Rdbms/UmbracoDeployChecksumDto.cs b/src/Umbraco.Core/Models/Rdbms/UmbracoDeployChecksumDto.cs
new file mode 100644
index 0000000000..06d904ee88
--- /dev/null
+++ b/src/Umbraco.Core/Models/Rdbms/UmbracoDeployChecksumDto.cs
@@ -0,0 +1,43 @@
+using System;
+using System.Collections.Generic;
+using Umbraco.Core.Persistence;
+using Umbraco.Core.Persistence.DatabaseAnnotations;
+using Umbraco.Core.Persistence.DatabaseModelDefinitions;
+
+namespace Umbraco.Core.Models.Rdbms
+{
+ [TableName("umbracoDeployChecksum")]
+ [PrimaryKey("id")]
+ [ExplicitColumns]
+ internal class UmbracoDeployChecksumDto
+ {
+ [Column("id")]
+ [PrimaryKeyColumn(Name = "PK_umbracoDeployChecksum")]
+ public int Id { get; set; }
+
+ [Column("entityType")]
+ [Length(32)]
+ [NullSetting(NullSetting = NullSettings.NotNull)]
+ [Index(IndexTypes.UniqueNonClustered, Name = "IX_umbracoDeployChecksum", ForColumns = "entityType,entityGuid,entityPath")]
+ public string EntityType { get; set; }
+
+ [Column("entityGuid")]
+ [NullSetting(NullSetting = NullSettings.Null)]
+ public Guid EntityGuid { get; set; }
+
+ [Column("entityPath")]
+ [Length(256)]
+ [NullSetting(NullSetting = NullSettings.Null)]
+ public string EntityPath { get; set; }
+
+ [Column("localChecksum")]
+ [NullSetting(NullSetting = NullSettings.NotNull)]
+ [Length(32)]
+ public string LocalChecksum { get; set; }
+
+ [Column("compositeChecksum")]
+ [NullSetting(NullSetting = NullSettings.Null)]
+ [Length(32)]
+ public string CompositeChecksum { get; set; }
+ }
+}
\ No newline at end of file
diff --git a/src/Umbraco.Core/Models/Rdbms/UmbracoDeployDependencyDto.cs b/src/Umbraco.Core/Models/Rdbms/UmbracoDeployDependencyDto.cs
new file mode 100644
index 0000000000..765a32c929
--- /dev/null
+++ b/src/Umbraco.Core/Models/Rdbms/UmbracoDeployDependencyDto.cs
@@ -0,0 +1,26 @@
+using Umbraco.Core.Persistence;
+using Umbraco.Core.Persistence.DatabaseAnnotations;
+
+namespace Umbraco.Core.Models.Rdbms
+{
+ [TableName("umbracoDeployDependency")]
+ [ExplicitColumns]
+ internal class UmbracoDeployDependencyDto
+ {
+ [Column("sourceId")]
+ [PrimaryKeyColumn(AutoIncrement = false, Clustered = true, Name = "PK_umbracoDeployDependency", OnColumns = "sourceId, targetId")]
+ [ForeignKey(typeof(UmbracoDeployChecksumDto), Name = "FK_umbracoDeployDependency_umbracoDeployChecksum_id1")]
+ [NullSetting(NullSetting = NullSettings.NotNull)]
+ public int SourceId { get; set; }
+
+ [Column("targetId")]
+ [ForeignKey(typeof(UmbracoDeployChecksumDto), Name = "FK_umbracoDeployDependency_umbracoDeployChecksum_id2")]
+ [NullSetting(NullSetting = NullSettings.NotNull)]
+ public int TargetId { get; set; }
+
+ [Column("mode")]
+ [NullSetting(NullSetting = NullSettings.NotNull)]
+ public int Mode { get; set; }
+
+ }
+}
\ No newline at end of file
diff --git a/src/Umbraco.Core/Models/Rdbms/UserLoginDto.cs b/src/Umbraco.Core/Models/Rdbms/UserLoginDto.cs
deleted file mode 100644
index 6826377856..0000000000
--- a/src/Umbraco.Core/Models/Rdbms/UserLoginDto.cs
+++ /dev/null
@@ -1,21 +0,0 @@
-using System;
-using Umbraco.Core.Persistence;
-using Umbraco.Core.Persistence.DatabaseAnnotations;
-
-namespace Umbraco.Core.Models.Rdbms
-{
- [TableName("umbracoUserLogins")]
- [ExplicitColumns]
- internal class UserLoginDto
- {
- [Column("contextID")]
- [Index(IndexTypes.Clustered, Name = "IX_umbracoUserLogins_Index")]
- public Guid ContextId { get; set; }
-
- [Column("userID")]
- public int UserId { get; set; }
-
- [Column("timeout")]
- public long Timeout { get; set; }
- }
-}
\ No newline at end of file
diff --git a/src/Umbraco.Core/Models/Script.cs b/src/Umbraco.Core/Models/Script.cs
index 71f0684447..325885d9ba 100644
--- a/src/Umbraco.Core/Models/Script.cs
+++ b/src/Umbraco.Core/Models/Script.cs
@@ -1,7 +1,5 @@
using System;
-using System.Linq;
using System.Runtime.Serialization;
-using Umbraco.Core.Configuration;
using Umbraco.Core.Configuration.UmbracoSettings;
using Umbraco.Core.IO;
@@ -15,16 +13,17 @@ namespace Umbraco.Core.Models
public class Script : File
{
public Script(string path)
- : base(path)
- {
-
- }
+ : this(path, (Func) null)
+ { }
+
+ internal Script(string path, Func getFileContent)
+ : base(path, getFileContent)
+ { }
[Obsolete("This is no longer used and will be removed from the codebase in future versions")]
public Script(string path, IContentSection contentConfig)
- : this(path)
- {
- }
+ : base(path)
+ { }
///
/// Indicates whether the current entity has an identity, which in this case is a path/name.
diff --git a/src/Umbraco.Core/Models/ServerRegistration.cs b/src/Umbraco.Core/Models/ServerRegistration.cs
index 900d6deb94..cee70893d0 100644
--- a/src/Umbraco.Core/Models/ServerRegistration.cs
+++ b/src/Umbraco.Core/Models/ServerRegistration.cs
@@ -2,22 +2,23 @@
using System.Globalization;
using System.Reflection;
using Umbraco.Core.Models.EntityBase;
-using Umbraco.Core.Sync;
namespace Umbraco.Core.Models
{
///
/// Represents a registered server in a multiple-servers environment.
///
- public class ServerRegistration : Entity, IServerAddress, IAggregateRoot
+ public class ServerRegistration : Entity, IServerRegistration
{
private string _serverAddress;
private string _serverIdentity;
private bool _isActive;
+ private bool _isMaster;
private static readonly PropertyInfo ServerAddressSelector = ExpressionHelper.GetPropertyInfo(x => x.ServerAddress);
private static readonly PropertyInfo ServerIdentitySelector = ExpressionHelper.GetPropertyInfo(x => x.ServerIdentity);
private static readonly PropertyInfo IsActiveSelector = ExpressionHelper.GetPropertyInfo(x => x.IsActive);
+ private static readonly PropertyInfo IsMasterSelector = ExpressionHelper.GetPropertyInfo(x => x.IsMaster);
///
/// Initialiazes a new instance of the class.
@@ -34,7 +35,8 @@ namespace Umbraco.Core.Models
/// The date and time the registration was created.
/// The date and time the registration was last accessed.
/// A value indicating whether the registration is active.
- public ServerRegistration(int id, string serverAddress, string serverIdentity, DateTime registered, DateTime accessed, bool isActive)
+ /// A value indicating whether the registration is master.
+ public ServerRegistration(int id, string serverAddress, string serverIdentity, DateTime registered, DateTime accessed, bool isActive, bool isMaster)
{
UpdateDate = accessed;
CreateDate = registered;
@@ -43,6 +45,7 @@ namespace Umbraco.Core.Models
ServerAddress = serverAddress;
ServerIdentity = serverIdentity;
IsActive = isActive;
+ IsMaster = isMaster;
}
///
@@ -108,6 +111,22 @@ namespace Umbraco.Core.Models
}
}
+ ///
+ /// Gets or sets a value indicating whether the server is master.
+ ///
+ public bool IsMaster
+ {
+ get { return _isMaster; }
+ set
+ {
+ SetPropertyValueAndDetectChanges(o =>
+ {
+ _isMaster = value;
+ return _isMaster;
+ }, _isMaster, IsMasterSelector);
+ }
+ }
+
///
/// Gets the date and time the registration was created.
///
@@ -124,7 +143,7 @@ namespace Umbraco.Core.Models
///
public override string ToString()
{
- return string.Format("{{\"{0}\", \"{1}\", {2}active}}", ServerAddress, ServerIdentity, IsActive ? "" : "!");
+ return string.Format("{{\"{0}\", \"{1}\", {2}active, {3}master}}", ServerAddress, ServerIdentity, IsActive ? "" : "!", IsMaster ? "" : "!");
}
}
}
\ No newline at end of file
diff --git a/src/Umbraco.Core/Models/Stylesheet.cs b/src/Umbraco.Core/Models/Stylesheet.cs
index 7381da0930..060246df54 100644
--- a/src/Umbraco.Core/Models/Stylesheet.cs
+++ b/src/Umbraco.Core/Models/Stylesheet.cs
@@ -1,9 +1,9 @@
using System;
using System.Collections.Generic;
+using System.ComponentModel;
using System.Data;
using System.Linq;
using System.Runtime.Serialization;
-using System.Text;
using Umbraco.Core.IO;
using Umbraco.Core.Strings.Css;
@@ -16,12 +16,16 @@ namespace Umbraco.Core.Models
[DataContract(IsReference = true)]
public class Stylesheet : File
{
- public Stylesheet(string path)
- : base(path)
- {
+ public Stylesheet(string path)
+ : this(path, null)
+ { }
+
+ internal Stylesheet(string path, Func getFileContent)
+ : base(path.EnsureEndsWith(".css"), getFileContent)
+ {
InitializeProperties();
}
-
+
private Lazy> _properties;
private void InitializeProperties()
@@ -33,11 +37,11 @@ namespace Umbraco.Core.Models
//re-parse it so we can check what properties are different and adjust the event handlers
var parsed = StylesheetHelper.ParseRules(Content).ToArray();
var names = parsed.Select(x => x.Name).ToArray();
- var existing = _properties.Value.Where(x => names.Contains(x.Name)).ToArray();
+ var existing = _properties.Value.Where(x => names.InvariantContains(x.Name)).ToArray();
//update existing
foreach (var stylesheetProperty in existing)
{
- var updateFrom = parsed.Single(x => x.Name == stylesheetProperty.Name);
+ var updateFrom = parsed.Single(x => x.Name.InvariantEquals(stylesheetProperty.Name));
//remove current event handler while we update, we'll reset it after
stylesheetProperty.PropertyChanged -= Property_PropertyChanged;
stylesheetProperty.Alias = updateFrom.Selector;
@@ -46,14 +50,14 @@ namespace Umbraco.Core.Models
stylesheetProperty.PropertyChanged += Property_PropertyChanged;
}
//remove no longer existing
- var nonExisting = _properties.Value.Where(x => names.Contains(x.Name) == false).ToArray();
+ var nonExisting = _properties.Value.Where(x => names.InvariantContains(x.Name) == false).ToArray();
foreach (var stylesheetProperty in nonExisting)
{
stylesheetProperty.PropertyChanged -= Property_PropertyChanged;
_properties.Value.Remove(stylesheetProperty);
}
//add new ones
- var newItems = parsed.Where(x => _properties.Value.Select(p => p.Name).Contains(x.Name) == false);
+ var newItems = parsed.Where(x => _properties.Value.Select(p => p.Name).InvariantContains(x.Name) == false);
foreach (var stylesheetRule in newItems)
{
var prop = new StylesheetProperty(stylesheetRule.Name, stylesheetRule.Selector, stylesheetRule.Styles);
@@ -81,7 +85,7 @@ namespace Umbraco.Core.Models
///
///
///
- void Property_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
+ void Property_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
var prop = (StylesheetProperty) sender;
@@ -128,7 +132,7 @@ namespace Umbraco.Core.Models
///
public void AddProperty(StylesheetProperty property)
{
- if (Properties.Any(x => x.Name == property.Name))
+ if (Properties.Any(x => x.Name.InvariantEquals(property.Name)))
{
throw new DuplicateNameException("The property with the name " + property.Name + " already exists in the collection");
}
@@ -151,7 +155,7 @@ namespace Umbraco.Core.Models
///
public void RemoveProperty(string name)
{
- if (Properties.Any(x => x.Name == name))
+ if (Properties.Any(x => x.Name.InvariantEquals(name)))
{
Content = StylesheetHelper.ReplaceRule(Content, name, null);
}
diff --git a/src/Umbraco.Core/Models/Template.cs b/src/Umbraco.Core/Models/Template.cs
index 3a8cbf8794..4aca88f286 100644
--- a/src/Umbraco.Core/Models/Template.cs
+++ b/src/Umbraco.Core/Models/Template.cs
@@ -1,7 +1,10 @@
using System;
using System.Collections.Generic;
+using System.Drawing;
+using System.IO;
using System.Reflection;
using System.Runtime.Serialization;
+using System.Text;
using Umbraco.Core.Configuration;
using Umbraco.Core.Configuration.UmbracoSettings;
using Umbraco.Core.IO;
@@ -29,12 +32,16 @@ namespace Umbraco.Core.Models
private static readonly PropertyInfo NameSelector = ExpressionHelper.GetPropertyInfo(x => x.Name);
public Template(string name, string alias)
- : base(string.Empty)
+ : this(name, alias, (Func) null)
+ { }
+
+ internal Template(string name, string alias, Func getFileContent)
+ : base(string.Empty, getFileContent)
{
_name = name;
_alias = alias.ToCleanString(CleanStringType.UnderscoreAlias);
_masterTemplateId = new Lazy(() => -1);
- }
+ }
[Obsolete("This constructor should not be used, file path is determined by alias, setting the path here will have no affect")]
public Template(string path, string name, string alias)
@@ -123,7 +130,6 @@ namespace Umbraco.Core.Models
Key = Guid.NewGuid();
}
-
public void SetMasterTemplate(ITemplate masterTemplate)
{
if (masterTemplate == null)
@@ -139,27 +145,9 @@ namespace Umbraco.Core.Models
}
- public override object DeepClone()
+ protected override void DeepCloneNameAndAlias(File clone)
{
-
- //We cannot call in to the base classes to clone because the base File class treats Alias, Name.. differently so we need to manually do the clone
-
- //Memberwise clone on Entity will work since it doesn't have any deep elements
- // for any sub class this will work for standard properties as well that aren't complex object's themselves.
- var clone = (Template)MemberwiseClone();
- //turn off change tracking
- clone.DisableChangeTracking();
- //Automatically deep clone ref properties that are IDeepCloneable
- DeepCloneHelper.DeepCloneRefProperties(this, clone);
-
- //this shouldn't really be needed since we're not tracking
- clone.ResetDirtyProperties(false);
- //re-enable tracking
- clone.EnableChangeTracking();
-
- return clone;
+ // do nothing - prevents File from doing its stuff
}
-
-
}
}
diff --git a/src/Umbraco.Core/Models/UmbracoDomain.cs b/src/Umbraco.Core/Models/UmbracoDomain.cs
index 963535729c..943f96c9f9 100644
--- a/src/Umbraco.Core/Models/UmbracoDomain.cs
+++ b/src/Umbraco.Core/Models/UmbracoDomain.cs
@@ -5,8 +5,6 @@ using Umbraco.Core.Models.EntityBase;
namespace Umbraco.Core.Models
{
- //TODO: Need to custom serialize this
-
[Serializable]
[DataContract(IsReference = true)]
public class UmbracoDomain : Entity, IDomain
@@ -16,25 +14,32 @@ namespace Umbraco.Core.Models
_domainName = domainName;
}
- private IContent _content;
- private ILanguage _language;
+ public UmbracoDomain(string domainName, string languageIsoCode)
+ : this(domainName)
+ {
+ LanguageIsoCode = languageIsoCode;
+ }
+
+ private int? _contentId;
+ private int? _languageId;
private string _domainName;
- private static readonly PropertyInfo DefaultLanguageSelector = ExpressionHelper.GetPropertyInfo(x => x.Language);
+ private static readonly PropertyInfo ContentSelector = ExpressionHelper.GetPropertyInfo(x => x.RootContentId);
+ private static readonly PropertyInfo DefaultLanguageSelector = ExpressionHelper.GetPropertyInfo(x => x.LanguageId);
private static readonly PropertyInfo DomainNameSelector = ExpressionHelper.GetPropertyInfo(x => x.DomainName);
- private static readonly PropertyInfo ContentSelector = ExpressionHelper.GetPropertyInfo(x => x.RootContent);
+
[DataMember]
- public ILanguage Language
+ public int? LanguageId
{
- get { return _language; }
+ get { return _languageId; }
set
{
SetPropertyValueAndDetectChanges(o =>
{
- _language = value;
- return _language;
- }, _language, DefaultLanguageSelector);
+ _languageId = value;
+ return _languageId;
+ }, _languageId, DefaultLanguageSelector);
}
}
@@ -53,16 +58,16 @@ namespace Umbraco.Core.Models
}
[DataMember]
- public IContent RootContent
+ public int? RootContentId
{
- get { return _content; }
+ get { return _contentId; }
set
{
SetPropertyValueAndDetectChanges(o =>
{
- _content = value;
- return _content;
- }, _content, ContentSelector);
+ _contentId = value;
+ return _contentId;
+ }, _contentId, ContentSelector);
}
}
@@ -70,5 +75,10 @@ namespace Umbraco.Core.Models
{
get { return string.IsNullOrWhiteSpace(DomainName) || DomainName.StartsWith("*"); }
}
+
+ ///
+ /// Readonly value of the language ISO code for the domain
+ ///
+ public string LanguageIsoCode { get; internal set; }
}
}
\ No newline at end of file
diff --git a/src/Umbraco.Core/Models/UmbracoEntity.cs b/src/Umbraco.Core/Models/UmbracoEntity.cs
index 4975ad3bd2..70ef056068 100644
--- a/src/Umbraco.Core/Models/UmbracoEntity.cs
+++ b/src/Umbraco.Core/Models/UmbracoEntity.cs
@@ -11,7 +11,7 @@ namespace Umbraco.Core.Models
///
/// Implementation of the for internal use.
///
- internal class UmbracoEntity : Entity, IUmbracoEntity
+ public class UmbracoEntity : Entity, IUmbracoEntity
{
private int _creatorId;
private int _level;
diff --git a/src/Umbraco.Core/Models/UmbracoEntityExtensions.cs b/src/Umbraco.Core/Models/UmbracoEntityExtensions.cs
index edcc25ade8..987be2a276 100644
--- a/src/Umbraco.Core/Models/UmbracoEntityExtensions.cs
+++ b/src/Umbraco.Core/Models/UmbracoEntityExtensions.cs
@@ -11,6 +11,20 @@ namespace Umbraco.Core.Models
internal static class UmbracoEntityExtensions
{
+ public static bool HasChildren(this IUmbracoEntity entity)
+ {
+ if (entity.AdditionalData.ContainsKey("HasChildren"))
+ {
+ var convert = entity.AdditionalData["HasChildren"].TryConvertTo();
+ if (convert)
+ {
+ return convert.Result;
+ }
+ }
+ return false;
+ }
+
+
public static object GetAdditionalDataValueIgnoreCase(this IUmbracoEntity entity, string key, object defaultVal)
{
if (entity.AdditionalData.ContainsKeyIgnoreCase(key) == false) return defaultVal;
diff --git a/src/Umbraco.Core/Models/UmbracoObjectTypes.cs b/src/Umbraco.Core/Models/UmbracoObjectTypes.cs
index 37ad181682..2be2010301 100644
--- a/src/Umbraco.Core/Models/UmbracoObjectTypes.cs
+++ b/src/Umbraco.Core/Models/UmbracoObjectTypes.cs
@@ -15,49 +15,49 @@ namespace Umbraco.Core.Models
///
/// Content Item Type
///
- [UmbracoObjectTypeAttribute(Constants.ObjectTypes.ContentItemType)]
+ [UmbracoObjectType(Constants.ObjectTypes.ContentItemType)]
[FriendlyName("Content Item Type")]
ContentItemType,
///
/// Root
///
- [UmbracoObjectTypeAttribute(Constants.ObjectTypes.SystemRoot)]
+ [UmbracoObjectType(Constants.ObjectTypes.SystemRoot)]
[FriendlyName("Root")]
ROOT,
///
/// Document
///
- [UmbracoObjectTypeAttribute(Constants.ObjectTypes.Document, typeof(IContent))]
+ [UmbracoObjectType(Constants.ObjectTypes.Document, typeof(IContent))]
[FriendlyName("Document")]
Document,
///
/// Media
///
- [UmbracoObjectTypeAttribute(Constants.ObjectTypes.Media, typeof(IMedia))]
+ [UmbracoObjectType(Constants.ObjectTypes.Media, typeof(IMedia))]
[FriendlyName("Media")]
Media,
///
/// Member Type
///
- [UmbracoObjectTypeAttribute(Constants.ObjectTypes.MemberType, typeof(IMemberType))]
+ [UmbracoObjectType(Constants.ObjectTypes.MemberType, typeof(IMemberType))]
[FriendlyName("Member Type")]
MemberType,
///
/// Template
///
- [UmbracoObjectTypeAttribute(Constants.ObjectTypes.Template, typeof(ITemplate))]
+ [UmbracoObjectType(Constants.ObjectTypes.Template, typeof(ITemplate))]
[FriendlyName("Template")]
Template,
///
/// Member Group
///
- [UmbracoObjectTypeAttribute(Constants.ObjectTypes.MemberGroup)]
+ [UmbracoObjectType(Constants.ObjectTypes.MemberGroup)]
[FriendlyName("Member Group")]
MemberGroup,
@@ -65,50 +65,73 @@ namespace Umbraco.Core.Models
///
/// Content Item
///
- [UmbracoObjectTypeAttribute(Constants.ObjectTypes.ContentItem)]
+ [UmbracoObjectType(Constants.ObjectTypes.ContentItem)]
[FriendlyName("Content Item")]
ContentItem,
///
/// "Media Type
///
- [UmbracoObjectTypeAttribute(Constants.ObjectTypes.MediaType, typeof(IMediaType))]
+ [UmbracoObjectType(Constants.ObjectTypes.MediaType, typeof(IMediaType))]
[FriendlyName("Media Type")]
MediaType,
///
/// Document Type
///
- [UmbracoObjectTypeAttribute(Constants.ObjectTypes.DocumentType, typeof(IContentType))]
+ [UmbracoObjectType(Constants.ObjectTypes.DocumentType, typeof(IContentType))]
[FriendlyName("Document Type")]
DocumentType,
///
/// Recycle Bin
///
- [UmbracoObjectTypeAttribute(Constants.ObjectTypes.ContentRecycleBin)]
+ [UmbracoObjectType(Constants.ObjectTypes.ContentRecycleBin)]
[FriendlyName("Recycle Bin")]
RecycleBin,
///
/// Stylesheet
///
- [UmbracoObjectTypeAttribute(Constants.ObjectTypes.Stylesheet)]
+ [UmbracoObjectType(Constants.ObjectTypes.Stylesheet)]
[FriendlyName("Stylesheet")]
Stylesheet,
///
/// Member
///
- [UmbracoObjectTypeAttribute(Constants.ObjectTypes.Member, typeof(IMember))]
+ [UmbracoObjectType(Constants.ObjectTypes.Member, typeof(IMember))]
[FriendlyName("Member")]
Member,
///
/// Data Type
///
- [UmbracoObjectTypeAttribute(Constants.ObjectTypes.DataType, typeof(IDataTypeDefinition))]
+ [UmbracoObjectType(Constants.ObjectTypes.DataType, typeof(IDataTypeDefinition))]
[FriendlyName("Data Type")]
- DataType
+ DataType,
+
+ ///
+ /// Document type container
+ ///
+ [UmbracoObjectType(Constants.ObjectTypes.DocumentTypeContainer)]
+ [FriendlyName("Document Type Container")]
+ DocumentTypeContainer,
+
+ ///
+ /// Media type container
+ ///
+ [UmbracoObjectType(Constants.ObjectTypes.MediaTypeContainer)]
+ [FriendlyName("Media Type Container")]
+ MediaTypeContainer,
+
+ ///
+ /// Media type container
+ ///
+ [UmbracoObjectType(Constants.ObjectTypes.DataTypeContainer)]
+ [FriendlyName("Data Type Container")]
+ DataTypeContainer
+
+
}
}
\ No newline at end of file
diff --git a/src/Umbraco.Core/Models/UserExtensions.cs b/src/Umbraco.Core/Models/UserExtensions.cs
index bd670f3836..5b9f63cf48 100644
--- a/src/Umbraco.Core/Models/UserExtensions.cs
+++ b/src/Umbraco.Core/Models/UserExtensions.cs
@@ -2,6 +2,7 @@
using System.Globalization;
using System.Linq;
using System.Threading;
+using Umbraco.Core.Models.Identity;
using Umbraco.Core.Models.Membership;
using Umbraco.Core.Services;
diff --git a/src/Umbraco.Core/ObjectResolution/Resolution.cs b/src/Umbraco.Core/ObjectResolution/Resolution.cs
index 87eb06e295..0b478d54cf 100644
--- a/src/Umbraco.Core/ObjectResolution/Resolution.cs
+++ b/src/Umbraco.Core/ObjectResolution/Resolution.cs
@@ -112,7 +112,7 @@ namespace Umbraco.Core.ObjectResolution
/// resolution is already frozen.
public static void Freeze()
{
- LogHelper.Debug(typeof(Resolution), "Freezing resolution");
+ LogHelper.Debug(typeof (Resolution), "Freezing resolution");
using (new WriteLock(ConfigurationLock))
{
@@ -121,9 +121,20 @@ namespace Umbraco.Core.ObjectResolution
_isFrozen = true;
}
-
- if (Frozen != null)
- Frozen(null, null);
+
+ LogHelper.Debug(typeof(Resolution), "Resolution is frozen");
+
+ if (Frozen == null) return;
+
+ try
+ {
+ Frozen(null, null);
+ }
+ catch (Exception e)
+ {
+ LogHelper.Error(typeof (Resolution), "Exception in Frozen event handler.", e);
+ throw;
+ }
}
///
diff --git a/src/Umbraco.Core/Persistence/DatabaseAnnotations/ConstraintAttribute.cs b/src/Umbraco.Core/Persistence/DatabaseAnnotations/ConstraintAttribute.cs
index 599f599f85..33c7616687 100644
--- a/src/Umbraco.Core/Persistence/DatabaseAnnotations/ConstraintAttribute.cs
+++ b/src/Umbraco.Core/Persistence/DatabaseAnnotations/ConstraintAttribute.cs
@@ -20,6 +20,6 @@ namespace Umbraco.Core.Persistence.DatabaseAnnotations
///
/// Gets or sets the Default value
///
- public string Default { get; set; }
+ public object Default { get; set; }
}
}
\ No newline at end of file
diff --git a/src/Umbraco.Core/Persistence/DatabaseModelDefinitions/SystemMethods.cs b/src/Umbraco.Core/Persistence/DatabaseModelDefinitions/SystemMethods.cs
index b7cacf0899..24c2294b59 100644
--- a/src/Umbraco.Core/Persistence/DatabaseModelDefinitions/SystemMethods.cs
+++ b/src/Umbraco.Core/Persistence/DatabaseModelDefinitions/SystemMethods.cs
@@ -3,8 +3,8 @@ namespace Umbraco.Core.Persistence.DatabaseModelDefinitions
public enum SystemMethods
{
NewGuid,
- NewSequentialId,
CurrentDateTime,
- CurrentUTCDateTime
+ //NewSequentialId,
+ //CurrentUTCDateTime
}
}
\ No newline at end of file
diff --git a/src/Umbraco.Core/Persistence/DatabaseSchemaHelper.cs b/src/Umbraco.Core/Persistence/DatabaseSchemaHelper.cs
index 12fd437b96..418e5b953d 100644
--- a/src/Umbraco.Core/Persistence/DatabaseSchemaHelper.cs
+++ b/src/Umbraco.Core/Persistence/DatabaseSchemaHelper.cs
@@ -1,10 +1,12 @@
using System;
using System.Linq;
+using Umbraco.Core.Configuration;
using Umbraco.Core.Logging;
using Umbraco.Core.Models.Rdbms;
using Umbraco.Core.Persistence.DatabaseModelDefinitions;
using Umbraco.Core.Persistence.Migrations.Initial;
using Umbraco.Core.Persistence.SqlSyntax;
+using Umbraco.Core.Services;
namespace Umbraco.Core.Persistence
{
@@ -37,13 +39,41 @@ namespace Umbraco.Core.Persistence
///
/// Creates the Umbraco db schema in the Database of the current Database.
+ /// Safe method that is only able to create the schema in non-configured
+ /// umbraco instances.
///
- public void CreateDatabaseSchema()
+ public void CreateDatabaseSchema(ApplicationContext applicationContext)
{
- CreateDatabaseSchemaDo();
+ if (applicationContext == null) throw new ArgumentNullException("applicationContext");
+ CreateDatabaseSchema(true, applicationContext);
}
- private void CreateDatabaseSchemaDo()
+ ///
+ /// Creates the Umbraco db schema in the Database of the current Database
+ /// with the option to guard the db from having the schema created
+ /// multiple times.
+ ///
+ ///
+ ///
+ public void CreateDatabaseSchema(bool guardConfiguration, ApplicationContext applicationContext)
+ {
+ if (applicationContext == null) throw new ArgumentNullException("applicationContext");
+
+ if (guardConfiguration && applicationContext.IsConfigured)
+ throw new Exception("Umbraco is already configured!");
+
+ CreateDatabaseSchemaDo(applicationContext.Services.MigrationEntryService);
+ }
+
+ internal void CreateDatabaseSchemaDo(bool guardConfiguration, ApplicationContext applicationContext)
+ {
+ if (guardConfiguration && applicationContext.IsConfigured)
+ throw new Exception("Umbraco is already configured!");
+
+ CreateDatabaseSchemaDo(applicationContext.Services.MigrationEntryService);
+ }
+
+ internal void CreateDatabaseSchemaDo(IMigrationEntryService migrationEntryService)
{
_logger.Info("Initializing database schema creation");
@@ -118,6 +148,13 @@ namespace Umbraco.Core.Persistence
_db.Update("SET id = @IdAfter WHERE id = @IdBefore AND userLogin = @Login", new { IdAfter = 0, IdBefore = 1, Login = "admin" });
}
+ //Loop through index statements and execute sql
+ foreach (var sql in indexSql)
+ {
+ int createdIndex = _db.Execute(new Sql(sql));
+ _logger.Info(string.Format("Create Index sql {0}:\n {1}", createdIndex, sql));
+ }
+
//Loop through foreignkey statements and execute sql
foreach (var sql in foreignSql)
{
@@ -125,12 +162,7 @@ namespace Umbraco.Core.Persistence
_logger.Info(string.Format("Create Foreign Key sql {0}:\n {1}", createdFk, sql));
}
- //Loop through index statements and execute sql
- foreach (var sql in indexSql)
- {
- int createdIndex = _db.Execute(new Sql(sql));
- _logger.Info(string.Format("Create Index sql {0}:\n {1}", createdIndex, sql));
- }
+
transaction.Complete();
}
diff --git a/src/Umbraco.Core/Persistence/DatabasenodeLockExtensions.cs b/src/Umbraco.Core/Persistence/DatabasenodeLockExtensions.cs
new file mode 100644
index 0000000000..3e6d245416
--- /dev/null
+++ b/src/Umbraco.Core/Persistence/DatabasenodeLockExtensions.cs
@@ -0,0 +1,43 @@
+using System;
+using System.Collections.Generic;
+using System.Data;
+using System.Runtime.CompilerServices;
+
+namespace Umbraco.Core.Persistence
+{
+ internal static class DatabaseNodeLockExtensions
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ private static void ValidateDatabase(UmbracoDatabase database)
+ {
+ if (database == null)
+ throw new ArgumentNullException("database");
+ if (database.CurrentTransactionIsolationLevel < IsolationLevel.RepeatableRead)
+ throw new InvalidOperationException("A transaction with minimum RepeatableRead isolation level is required.");
+ }
+
+ // updating a record within a repeatable-read transaction gets an exclusive lock on
+ // that record which will be kept until the transaction is ended, effectively locking
+ // out all other accesses to that record - thus obtaining an exclusive lock over the
+ // protected resources.
+ public static void AcquireLockNodeWriteLock(this UmbracoDatabase database, int nodeId)
+ {
+ ValidateDatabase(database);
+
+ database.Execute("UPDATE umbracoNode SET sortOrder = (CASE WHEN (sortOrder=1) THEN -1 ELSE 1 END) WHERE id=@id",
+ new { @id = nodeId });
+ }
+
+ // reading a record within a repeatable-read transaction gets a shared lock on
+ // that record which will be kept until the transaction is ended, effectively preventing
+ // other write accesses to that record - thus obtaining a shared lock over the protected
+ // resources.
+ public static void AcquireLockNodeReadLock(this UmbracoDatabase database, int nodeId)
+ {
+ ValidateDatabase(database);
+
+ database.ExecuteScalar("SELECT sortOrder FROM umbracoNode WHERE id=@id",
+ new { @id = nodeId });
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Umbraco.Core/Persistence/DbConnectionExtensions.cs b/src/Umbraco.Core/Persistence/DbConnectionExtensions.cs
index c4ca875eb2..5145ec95c5 100644
--- a/src/Umbraco.Core/Persistence/DbConnectionExtensions.cs
+++ b/src/Umbraco.Core/Persistence/DbConnectionExtensions.cs
@@ -7,6 +7,7 @@ using System.Data.SqlClient;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
+using Umbraco.Core.Logging;
namespace Umbraco.Core.Persistence
{
@@ -75,8 +76,10 @@ namespace Umbraco.Core.Persistence
connection.Open();
connection.Close();
}
- catch (DbException)
+ catch (DbException exc)
{
+ // Don't swallow this error, the exception is super handy for knowing "why" its not available
+ LogHelper.WarnWithException("Configured database is reporting as not being available!", exc);
return false;
}
diff --git a/src/Umbraco.Core/Persistence/Factories/ContentTypeFactory.cs b/src/Umbraco.Core/Persistence/Factories/ContentTypeFactory.cs
index fdd2759d76..54c7d8d2c9 100644
--- a/src/Umbraco.Core/Persistence/Factories/ContentTypeFactory.cs
+++ b/src/Umbraco.Core/Persistence/Factories/ContentTypeFactory.cs
@@ -1,98 +1,144 @@
using System;
+using System.Collections.Generic;
using System.Globalization;
+using System.Linq;
using Umbraco.Core.Models;
+using Umbraco.Core.Models.EntityBase;
using Umbraco.Core.Models.Membership;
using Umbraco.Core.Models.Rdbms;
namespace Umbraco.Core.Persistence.Factories
{
+ // factory for
+ // IContentType (document types)
+ // IMediaType (media types)
+ // IMemberType (member types)
+ //
internal class ContentTypeFactory
{
- private readonly Guid _nodeObjectType;
+ #region IContentType
- public ContentTypeFactory(Guid nodeObjectType)
+ public IContentType BuildContentTypeEntity(ContentTypeDto dto)
{
- _nodeObjectType = nodeObjectType;
- }
+ var contentType = new ContentType(dto.NodeDto.ParentId);
+ BuildCommonEntity(contentType, dto);
- #region Implementation of IEntityFactory
-
- public IContentType BuildEntity(DocumentTypeDto dto)
- {
- var contentType = new ContentType(dto.ContentTypeDto.NodeDto.ParentId)
- {
- Id = dto.ContentTypeDto.NodeDto.NodeId,
- Key = dto.ContentTypeDto.NodeDto.UniqueId,
- Alias = dto.ContentTypeDto.Alias,
- Name = dto.ContentTypeDto.NodeDto.Text,
- Icon = dto.ContentTypeDto.Icon,
- Thumbnail = dto.ContentTypeDto.Thumbnail,
- SortOrder = dto.ContentTypeDto.NodeDto.SortOrder,
- Description = dto.ContentTypeDto.Description,
- CreateDate = dto.ContentTypeDto.NodeDto.CreateDate,
- Path = dto.ContentTypeDto.NodeDto.Path,
- Level = dto.ContentTypeDto.NodeDto.Level,
- CreatorId = dto.ContentTypeDto.NodeDto.UserId.Value,
- AllowedAsRoot = dto.ContentTypeDto.AllowAtRoot,
- IsContainer = dto.ContentTypeDto.IsContainer,
- Trashed = dto.ContentTypeDto.NodeDto.Trashed,
- DefaultTemplateId = dto.TemplateNodeId
- };
//on initial construction we don't want to have dirty properties tracked
// http://issues.umbraco.org/issue/U4-1946
contentType.ResetDirtyProperties(false);
- return contentType;
- }
- public DocumentTypeDto BuildDto(IContentType entity)
- {
- var documentTypeDto = new DocumentTypeDto
- {ContentTypeDto = BuildContentTypeDto(entity), ContentTypeNodeId = entity.Id};
-
- var contentType = entity as ContentType;
- if(contentType != null)
- {
- documentTypeDto.TemplateNodeId = contentType.DefaultTemplateId;
- documentTypeDto.IsDefault = true;
- }
- return documentTypeDto;
+ return contentType;
}
#endregion
- private ContentTypeDto BuildContentTypeDto(IContentType entity)
+ #region IMediaType
+
+ public IMediaType BuildMediaTypeEntity(ContentTypeDto dto)
{
+ var contentType = new MediaType(dto.NodeDto.ParentId);
+ BuildCommonEntity(contentType, dto);
+
+ //on initial construction we don't want to have dirty properties tracked
+ // http://issues.umbraco.org/issue/U4-1946
+ contentType.ResetDirtyProperties(false);
+
+ return contentType;
+ }
+
+ #endregion
+
+ #region IMemberType
+
+ public IMemberType BuildMemberTypeEntity(ContentTypeDto dto)
+ {
+ throw new NotImplementedException();
+ }
+
+ public IEnumerable BuildMemberTypeDtos(IMemberType entity)
+ {
+ var memberType = entity as MemberType;
+ if (memberType == null || memberType.PropertyTypes.Any() == false)
+ return Enumerable.Empty();
+
+ var dtos = memberType.PropertyTypes.Select(x => new MemberTypeDto
+ {
+ NodeId = entity.Id,
+ PropertyTypeId = x.Id,
+ CanEdit = memberType.MemberCanEditProperty(x.Alias),
+ ViewOnProfile = memberType.MemberCanViewProperty(x.Alias)
+ }).ToList();
+ return dtos;
+ }
+
+ #endregion
+
+ #region Common
+
+ private static void BuildCommonEntity(ContentTypeBase entity, ContentTypeDto dto)
+ {
+ entity.Id = dto.NodeDto.NodeId;
+ entity.Key = dto.NodeDto.UniqueId;
+ entity.Alias = dto.Alias;
+ entity.Name = dto.NodeDto.Text;
+ entity.Icon = dto.Icon;
+ entity.Thumbnail = dto.Thumbnail;
+ entity.SortOrder = dto.NodeDto.SortOrder;
+ entity.Description = dto.Description;
+ entity.CreateDate = dto.NodeDto.CreateDate;
+ entity.Path = dto.NodeDto.Path;
+ entity.Level = dto.NodeDto.Level;
+ entity.CreatorId = dto.NodeDto.UserId.Value;
+ entity.AllowedAsRoot = dto.AllowAtRoot;
+ entity.IsContainer = dto.IsContainer;
+ entity.Trashed = dto.NodeDto.Trashed;
+ }
+
+ public ContentTypeDto BuildContentTypeDto(IContentTypeBase entity)
+ {
+ Guid nodeObjectType;
+ if (entity is IContentType)
+ nodeObjectType = Constants.ObjectTypes.DocumentTypeGuid;
+ else if (entity is IMediaType)
+ nodeObjectType = Constants.ObjectTypes.MediaTypeGuid;
+ else if (entity is IMemberType)
+ nodeObjectType = Constants.ObjectTypes.MemberTypeGuid;
+ else
+ throw new Exception("oops: invalid entity.");
+
var contentTypeDto = new ContentTypeDto
- {
- Alias = entity.Alias,
- Description = entity.Description,
- Icon = entity.Icon,
- Thumbnail = entity.Thumbnail,
- NodeId = entity.Id,
- AllowAtRoot = entity.AllowedAsRoot,
- IsContainer = entity.IsContainer,
- NodeDto = BuildNodeDto(entity)
- };
+ {
+ Alias = entity.Alias,
+ Description = entity.Description,
+ Icon = entity.Icon,
+ Thumbnail = entity.Thumbnail,
+ NodeId = entity.Id,
+ AllowAtRoot = entity.AllowedAsRoot,
+ IsContainer = entity.IsContainer,
+ NodeDto = BuildNodeDto(entity, nodeObjectType)
+ };
return contentTypeDto;
}
- private NodeDto BuildNodeDto(IContentType entity)
+ private static NodeDto BuildNodeDto(IUmbracoEntity entity, Guid nodeObjectType)
{
var nodeDto = new NodeDto
- {
- CreateDate = entity.CreateDate,
- NodeId = entity.Id,
- Level = short.Parse(entity.Level.ToString(CultureInfo.InvariantCulture)),
- NodeObjectType = _nodeObjectType,
- ParentId = entity.ParentId,
- Path = entity.Path,
- SortOrder = entity.SortOrder,
- Text = entity.Name,
- Trashed = false,
- UniqueId = entity.Key,
- UserId = entity.CreatorId
- };
+ {
+ CreateDate = entity.CreateDate,
+ NodeId = entity.Id,
+ Level = short.Parse(entity.Level.ToString(CultureInfo.InvariantCulture)),
+ NodeObjectType = nodeObjectType,
+ ParentId = entity.ParentId,
+ Path = entity.Path,
+ SortOrder = entity.SortOrder,
+ Text = entity.Name,
+ Trashed = false,
+ UniqueId = entity.Key,
+ UserId = entity.CreatorId
+ };
return nodeDto;
}
+
+ #endregion
}
}
\ No newline at end of file
diff --git a/src/Umbraco.Core/Persistence/Factories/DataTypeDefinitionFactory.cs b/src/Umbraco.Core/Persistence/Factories/DataTypeDefinitionFactory.cs
index 6fd6500552..377d70e3cb 100644
--- a/src/Umbraco.Core/Persistence/Factories/DataTypeDefinitionFactory.cs
+++ b/src/Umbraco.Core/Persistence/Factories/DataTypeDefinitionFactory.cs
@@ -19,7 +19,7 @@ namespace Umbraco.Core.Persistence.Factories
public IDataTypeDefinition BuildEntity(DataTypeDto dto)
{
- var dataTypeDefinition = new DataTypeDefinition(dto.NodeDto.ParentId, dto.PropertyEditorAlias)
+ var dataTypeDefinition = new DataTypeDefinition(dto.PropertyEditorAlias)
{
CreateDate = dto.NodeDto.CreateDate,
DatabaseType = dto.DbType.EnumParse(true),
diff --git a/src/Umbraco.Core/Persistence/Factories/MediaTypeFactory.cs b/src/Umbraco.Core/Persistence/Factories/MediaTypeFactory.cs
deleted file mode 100644
index 98048cd3a7..0000000000
--- a/src/Umbraco.Core/Persistence/Factories/MediaTypeFactory.cs
+++ /dev/null
@@ -1,82 +0,0 @@
-using System;
-using System.Globalization;
-using Umbraco.Core.Models;
-using Umbraco.Core.Models.Rdbms;
-
-namespace Umbraco.Core.Persistence.Factories
-{
- internal class MediaTypeFactory
- {
- private readonly Guid _nodeObjectType;
-
- public MediaTypeFactory(Guid nodeObjectType)
- {
- _nodeObjectType = nodeObjectType;
- }
-
- #region Implementation of IEntityFactory
-
- public IMediaType BuildEntity(ContentTypeDto dto)
- {
- var contentType = new MediaType(dto.NodeDto.ParentId)
- {
- Id = dto.NodeDto.NodeId,
- Key = dto.NodeDto.UniqueId,
- Alias = dto.Alias,
- Name = dto.NodeDto.Text,
- Icon = dto.Icon,
- Thumbnail = dto.Thumbnail,
- SortOrder = dto.NodeDto.SortOrder,
- Description = dto.Description,
- CreateDate = dto.NodeDto.CreateDate,
- Path = dto.NodeDto.Path,
- Level = dto.NodeDto.Level,
- CreatorId = dto.NodeDto.UserId.Value,
- AllowedAsRoot = dto.AllowAtRoot,
- IsContainer = dto.IsContainer,
- Trashed = dto.NodeDto.Trashed
- };
- //on initial construction we don't want to have dirty properties tracked
- // http://issues.umbraco.org/issue/U4-1946
- contentType.ResetDirtyProperties(false);
- return contentType;
- }
-
- public ContentTypeDto BuildDto(IMediaType entity)
- {
- var contentTypeDto = new ContentTypeDto
- {
- Alias = entity.Alias,
- Description = entity.Description,
- Icon = entity.Icon,
- Thumbnail = entity.Thumbnail,
- NodeId = entity.Id,
- AllowAtRoot = entity.AllowedAsRoot,
- IsContainer = entity.IsContainer,
- NodeDto = BuildNodeDto(entity)
- };
- return contentTypeDto;
- }
-
- #endregion
-
- private NodeDto BuildNodeDto(IMediaType entity)
- {
- var nodeDto = new NodeDto
- {
- CreateDate = entity.CreateDate,
- NodeId = entity.Id,
- Level = short.Parse(entity.Level.ToString(CultureInfo.InvariantCulture)),
- NodeObjectType = _nodeObjectType,
- ParentId = entity.ParentId,
- Path = entity.Path,
- SortOrder = entity.SortOrder,
- Text = entity.Name,
- Trashed = false,
- UniqueId = entity.Key,
- UserId = entity.CreatorId
- };
- return nodeDto;
- }
- }
-}
\ No newline at end of file
diff --git a/src/Umbraco.Core/Persistence/Factories/MemberTypeFactory.cs b/src/Umbraco.Core/Persistence/Factories/MemberTypeFactory.cs
deleted file mode 100644
index 345981342b..0000000000
--- a/src/Umbraco.Core/Persistence/Factories/MemberTypeFactory.cs
+++ /dev/null
@@ -1,80 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Globalization;
-using System.Linq;
-using Umbraco.Core.Models;
-using Umbraco.Core.Models.Rdbms;
-
-namespace Umbraco.Core.Persistence.Factories
-{
- internal class MemberTypeFactory
- {
- private readonly Guid _nodeObjectType;
-
- public MemberTypeFactory(Guid nodeObjectType)
- {
- _nodeObjectType = nodeObjectType;
- }
-
- public IMemberType BuildEntity(ContentTypeDto dto)
- {
- throw new System.NotImplementedException();
- }
-
- public ContentTypeDto BuildDto(IMemberType entity)
- {
- var contentTypeDto = new ContentTypeDto
- {
- Alias = entity.Alias,
- Description = entity.Description,
- Icon = entity.Icon,
- Thumbnail = entity.Thumbnail,
- NodeId = entity.Id,
- AllowAtRoot = entity.AllowedAsRoot,
- IsContainer = entity.IsContainer,
- NodeDto = BuildNodeDto(entity)
- };
- return contentTypeDto;
- }
-
- public IEnumerable BuildMemberTypeDtos(IMemberType entity)
- {
- var memberType = entity as MemberType;
- if (memberType == null || memberType.PropertyTypes.Any() == false)
- return Enumerable.Empty();
-
- var memberTypes = new List();
- foreach (var propertyType in memberType.PropertyTypes)
- {
- memberTypes.Add(new MemberTypeDto
- {
- NodeId = entity.Id,
- PropertyTypeId = propertyType.Id,
- CanEdit = memberType.MemberCanEditProperty(propertyType.Alias),
- ViewOnProfile = memberType.MemberCanViewProperty(propertyType.Alias)
- });
- }
-
- return memberTypes;
- }
-
- private NodeDto BuildNodeDto(IMemberType entity)
- {
- var nodeDto = new NodeDto
- {
- CreateDate = entity.CreateDate,
- NodeId = entity.Id,
- Level = short.Parse(entity.Level.ToString(CultureInfo.InvariantCulture)),
- NodeObjectType = _nodeObjectType,
- ParentId = entity.ParentId,
- Path = entity.Path,
- SortOrder = entity.SortOrder,
- Text = entity.Name,
- Trashed = false,
- UniqueId = entity.Key,
- UserId = entity.CreatorId
- };
- return nodeDto;
- }
- }
-}
\ No newline at end of file
diff --git a/src/Umbraco.Core/Persistence/Factories/MemberTypeReadOnlyFactory.cs b/src/Umbraco.Core/Persistence/Factories/MemberTypeReadOnlyFactory.cs
index 38ef4542f4..93ad99a5c1 100644
--- a/src/Umbraco.Core/Persistence/Factories/MemberTypeReadOnlyFactory.cs
+++ b/src/Umbraco.Core/Persistence/Factories/MemberTypeReadOnlyFactory.cs
@@ -51,31 +51,28 @@ namespace Umbraco.Core.Persistence.Factories
memberType.MemberTypePropertyTypes.Add(standardPropertyType.Key,
new MemberTypePropertyProfileAccess(false, false));
}
- memberType.PropertyTypes = propertyTypes;
+ memberType.NoGroupPropertyTypes = propertyTypes;
return memberType;
}
private PropertyGroupCollection GetPropertyTypeGroupCollection(MemberTypeReadOnlyDto dto, MemberType memberType, Dictionary standardProps)
{
- var propertyGroups = new PropertyGroupCollection();
-
+ // see PropertyGroupFactory, repeating code here...
+
+ var propertyGroups = new PropertyGroupCollection();
foreach (var groupDto in dto.PropertyTypeGroups.Where(x => x.Id.HasValue))
{
var group = new PropertyGroup();
-
- //Only assign an Id if the PropertyGroup belongs to this ContentType
+
+ // if the group is defined on the current member type,
+ // assign its identifier, else it will be zero
if (groupDto.ContentTypeNodeId == memberType.Id)
{
+ // note: no idea why Id is nullable here, but better check
+ if (groupDto.Id.HasValue == false)
+ throw new Exception("oops: groupDto.Id has no value.");
group.Id = groupDto.Id.Value;
-
- if (groupDto.ParentGroupId.HasValue)
- group.ParentId = groupDto.ParentGroupId.Value;
- }
- else
- {
- //If the PropertyGroup is inherited, we add a reference to the group as a Parent.
- group.ParentId = groupDto.Id;
}
group.Name = groupDto.Text;
diff --git a/src/Umbraco.Core/Persistence/Factories/MigrationEntryFactory.cs b/src/Umbraco.Core/Persistence/Factories/MigrationEntryFactory.cs
new file mode 100644
index 0000000000..1cb7000293
--- /dev/null
+++ b/src/Umbraco.Core/Persistence/Factories/MigrationEntryFactory.cs
@@ -0,0 +1,40 @@
+using System;
+using Semver;
+using Umbraco.Core.Models;
+using Umbraco.Core.Models.Rdbms;
+
+namespace Umbraco.Core.Persistence.Factories
+{
+ internal class MigrationEntryFactory
+ {
+ public MigrationEntry BuildEntity(MigrationDto dto)
+ {
+ SemVersion parsed;
+ if (SemVersion.TryParse(dto.Version, out parsed) == false)
+ {
+ throw new FormatException("Cannot parse the version string in the database to a SemVersion object: " + dto.Version);
+ }
+
+ var model = new MigrationEntry(dto.Id, dto.CreateDate, dto.Name, parsed);
+ //on initial construction we don't want to have dirty properties tracked
+ // http://issues.umbraco.org/issue/U4-1946
+ model.ResetDirtyProperties(false);
+ return model;
+ }
+
+ public MigrationDto BuildDto(IMigrationEntry entity)
+ {
+ var dto = new MigrationDto
+ {
+ CreateDate = entity.CreateDate,
+ Name = entity.MigrationName,
+ Version = entity.Version.ToString()
+ };
+
+ if (entity.HasIdentity)
+ dto.Id = entity.Id;
+
+ return dto;
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Umbraco.Core/Persistence/Factories/PropertyFactory.cs b/src/Umbraco.Core/Persistence/Factories/PropertyFactory.cs
index 4e3653bf9e..8d51b627ea 100644
--- a/src/Umbraco.Core/Persistence/Factories/PropertyFactory.cs
+++ b/src/Umbraco.Core/Persistence/Factories/PropertyFactory.cs
@@ -63,7 +63,9 @@ namespace Umbraco.Core.Persistence.Factories
//Check if property has an Id and set it, so that it can be updated if it already exists
if (property.HasIdentity)
+ {
dto.Id = property.Id;
+ }
if (property.DataTypeDatabaseType == DataTypeDatabaseType.Integer)
{
@@ -82,11 +84,21 @@ namespace Umbraco.Core.Persistence.Factories
}
}
}
+ else if (property.DataTypeDatabaseType == DataTypeDatabaseType.Decimal && property.Value != null)
+ {
+ decimal val;
+ if (decimal.TryParse(property.Value.ToString(), out val))
+ {
+ dto.Decimal = val;
+ }
+ }
else if (property.DataTypeDatabaseType == DataTypeDatabaseType.Date && property.Value != null && string.IsNullOrWhiteSpace(property.Value.ToString()) == false)
{
DateTime date;
- if(DateTime.TryParse(property.Value.ToString(), out date))
+ if (DateTime.TryParse(property.Value.ToString(), out date))
+ {
dto.Date = date;
+ }
}
else if (property.DataTypeDatabaseType == DataTypeDatabaseType.Ntext && property.Value != null)
{
diff --git a/src/Umbraco.Core/Persistence/Factories/PropertyGroupFactory.cs b/src/Umbraco.Core/Persistence/Factories/PropertyGroupFactory.cs
index c7cd2094a1..5b2cad3415 100644
--- a/src/Umbraco.Core/Persistence/Factories/PropertyGroupFactory.cs
+++ b/src/Umbraco.Core/Persistence/Factories/PropertyGroupFactory.cs
@@ -8,21 +8,21 @@ namespace Umbraco.Core.Persistence.Factories
{
internal class PropertyGroupFactory
{
- private readonly int _id;
+ private readonly int _contentTypeId;
private readonly DateTime _createDate;
private readonly DateTime _updateDate;
//a callback to create a property type which can be injected via a contructor
private readonly Func _propertyTypeCtor;
- public PropertyGroupFactory(int id)
+ public PropertyGroupFactory(int contentTypeId)
{
- _id = id;
+ _contentTypeId = contentTypeId;
_propertyTypeCtor = (propertyEditorAlias, dbType, alias) => new PropertyType(propertyEditorAlias, dbType);
}
- public PropertyGroupFactory(int id, DateTime createDate, DateTime updateDate, Func propertyTypeCtor)
+ public PropertyGroupFactory(int contentTypeId, DateTime createDate, DateTime updateDate, Func propertyTypeCtor)
{
- _id = id;
+ _contentTypeId = contentTypeId;
_createDate = createDate;
_updateDate = updateDate;
_propertyTypeCtor = propertyTypeCtor;
@@ -30,29 +30,24 @@ namespace Umbraco.Core.Persistence.Factories
#region Implementation of IEntityFactory,IEnumerable>
- public IEnumerable BuildEntity(IEnumerable dto)
+ public IEnumerable BuildEntity(IEnumerable groupDtos)
{
+ // groupDtos contains all the groups, those that are defined on the current
+ // content type, and those that are inherited from composition content types
var propertyGroups = new PropertyGroupCollection();
- foreach (var groupDto in dto)
+ foreach (var groupDto in groupDtos)
{
var group = new PropertyGroup();
- //Only assign an Id if the PropertyGroup belongs to this ContentType
- if (groupDto.ContentTypeNodeId == _id)
- {
- group.Id = groupDto.Id;
- if (groupDto.ParentGroupId.HasValue)
- group.ParentId = groupDto.ParentGroupId.Value;
- }
- else
- {
- //If the PropertyGroup is inherited, we add a reference to the group as a Parent.
- group.ParentId = groupDto.Id;
- }
+ // if the group is defined on the current content type,
+ // assign its identifier, else it will be zero
+ if (groupDto.ContentTypeNodeId == _contentTypeId)
+ group.Id = groupDto.Id;
group.Name = groupDto.Text;
group.SortOrder = groupDto.SortOrder;
group.PropertyTypes = new PropertyTypeCollection();
+ group.Key = groupDto.UniqueId;
//Because we are likely to have a group with no PropertyTypes we need to ensure that these are excluded
var typeDtos = groupDto.PropertyTypeDtos.Where(x => x.Id > 0);
@@ -67,6 +62,7 @@ namespace Umbraco.Core.Persistence.Factories
propertyType.DataTypeDefinitionId = typeDto.DataTypeId;
propertyType.Description = typeDto.Description;
propertyType.Id = typeDto.Id;
+ propertyType.Key = typeDto.UniqueId;
propertyType.Name = typeDto.Name;
propertyType.Mandatory = typeDto.Mandatory;
propertyType.SortOrder = typeDto.SortOrder;
@@ -100,14 +96,14 @@ namespace Umbraco.Core.Persistence.Factories
{
var dto = new PropertyTypeGroupDto
{
- ContentTypeNodeId = _id,
+ ContentTypeNodeId = _contentTypeId,
SortOrder = propertyGroup.SortOrder,
- Text = propertyGroup.Name
+ Text = propertyGroup.Name,
+ UniqueId = propertyGroup.Key == Guid.Empty
+ ? Guid.NewGuid()
+ : propertyGroup.Key
};
- if (propertyGroup.ParentId.HasValue)
- dto.ParentGroupId = propertyGroup.ParentId.Value;
-
if (propertyGroup.HasIdentity)
dto.Id = propertyGroup.Id;
@@ -119,16 +115,19 @@ namespace Umbraco.Core.Persistence.Factories
internal PropertyTypeDto BuildPropertyTypeDto(int tabId, PropertyType propertyType)
{
var propertyTypeDto = new PropertyTypeDto
- {
- Alias = propertyType.Alias,
- ContentTypeId = _id,
- DataTypeId = propertyType.DataTypeDefinitionId,
- Description = propertyType.Description,
- Mandatory = propertyType.Mandatory,
- Name = propertyType.Name,
- SortOrder = propertyType.SortOrder,
- ValidationRegExp = propertyType.ValidationRegExp
- };
+ {
+ Alias = propertyType.Alias,
+ ContentTypeId = _contentTypeId,
+ DataTypeId = propertyType.DataTypeDefinitionId,
+ Description = propertyType.Description,
+ Mandatory = propertyType.Mandatory,
+ Name = propertyType.Name,
+ SortOrder = propertyType.SortOrder,
+ ValidationRegExp = propertyType.ValidationRegExp,
+ UniqueId = propertyType.Key == Guid.Empty
+ ? Guid.NewGuid()
+ : propertyType.Key
+ };
if (tabId != default(int))
{
diff --git a/src/Umbraco.Core/Persistence/Factories/ServerRegistrationFactory.cs b/src/Umbraco.Core/Persistence/Factories/ServerRegistrationFactory.cs
index aa0ed25ccd..9c315aef46 100644
--- a/src/Umbraco.Core/Persistence/Factories/ServerRegistrationFactory.cs
+++ b/src/Umbraco.Core/Persistence/Factories/ServerRegistrationFactory.cs
@@ -5,34 +5,31 @@ namespace Umbraco.Core.Persistence.Factories
{
internal class ServerRegistrationFactory
{
- #region Implementation of IEntityFactory
-
public ServerRegistration BuildEntity(ServerRegistrationDto dto)
{
- var model = new ServerRegistration(dto.Id, dto.ServerAddress, dto.ServerIdentity, dto.DateRegistered, dto.DateAccessed, dto.IsActive);
+ var model = new ServerRegistration(dto.Id, dto.ServerAddress, dto.ServerIdentity, dto.DateRegistered, dto.DateAccessed, dto.IsActive, dto.IsMaster);
//on initial construction we don't want to have dirty properties tracked
// http://issues.umbraco.org/issue/U4-1946
model.ResetDirtyProperties(false);
return model;
}
- public ServerRegistrationDto BuildDto(ServerRegistration entity)
+ public ServerRegistrationDto BuildDto(IServerRegistration entity)
{
var dto = new ServerRegistrationDto
- {
- ServerAddress = entity.ServerAddress,
- DateRegistered = entity.CreateDate,
- IsActive = entity.IsActive,
- DateAccessed = entity.UpdateDate,
- ServerIdentity = entity.ServerIdentity
- };
+ {
+ ServerAddress = entity.ServerAddress,
+ DateRegistered = entity.CreateDate,
+ IsActive = entity.IsActive,
+ IsMaster = ((ServerRegistration) entity).IsMaster,
+ DateAccessed = entity.UpdateDate,
+ ServerIdentity = entity.ServerIdentity
+ };
if (entity.HasIdentity)
dto.Id = entity.Id;
return dto;
}
-
- #endregion
}
}
\ No newline at end of file
diff --git a/src/Umbraco.Core/Persistence/Factories/TemplateFactory.cs b/src/Umbraco.Core/Persistence/Factories/TemplateFactory.cs
index 1e59c8e920..60cde916b6 100644
--- a/src/Umbraco.Core/Persistence/Factories/TemplateFactory.cs
+++ b/src/Umbraco.Core/Persistence/Factories/TemplateFactory.cs
@@ -33,9 +33,9 @@ namespace Umbraco.Core.Persistence.Factories
#region Implementation of IEntityFactory
- public Template BuildEntity(TemplateDto dto, IEnumerable childDefinitions)
+ public Template BuildEntity(TemplateDto dto, IEnumerable childDefinitions, Func getFileContent)
{
- var template = new Template(dto.NodeDto.Text, dto.Alias)
+ var template = new Template(dto.NodeDto.Text, dto.Alias, getFileContent)
{
CreateDate = dto.NodeDto.CreateDate,
Id = dto.NodeId,
@@ -43,12 +43,8 @@ namespace Umbraco.Core.Persistence.Factories
Path = dto.NodeDto.Path
};
- if (childDefinitions.Any(x => x.ParentId == dto.NodeId))
- {
- template.IsMasterTemplate = true;
- }
+ template.IsMasterTemplate = childDefinitions.Any(x => x.ParentId == dto.NodeId);
- //TODO: Change this to ParentId: http://issues.umbraco.org/issue/U4-5846
if(dto.NodeDto.ParentId > 0)
template.MasterTemplateId = new Lazy(() => dto.NodeDto.ParentId);
diff --git a/src/Umbraco.Core/Persistence/LockedRepository.cs b/src/Umbraco.Core/Persistence/LockedRepository.cs
new file mode 100644
index 0000000000..b5d2d672f2
--- /dev/null
+++ b/src/Umbraco.Core/Persistence/LockedRepository.cs
@@ -0,0 +1,27 @@
+using System;
+using Umbraco.Core.Persistence.Repositories;
+using Umbraco.Core.Persistence.UnitOfWork;
+
+namespace Umbraco.Core.Persistence
+{
+ internal class LockedRepository
+ where TRepository : IDisposable, IRepository
+ {
+ public LockedRepository(Transaction transaction, IDatabaseUnitOfWork unitOfWork, TRepository repository)
+ {
+ Transaction = transaction;
+ UnitOfWork = unitOfWork;
+ Repository = repository;
+ }
+
+ public Transaction Transaction { get; private set; }
+ public IDatabaseUnitOfWork UnitOfWork { get; private set; }
+ public TRepository Repository { get; private set; }
+
+ public void Commit()
+ {
+ UnitOfWork.Commit();
+ Transaction.Complete();
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Umbraco.Core/Persistence/LockingRepository.cs b/src/Umbraco.Core/Persistence/LockingRepository.cs
new file mode 100644
index 0000000000..f513073e71
--- /dev/null
+++ b/src/Umbraco.Core/Persistence/LockingRepository.cs
@@ -0,0 +1,103 @@
+using System;
+using System.Collections.Generic;
+using System.Data;
+using System.Linq;
+using Umbraco.Core.Persistence.Repositories;
+using Umbraco.Core.Persistence.UnitOfWork;
+
+namespace Umbraco.Core.Persistence
+{
+ internal class LockingRepository
+ where TRepository : IDisposable, IRepository
+ {
+ private readonly IDatabaseUnitOfWorkProvider _uowProvider;
+ private readonly Func _repositoryFactory;
+ private readonly int[] _readLockIds, _writeLockIds;
+
+ public LockingRepository(IDatabaseUnitOfWorkProvider uowProvider, Func repositoryFactory,
+ IEnumerable readLockIds, IEnumerable writeLockIds)
+ {
+ Mandate.ParameterNotNull(uowProvider, "uowProvider");
+ Mandate.ParameterNotNull(repositoryFactory, "repositoryFactory");
+
+ _uowProvider = uowProvider;
+ _repositoryFactory = repositoryFactory;
+ _readLockIds = readLockIds == null ? new int[0] : readLockIds.ToArray();
+ _writeLockIds = writeLockIds == null ? new int[0] : writeLockIds.ToArray();
+ }
+
+ public void WithReadLocked(Action> action, bool autoCommit = true)
+ {
+ var uow = _uowProvider.GetUnitOfWork();
+ using (var transaction = uow.Database.GetTransaction(IsolationLevel.RepeatableRead))
+ {
+ foreach (var lockId in _readLockIds)
+ uow.Database.AcquireLockNodeReadLock(lockId);
+
+ using (var repository = _repositoryFactory(uow))
+ {
+ action(new LockedRepository(transaction, uow, repository));
+ if (autoCommit == false) return;
+ uow.Commit();
+ transaction.Complete();
+ }
+ }
+ }
+
+ public TResult WithReadLocked(Func, TResult> func, bool autoCommit = true)
+ {
+ var uow = _uowProvider.GetUnitOfWork();
+ using (var transaction = uow.Database.GetTransaction(IsolationLevel.RepeatableRead))
+ {
+ foreach (var lockId in _readLockIds)
+ uow.Database.AcquireLockNodeReadLock(lockId);
+
+ using (var repository = _repositoryFactory(uow))
+ {
+ var ret = func(new LockedRepository(transaction, uow, repository));
+ if (autoCommit == false) return ret;
+ uow.Commit();
+ transaction.Complete();
+ return ret;
+ }
+ }
+ }
+
+ public void WithWriteLocked(Action> action, bool autoCommit = true)
+ {
+ var uow = _uowProvider.GetUnitOfWork();
+ using (var transaction = uow.Database.GetTransaction(IsolationLevel.RepeatableRead))
+ {
+ foreach (var lockId in _writeLockIds)
+ uow.Database.AcquireLockNodeWriteLock(lockId);
+
+ using (var repository = _repositoryFactory(uow))
+ {
+ action(new LockedRepository(transaction, uow, repository));
+ if (autoCommit == false) return;
+ uow.Commit();
+ transaction.Complete();
+ }
+ }
+ }
+
+ public TResult WithWriteLocked(Func, TResult> func, bool autoCommit = true)
+ {
+ var uow = _uowProvider.GetUnitOfWork();
+ using (var transaction = uow.Database.GetTransaction(IsolationLevel.RepeatableRead))
+ {
+ foreach (var lockId in _writeLockIds)
+ uow.Database.AcquireLockNodeReadLock(lockId);
+
+ using (var repository = _repositoryFactory(uow))
+ {
+ var ret = func(new LockedRepository(transaction, uow, repository));
+ if (autoCommit == false) return ret;
+ uow.Commit();
+ transaction.Complete();
+ return ret;
+ }
+ }
+ }
+ }
+}
diff --git a/src/Umbraco.Core/Persistence/Mappers/DomainMapper.cs b/src/Umbraco.Core/Persistence/Mappers/DomainMapper.cs
index 46c67e762d..5ea4426a21 100644
--- a/src/Umbraco.Core/Persistence/Mappers/DomainMapper.cs
+++ b/src/Umbraco.Core/Persistence/Mappers/DomainMapper.cs
@@ -6,7 +6,8 @@ using Umbraco.Core.Persistence.SqlSyntax;
namespace Umbraco.Core.Persistence.Mappers
{
- [MapperFor(typeof(DomainRepository.CacheableDomain))]
+ [MapperFor(typeof(IDomain))]
+ [MapperFor(typeof(UmbracoDomain))]
public sealed class DomainMapper : BaseMapper
{
private static readonly ConcurrentDictionary PropertyInfoCacheInstance = new ConcurrentDictionary();
@@ -23,10 +24,10 @@ namespace Umbraco.Core.Persistence.Mappers
protected override void BuildMap()
{
- CacheMap(src => src.Id, dto => dto.Id);
- CacheMap(src => src.RootContentId, dto => dto.RootStructureId);
- CacheMap(src => src.DefaultLanguageId, dto => dto.DefaultLanguage);
- CacheMap(src => src.DomainName, dto => dto.DomainName);
+ CacheMap(src => src.Id, dto => dto.Id);
+ CacheMap(src => src.RootContentId, dto => dto.RootStructureId);
+ CacheMap(src => src.LanguageId, dto => dto.DefaultLanguage);
+ CacheMap(src => src.DomainName, dto => dto.DomainName);
}
}
}
\ No newline at end of file
diff --git a/src/Umbraco.Core/Persistence/Mappers/MigrationEntryMapper.cs b/src/Umbraco.Core/Persistence/Mappers/MigrationEntryMapper.cs
new file mode 100644
index 0000000000..5c0838ede9
--- /dev/null
+++ b/src/Umbraco.Core/Persistence/Mappers/MigrationEntryMapper.cs
@@ -0,0 +1,38 @@
+using System.Collections.Concurrent;
+using Umbraco.Core.Models;
+using Umbraco.Core.Models.Rdbms;
+
+namespace Umbraco.Core.Persistence.Mappers
+{
+ [MapperFor(typeof(MigrationEntry))]
+ [MapperFor(typeof(IMigrationEntry))]
+ internal sealed class MigrationEntryMapper : BaseMapper
+ {
+ private static readonly ConcurrentDictionary PropertyInfoCacheInstance = new ConcurrentDictionary();
+
+ //NOTE: its an internal class but the ctor must be public since we're using Activator.CreateInstance to create it
+ // otherwise that would fail because there is no public constructor.
+ public MigrationEntryMapper()
+ {
+ BuildMap();
+ }
+
+ #region Overrides of BaseMapper
+
+ internal override ConcurrentDictionary PropertyInfoCache
+ {
+ get { return PropertyInfoCacheInstance; }
+ }
+
+ internal override void BuildMap()
+ {
+ CacheMap(src => src.Id, dto => dto.Id);
+ CacheMap(src => src.CreateDate, dto => dto.CreateDate);
+ CacheMap(src => src.UpdateDate, dto => dto.CreateDate);
+ CacheMap(src => src.Version, dto => dto.Version);
+ CacheMap(src => src.MigrationName, dto => dto.Name);
+ }
+
+ #endregion
+ }
+}
\ No newline at end of file
diff --git a/src/Umbraco.Core/Persistence/Mappers/PropertyGroupMapper.cs b/src/Umbraco.Core/Persistence/Mappers/PropertyGroupMapper.cs
index 0b44b24702..384f3e3866 100644
--- a/src/Umbraco.Core/Persistence/Mappers/PropertyGroupMapper.cs
+++ b/src/Umbraco.Core/Persistence/Mappers/PropertyGroupMapper.cs
@@ -31,7 +31,7 @@ namespace Umbraco.Core.Persistence.Mappers
protected override void BuildMap()
{
CacheMap(src => src.Id, dto => dto.Id);
- CacheMap(src => src.ParentId, dto => dto.ParentGroupId);
+ CacheMap(src => src.Key, dto => dto.UniqueId);
CacheMap(src => src.SortOrder, dto => dto.SortOrder);
CacheMap(src => src.Name, dto => dto.Text);
}
diff --git a/src/Umbraco.Core/Persistence/Mappers/PropertyTypeMapper.cs b/src/Umbraco.Core/Persistence/Mappers/PropertyTypeMapper.cs
index 216d68b7cc..f770874c02 100644
--- a/src/Umbraco.Core/Persistence/Mappers/PropertyTypeMapper.cs
+++ b/src/Umbraco.Core/Persistence/Mappers/PropertyTypeMapper.cs
@@ -31,6 +31,7 @@ namespace Umbraco.Core.Persistence.Mappers
{
if(PropertyInfoCache.IsEmpty)
{
+ CacheMap(src => src.Key, dto => dto.UniqueId);
CacheMap(src => src.Id, dto => dto.Id);
CacheMap(src => src.Alias, dto => dto.Alias);
CacheMap(src => src.DataTypeDefinitionId, dto => dto.DataTypeId);
diff --git a/src/Umbraco.Core/Persistence/Mappers/ServerRegistrationMapper.cs b/src/Umbraco.Core/Persistence/Mappers/ServerRegistrationMapper.cs
index ea35da3e58..f57a258d7a 100644
--- a/src/Umbraco.Core/Persistence/Mappers/ServerRegistrationMapper.cs
+++ b/src/Umbraco.Core/Persistence/Mappers/ServerRegistrationMapper.cs
@@ -8,6 +8,7 @@ using Umbraco.Core.Persistence.SqlSyntax;
namespace Umbraco.Core.Persistence.Mappers
{
[MapperFor(typeof(ServerRegistration))]
+ [MapperFor(typeof(IServerRegistration))]
internal sealed class ServerRegistrationMapper : BaseMapper
{
private static readonly ConcurrentDictionary PropertyInfoCacheInstance = new ConcurrentDictionary();
@@ -28,6 +29,7 @@ namespace Umbraco.Core.Persistence.Mappers
{
CacheMap(src => src.Id, dto => dto.Id);
CacheMap(src => src.IsActive, dto => dto.IsActive);
+ CacheMap(src => src.IsMaster, dto => dto.IsMaster);
CacheMap(src => src.ServerAddress, dto => dto.ServerAddress);
CacheMap(src => src.CreateDate, dto => dto.DateRegistered);
CacheMap(src => src.UpdateDate, dto => dto.DateAccessed);
diff --git a/src/Umbraco.Core/Persistence/Mappers/TaskTypeMapper.cs b/src/Umbraco.Core/Persistence/Mappers/TaskTypeMapper.cs
new file mode 100644
index 0000000000..4399fd323b
--- /dev/null
+++ b/src/Umbraco.Core/Persistence/Mappers/TaskTypeMapper.cs
@@ -0,0 +1,38 @@
+using System.Collections.Concurrent;
+using Umbraco.Core.Models;
+using Umbraco.Core.Models.Rdbms;
+
+namespace Umbraco.Core.Persistence.Mappers
+{
+ ///
+ /// Represents a to DTO mapper used to translate the properties of the public api
+ /// implementation to that of the database's DTO as sql: [tableName].[columnName].
+ ///
+ [MapperFor(typeof(TaskType))]
+ public sealed class TaskTypeMapper : BaseMapper
+ {
+ private static readonly ConcurrentDictionary PropertyInfoCacheInstance = new ConcurrentDictionary();
+
+ //NOTE: its an internal class but the ctor must be public since we're using Activator.CreateInstance to create it
+ // otherwise that would fail because there is no public constructor.
+ public TaskTypeMapper()
+ {
+ BuildMap();
+ }
+
+ #region Overrides of BaseMapper
+
+ internal override ConcurrentDictionary PropertyInfoCache
+ {
+ get { return PropertyInfoCacheInstance; }
+ }
+
+ internal override void BuildMap()
+ {
+ CacheMap(src => src.Id, dto => dto.Id);
+ CacheMap(src => src.Alias, dto => dto.Alias);
+ }
+
+ #endregion
+ }
+}
\ No newline at end of file
diff --git a/src/Umbraco.Core/Persistence/Mappers/TemplateMapper.cs b/src/Umbraco.Core/Persistence/Mappers/TemplateMapper.cs
index 6e1ad761ac..b4613ed9e4 100644
--- a/src/Umbraco.Core/Persistence/Mappers/TemplateMapper.cs
+++ b/src/Umbraco.Core/Persistence/Mappers/TemplateMapper.cs
@@ -38,6 +38,7 @@ namespace Umbraco.Core.Persistence.Mappers
{
CacheMap(src => src.Id, dto => dto.NodeId);
CacheMap(src => src.MasterTemplateId, dto => dto.ParentId);
+ CacheMap(src => src.Key, dto => dto.UniqueId);
CacheMap(src => src.Alias, dto => dto.Alias);
CacheMap(src => src.Content, dto => dto.Design);
}
diff --git a/src/Umbraco.Core/Persistence/Migrations/Initial/BaseDataCreation.cs b/src/Umbraco.Core/Persistence/Migrations/Initial/BaseDataCreation.cs
index 27f5ec434d..5e07a617d4 100644
--- a/src/Umbraco.Core/Persistence/Migrations/Initial/BaseDataCreation.cs
+++ b/src/Umbraco.Core/Persistence/Migrations/Initial/BaseDataCreation.cs
@@ -1,4 +1,5 @@
using System;
+using Umbraco.Core.Configuration;
using Umbraco.Core.Logging;
using Umbraco.Core.Models.Rdbms;
@@ -86,11 +87,17 @@ namespace Umbraco.Core.Persistence.Migrations.Initial
{
CreateUmbracoRelationTypeData();
}
+
if (tableName.Equals("cmsTaskType"))
{
CreateCmsTaskTypeData();
}
+ if (tableName.Equals("umbracoMigration"))
+ {
+ CreateUmbracoMigrationData();
+ }
+
_logger.Info(string.Format("Done creating data in table {0}", tableName));
}
@@ -101,7 +108,7 @@ namespace Umbraco.Core.Persistence.Migrations.Initial
_database.Insert("umbracoNode", "id", false, new NodeDto { NodeId = -21, Trashed = false, ParentId = -1, UserId = 0, Level = 0, Path = "-1,-21", SortOrder = 0, UniqueId = new Guid("BF7C7CBC-952F-4518-97A2-69E9C7B33842"), Text = "Recycle Bin", NodeObjectType = new Guid(Constants.ObjectTypes.MediaRecycleBin), CreateDate = DateTime.Now });
_database.Insert("umbracoNode", "id", false, new NodeDto { NodeId = -92, Trashed = false, ParentId = -1, UserId = 0, Level = 1, Path = "-1,-92", SortOrder = 35, UniqueId = new Guid("f0bc4bfb-b499-40d6-ba86-058885a5178c"), Text = "Label", NodeObjectType = new Guid(Constants.ObjectTypes.DataType), CreateDate = DateTime.Now });
_database.Insert("umbracoNode", "id", false, new NodeDto { NodeId = -90, Trashed = false, ParentId = -1, UserId = 0, Level = 1, Path = "-1,-90", SortOrder = 34, UniqueId = new Guid("84c6b441-31df-4ffe-b67e-67d5bc3ae65a"), Text = "Upload", NodeObjectType = new Guid(Constants.ObjectTypes.DataType), CreateDate = DateTime.Now });
- _database.Insert("umbracoNode", "id", false, new NodeDto { NodeId = -89, Trashed = false, ParentId = -1, UserId = 0, Level = 1, Path = "-1,-89", SortOrder = 33, UniqueId = new Guid("c6bac0dd-4ab9-45b1-8e30-e4b619ee5da3"), Text = "Textbox multiple", NodeObjectType = new Guid(Constants.ObjectTypes.DataType), CreateDate = DateTime.Now });
+ _database.Insert("umbracoNode", "id", false, new NodeDto { NodeId = -89, Trashed = false, ParentId = -1, UserId = 0, Level = 1, Path = "-1,-89", SortOrder = 33, UniqueId = new Guid("c6bac0dd-4ab9-45b1-8e30-e4b619ee5da3"), Text = "Textarea", NodeObjectType = new Guid(Constants.ObjectTypes.DataType), CreateDate = DateTime.Now });
_database.Insert("umbracoNode", "id", false, new NodeDto { NodeId = -88, Trashed = false, ParentId = -1, UserId = 0, Level = 1, Path = "-1,-88", SortOrder = 32, UniqueId = new Guid("0cc0eba1-9960-42c9-bf9b-60e150b429ae"), Text = "Textstring", NodeObjectType = new Guid(Constants.ObjectTypes.DataType), CreateDate = DateTime.Now });
_database.Insert("umbracoNode", "id", false, new NodeDto { NodeId = -87, Trashed = false, ParentId = -1, UserId = 0, Level = 1, Path = "-1,-87", SortOrder = 4, UniqueId = new Guid("ca90c950-0aff-4e72-b976-a30b1ac57dad"), Text = "Richtext editor", NodeObjectType = new Guid(Constants.ObjectTypes.DataType), CreateDate = DateTime.Now });
_database.Insert("umbracoNode", "id", false, new NodeDto { NodeId = -51, Trashed = false, ParentId = -1, UserId = 0, Level = 1, Path = "-1,-51", SortOrder = 2, UniqueId = new Guid("2e6d3631-066e-44b8-aec4-96f09099b2b5"), Text = "Numeric", NodeObjectType = new Guid(Constants.ObjectTypes.DataType), CreateDate = DateTime.Now });
@@ -110,8 +117,7 @@ namespace Umbraco.Core.Persistence.Migrations.Initial
_database.Insert("umbracoNode", "id", false, new NodeDto { NodeId = -42, Trashed = false, ParentId = -1, UserId = 0, Level = 1, Path = "-1,-42", SortOrder = 2, UniqueId = new Guid("0b6a45e7-44ba-430d-9da5-4e46060b9e03"), Text = "Dropdown", NodeObjectType = new Guid(Constants.ObjectTypes.DataType), CreateDate = DateTime.Now });
_database.Insert("umbracoNode", "id", false, new NodeDto { NodeId = -41, Trashed = false, ParentId = -1, UserId = 0, Level = 1, Path = "-1,-41", SortOrder = 2, UniqueId = new Guid("5046194e-4237-453c-a547-15db3a07c4e1"), Text = "Date Picker", NodeObjectType = new Guid(Constants.ObjectTypes.DataType), CreateDate = DateTime.Now });
_database.Insert("umbracoNode", "id", false, new NodeDto { NodeId = -40, Trashed = false, ParentId = -1, UserId = 0, Level = 1, Path = "-1,-40", SortOrder = 2, UniqueId = new Guid("bb5f57c9-ce2b-4bb9-b697-4caca783a805"), Text = "Radiobox", NodeObjectType = new Guid(Constants.ObjectTypes.DataType), CreateDate = DateTime.Now });
- _database.Insert("umbracoNode", "id", false, new NodeDto { NodeId = -39, Trashed = false, ParentId = -1, UserId = 0, Level = 1, Path = "-1,-39", SortOrder = 2, UniqueId = new Guid("f38f0ac7-1d27-439c-9f3f-089cd8825a53"), Text = "Dropdown multiple", NodeObjectType = new Guid(Constants.ObjectTypes.DataType), CreateDate = DateTime.Now });
- _database.Insert("umbracoNode", "id", false, new NodeDto { NodeId = -38, Trashed = false, ParentId = -1, UserId = 0, Level = 1, Path = "-1,-38", SortOrder = 2, UniqueId = new Guid("fd9f1447-6c61-4a7c-9595-5aa39147d318"), Text = "Folder Browser", NodeObjectType = new Guid(Constants.ObjectTypes.DataType), CreateDate = DateTime.Now });
+ _database.Insert("umbracoNode", "id", false, new NodeDto { NodeId = -39, Trashed = false, ParentId = -1, UserId = 0, Level = 1, Path = "-1,-39", SortOrder = 2, UniqueId = new Guid("f38f0ac7-1d27-439c-9f3f-089cd8825a53"), Text = "Dropdown multiple", NodeObjectType = new Guid(Constants.ObjectTypes.DataType), CreateDate = DateTime.Now });
_database.Insert("umbracoNode", "id", false, new NodeDto { NodeId = -37, Trashed = false, ParentId = -1, UserId = 0, Level = 1, Path = "-1,-37", SortOrder = 2, UniqueId = new Guid("0225af17-b302-49cb-9176-b9f35cab9c17"), Text = "Approved Color", NodeObjectType = new Guid(Constants.ObjectTypes.DataType), CreateDate = DateTime.Now });
_database.Insert("umbracoNode", "id", false, new NodeDto { NodeId = -36, Trashed = false, ParentId = -1, UserId = 0, Level = 1, Path = "-1,-36", SortOrder = 2, UniqueId = new Guid("e4d66c0f-b935-4200-81f0-025f7256b89a"), Text = "Date Picker with time", NodeObjectType = new Guid(Constants.ObjectTypes.DataType), CreateDate = DateTime.Now });
_database.Insert("umbracoNode", "id", false, new NodeDto { NodeId = Constants.System.DefaultContentListViewDataTypeId, Trashed = false, ParentId = -1, UserId = 0, Level = 1, Path = "-1,-95", SortOrder = 2, UniqueId = new Guid("C0808DD3-8133-4E4B-8CE8-E2BEA84A96A4"), Text = Constants.Conventions.DataTypes.ListViewPrefix + "Content", NodeObjectType = new Guid(Constants.ObjectTypes.DataType), CreateDate = DateTime.Now });
@@ -172,37 +178,33 @@ namespace Umbraco.Core.Persistence.Migrations.Initial
private void CreateCmsPropertyTypeGroupData()
{
- _database.Insert("cmsPropertyTypeGroup", "id", false, new PropertyTypeGroupDto { Id = 3, ContentTypeNodeId = 1032, Text = "Image", SortOrder = 1 });
- _database.Insert("cmsPropertyTypeGroup", "id", false, new PropertyTypeGroupDto { Id = 4, ContentTypeNodeId = 1033, Text = "File", SortOrder = 1 });
- _database.Insert("cmsPropertyTypeGroup", "id", false, new PropertyTypeGroupDto { Id = 5, ContentTypeNodeId = 1031, Text = "Contents", SortOrder = 1 });
+ _database.Insert("cmsPropertyTypeGroup", "id", false, new PropertyTypeGroupDto { Id = 3, ContentTypeNodeId = 1032, Text = "Image", SortOrder = 1, UniqueId = new Guid(Constants.PropertyTypeGroups.Image) });
+ _database.Insert("cmsPropertyTypeGroup", "id", false, new PropertyTypeGroupDto { Id = 4, ContentTypeNodeId = 1033, Text = "File", SortOrder = 1, UniqueId = new Guid(Constants.PropertyTypeGroups.File) });
+ _database.Insert("cmsPropertyTypeGroup", "id", false, new PropertyTypeGroupDto { Id = 5, ContentTypeNodeId = 1031, Text = "Contents", SortOrder = 1, UniqueId = new Guid(Constants.PropertyTypeGroups.Contents) });
//membership property group
- _database.Insert("cmsPropertyTypeGroup", "id", false, new PropertyTypeGroupDto { Id = 11, ContentTypeNodeId = 1044, Text = "Membership", SortOrder = 1 });
+ _database.Insert("cmsPropertyTypeGroup", "id", false, new PropertyTypeGroupDto { Id = 11, ContentTypeNodeId = 1044, Text = "Membership", SortOrder = 1, UniqueId = new Guid(Constants.PropertyTypeGroups.Membership) });
}
private void CreateCmsPropertyTypeData()
{
- _database.Insert("cmsPropertyType", "id", false, new PropertyTypeDto { Id = 6, DataTypeId = -90, ContentTypeId = 1032, PropertyTypeGroupId = 3, Alias = Constants.Conventions.Media.File, Name = "Upload image", SortOrder = 0, Mandatory = false, ValidationRegExp = null, Description = null });
- _database.Insert("cmsPropertyType", "id", false, new PropertyTypeDto { Id = 7, DataTypeId = -92, ContentTypeId = 1032, PropertyTypeGroupId = 3, Alias = Constants.Conventions.Media.Width, Name = "Width", SortOrder = 0, Mandatory = false, ValidationRegExp = null, Description = null });
- _database.Insert("cmsPropertyType", "id", false, new PropertyTypeDto { Id = 8, DataTypeId = -92, ContentTypeId = 1032, PropertyTypeGroupId = 3, Alias = Constants.Conventions.Media.Height, Name = "Height", SortOrder = 0, Mandatory = false, ValidationRegExp = null, Description = null });
- _database.Insert("cmsPropertyType", "id", false, new PropertyTypeDto { Id = 9, DataTypeId = -92, ContentTypeId = 1032, PropertyTypeGroupId = 3, Alias = Constants.Conventions.Media.Bytes, Name = "Size", SortOrder = 0, Mandatory = false, ValidationRegExp = null, Description = null });
- _database.Insert("cmsPropertyType", "id", false, new PropertyTypeDto { Id = 10, DataTypeId = -92, ContentTypeId = 1032, PropertyTypeGroupId = 3, Alias = Constants.Conventions.Media.Extension, Name = "Type", SortOrder = 0, Mandatory = false, ValidationRegExp = null, Description = null });
- _database.Insert("cmsPropertyType", "id", false, new PropertyTypeDto { Id = 24, DataTypeId = -90, ContentTypeId = 1033, PropertyTypeGroupId = 4, Alias = Constants.Conventions.Media.File, Name = "Upload file", SortOrder = 0, Mandatory = false, ValidationRegExp = null, Description = null });
- _database.Insert("cmsPropertyType", "id", false, new PropertyTypeDto { Id = 25, DataTypeId = -92, ContentTypeId = 1033, PropertyTypeGroupId = 4, Alias = Constants.Conventions.Media.Extension, Name = "Type", SortOrder = 0, Mandatory = false, ValidationRegExp = null, Description = null });
- _database.Insert("cmsPropertyType", "id", false, new PropertyTypeDto { Id = 26, DataTypeId = -92, ContentTypeId = 1033, PropertyTypeGroupId = 4, Alias = Constants.Conventions.Media.Bytes, Name = "Size", SortOrder = 0, Mandatory = false, ValidationRegExp = null, Description = null });
- _database.Insert("cmsPropertyType", "id", false, new PropertyTypeDto { Id = 27, DataTypeId = -38, ContentTypeId = 1031, PropertyTypeGroupId = 5, Alias = "contents", Name = "Contents:", SortOrder = 0, Mandatory = false, ValidationRegExp = null, Description = null });
+ _database.Insert("cmsPropertyType", "id", false, new PropertyTypeDto { Id = 6, UniqueId = 6.ToGuid(), DataTypeId = 1043, ContentTypeId = 1032, PropertyTypeGroupId = 3, Alias = Constants.Conventions.Media.File, Name = "Upload image", SortOrder = 0, Mandatory = false, ValidationRegExp = null, Description = null });
+ _database.Insert("cmsPropertyType", "id", false, new PropertyTypeDto { Id = 7, UniqueId = 7.ToGuid(), DataTypeId = -92, ContentTypeId = 1032, PropertyTypeGroupId = 3, Alias = Constants.Conventions.Media.Width, Name = "Width", SortOrder = 0, Mandatory = false, ValidationRegExp = null, Description = null });
+ _database.Insert("cmsPropertyType", "id", false, new PropertyTypeDto { Id = 8, UniqueId = 8.ToGuid(), DataTypeId = -92, ContentTypeId = 1032, PropertyTypeGroupId = 3, Alias = Constants.Conventions.Media.Height, Name = "Height", SortOrder = 0, Mandatory = false, ValidationRegExp = null, Description = null });
+ _database.Insert("cmsPropertyType", "id", false, new PropertyTypeDto { Id = 9, UniqueId = 9.ToGuid(), DataTypeId = -92, ContentTypeId = 1032, PropertyTypeGroupId = 3, Alias = Constants.Conventions.Media.Bytes, Name = "Size", SortOrder = 0, Mandatory = false, ValidationRegExp = null, Description = null });
+ _database.Insert("cmsPropertyType", "id", false, new PropertyTypeDto { Id = 10, UniqueId = 10.ToGuid(), DataTypeId = -92, ContentTypeId = 1032, PropertyTypeGroupId = 3, Alias = Constants.Conventions.Media.Extension, Name = "Type", SortOrder = 0, Mandatory = false, ValidationRegExp = null, Description = null });
+ _database.Insert("cmsPropertyType", "id", false, new PropertyTypeDto { Id = 24, UniqueId = 24.ToGuid(), DataTypeId = -90, ContentTypeId = 1033, PropertyTypeGroupId = 4, Alias = Constants.Conventions.Media.File, Name = "Upload file", SortOrder = 0, Mandatory = false, ValidationRegExp = null, Description = null });
+ _database.Insert("cmsPropertyType", "id", false, new PropertyTypeDto { Id = 25, UniqueId = 25.ToGuid(), DataTypeId = -92, ContentTypeId = 1033, PropertyTypeGroupId = 4, Alias = Constants.Conventions.Media.Extension, Name = "Type", SortOrder = 0, Mandatory = false, ValidationRegExp = null, Description = null });
+ _database.Insert("cmsPropertyType", "id", false, new PropertyTypeDto { Id = 26, UniqueId = 26.ToGuid(), DataTypeId = -92, ContentTypeId = 1033, PropertyTypeGroupId = 4, Alias = Constants.Conventions.Media.Bytes, Name = "Size", SortOrder = 0, Mandatory = false, ValidationRegExp = null, Description = null });
+ _database.Insert("cmsPropertyType", "id", false, new PropertyTypeDto { Id = 27, UniqueId = 27.ToGuid(), DataTypeId = Constants.System.DefaultMediaListViewDataTypeId, ContentTypeId = 1031, PropertyTypeGroupId = 5, Alias = "contents", Name = "Contents:", SortOrder = 0, Mandatory = false, ValidationRegExp = null, Description = null });
//membership property types
- _database.Insert("cmsPropertyType", "id", false, new PropertyTypeDto { Id = 28, DataTypeId = -89, ContentTypeId = 1044, PropertyTypeGroupId = 11, Alias = Constants.Conventions.Member.Comments, Name = Constants.Conventions.Member.CommentsLabel, SortOrder = 0, Mandatory = false, ValidationRegExp = null, Description = null });
- _database.Insert("cmsPropertyType", "id", false, new PropertyTypeDto { Id = 29, DataTypeId = -92, ContentTypeId = 1044, PropertyTypeGroupId = 11, Alias = Constants.Conventions.Member.FailedPasswordAttempts, Name = Constants.Conventions.Member.FailedPasswordAttemptsLabel, SortOrder = 1, Mandatory = false, ValidationRegExp = null, Description = null });
- _database.Insert("cmsPropertyType", "id", false, new PropertyTypeDto { Id = 30, DataTypeId = -49, ContentTypeId = 1044, PropertyTypeGroupId = 11, Alias = Constants.Conventions.Member.IsApproved, Name = Constants.Conventions.Member.IsApprovedLabel, SortOrder = 2, Mandatory = false, ValidationRegExp = null, Description = null });
- _database.Insert("cmsPropertyType", "id", false, new PropertyTypeDto { Id = 31, DataTypeId = -49, ContentTypeId = 1044, PropertyTypeGroupId = 11, Alias = Constants.Conventions.Member.IsLockedOut, Name = Constants.Conventions.Member.IsLockedOutLabel, SortOrder = 3, Mandatory = false, ValidationRegExp = null, Description = null });
- _database.Insert("cmsPropertyType", "id", false, new PropertyTypeDto { Id = 32, DataTypeId = -92, ContentTypeId = 1044, PropertyTypeGroupId = 11, Alias = Constants.Conventions.Member.LastLockoutDate, Name = Constants.Conventions.Member.LastLockoutDateLabel, SortOrder = 4, Mandatory = false, ValidationRegExp = null, Description = null });
- _database.Insert("cmsPropertyType", "id", false, new PropertyTypeDto { Id = 33, DataTypeId = -92, ContentTypeId = 1044, PropertyTypeGroupId = 11, Alias = Constants.Conventions.Member.LastLoginDate, Name = Constants.Conventions.Member.LastLoginDateLabel, SortOrder = 5, Mandatory = false, ValidationRegExp = null, Description = null });
- _database.Insert("cmsPropertyType", "id", false, new PropertyTypeDto { Id = 34, DataTypeId = -92, ContentTypeId = 1044, PropertyTypeGroupId = 11, Alias = Constants.Conventions.Member.LastPasswordChangeDate, Name = Constants.Conventions.Member.LastPasswordChangeDateLabel, SortOrder = 6, Mandatory = false, ValidationRegExp = null, Description = null });
+ _database.Insert("cmsPropertyType", "id", false, new PropertyTypeDto { Id = 28, UniqueId = 28.ToGuid(), DataTypeId = -89, ContentTypeId = 1044, PropertyTypeGroupId = 11, Alias = Constants.Conventions.Member.Comments, Name = Constants.Conventions.Member.CommentsLabel, SortOrder = 0, Mandatory = false, ValidationRegExp = null, Description = null });
+ _database.Insert("cmsPropertyType", "id", false, new PropertyTypeDto { Id = 29, UniqueId = 29.ToGuid(), DataTypeId = -92, ContentTypeId = 1044, PropertyTypeGroupId = 11, Alias = Constants.Conventions.Member.FailedPasswordAttempts, Name = Constants.Conventions.Member.FailedPasswordAttemptsLabel, SortOrder = 1, Mandatory = false, ValidationRegExp = null, Description = null });
+ _database.Insert("cmsPropertyType", "id", false, new PropertyTypeDto { Id = 30, UniqueId = 30.ToGuid(), DataTypeId = -49, ContentTypeId = 1044, PropertyTypeGroupId = 11, Alias = Constants.Conventions.Member.IsApproved, Name = Constants.Conventions.Member.IsApprovedLabel, SortOrder = 2, Mandatory = false, ValidationRegExp = null, Description = null });
+ _database.Insert("cmsPropertyType", "id", false, new PropertyTypeDto { Id = 31, UniqueId = 31.ToGuid(), DataTypeId = -49, ContentTypeId = 1044, PropertyTypeGroupId = 11, Alias = Constants.Conventions.Member.IsLockedOut, Name = Constants.Conventions.Member.IsLockedOutLabel, SortOrder = 3, Mandatory = false, ValidationRegExp = null, Description = null });
+ _database.Insert("cmsPropertyType", "id", false, new PropertyTypeDto { Id = 32, UniqueId = 32.ToGuid(), DataTypeId = -92, ContentTypeId = 1044, PropertyTypeGroupId = 11, Alias = Constants.Conventions.Member.LastLockoutDate, Name = Constants.Conventions.Member.LastLockoutDateLabel, SortOrder = 4, Mandatory = false, ValidationRegExp = null, Description = null });
+ _database.Insert("cmsPropertyType", "id", false, new PropertyTypeDto { Id = 33, UniqueId = 33.ToGuid(), DataTypeId = -92, ContentTypeId = 1044, PropertyTypeGroupId = 11, Alias = Constants.Conventions.Member.LastLoginDate, Name = Constants.Conventions.Member.LastLoginDateLabel, SortOrder = 5, Mandatory = false, ValidationRegExp = null, Description = null });
+ _database.Insert("cmsPropertyType", "id", false, new PropertyTypeDto { Id = 34, UniqueId = 34.ToGuid(), DataTypeId = -92, ContentTypeId = 1044, PropertyTypeGroupId = 11, Alias = Constants.Conventions.Member.LastPasswordChangeDate, Name = Constants.Conventions.Member.LastPasswordChangeDateLabel, SortOrder = 6, Mandatory = false, ValidationRegExp = null, Description = null });
- //TODO: The member editor doesn't currently support providers that have question/answer so we'll leave these out for now.
- // Also, it's worth noting that the built in ASP.Net providers encrypt the answer so that admins cannot see it for added security which is something we should actually do!
- //_database.Insert("cmsPropertyType", "id", false, new PropertyTypeDto { Id = 35, DataTypeId = -92, ContentTypeId = 1044, PropertyTypeGroupId = 11, Alias = Constants.Conventions.Member.PasswordAnswer, Name = Constants.Conventions.Member.PasswordAnswerLabel, HelpText = null, SortOrder = 0, Mandatory = false, ValidationRegExp = null, Description = null });
- //_database.Insert("cmsPropertyType", "id", false, new PropertyTypeDto { Id = 36, DataTypeId = -92, ContentTypeId = 1044, PropertyTypeGroupId = 11, Alias = Constants.Conventions.Member.PasswordQuestion, Name = Constants.Conventions.Member.PasswordQuestionLabel, HelpText = null, SortOrder = 0, Mandatory = false, ValidationRegExp = null, Description = null });
}
private void CreateUmbracoLanguageData()
@@ -228,8 +230,7 @@ namespace Umbraco.Core.Persistence.Migrations.Initial
_database.Insert("cmsDataType", "pk", false, new DataTypeDto { PrimaryKey = 6, DataTypeId = -90, PropertyEditorAlias = Constants.PropertyEditors.UploadFieldAlias, DbType = "Nvarchar" });
_database.Insert("cmsDataType", "pk", false, new DataTypeDto { PrimaryKey = 7, DataTypeId = -92, PropertyEditorAlias = Constants.PropertyEditors.NoEditAlias, DbType = "Nvarchar" });
_database.Insert("cmsDataType", "pk", false, new DataTypeDto { PrimaryKey = 8, DataTypeId = -36, PropertyEditorAlias = Constants.PropertyEditors.DateTimeAlias, DbType = "Date" });
- _database.Insert("cmsDataType", "pk", false, new DataTypeDto { PrimaryKey = 9, DataTypeId = -37, PropertyEditorAlias = Constants.PropertyEditors.ColorPickerAlias, DbType = "Nvarchar" });
- _database.Insert("cmsDataType", "pk", false, new DataTypeDto { PrimaryKey = 10, DataTypeId = -38, PropertyEditorAlias = Constants.PropertyEditors.FolderBrowserAlias, DbType = "Nvarchar" });
+ _database.Insert("cmsDataType", "pk", false, new DataTypeDto { PrimaryKey = 9, DataTypeId = -37, PropertyEditorAlias = Constants.PropertyEditors.ColorPickerAlias, DbType = "Nvarchar" });
_database.Insert("cmsDataType", "pk", false, new DataTypeDto { PrimaryKey = 11, DataTypeId = -39, PropertyEditorAlias = Constants.PropertyEditors.DropDownListMultipleAlias, DbType = "Nvarchar" });
_database.Insert("cmsDataType", "pk", false, new DataTypeDto { PrimaryKey = 12, DataTypeId = -40, PropertyEditorAlias = Constants.PropertyEditors.RadioButtonListAlias, DbType = "Nvarchar" });
_database.Insert("cmsDataType", "pk", false, new DataTypeDto { PrimaryKey = 13, DataTypeId = -41, PropertyEditorAlias = Constants.PropertyEditors.DateAlias, DbType = "Date" });
@@ -250,7 +251,6 @@ namespace Umbraco.Core.Persistence.Migrations.Initial
//_database.Insert("cmsDataType", "pk", false, new DataTypeDto { PrimaryKey = 19, DataTypeId = 1038, PropertyEditorAlias = Constants.PropertyEditors.MarkdownEditorAlias, DbType = "Ntext" });
//_database.Insert("cmsDataType", "pk", false, new DataTypeDto { PrimaryKey = 20, DataTypeId = 1039, PropertyEditorAlias = Constants.PropertyEditors.UltimatePickerAlias, DbType = "Ntext" });
//_database.Insert("cmsDataType", "pk", false, new DataTypeDto { PrimaryKey = 23, DataTypeId = 1042, PropertyEditorAlias = Constants.PropertyEditors.MacroContainerAlias, DbType = "Ntext" });
-
}
private void CreateCmsDataTypePreValuesData()
@@ -266,6 +266,17 @@ namespace Umbraco.Core.Persistence.Migrations.Initial
_database.Insert("cmsDataTypePreValues", "id", false, new DataTypePreValueDto { Id = -2, Alias = "orderBy", SortOrder = 2, DataTypeNodeId = Constants.System.DefaultMembersListViewDataTypeId, Value = "Name" });
_database.Insert("cmsDataTypePreValues", "id", false, new DataTypePreValueDto { Id = -3, Alias = "orderDirection", SortOrder = 3, DataTypeNodeId = Constants.System.DefaultMembersListViewDataTypeId, Value = "asc" });
_database.Insert("cmsDataTypePreValues", "id", false, new DataTypePreValueDto { Id = -4, Alias = "includeProperties", SortOrder = 4, DataTypeNodeId = Constants.System.DefaultMembersListViewDataTypeId, Value = "[{\"alias\":\"email\",\"isSystem\":1},{\"alias\":\"username\",\"isSystem\":1},{\"alias\":\"updateDate\",\"header\":\"Last edited\",\"isSystem\":1}]" });
+
+ //layouts for the list view
+ var cardLayout = "{\"name\": \"Grid\",\"path\": \"views/propertyeditors/listview/layouts/grid/grid.html\", \"icon\": \"icon-thumbnails-small\", \"isSystem\": 1, \"selected\": true}";
+ var listLayout = "{\"name\": \"List\",\"path\": \"views/propertyeditors/listview/layouts/list/list.html\",\"icon\": \"icon-list\", \"isSystem\": 1,\"selected\": true}";
+
+ //defaults for the media list
+ _database.Insert("cmsDataTypePreValues", "id", false, new DataTypePreValueDto { Id = -5, Alias = "pageSize", SortOrder = 1, DataTypeNodeId = Constants.System.DefaultMediaListViewDataTypeId, Value = "100" });
+ _database.Insert("cmsDataTypePreValues", "id", false, new DataTypePreValueDto { Id = -6, Alias = "orderBy", SortOrder = 2, DataTypeNodeId = Constants.System.DefaultMediaListViewDataTypeId, Value = "VersionDate" });
+ _database.Insert("cmsDataTypePreValues", "id", false, new DataTypePreValueDto { Id = -7, Alias = "orderDirection", SortOrder = 3, DataTypeNodeId = Constants.System.DefaultMediaListViewDataTypeId, Value = "desc" });
+ _database.Insert("cmsDataTypePreValues", "id", false, new DataTypePreValueDto { Id = -8, Alias = "layouts", SortOrder = 4, DataTypeNodeId = Constants.System.DefaultMediaListViewDataTypeId, Value = "[" + cardLayout + "," + listLayout + "]" });
+ _database.Insert("cmsDataTypePreValues", "id", false, new DataTypePreValueDto { Id = -9, Alias = "includeProperties", SortOrder = 5, DataTypeNodeId = Constants.System.DefaultMediaListViewDataTypeId, Value = "[{\"alias\":\"sortOrder\",\"isSystem\":1, \"header\": \"Sort order\"},{\"alias\":\"updateDate\",\"header\":\"Last edited\",\"isSystem\":1},{\"alias\":\"owner\",\"header\":\"Updated by\",\"isSystem\":1}]" });
}
private void CreateUmbracoRelationTypeData()
@@ -278,5 +289,18 @@ namespace Umbraco.Core.Persistence.Migrations.Initial
{
_database.Insert("cmsTaskType", "id", false, new TaskTypeDto { Id = 1, Alias = "toTranslate" });
}
+
+ private void CreateUmbracoMigrationData()
+ {
+ var dto = new MigrationDto
+ {
+ Id = 1,
+ Name = GlobalSettings.UmbracoMigrationName,
+ Version = UmbracoVersion.GetSemanticVersion().ToString(),
+ CreateDate = DateTime.Now
+ };
+
+ _database.Insert("umbracoMigration", "pk", false, dto);
+ }
}
}
\ No newline at end of file
diff --git a/src/Umbraco.Core/Persistence/Migrations/Initial/DatabaseSchemaCreation.cs b/src/Umbraco.Core/Persistence/Migrations/Initial/DatabaseSchemaCreation.cs
index 1888a9f0aa..f789c18aa1 100644
--- a/src/Umbraco.Core/Persistence/Migrations/Initial/DatabaseSchemaCreation.cs
+++ b/src/Umbraco.Core/Persistence/Migrations/Initial/DatabaseSchemaCreation.cs
@@ -44,12 +44,12 @@ namespace Umbraco.Core.Persistence.Migrations.Initial
{4, typeof (ContentVersionDto)},
{5, typeof (DocumentDto)},
- {6, typeof (DocumentTypeDto)},
+ {6, typeof (ContentTypeTemplateDto)},
{7, typeof (DataTypeDto)},
{8, typeof (DataTypePreValueDto)},
{9, typeof (DictionaryDto)},
- {10, typeof (LanguageTextDto)},
- {11, typeof (LanguageDto)},
+ {10, typeof (LanguageDto)},
+ {11, typeof (LanguageTextDto)},
{12, typeof (DomainDto)},
{13, typeof (LogDto)},
{14, typeof (MacroDto)},
@@ -68,7 +68,6 @@ namespace Umbraco.Core.Persistence.Migrations.Initial
{27, typeof (StylesheetPropertyDto)},
{28, typeof (TagDto)},
{29, typeof (TagRelationshipDto)},
- {30, typeof (UserLoginDto)},
{31, typeof (UserTypeDto)},
{32, typeof (UserDto)},
{33, typeof (TaskTypeDto)},
@@ -82,7 +81,10 @@ namespace Umbraco.Core.Persistence.Migrations.Initial
{41, typeof (AccessDto)},
{42, typeof (AccessRuleDto)},
{43, typeof(CacheInstructionDto)},
- {44, typeof (ExternalLoginDto)}
+ {44, typeof (ExternalLoginDto)},
+ {45, typeof (MigrationDto)},
+ {46, typeof (UmbracoDeployChecksumDto)},
+ {47, typeof (UmbracoDeployDependencyDto)}
};
#endregion
diff --git a/src/Umbraco.Core/Persistence/Migrations/Initial/DatabaseSchemaResult.cs b/src/Umbraco.Core/Persistence/Migrations/Initial/DatabaseSchemaResult.cs
index 5c5678a90a..70ab65b422 100644
--- a/src/Umbraco.Core/Persistence/Migrations/Initial/DatabaseSchemaResult.cs
+++ b/src/Umbraco.Core/Persistence/Migrations/Initial/DatabaseSchemaResult.cs
@@ -2,9 +2,11 @@ using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
+using Semver;
using Umbraco.Core.Configuration;
using Umbraco.Core.Persistence.DatabaseModelDefinitions;
using Umbraco.Core.Persistence.SqlSyntax;
+using Umbraco.Core.Services;
namespace Umbraco.Core.Persistence.Migrations.Initial
{
@@ -38,7 +40,20 @@ namespace Umbraco.Core.Persistence.Migrations.Initial
internal IEnumerable DbIndexDefinitions { get; set; }
///
- /// Determines the version of the currently installed database.
+ /// Checks in the db which version is installed based on the migrations that have been run
+ ///
+ ///
+ ///
+ public SemVersion DetermineInstalledVersionByMigrations(IMigrationEntryService migrationEntryService)
+ {
+ var allMigrations = migrationEntryService.GetAll(GlobalSettings.UmbracoMigrationName);
+ var mostrecent = allMigrations.OrderByDescending(x => x.Version).Select(x => x.Version).FirstOrDefault();
+
+ return mostrecent ?? new SemVersion(new Version(0, 0, 0));
+ }
+
+ ///
+ /// Determines the version of the currently installed database by detecting the current database structure
///
///
/// A with Major and Minor values for
@@ -107,6 +122,12 @@ namespace Umbraco.Core.Persistence.Migrations.Initial
return new Version(7, 2, 5);
}
+ //if the error is for umbracoDeployChecksum it must be the previous version to 7.4 since that is when it is added
+ if (Errors.Any(x => x.Item1.Equals("Table") && (x.Item2.InvariantEquals("umbracoDeployChecksum"))))
+ {
+ return new Version(7, 3, 4);
+ }
+
return UmbracoVersion.Current;
}
diff --git a/src/Umbraco.Core/Persistence/Migrations/MigrationBase.cs b/src/Umbraco.Core/Persistence/Migrations/MigrationBase.cs
index 64ad867008..70821d301f 100644
--- a/src/Umbraco.Core/Persistence/Migrations/MigrationBase.cs
+++ b/src/Umbraco.Core/Persistence/Migrations/MigrationBase.cs
@@ -42,7 +42,7 @@ namespace Umbraco.Core.Persistence.Migrations
public IAlterSyntaxBuilder Alter
{
- get { return new AlterSyntaxBuilder(Context); }
+ get { return new AlterSyntaxBuilder(Context, SqlSyntax); }
}
public ICreateBuilder Create
@@ -57,27 +57,27 @@ namespace Umbraco.Core.Persistence.Migrations
public IExecuteBuilder Execute
{
- get { return new ExecuteBuilder(Context); }
+ get { return new ExecuteBuilder(Context, SqlSyntax); }
}
public IInsertBuilder Insert
{
- get { return new InsertBuilder(Context); }
+ get { return new InsertBuilder(Context, SqlSyntax); }
}
public IRenameBuilder Rename
{
- get { return new RenameBuilder(Context); }
+ get { return new RenameBuilder(Context, SqlSyntax); }
}
public IUpdateBuilder Update
{
- get { return new UpdateBuilder(Context); }
+ get { return new UpdateBuilder(Context, SqlSyntax); }
}
public IIfDatabaseBuilder IfDatabase(params DatabaseProviders[] databaseProviders)
{
- return new IfDatabaseBuilder(Context, databaseProviders);
+ return new IfDatabaseBuilder(Context, SqlSyntax, databaseProviders);
}
}
}
\ No newline at end of file
diff --git a/src/Umbraco.Core/Persistence/Migrations/MigrationExpressionBase.cs b/src/Umbraco.Core/Persistence/Migrations/MigrationExpressionBase.cs
index 5349658c8e..6c3ef66735 100644
--- a/src/Umbraco.Core/Persistence/Migrations/MigrationExpressionBase.cs
+++ b/src/Umbraco.Core/Persistence/Migrations/MigrationExpressionBase.cs
@@ -42,5 +42,34 @@ namespace Umbraco.Core.Persistence.Migrations
/// to ensure they are not executed twice.
///
internal string Name { get; set; }
+
+ protected string GetQuotedValue(object val)
+ {
+ if (val == null) return "NULL";
+
+ var type = val.GetType();
+
+ switch (Type.GetTypeCode(type))
+ {
+ case TypeCode.Boolean:
+ return ((bool)val) ? "1" : "0";
+ case TypeCode.Single:
+ case TypeCode.Double:
+ case TypeCode.Decimal:
+ case TypeCode.SByte:
+ case TypeCode.Int16:
+ case TypeCode.Int32:
+ case TypeCode.Int64:
+ case TypeCode.Byte:
+ case TypeCode.UInt16:
+ case TypeCode.UInt32:
+ case TypeCode.UInt64:
+ return val.ToString();
+ case TypeCode.DateTime:
+ return SqlSyntax.GetQuotedValue(SqlSyntax.FormatDateTime((DateTime) val));
+ default:
+ return SqlSyntax.GetQuotedValue(val.ToString());
+ }
+ }
}
}
\ No newline at end of file
diff --git a/src/Umbraco.Core/Persistence/Migrations/MigrationResolver.cs b/src/Umbraco.Core/Persistence/Migrations/MigrationResolver.cs
index 54ee5f507b..2b81424a21 100644
--- a/src/Umbraco.Core/Persistence/Migrations/MigrationResolver.cs
+++ b/src/Umbraco.Core/Persistence/Migrations/MigrationResolver.cs
@@ -27,5 +27,9 @@ namespace Umbraco.Core.Persistence.Migrations
get { return Values; }
}
+ {
+ ApplicationContext.Current.DatabaseContext.SqlSyntax,
+ ApplicationContext.Current.ProfilingLogger.Logger
+ });
}
}
\ No newline at end of file
diff --git a/src/Umbraco.Core/Persistence/Migrations/MigrationRunner.cs b/src/Umbraco.Core/Persistence/Migrations/MigrationRunner.cs
index 90b8933b17..0d4cc66ad5 100644
--- a/src/Umbraco.Core/Persistence/Migrations/MigrationRunner.cs
+++ b/src/Umbraco.Core/Persistence/Migrations/MigrationRunner.cs
@@ -1,10 +1,16 @@
using System;
using System.Collections.Generic;
+using System.IO;
using System.Linq;
+using System.Text;
+using log4net;
+using Semver;
+using Umbraco.Core.Configuration;
using Umbraco.Core.Events;
using Umbraco.Core.Logging;
using Umbraco.Core.Persistence.Migrations.Syntax.IfDatabase;
using Umbraco.Core.Persistence.SqlSyntax;
+using Umbraco.Core.Services;
namespace Umbraco.Core.Persistence.Migrations
{
@@ -14,42 +20,54 @@ namespace Umbraco.Core.Persistence.Migrations
///
public class MigrationRunner
{
- private readonly IMigrationResolver _resolver;
+ private readonly IMigrationEntryService _migrationEntryService;
private readonly ILogger _logger;
- private readonly Version _currentVersion;
- private readonly Version _targetVersion;
+ private readonly SemVersion _currentVersion;
+ private readonly SemVersion _targetVersion;
private readonly string _productName;
private readonly IMigration[] _migrations;
- public MigrationRunner(IMigrationResolver resolver, ILogger logger, Version currentVersion, Version targetVersion, string productName)
+ [Obsolete("Use the ctor that specifies all dependencies instead")]
+ public MigrationRunner(ILogger logger, Version currentVersion, Version targetVersion, string productName)
+ : this(logger, currentVersion, targetVersion, productName, null)
{
- if (logger == null) throw new ArgumentNullException("logger");
- if (currentVersion == null) throw new ArgumentNullException("currentVersion");
- if (targetVersion == null) throw new ArgumentNullException("targetVersion");
- Mandate.ParameterNotNullOrEmpty(productName, "productName");
-
- _resolver = resolver;
- _logger = logger;
- _currentVersion = currentVersion;
- _targetVersion = targetVersion;
- _productName = productName;
}
-
+
+ [Obsolete("Use the ctor that specifies all dependencies instead")]
public MigrationRunner(ILogger logger, Version currentVersion, Version targetVersion, string productName, params IMigration[] migrations)
+ : this(ApplicationContext.Current.Services.MigrationEntryService, logger, new SemVersion(currentVersion), new SemVersion(targetVersion), productName, migrations)
{
+
+ }
+
+ public MigrationRunner(IMigrationEntryService migrationEntryService, ILogger logger, SemVersion currentVersion, SemVersion targetVersion, string productName, params IMigration[] migrations)
+ {
+ if (migrationEntryService == null) throw new ArgumentNullException("migrationEntryService");
if (logger == null) throw new ArgumentNullException("logger");
if (currentVersion == null) throw new ArgumentNullException("currentVersion");
if (targetVersion == null) throw new ArgumentNullException("targetVersion");
Mandate.ParameterNotNullOrEmpty(productName, "productName");
+ _migrationEntryService = migrationEntryService;
_logger = logger;
_currentVersion = currentVersion;
_targetVersion = targetVersion;
_productName = productName;
- _migrations = migrations;
+ //ensure this is null if there aren't any
+ _migrations = migrations.Length == 0 ? null : migrations;
}
+ ///
+ /// Executes the migrations against the database.
+ ///
+ /// The PetaPoco Database, which the migrations will be run against
+ /// Boolean indicating whether this is an upgrade or downgrade
+ /// True if migrations were applied, otherwise False
+ public virtual bool Execute(Database database, bool isUpgrade = true)
+ {
+ return Execute(database, database.GetDatabaseProvider(), isUpgrade);
+ }
///
/// Executes the migrations against the database.
@@ -70,10 +88,12 @@ namespace Umbraco.Core.Persistence.Migrations
? OrderedUpgradeMigrations(foundMigrations).ToList()
: OrderedDowngradeMigrations(foundMigrations).ToList();
- //SD: Why do we want this?
- //MCH: Because extensibility ... Mostly relevant to package developers who needs to utilize this type of event to add or remove migrations from the list
+
if (Migrating.IsRaisedEventCancelled(new MigrationEventArgs(migrations, _currentVersion, _targetVersion, true), this))
+ {
+ _logger.Warn("Migration was cancelled by an event");
return false;
+ }
//Loop through migrations to generate sql
var migrationContext = InitializeMigrations(migrations, database, databaseProvider, sqlSyntaxProvider, isUpgrade);
@@ -110,17 +130,21 @@ namespace Umbraco.Core.Persistence.Migrations
///
public IEnumerable OrderedUpgradeMigrations(IEnumerable foundMigrations)
{
+ //get the version instance to compare with the migrations, this will be a normal c# Version object with only 3 parts
+ var targetVersionToCompare = _targetVersion.GetVersion(3);
+ var currentVersionToCompare = _currentVersion.GetVersion(3);
+
var migrations = (from migration in foundMigrations
- let migrationAttributes = migration.GetType().GetCustomAttributes(false)
- from migrationAttribute in migrationAttributes
- where migrationAttribute != null
- where migrationAttribute.TargetVersion > _currentVersion &&
- migrationAttribute.TargetVersion <= _targetVersion &&
- migrationAttribute.ProductName == _productName &&
- //filter if the migration specifies a minimum current version for which to execute
- (migrationAttribute.MinimumCurrentVersion == null || _currentVersion >= migrationAttribute.MinimumCurrentVersion)
- orderby migrationAttribute.TargetVersion, migrationAttribute.SortOrder ascending
- select migration).Distinct();
+ let migrationAttributes = migration.GetType().GetCustomAttributes(false)
+ from migrationAttribute in migrationAttributes
+ where migrationAttribute != null
+ where migrationAttribute.TargetVersion > currentVersionToCompare &&
+ migrationAttribute.TargetVersion <= targetVersionToCompare &&
+ migrationAttribute.ProductName == _productName &&
+ //filter if the migration specifies a minimum current version for which to execute
+ (migrationAttribute.MinimumCurrentVersion == null || currentVersionToCompare >= migrationAttribute.MinimumCurrentVersion)
+ orderby migrationAttribute.TargetVersion, migrationAttribute.SortOrder ascending
+ select migration).Distinct();
return migrations;
}
@@ -131,18 +155,22 @@ namespace Umbraco.Core.Persistence.Migrations
///
public IEnumerable OrderedDowngradeMigrations(IEnumerable foundMigrations)
{
+ //get the version instance to compare with the migrations, this will be a normal c# Version object with only 3 parts
+ var targetVersionToCompare = _targetVersion.GetVersion(3);
+ var currentVersionToCompare = _currentVersion.GetVersion(3);
+
var migrations = (from migration in foundMigrations
- let migrationAttributes = migration.GetType().GetCustomAttributes(false)
- from migrationAttribute in migrationAttributes
- where migrationAttribute != null
- where
- migrationAttribute.TargetVersion > _currentVersion &&
- migrationAttribute.TargetVersion <= _targetVersion &&
+ let migrationAttributes = migration.GetType().GetCustomAttributes(false)
+ from migrationAttribute in migrationAttributes
+ where migrationAttribute != null
+ where
+ migrationAttribute.TargetVersion > currentVersionToCompare &&
+ migrationAttribute.TargetVersion <= targetVersionToCompare &&
migrationAttribute.ProductName == _productName &&
//filter if the migration specifies a minimum current version for which to execute
- (migrationAttribute.MinimumCurrentVersion == null || _currentVersion >= migrationAttribute.MinimumCurrentVersion)
- orderby migrationAttribute.TargetVersion, migrationAttribute.SortOrder descending
- select migration).Distinct();
+ (migrationAttribute.MinimumCurrentVersion == null || currentVersionToCompare >= migrationAttribute.MinimumCurrentVersion)
+ orderby migrationAttribute.TargetVersion, migrationAttribute.SortOrder descending
+ select migration).Distinct();
return migrations;
}
@@ -216,12 +244,59 @@ namespace Umbraco.Core.Persistence.Migrations
continue;
}
- _logger.Info("Executing sql statement " + i + ": " + sql);
- database.Execute(sql);
+ //TODO: We should output all of these SQL calls to files in a migration folder in App_Data/TEMP
+ // so if people want to executed them manually on another environment, they can.
+
+ //The following ensures the multiple statement sare executed one at a time, this is a requirement
+ // of SQLCE, it's unfortunate but necessary.
+ // http://stackoverflow.com/questions/13665491/sql-ce-inconsistent-with-multiple-statements
+ var sb = new StringBuilder();
+ using (var reader = new StringReader(sql))
+ {
+ string line;
+ while ((line = reader.ReadLine()) != null)
+ {
+ line = line.Trim();
+ if (line.Equals("GO", StringComparison.OrdinalIgnoreCase))
+ {
+ //Execute the SQL up to the point of a GO statement
+ var exeSql = sb.ToString();
+ _logger.Info("Executing sql statement " + i + ": " + exeSql);
+ database.Execute(exeSql);
+
+ //restart the string builder
+ sb.Remove(0, sb.Length);
+ }
+ else
+ {
+ sb.AppendLine(line);
+ }
+ }
+ //execute anything remaining
+ if (sb.Length > 0)
+ {
+ var exeSql = sb.ToString();
+ _logger.Info("Executing sql statement " + i + ": " + exeSql);
+ database.Execute(exeSql);
+ }
+ }
+
i++;
}
transaction.Complete();
+
+ //Now that this is all complete, we need to add an entry to the migrations table flagging that migrations
+ // for this version have executed.
+ //NOTE: We CANNOT do this as part of the transaction!!! This is because when upgrading to 7.3, we cannot
+ // create the migrations table and then add data to it in the same transaction without issuing things like GO
+ // commands and since we need to support all Dbs, we need to just do this after the fact.
+ var exists = _migrationEntryService.FindEntry(GlobalSettings.UmbracoMigrationName, _targetVersion);
+ if (exists == null)
+ {
+ _migrationEntryService.CreateEntry(_productName, _targetVersion);
+ }
+
}
}
diff --git a/src/Umbraco.Core/Persistence/Migrations/Syntax/Alter/AlterSyntaxBuilder.cs b/src/Umbraco.Core/Persistence/Migrations/Syntax/Alter/AlterSyntaxBuilder.cs
index 93499141ca..e253f8e607 100644
--- a/src/Umbraco.Core/Persistence/Migrations/Syntax/Alter/AlterSyntaxBuilder.cs
+++ b/src/Umbraco.Core/Persistence/Migrations/Syntax/Alter/AlterSyntaxBuilder.cs
@@ -1,30 +1,35 @@
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;
namespace Umbraco.Core.Persistence.Migrations.Syntax.Alter
{
public class AlterSyntaxBuilder : IAlterSyntaxBuilder
{
private readonly IMigrationContext _context;
+ private readonly ISqlSyntaxProvider _sqlSyntax;
+ private readonly DatabaseProviders[] _databaseProviders;
- public AlterSyntaxBuilder(IMigrationContext context)
+ public AlterSyntaxBuilder(IMigrationContext context, ISqlSyntaxProvider sqlSyntax, params DatabaseProviders[] databaseProviders)
{
_context = context;
+ _sqlSyntax = sqlSyntax;
+ _databaseProviders = databaseProviders;
}
public IAlterTableSyntax Table(string tableName)
{
- var expression = new AlterTableExpression(_context.SqlSyntax, _context.CurrentDatabaseProvider) { TableName = tableName };
+ var expression = new AlterTableExpression(_context.CurrentDatabaseProvider, _databaseProviders, _sqlSyntax) { TableName = tableName };
//_context.Expressions.Add(expression);
- return new AlterTableBuilder(expression, _context);
+ return new AlterTableBuilder(_context, _databaseProviders, expression);
}
public IAlterColumnSyntax Column(string columnName)
{
- var expression = new AlterColumnExpression(_context.SqlSyntax, _context.CurrentDatabaseProvider) { Column = { Name = columnName } };
+ var expression = new AlterColumnExpression(_context.CurrentDatabaseProvider, _databaseProviders, _sqlSyntax) {Column = {Name = columnName}};
//_context.Expressions.Add(expression);
- return new AlterColumnBuilder(expression, _context);
+ return new AlterColumnBuilder(_context, _databaseProviders, expression);
}
}
}
\ No newline at end of file
diff --git a/src/Umbraco.Core/Persistence/Migrations/Syntax/Alter/Column/AlterColumnBuilder.cs b/src/Umbraco.Core/Persistence/Migrations/Syntax/Alter/Column/AlterColumnBuilder.cs
index 3eaecb00bd..a7077f2d35 100644
--- a/src/Umbraco.Core/Persistence/Migrations/Syntax/Alter/Column/AlterColumnBuilder.cs
+++ b/src/Umbraco.Core/Persistence/Migrations/Syntax/Alter/Column/AlterColumnBuilder.cs
@@ -2,6 +2,7 @@
using Umbraco.Core.Persistence.DatabaseModelDefinitions;
using Umbraco.Core.Persistence.Migrations.Syntax.Alter.Expressions;
using Umbraco.Core.Persistence.Migrations.Syntax.Expressions;
+using Umbraco.Core.Persistence.SqlSyntax;
namespace Umbraco.Core.Persistence.Migrations.Syntax.Alter.Column
{
@@ -11,11 +12,13 @@ namespace Umbraco.Core.Persistence.Migrations.Syntax.Alter.Column
IAlterColumnOptionForeignKeyCascadeSyntax
{
private readonly IMigrationContext _context;
+ private readonly DatabaseProviders[] _databaseProviders;
- public AlterColumnBuilder(AlterColumnExpression expression, IMigrationContext context)
+ public AlterColumnBuilder(IMigrationContext context, DatabaseProviders[] databaseProviders, AlterColumnExpression expression)
: base(expression)
{
_context = context;
+ _databaseProviders = databaseProviders;
}
public ForeignKeyDefinition CurrentForeignKey { get; set; }
@@ -33,7 +36,7 @@ namespace Umbraco.Core.Persistence.Migrations.Syntax.Alter.Column
public IAlterColumnOptionSyntax WithDefault(SystemMethods method)
{
- var dc = new AlterDefaultConstraintExpression(_context.SqlSyntax, _context.CurrentDatabaseProvider)
+ var dc = new AlterDefaultConstraintExpression(_context.CurrentDatabaseProvider, _databaseProviders, Expression.SqlSyntax)
{
TableName = Expression.TableName,
SchemaName = Expression.SchemaName,
@@ -51,7 +54,7 @@ namespace Umbraco.Core.Persistence.Migrations.Syntax.Alter.Column
public IAlterColumnOptionSyntax WithDefaultValue(object value)
{
- var dc = new AlterDefaultConstraintExpression(_context.SqlSyntax, _context.CurrentDatabaseProvider)
+ var dc = new AlterDefaultConstraintExpression(_context.CurrentDatabaseProvider, _databaseProviders, Expression.SqlSyntax)
{
TableName = Expression.TableName,
SchemaName = Expression.SchemaName,
@@ -81,15 +84,12 @@ namespace Umbraco.Core.Persistence.Migrations.Syntax.Alter.Column
{
Expression.Column.IsIndexed = true;
- var index = new CreateIndexExpression(_context.SqlSyntax, _context.CurrentDatabaseProvider)
- {
- Index = new IndexDefinition
- {
- Name = indexName,
- SchemaName = Expression.SchemaName,
- TableName = Expression.TableName
- }
- };
+ var index = new CreateIndexExpression(_context.CurrentDatabaseProvider, _databaseProviders, Expression.SqlSyntax, new IndexDefinition
+ {
+ Name = indexName,
+ SchemaName = Expression.SchemaName,
+ TableName = Expression.TableName
+ });
index.Index.Columns.Add(new IndexColumnDefinition
{
@@ -135,16 +135,13 @@ namespace Umbraco.Core.Persistence.Migrations.Syntax.Alter.Column
{
Expression.Column.IsUnique = true;
- var index = new CreateIndexExpression(_context.SqlSyntax, _context.CurrentDatabaseProvider)
- {
- Index = new IndexDefinition
- {
- Name = indexName,
- SchemaName = Expression.SchemaName,
- TableName = Expression.TableName,
- IsUnique = true
- }
- };
+ var index = new CreateIndexExpression(_context.CurrentDatabaseProvider, _databaseProviders, Expression.SqlSyntax, new IndexDefinition
+ {
+ Name = indexName,
+ SchemaName = Expression.SchemaName,
+ TableName = Expression.TableName,
+ IsUnique = true
+ });
index.Index.Columns.Add(new IndexColumnDefinition
{
@@ -172,17 +169,14 @@ namespace Umbraco.Core.Persistence.Migrations.Syntax.Alter.Column
{
Expression.Column.IsForeignKey = true;
- var fk = new CreateForeignKeyExpression(_context.SqlSyntax, _context.CurrentDatabaseProvider)
- {
- ForeignKey = new ForeignKeyDefinition
- {
- Name = foreignKeyName,
- PrimaryTable = primaryTableName,
- PrimaryTableSchema = primaryTableSchema,
- ForeignTable = Expression.TableName,
- ForeignTableSchema = Expression.SchemaName
- }
- };
+ var fk = new CreateForeignKeyExpression(_context.CurrentDatabaseProvider, _databaseProviders, Expression.SqlSyntax, new ForeignKeyDefinition
+ {
+ Name = foreignKeyName,
+ PrimaryTable = primaryTableName,
+ PrimaryTableSchema = primaryTableSchema,
+ ForeignTable = Expression.TableName,
+ ForeignTableSchema = Expression.SchemaName
+ });
fk.ForeignKey.PrimaryColumns.Add(primaryColumnName);
fk.ForeignKey.ForeignColumns.Add(Expression.Column.Name);
@@ -212,17 +206,14 @@ namespace Umbraco.Core.Persistence.Migrations.Syntax.Alter.Column
public IAlterColumnOptionForeignKeyCascadeSyntax ReferencedBy(string foreignKeyName, string foreignTableSchema,
string foreignTableName, string foreignColumnName)
{
- var fk = new CreateForeignKeyExpression(_context.SqlSyntax, _context.CurrentDatabaseProvider)
- {
- ForeignKey = new ForeignKeyDefinition
- {
- Name = foreignKeyName,
- PrimaryTable = Expression.TableName,
- PrimaryTableSchema = Expression.SchemaName,
- ForeignTable = foreignTableName,
- ForeignTableSchema = foreignTableSchema
- }
- };
+ var fk = new CreateForeignKeyExpression(_context.CurrentDatabaseProvider, _databaseProviders, Expression.SqlSyntax, new ForeignKeyDefinition
+ {
+ Name = foreignKeyName,
+ PrimaryTable = Expression.TableName,
+ PrimaryTableSchema = Expression.SchemaName,
+ ForeignTable = foreignTableName,
+ ForeignTableSchema = foreignTableSchema
+ });
fk.ForeignKey.PrimaryColumns.Add(Expression.Column.Name);
fk.ForeignKey.ForeignColumns.Add(foreignColumnName);
diff --git a/src/Umbraco.Core/Persistence/Migrations/Syntax/Alter/Expressions/AlterColumnExpression.cs b/src/Umbraco.Core/Persistence/Migrations/Syntax/Alter/Expressions/AlterColumnExpression.cs
index 578210f018..77234d1cfb 100644
--- a/src/Umbraco.Core/Persistence/Migrations/Syntax/Alter/Expressions/AlterColumnExpression.cs
+++ b/src/Umbraco.Core/Persistence/Migrations/Syntax/Alter/Expressions/AlterColumnExpression.cs
@@ -6,30 +6,27 @@ namespace Umbraco.Core.Persistence.Migrations.Syntax.Alter.Expressions
{
public class AlterColumnExpression : MigrationExpressionBase
{
- public AlterColumnExpression(ISqlSyntaxProvider sqlSyntax, DatabaseProviders currentDatabaseProvider, DatabaseProviders[] supportedDatabaseProviders = null)
- : base(sqlSyntax, currentDatabaseProvider, supportedDatabaseProviders)
+
+ public AlterColumnExpression(DatabaseProviders current, DatabaseProviders[] databaseProviders, ISqlSyntaxProvider sqlSyntax)
+ : base(current, databaseProviders, sqlSyntax)
{
Column = new ColumnDefinition() { ModificationType = ModificationType.Alter };
}
+
+
+
public virtual string SchemaName { get; set; }
public virtual string TableName { get; set; }
public virtual ColumnDefinition Column { get; set; }
public override string ToString()
{
- //string columnNameFormat = string.Format("{0} {1}",
- // SqlSyntaxContext.SqlSyntaxProvider.GetQuotedColumnName(Column.Name),
- // SqlSyntaxContext.SqlSyntaxProvider.Format(Column));
- return string.Format(
- SqlSyntax.AlterColumn,
- SqlSyntax.GetQuotedTableName(TableName),
- SqlSyntax.Format(Column));
+ return string.Format(SqlSyntax.AlterColumn,
+ SqlSyntax.GetQuotedTableName(TableName),
+ SqlSyntax.Format(Column));
- //return string.Format(SqlSyntaxContext.SqlSyntaxProvider.AlterColumn,
- // SqlSyntaxContext.SqlSyntaxProvider.GetQuotedTableName(TableName),
- // SqlSyntaxContext.SqlSyntaxProvider.GetQuotedColumnName(Column.Name));
}
}
}
\ No newline at end of file
diff --git a/src/Umbraco.Core/Persistence/Migrations/Syntax/Alter/Expressions/AlterDefaultConstraintExpression.cs b/src/Umbraco.Core/Persistence/Migrations/Syntax/Alter/Expressions/AlterDefaultConstraintExpression.cs
index 9998db7fc4..cd83f55a35 100644
--- a/src/Umbraco.Core/Persistence/Migrations/Syntax/Alter/Expressions/AlterDefaultConstraintExpression.cs
+++ b/src/Umbraco.Core/Persistence/Migrations/Syntax/Alter/Expressions/AlterDefaultConstraintExpression.cs
@@ -4,8 +4,8 @@ namespace Umbraco.Core.Persistence.Migrations.Syntax.Alter.Expressions
{
public class AlterDefaultConstraintExpression : MigrationExpressionBase
{
- public AlterDefaultConstraintExpression(ISqlSyntaxProvider sqlSyntax, DatabaseProviders currentDatabaseProvider, DatabaseProviders[] supportedDatabaseProviders = null)
- : base(sqlSyntax, currentDatabaseProvider, supportedDatabaseProviders)
+ public AlterDefaultConstraintExpression(DatabaseProviders current, DatabaseProviders[] databaseProviders, ISqlSyntaxProvider sqlSyntax)
+ : base(current, databaseProviders, sqlSyntax)
{
}
diff --git a/src/Umbraco.Core/Persistence/Migrations/Syntax/Alter/Expressions/AlterTableExpression.cs b/src/Umbraco.Core/Persistence/Migrations/Syntax/Alter/Expressions/AlterTableExpression.cs
index 9e09b39e70..b5906ed324 100644
--- a/src/Umbraco.Core/Persistence/Migrations/Syntax/Alter/Expressions/AlterTableExpression.cs
+++ b/src/Umbraco.Core/Persistence/Migrations/Syntax/Alter/Expressions/AlterTableExpression.cs
@@ -4,8 +4,9 @@ namespace Umbraco.Core.Persistence.Migrations.Syntax.Alter.Expressions
{
public class AlterTableExpression : MigrationExpressionBase
{
- public AlterTableExpression(ISqlSyntaxProvider sqlSyntax, DatabaseProviders currentDatabaseProvider, DatabaseProviders[] supportedDatabaseProviders = null)
- : base(sqlSyntax, currentDatabaseProvider, supportedDatabaseProviders)
+
+ public AlterTableExpression(DatabaseProviders current, DatabaseProviders[] databaseProviders, ISqlSyntaxProvider sqlSyntax)
+ : base(current, databaseProviders, sqlSyntax)
{
}
diff --git a/src/Umbraco.Core/Persistence/Migrations/Syntax/Alter/Table/AlterTableBuilder.cs b/src/Umbraco.Core/Persistence/Migrations/Syntax/Alter/Table/AlterTableBuilder.cs
index 150d662b7b..7a6ab8d47a 100644
--- a/src/Umbraco.Core/Persistence/Migrations/Syntax/Alter/Table/AlterTableBuilder.cs
+++ b/src/Umbraco.Core/Persistence/Migrations/Syntax/Alter/Table/AlterTableBuilder.cs
@@ -2,6 +2,7 @@
using Umbraco.Core.Persistence.DatabaseModelDefinitions;
using Umbraco.Core.Persistence.Migrations.Syntax.Alter.Expressions;
using Umbraco.Core.Persistence.Migrations.Syntax.Expressions;
+using Umbraco.Core.Persistence.SqlSyntax;
namespace Umbraco.Core.Persistence.Migrations.Syntax.Alter.Table
{
@@ -10,11 +11,13 @@ namespace Umbraco.Core.Persistence.Migrations.Syntax.Alter.Table
IAlterTableColumnOptionForeignKeyCascadeSyntax
{
private readonly IMigrationContext _context;
+ private readonly DatabaseProviders[] _databaseProviders;
- public AlterTableBuilder(AlterTableExpression expression, IMigrationContext context)
+ public AlterTableBuilder(IMigrationContext context, DatabaseProviders[] databaseProviders, AlterTableExpression expression)
: base(expression)
{
_context = context;
+ _databaseProviders = databaseProviders;
}
public ColumnDefinition CurrentColumn { get; set; }
@@ -36,7 +39,7 @@ namespace Umbraco.Core.Persistence.Migrations.Syntax.Alter.Table
{
if (CurrentColumn.ModificationType == ModificationType.Alter)
{
- var dc = new AlterDefaultConstraintExpression(_context.SqlSyntax, _context.CurrentDatabaseProvider)
+ var dc = new AlterDefaultConstraintExpression(_context.CurrentDatabaseProvider, _databaseProviders, Expression.SqlSyntax)
{
TableName = Expression.TableName,
SchemaName = Expression.SchemaName,
@@ -66,15 +69,12 @@ namespace Umbraco.Core.Persistence.Migrations.Syntax.Alter.Table
{
CurrentColumn.IsIndexed = true;
- var index = new CreateIndexExpression(_context.SqlSyntax, _context.CurrentDatabaseProvider)
- {
- Index = new IndexDefinition
- {
- Name = indexName,
- SchemaName = Expression.SchemaName,
- TableName = Expression.TableName
- }
- };
+ var index = new CreateIndexExpression(_context.CurrentDatabaseProvider, _databaseProviders, Expression.SqlSyntax, new IndexDefinition
+ {
+ Name = indexName,
+ SchemaName = Expression.SchemaName,
+ TableName = Expression.TableName
+ });
index.Index.Columns.Add(new IndexColumnDefinition
{
@@ -120,16 +120,13 @@ namespace Umbraco.Core.Persistence.Migrations.Syntax.Alter.Table
{
CurrentColumn.IsUnique = true;
- var index = new CreateIndexExpression(_context.SqlSyntax, _context.CurrentDatabaseProvider)
- {
- Index = new IndexDefinition
- {
- Name = indexName,
- SchemaName = Expression.SchemaName,
- TableName = Expression.TableName,
- IsUnique = true
- }
- };
+ var index = new CreateIndexExpression(_context.CurrentDatabaseProvider, _databaseProviders, Expression.SqlSyntax, new IndexDefinition
+ {
+ Name = indexName,
+ SchemaName = Expression.SchemaName,
+ TableName = Expression.TableName,
+ IsUnique = true
+ });
index.Index.Columns.Add(new IndexColumnDefinition
{
@@ -157,17 +154,14 @@ namespace Umbraco.Core.Persistence.Migrations.Syntax.Alter.Table
{
CurrentColumn.IsForeignKey = true;
- var fk = new CreateForeignKeyExpression(_context.SqlSyntax, _context.CurrentDatabaseProvider)
- {
- ForeignKey = new ForeignKeyDefinition
- {
- Name = foreignKeyName,
- PrimaryTable = primaryTableName,
- PrimaryTableSchema = primaryTableSchema,
- ForeignTable = Expression.TableName,
- ForeignTableSchema = Expression.SchemaName
- }
- };
+ var fk = new CreateForeignKeyExpression(_context.CurrentDatabaseProvider, _databaseProviders, Expression.SqlSyntax, new ForeignKeyDefinition
+ {
+ Name = foreignKeyName,
+ PrimaryTable = primaryTableName,
+ PrimaryTableSchema = primaryTableSchema,
+ ForeignTable = Expression.TableName,
+ ForeignTableSchema = Expression.SchemaName
+ });
fk.ForeignKey.PrimaryColumns.Add(primaryColumnName);
fk.ForeignKey.ForeignColumns.Add(CurrentColumn.Name);
@@ -197,17 +191,14 @@ namespace Umbraco.Core.Persistence.Migrations.Syntax.Alter.Table
public IAlterTableColumnOptionForeignKeyCascadeSyntax ReferencedBy(string foreignKeyName, string foreignTableSchema,
string foreignTableName, string foreignColumnName)
{
- var fk = new CreateForeignKeyExpression(_context.SqlSyntax, _context.CurrentDatabaseProvider)
- {
- ForeignKey = new ForeignKeyDefinition
- {
- Name = foreignKeyName,
- PrimaryTable = Expression.TableName,
- PrimaryTableSchema = Expression.SchemaName,
- ForeignTable = foreignTableName,
- ForeignTableSchema = foreignTableSchema
- }
- };
+ var fk = new CreateForeignKeyExpression(_context.CurrentDatabaseProvider, _databaseProviders, Expression.SqlSyntax, new ForeignKeyDefinition
+ {
+ Name = foreignKeyName,
+ PrimaryTable = Expression.TableName,
+ PrimaryTableSchema = Expression.SchemaName,
+ ForeignTable = foreignTableName,
+ ForeignTableSchema = foreignTableSchema
+ });
fk.ForeignKey.PrimaryColumns.Add(CurrentColumn.Name);
fk.ForeignKey.ForeignColumns.Add(foreignColumnName);
@@ -220,7 +211,7 @@ namespace Umbraco.Core.Persistence.Migrations.Syntax.Alter.Table
public IAlterTableColumnTypeSyntax AddColumn(string name)
{
var column = new ColumnDefinition { Name = name, ModificationType = ModificationType.Create };
- var createColumn = new CreateColumnExpression(_context.SqlSyntax, _context.CurrentDatabaseProvider)
+ var createColumn = new CreateColumnExpression(_context.CurrentDatabaseProvider, _databaseProviders, Expression.SqlSyntax)
{
Column = column,
SchemaName = Expression.SchemaName,
@@ -236,12 +227,12 @@ namespace Umbraco.Core.Persistence.Migrations.Syntax.Alter.Table
public IAlterTableColumnTypeSyntax AlterColumn(string name)
{
var column = new ColumnDefinition { Name = name, ModificationType = ModificationType.Alter };
- var alterColumn = new AlterColumnExpression(_context.SqlSyntax, _context.CurrentDatabaseProvider)
- {
- Column = column,
- SchemaName = Expression.SchemaName,
- TableName = Expression.TableName
- };
+ var alterColumn = new AlterColumnExpression(_context.CurrentDatabaseProvider, _databaseProviders, Expression.SqlSyntax)
+ {
+ Column = column,
+ SchemaName = Expression.SchemaName,
+ TableName = Expression.TableName
+ };
CurrentColumn = column;
diff --git a/src/Umbraco.Core/Persistence/Migrations/Syntax/Create/Column/CreateColumnBuilder.cs b/src/Umbraco.Core/Persistence/Migrations/Syntax/Create/Column/CreateColumnBuilder.cs
index b494d858dc..d004cd1176 100644
--- a/src/Umbraco.Core/Persistence/Migrations/Syntax/Create/Column/CreateColumnBuilder.cs
+++ b/src/Umbraco.Core/Persistence/Migrations/Syntax/Create/Column/CreateColumnBuilder.cs
@@ -1,6 +1,7 @@
using System.Data;
using Umbraco.Core.Persistence.DatabaseModelDefinitions;
using Umbraco.Core.Persistence.Migrations.Syntax.Expressions;
+using Umbraco.Core.Persistence.SqlSyntax;
namespace Umbraco.Core.Persistence.Migrations.Syntax.Create.Column
{
@@ -10,11 +11,13 @@ namespace Umbraco.Core.Persistence.Migrations.Syntax.Create.Column
ICreateColumnOptionForeignKeyCascadeSyntax
{
private readonly IMigrationContext _context;
+ private readonly DatabaseProviders[] _databaseProviders;
- public CreateColumnBuilder(CreateColumnExpression expression, IMigrationContext context)
+ public CreateColumnBuilder(IMigrationContext context, DatabaseProviders[] databaseProviders, CreateColumnExpression expression)
: base(expression)
{
_context = context;
+ _databaseProviders = databaseProviders;
}
public ForeignKeyDefinition CurrentForeignKey { get; set; }
@@ -56,15 +59,12 @@ namespace Umbraco.Core.Persistence.Migrations.Syntax.Create.Column
{
Expression.Column.IsIndexed = true;
- var index = new CreateIndexExpression(_context.SqlSyntax, _context.CurrentDatabaseProvider)
- {
- Index = new IndexDefinition
- {
- Name = indexName,
- SchemaName = Expression.SchemaName,
- TableName = Expression.TableName
- }
- };
+ var index = new CreateIndexExpression(_context.CurrentDatabaseProvider, _databaseProviders, Expression.SqlSyntax, new IndexDefinition
+ {
+ Name = indexName,
+ SchemaName = Expression.SchemaName,
+ TableName = Expression.TableName
+ });
index.Index.Columns.Add(new IndexColumnDefinition
{
@@ -110,16 +110,13 @@ namespace Umbraco.Core.Persistence.Migrations.Syntax.Create.Column
{
Expression.Column.IsUnique = true;
- var index = new CreateIndexExpression(_context.SqlSyntax, _context.CurrentDatabaseProvider)
- {
- Index = new IndexDefinition
- {
- Name = indexName,
- SchemaName = Expression.SchemaName,
- TableName = Expression.TableName,
- IsUnique = true
- }
- };
+ var index = new CreateIndexExpression(_context.CurrentDatabaseProvider, _databaseProviders, Expression.SqlSyntax, new IndexDefinition
+ {
+ Name = indexName,
+ SchemaName = Expression.SchemaName,
+ TableName = Expression.TableName,
+ IsUnique = true
+ });
index.Index.Columns.Add(new IndexColumnDefinition
{
@@ -147,17 +144,14 @@ namespace Umbraco.Core.Persistence.Migrations.Syntax.Create.Column
{
Expression.Column.IsForeignKey = true;
- var fk = new CreateForeignKeyExpression(_context.SqlSyntax, _context.CurrentDatabaseProvider)
- {
- ForeignKey = new ForeignKeyDefinition
- {
- Name = foreignKeyName,
- PrimaryTable = primaryTableName,
- PrimaryTableSchema = primaryTableSchema,
- ForeignTable = Expression.TableName,
- ForeignTableSchema = Expression.SchemaName
- }
- };
+ var fk = new CreateForeignKeyExpression(_context.CurrentDatabaseProvider, _databaseProviders, Expression.SqlSyntax, new ForeignKeyDefinition
+ {
+ Name = foreignKeyName,
+ PrimaryTable = primaryTableName,
+ PrimaryTableSchema = primaryTableSchema,
+ ForeignTable = Expression.TableName,
+ ForeignTableSchema = Expression.SchemaName
+ });
fk.ForeignKey.PrimaryColumns.Add(primaryColumnName);
fk.ForeignKey.ForeignColumns.Add(Expression.Column.Name);
@@ -187,17 +181,14 @@ namespace Umbraco.Core.Persistence.Migrations.Syntax.Create.Column
public ICreateColumnOptionForeignKeyCascadeSyntax ReferencedBy(string foreignKeyName, string foreignTableSchema,
string foreignTableName, string foreignColumnName)
{
- var fk = new CreateForeignKeyExpression(_context.SqlSyntax, _context.CurrentDatabaseProvider)
- {
- ForeignKey = new ForeignKeyDefinition
- {
- Name = foreignKeyName,
- PrimaryTable = Expression.TableName,
- PrimaryTableSchema = Expression.SchemaName,
- ForeignTable = foreignTableName,
- ForeignTableSchema = foreignTableSchema
- }
- };
+ var fk = new CreateForeignKeyExpression(_context.CurrentDatabaseProvider, _databaseProviders, Expression.SqlSyntax, new ForeignKeyDefinition
+ {
+ Name = foreignKeyName,
+ PrimaryTable = Expression.TableName,
+ PrimaryTableSchema = Expression.SchemaName,
+ ForeignTable = foreignTableName,
+ ForeignTableSchema = foreignTableSchema
+ });
fk.ForeignKey.PrimaryColumns.Add(Expression.Column.Name);
fk.ForeignKey.ForeignColumns.Add(foreignColumnName);
diff --git a/src/Umbraco.Core/Persistence/Migrations/Syntax/Create/CreateBuilder.cs b/src/Umbraco.Core/Persistence/Migrations/Syntax/Create/CreateBuilder.cs
index 56e0d5632d..2bf6da8ec0 100644
--- a/src/Umbraco.Core/Persistence/Migrations/Syntax/Create/CreateBuilder.cs
+++ b/src/Umbraco.Core/Persistence/Migrations/Syntax/Create/CreateBuilder.cs
@@ -18,74 +18,65 @@ namespace Umbraco.Core.Persistence.Migrations.Syntax.Create
public CreateBuilder(IMigrationContext context, params DatabaseProviders[] databaseProviders)
{
+ if (context == null) throw new ArgumentNullException("context");
+ if (sqlSyntax == null) throw new ArgumentNullException("sqlSyntax");
+
_context = context;
_databaseProviders = databaseProviders;
}
public ICreateTableWithColumnSyntax Table(string tableName)
{
- var expression = new CreateTableExpression(_context.SqlSyntax, _context.CurrentDatabaseProvider, _databaseProviders)
- {
- TableName = tableName
- };
+ var expression = new CreateTableExpression(_context.CurrentDatabaseProvider, _databaseProviders, _sqlSyntax) { TableName = tableName };
_context.Expressions.Add(expression);
- return new CreateTableBuilder(expression, _context);
+ return new CreateTableBuilder(_context, _databaseProviders, expression);
}
public ICreateColumnOnTableSyntax Column(string columnName)
{
- var expression = new CreateColumnExpression(_context.SqlSyntax, _context.CurrentDatabaseProvider, _databaseProviders)
- {
- Column = { Name = columnName }
- };
+ var expression = new CreateColumnExpression(_context.CurrentDatabaseProvider, _databaseProviders, _sqlSyntax) { Column = { Name = columnName } };
_context.Expressions.Add(expression);
- return new CreateColumnBuilder(expression, _context);
+ return new CreateColumnBuilder(_context, _databaseProviders, expression);
}
public ICreateForeignKeyFromTableSyntax ForeignKey()
{
- var expression = new CreateForeignKeyExpression(_context.SqlSyntax, _context.CurrentDatabaseProvider, _databaseProviders);
+ var expression = new CreateForeignKeyExpression(_context.CurrentDatabaseProvider, _databaseProviders, _sqlSyntax);
_context.Expressions.Add(expression);
return new CreateForeignKeyBuilder(expression);
}
public ICreateForeignKeyFromTableSyntax ForeignKey(string foreignKeyName)
{
- var expression = new CreateForeignKeyExpression(_context.SqlSyntax, _context.CurrentDatabaseProvider, _databaseProviders)
- {
- ForeignKey = { Name = foreignKeyName }
- };
+ var expression = new CreateForeignKeyExpression(_context.CurrentDatabaseProvider, _databaseProviders, _sqlSyntax) { ForeignKey = { Name = foreignKeyName } };
_context.Expressions.Add(expression);
return new CreateForeignKeyBuilder(expression);
}
public ICreateIndexForTableSyntax Index()
{
- var expression = new CreateIndexExpression(_context.SqlSyntax, _context.CurrentDatabaseProvider, _databaseProviders);
+ var expression = new CreateIndexExpression(_context.CurrentDatabaseProvider, _databaseProviders, _sqlSyntax);
_context.Expressions.Add(expression);
return new CreateIndexBuilder(expression);
}
public ICreateIndexForTableSyntax Index(string indexName)
{
- var expression = new CreateIndexExpression(_context.SqlSyntax, _context.CurrentDatabaseProvider, _databaseProviders)
- {
- Index = { Name = indexName }
- };
+ var expression = new CreateIndexExpression(_context.CurrentDatabaseProvider, _databaseProviders, _sqlSyntax) { Index = { Name = indexName } };
_context.Expressions.Add(expression);
return new CreateIndexBuilder(expression);
}
public ICreateConstraintOnTableSyntax PrimaryKey()
{
- var expression = new CreateConstraintExpression(_context.SqlSyntax, _context.CurrentDatabaseProvider, ConstraintType.PrimaryKey, _databaseProviders);
+ var expression = new CreateConstraintExpression(_context.CurrentDatabaseProvider, _databaseProviders, _sqlSyntax, ConstraintType.PrimaryKey);
_context.Expressions.Add(expression);
return new CreateConstraintBuilder(expression);
}
public ICreateConstraintOnTableSyntax PrimaryKey(string primaryKeyName)
{
- var expression = new CreateConstraintExpression(_context.SqlSyntax, _context.CurrentDatabaseProvider, ConstraintType.PrimaryKey, _databaseProviders);
+ var expression = new CreateConstraintExpression(_context.CurrentDatabaseProvider, _databaseProviders, _sqlSyntax, ConstraintType.PrimaryKey);
expression.Constraint.ConstraintName = primaryKeyName;
_context.Expressions.Add(expression);
return new CreateConstraintBuilder(expression);
@@ -93,14 +84,14 @@ namespace Umbraco.Core.Persistence.Migrations.Syntax.Create
public ICreateConstraintOnTableSyntax UniqueConstraint()
{
- var expression = new CreateConstraintExpression(_context.SqlSyntax, _context.CurrentDatabaseProvider, ConstraintType.Unique, _databaseProviders);
+ var expression = new CreateConstraintExpression(_context.CurrentDatabaseProvider, _databaseProviders, _sqlSyntax, ConstraintType.Unique);
_context.Expressions.Add(expression);
return new CreateConstraintBuilder(expression);
}
public ICreateConstraintOnTableSyntax UniqueConstraint(string constraintName)
{
- var expression = new CreateConstraintExpression(_context.SqlSyntax, _context.CurrentDatabaseProvider, ConstraintType.Unique, _databaseProviders);
+ var expression = new CreateConstraintExpression(_context.CurrentDatabaseProvider, _databaseProviders, _sqlSyntax, ConstraintType.Unique);
expression.Constraint.ConstraintName = constraintName;
_context.Expressions.Add(expression);
return new CreateConstraintBuilder(expression);
@@ -108,7 +99,7 @@ namespace Umbraco.Core.Persistence.Migrations.Syntax.Create
public ICreateConstraintOnTableSyntax Constraint(string constraintName)
{
- var expression = new CreateConstraintExpression(_context.SqlSyntax, _context.CurrentDatabaseProvider, ConstraintType.NonUnique, _databaseProviders);
+ var expression = new CreateConstraintExpression(_context.CurrentDatabaseProvider, _databaseProviders, _sqlSyntax, ConstraintType.NonUnique);
expression.Constraint.ConstraintName = constraintName;
_context.Expressions.Add(expression);
return new CreateConstraintBuilder(expression);
diff --git a/src/Umbraco.Core/Persistence/Migrations/Syntax/Create/Expressions/CreateConstraintExpression.cs b/src/Umbraco.Core/Persistence/Migrations/Syntax/Create/Expressions/CreateConstraintExpression.cs
index 1184114bd4..a06775929b 100644
--- a/src/Umbraco.Core/Persistence/Migrations/Syntax/Create/Expressions/CreateConstraintExpression.cs
+++ b/src/Umbraco.Core/Persistence/Migrations/Syntax/Create/Expressions/CreateConstraintExpression.cs
@@ -6,11 +6,12 @@ namespace Umbraco.Core.Persistence.Migrations.Syntax.Create.Expressions
{
public class CreateConstraintExpression : MigrationExpressionBase
{
- public CreateConstraintExpression(ISqlSyntaxProvider sqlSyntax, DatabaseProviders currentDatabaseProvider, ConstraintType constraintType, DatabaseProviders[] supportedDatabaseProviders = null)
- : base(sqlSyntax, currentDatabaseProvider, supportedDatabaseProviders)
+ public CreateConstraintExpression(DatabaseProviders current, DatabaseProviders[] databaseProviders, ISqlSyntaxProvider sqlSyntax, ConstraintType constraint)
+ : base(current, databaseProviders, sqlSyntax)
{
- Constraint = new ConstraintDefinition(constraintType);
+ Constraint = new ConstraintDefinition(constraint);
}
+
public ConstraintDefinition Constraint { get; private set; }
diff --git a/src/Umbraco.Core/Persistence/Migrations/Syntax/Create/Expressions/CreateTableExpression.cs b/src/Umbraco.Core/Persistence/Migrations/Syntax/Create/Expressions/CreateTableExpression.cs
index 8755306b13..57d08b43ef 100644
--- a/src/Umbraco.Core/Persistence/Migrations/Syntax/Create/Expressions/CreateTableExpression.cs
+++ b/src/Umbraco.Core/Persistence/Migrations/Syntax/Create/Expressions/CreateTableExpression.cs
@@ -7,8 +7,10 @@ namespace Umbraco.Core.Persistence.Migrations.Syntax.Create.Expressions
{
public class CreateTableExpression : MigrationExpressionBase
{
- public CreateTableExpression(ISqlSyntaxProvider sqlSyntax, DatabaseProviders currentDatabaseProvider, DatabaseProviders[] supportedDatabaseProviders = null)
- : base(sqlSyntax, currentDatabaseProvider, supportedDatabaseProviders)
+
+
+ public CreateTableExpression(DatabaseProviders current, DatabaseProviders[] databaseProviders, ISqlSyntaxProvider sqlSyntax)
+ : base(current, databaseProviders, sqlSyntax)
{
Columns = new List();
}
diff --git a/src/Umbraco.Core/Persistence/Migrations/Syntax/Create/Table/CreateTableBuilder.cs b/src/Umbraco.Core/Persistence/Migrations/Syntax/Create/Table/CreateTableBuilder.cs
index 636d35084f..86760b71bc 100644
--- a/src/Umbraco.Core/Persistence/Migrations/Syntax/Create/Table/CreateTableBuilder.cs
+++ b/src/Umbraco.Core/Persistence/Migrations/Syntax/Create/Table/CreateTableBuilder.cs
@@ -2,6 +2,7 @@
using Umbraco.Core.Persistence.DatabaseModelDefinitions;
using Umbraco.Core.Persistence.Migrations.Syntax.Create.Expressions;
using Umbraco.Core.Persistence.Migrations.Syntax.Expressions;
+using Umbraco.Core.Persistence.SqlSyntax;
namespace Umbraco.Core.Persistence.Migrations.Syntax.Create.Table
{
@@ -11,11 +12,13 @@ namespace Umbraco.Core.Persistence.Migrations.Syntax.Create.Table
ICreateTableColumnOptionForeignKeyCascadeSyntax
{
private readonly IMigrationContext _context;
+ private readonly DatabaseProviders[] _databaseProviders;
- public CreateTableBuilder(CreateTableExpression expression, IMigrationContext context)
+ public CreateTableBuilder(IMigrationContext context, DatabaseProviders[] databaseProviders, CreateTableExpression expression)
: base(expression)
{
_context = context;
+ _databaseProviders = databaseProviders;
}
public ColumnDefinition CurrentColumn { get; set; }
@@ -62,15 +65,12 @@ namespace Umbraco.Core.Persistence.Migrations.Syntax.Create.Table
{
CurrentColumn.IsIndexed = true;
- var index = new CreateIndexExpression(_context.SqlSyntax, _context.CurrentDatabaseProvider)
- {
- Index = new IndexDefinition
- {
- Name = indexName,
- SchemaName = Expression.SchemaName,
- TableName = Expression.TableName
- }
- };
+ var index = new CreateIndexExpression(_context.CurrentDatabaseProvider, _databaseProviders, Expression.SqlSyntax, new IndexDefinition
+ {
+ Name = indexName,
+ SchemaName = Expression.SchemaName,
+ TableName = Expression.TableName
+ });
index.Index.Columns.Add(new IndexColumnDefinition
{
@@ -86,15 +86,25 @@ namespace Umbraco.Core.Persistence.Migrations.Syntax.Create.Table
{
CurrentColumn.IsPrimaryKey = true;
- var expression = new CreateConstraintExpression(_context.SqlSyntax, _context.CurrentDatabaseProvider, ConstraintType.PrimaryKey)
+ //For MySQL, the PK will be created WITH the create table expression, however for
+ // SQL Server, the PK get's created in a different Alter table expression afterwords.
+ // MySQL will choke if the same constraint is again added afterword
+ // TODO: This is a super hack, I'd rather not add another property like 'CreatesPkInCreateTableDefinition' to check
+ // for this, but I don't see another way around. MySQL doesn't support checking for a constraint before creating
+ // it... except in a very strange way but it doesn't actually provider error feedback if it doesn't work so we cannot use
+ // it. For now, this is what I'm doing
+ if (Expression.CurrentDatabaseProvider != DatabaseProviders.MySql)
{
- Constraint =
+ var expression = new CreateConstraintExpression(_context.CurrentDatabaseProvider, _databaseProviders, Expression.SqlSyntax, ConstraintType.PrimaryKey)
+ {
+ Constraint =
{
TableName = CurrentColumn.TableName,
Columns = new[] { CurrentColumn.Name }
}
- };
- _context.Expressions.Add(expression);
+ };
+ _context.Expressions.Add(expression);
+ }
return this;
}
@@ -104,16 +114,27 @@ namespace Umbraco.Core.Persistence.Migrations.Syntax.Create.Table
CurrentColumn.IsPrimaryKey = true;
CurrentColumn.PrimaryKeyName = primaryKeyName;
- var expression = new CreateConstraintExpression(_context.SqlSyntax, _context.CurrentDatabaseProvider, ConstraintType.PrimaryKey)
+ //For MySQL, the PK will be created WITH the create table expression, however for
+ // SQL Server, the PK get's created in a different Alter table expression afterwords.
+ // MySQL will choke if the same constraint is again added afterword
+ // TODO: This is a super hack, I'd rather not add another property like 'CreatesPkInCreateTableDefinition' to check
+ // for this, but I don't see another way around. MySQL doesn't support checking for a constraint before creating
+ // it... except in a very strange way but it doesn't actually provider error feedback if it doesn't work so we cannot use
+ // it. For now, this is what I'm doing
+
+ if (Expression.CurrentDatabaseProvider != DatabaseProviders.MySql)
{
- Constraint =
+ var expression = new CreateConstraintExpression(_context.CurrentDatabaseProvider, _databaseProviders, Expression.SqlSyntax, ConstraintType.PrimaryKey)
+ {
+ Constraint =
{
ConstraintName = primaryKeyName,
TableName = CurrentColumn.TableName,
Columns = new[] { CurrentColumn.Name }
}
- };
- _context.Expressions.Add(expression);
+ };
+ _context.Expressions.Add(expression);
+ }
return this;
}
@@ -139,16 +160,13 @@ namespace Umbraco.Core.Persistence.Migrations.Syntax.Create.Table
{
CurrentColumn.IsUnique = true;
- var index = new CreateIndexExpression(_context.SqlSyntax, _context.CurrentDatabaseProvider)
- {
- Index = new IndexDefinition
- {
- Name = indexName,
- SchemaName = Expression.SchemaName,
- TableName = Expression.TableName,
- IsUnique = true
- }
- };
+ var index = new CreateIndexExpression(_context.CurrentDatabaseProvider, _databaseProviders, Expression.SqlSyntax, new IndexDefinition
+ {
+ Name = indexName,
+ SchemaName = Expression.SchemaName,
+ TableName = Expression.TableName,
+ IsUnique = true
+ });
index.Index.Columns.Add(new IndexColumnDefinition
{
@@ -176,17 +194,14 @@ namespace Umbraco.Core.Persistence.Migrations.Syntax.Create.Table
{
CurrentColumn.IsForeignKey = true;
- var fk = new CreateForeignKeyExpression(_context.SqlSyntax, _context.CurrentDatabaseProvider)
- {
- ForeignKey = new ForeignKeyDefinition
- {
- Name = foreignKeyName,
- PrimaryTable = primaryTableName,
- PrimaryTableSchema = primaryTableSchema,
- ForeignTable = Expression.TableName,
- ForeignTableSchema = Expression.SchemaName
- }
- };
+ var fk = new CreateForeignKeyExpression(_context.CurrentDatabaseProvider, _databaseProviders, Expression.SqlSyntax, new ForeignKeyDefinition
+ {
+ Name = foreignKeyName,
+ PrimaryTable = primaryTableName,
+ PrimaryTableSchema = primaryTableSchema,
+ ForeignTable = Expression.TableName,
+ ForeignTableSchema = Expression.SchemaName
+ });
fk.ForeignKey.PrimaryColumns.Add(primaryColumnName);
fk.ForeignKey.ForeignColumns.Add(CurrentColumn.Name);
@@ -216,17 +231,14 @@ namespace Umbraco.Core.Persistence.Migrations.Syntax.Create.Table
public ICreateTableColumnOptionForeignKeyCascadeSyntax ReferencedBy(string foreignKeyName, string foreignTableSchema,
string foreignTableName, string foreignColumnName)
{
- var fk = new CreateForeignKeyExpression(_context.SqlSyntax, _context.CurrentDatabaseProvider)
- {
- ForeignKey = new ForeignKeyDefinition
- {
- Name = foreignKeyName,
- PrimaryTable = Expression.TableName,
- PrimaryTableSchema = Expression.SchemaName,
- ForeignTable = foreignTableName,
- ForeignTableSchema = foreignTableSchema
- }
- };
+ var fk = new CreateForeignKeyExpression(_context.CurrentDatabaseProvider, _databaseProviders, Expression.SqlSyntax, new ForeignKeyDefinition
+ {
+ Name = foreignKeyName,
+ PrimaryTable = Expression.TableName,
+ PrimaryTableSchema = Expression.SchemaName,
+ ForeignTable = foreignTableName,
+ ForeignTableSchema = foreignTableSchema
+ });
fk.ForeignKey.PrimaryColumns.Add(CurrentColumn.Name);
fk.ForeignKey.ForeignColumns.Add(foreignColumnName);
diff --git a/src/Umbraco.Core/Persistence/Migrations/Syntax/Delete/DeleteBuilder.cs b/src/Umbraco.Core/Persistence/Migrations/Syntax/Delete/DeleteBuilder.cs
index 878c2b8108..947005c856 100644
--- a/src/Umbraco.Core/Persistence/Migrations/Syntax/Delete/DeleteBuilder.cs
+++ b/src/Umbraco.Core/Persistence/Migrations/Syntax/Delete/DeleteBuilder.cs
@@ -23,70 +23,55 @@ namespace Umbraco.Core.Persistence.Migrations.Syntax.Delete
public void Table(string tableName)
{
- var expression = new DeleteTableExpression(_context.SqlSyntax, _context.CurrentDatabaseProvider, _databaseProviders)
- {
- TableName = tableName
- };
+ var expression = new DeleteTableExpression(_context.CurrentDatabaseProvider, _databaseProviders, _sqlSyntax) { TableName = tableName };
_context.Expressions.Add(expression);
}
public IDeleteColumnFromTableSyntax Column(string columnName)
{
- var expression = new DeleteColumnExpression(_context.SqlSyntax, _context.CurrentDatabaseProvider, _databaseProviders)
- {
- ColumnNames = { columnName }
- };
+ var expression = new DeleteColumnExpression(_context.CurrentDatabaseProvider, _databaseProviders, _sqlSyntax) {ColumnNames = {columnName}};
_context.Expressions.Add(expression);
return new DeleteColumnBuilder(expression);
}
public IDeleteForeignKeyFromTableSyntax ForeignKey()
{
- var expression = new DeleteForeignKeyExpression(_context.SqlSyntax, _context.CurrentDatabaseProvider, _databaseProviders);
+ var expression = new DeleteForeignKeyExpression(_context.CurrentDatabaseProvider, _databaseProviders, _sqlSyntax);
_context.Expressions.Add(expression);
return new DeleteForeignKeyBuilder(expression);
}
public IDeleteForeignKeyOnTableSyntax ForeignKey(string foreignKeyName)
{
- var expression = new DeleteForeignKeyExpression(_context.SqlSyntax, _context.CurrentDatabaseProvider, _databaseProviders)
- {
- ForeignKey = { Name = foreignKeyName }
- };
+ var expression = new DeleteForeignKeyExpression(_context.CurrentDatabaseProvider, _databaseProviders, _sqlSyntax) {ForeignKey = {Name = foreignKeyName}};
_context.Expressions.Add(expression);
return new DeleteForeignKeyBuilder(expression);
}
public IDeleteDataSyntax FromTable(string tableName)
{
- var expression = new DeleteDataExpression(_context.SqlSyntax, _context.CurrentDatabaseProvider, _databaseProviders)
- {
- TableName = tableName
- };
+ var expression = new DeleteDataExpression(_context.CurrentDatabaseProvider, _databaseProviders, _sqlSyntax) { TableName = tableName };
_context.Expressions.Add(expression);
return new DeleteDataBuilder(expression);
}
public IDeleteIndexForTableSyntax Index()
{
- var expression = new DeleteIndexExpression(_context.SqlSyntax, _context.CurrentDatabaseProvider, _databaseProviders);
+ var expression = new DeleteIndexExpression(_context.CurrentDatabaseProvider, _databaseProviders, _sqlSyntax);
_context.Expressions.Add(expression);
return new DeleteIndexBuilder(expression);
}
public IDeleteIndexForTableSyntax Index(string indexName)
{
- var expression = new DeleteIndexExpression(_context.SqlSyntax, _context.CurrentDatabaseProvider, _databaseProviders)
- {
- Index = { Name = indexName }
- };
+ var expression = new DeleteIndexExpression(_context.CurrentDatabaseProvider, _databaseProviders, _sqlSyntax) { Index = { Name = indexName } };
_context.Expressions.Add(expression);
return new DeleteIndexBuilder(expression);
}
public IDeleteConstraintOnTableSyntax PrimaryKey(string primaryKeyName)
{
- var expression = new DeleteConstraintExpression(_context.SqlSyntax, _context.CurrentDatabaseProvider, ConstraintType.PrimaryKey, _databaseProviders)
+ var expression = new DeleteConstraintExpression(_context.CurrentDatabaseProvider, _databaseProviders, _sqlSyntax, ConstraintType.PrimaryKey)
{
Constraint = { ConstraintName = primaryKeyName }
};
@@ -96,7 +81,7 @@ namespace Umbraco.Core.Persistence.Migrations.Syntax.Delete
public IDeleteConstraintOnTableSyntax UniqueConstraint(string constraintName)
{
- var expression = new DeleteConstraintExpression(_context.SqlSyntax, _context.CurrentDatabaseProvider, ConstraintType.Unique, _databaseProviders)
+ var expression = new DeleteConstraintExpression(_context.CurrentDatabaseProvider, _databaseProviders, _sqlSyntax, ConstraintType.Unique)
{
Constraint = { ConstraintName = constraintName }
};
@@ -106,7 +91,7 @@ namespace Umbraco.Core.Persistence.Migrations.Syntax.Delete
public IDeleteDefaultConstraintOnTableSyntax DefaultConstraint()
{
- var expression = new DeleteDefaultConstraintExpression(_context.SqlSyntax, _context.CurrentDatabaseProvider, _databaseProviders);
+ var expression = new DeleteDefaultConstraintExpression(_context.CurrentDatabaseProvider, _databaseProviders, _sqlSyntax);
_context.Expressions.Add(expression);
return new DeleteDefaultConstraintBuilder(expression);
}
diff --git a/src/Umbraco.Core/Persistence/Migrations/Syntax/Delete/Expressions/DeleteColumnExpression.cs b/src/Umbraco.Core/Persistence/Migrations/Syntax/Delete/Expressions/DeleteColumnExpression.cs
index 4792da1d41..381e5c8271 100644
--- a/src/Umbraco.Core/Persistence/Migrations/Syntax/Delete/Expressions/DeleteColumnExpression.cs
+++ b/src/Umbraco.Core/Persistence/Migrations/Syntax/Delete/Expressions/DeleteColumnExpression.cs
@@ -7,8 +7,9 @@ namespace Umbraco.Core.Persistence.Migrations.Syntax.Delete.Expressions
{
public class DeleteColumnExpression : MigrationExpressionBase
{
- public DeleteColumnExpression(ISqlSyntaxProvider sqlSyntax, DatabaseProviders currentDatabaseProvider, DatabaseProviders[] supportedDatabaseProviders = null)
- : base(sqlSyntax, currentDatabaseProvider, supportedDatabaseProviders)
+
+ public DeleteColumnExpression(DatabaseProviders current, DatabaseProviders[] databaseProviders, ISqlSyntaxProvider sqlSyntax)
+ : base(current, databaseProviders, sqlSyntax)
{
ColumnNames = new List();
}
diff --git a/src/Umbraco.Core/Persistence/Migrations/Syntax/Delete/Expressions/DeleteConstraintExpression.cs b/src/Umbraco.Core/Persistence/Migrations/Syntax/Delete/Expressions/DeleteConstraintExpression.cs
index 3f089aee34..21ef6bb517 100644
--- a/src/Umbraco.Core/Persistence/Migrations/Syntax/Delete/Expressions/DeleteConstraintExpression.cs
+++ b/src/Umbraco.Core/Persistence/Migrations/Syntax/Delete/Expressions/DeleteConstraintExpression.cs
@@ -5,8 +5,9 @@ namespace Umbraco.Core.Persistence.Migrations.Syntax.Delete.Expressions
{
public class DeleteConstraintExpression : MigrationExpressionBase
{
- public DeleteConstraintExpression(ISqlSyntaxProvider sqlSyntax, DatabaseProviders current, ConstraintType type, DatabaseProviders[] databaseProviders = null)
- : base(sqlSyntax, current, databaseProviders)
+
+ public DeleteConstraintExpression(DatabaseProviders current, DatabaseProviders[] databaseProviders, ISqlSyntaxProvider sqlSyntax, ConstraintType type)
+ : base(current, databaseProviders, sqlSyntax)
{
Constraint = new ConstraintDefinition(type);
}
@@ -20,27 +21,24 @@ namespace Umbraco.Core.Persistence.Migrations.Syntax.Delete.Expressions
{
if (Constraint.IsPrimaryKeyConstraint)
{
- return string.Format(
- SqlSyntax.DeleteConstraint,
- SqlSyntax.GetQuotedTableName(Constraint.TableName),
- "PRIMARY KEY",
- "");
+ return string.Format(SqlSyntax.DeleteConstraint,
+ SqlSyntax.GetQuotedTableName(Constraint.TableName),
+ "PRIMARY KEY",
+ "");
}
else
{
- return string.Format(
- SqlSyntax.DeleteConstraint,
- SqlSyntax.GetQuotedTableName(Constraint.TableName),
- "FOREIGN KEY",
- "");
+ return string.Format(SqlSyntax.DeleteConstraint,
+ SqlSyntax.GetQuotedTableName(Constraint.TableName),
+ "FOREIGN KEY",
+ "");
}
}
else
{
- return string.Format(
- SqlSyntax.DeleteConstraint,
- SqlSyntax.GetQuotedTableName(Constraint.TableName),
- SqlSyntax.GetQuotedName(Constraint.ConstraintName));
+ return string.Format(SqlSyntax.DeleteConstraint,
+ SqlSyntax.GetQuotedTableName(Constraint.TableName),
+ SqlSyntax.GetQuotedName(Constraint.ConstraintName));
}
}
}
diff --git a/src/Umbraco.Core/Persistence/Migrations/Syntax/Delete/Expressions/DeleteDataExpression.cs b/src/Umbraco.Core/Persistence/Migrations/Syntax/Delete/Expressions/DeleteDataExpression.cs
index f62c97da07..97c601a2a1 100644
--- a/src/Umbraco.Core/Persistence/Migrations/Syntax/Delete/Expressions/DeleteDataExpression.cs
+++ b/src/Umbraco.Core/Persistence/Migrations/Syntax/Delete/Expressions/DeleteDataExpression.cs
@@ -8,10 +8,9 @@ namespace Umbraco.Core.Persistence.Migrations.Syntax.Delete.Expressions
public class DeleteDataExpression : MigrationExpressionBase
{
private readonly List _rows = new List();
-
-
- public DeleteDataExpression(ISqlSyntaxProvider sqlSyntax, DatabaseProviders currentDatabaseProvider, DatabaseProviders[] supportedDatabaseProviders = null)
- : base(sqlSyntax, currentDatabaseProvider, supportedDatabaseProviders)
+
+ public DeleteDataExpression(DatabaseProviders current, DatabaseProviders[] databaseProviders, ISqlSyntaxProvider sqlSyntax)
+ : base(current, databaseProviders, sqlSyntax)
{
}
@@ -30,9 +29,7 @@ namespace Umbraco.Core.Persistence.Migrations.Syntax.Delete.Expressions
if (IsAllRows)
{
- deleteItems.Add(string.Format(
- SqlSyntax.DeleteData,
- SqlSyntax.GetQuotedTableName(TableName), "1 = 1"));
+ deleteItems.Add(string.Format(SqlSyntax.DeleteData, SqlSyntax.GetQuotedTableName(TableName), "1 = 1"));
}
else
{
@@ -44,7 +41,7 @@ namespace Umbraco.Core.Persistence.Migrations.Syntax.Delete.Expressions
whereClauses.Add(string.Format("{0} {1} {2}",
SqlSyntax.GetQuotedColumnName(item.Key),
item.Value == null ? "IS" : "=",
- SqlSyntax.GetQuotedValue(item.Value.ToString())));
+ GetQuotedValue(item.Value)));
}
deleteItems.Add(string.Format(SqlSyntax.DeleteData,
diff --git a/src/Umbraco.Core/Persistence/Migrations/Syntax/Delete/Expressions/DeleteDefaultConstraintExpression.cs b/src/Umbraco.Core/Persistence/Migrations/Syntax/Delete/Expressions/DeleteDefaultConstraintExpression.cs
index c610d8bdc1..70a74da28e 100644
--- a/src/Umbraco.Core/Persistence/Migrations/Syntax/Delete/Expressions/DeleteDefaultConstraintExpression.cs
+++ b/src/Umbraco.Core/Persistence/Migrations/Syntax/Delete/Expressions/DeleteDefaultConstraintExpression.cs
@@ -4,8 +4,8 @@ namespace Umbraco.Core.Persistence.Migrations.Syntax.Delete.Expressions
{
public class DeleteDefaultConstraintExpression : MigrationExpressionBase
{
- public DeleteDefaultConstraintExpression(ISqlSyntaxProvider sqlSyntax, DatabaseProviders currentDatabaseProvider, DatabaseProviders[] supportedDatabaseProviders = null)
- : base(sqlSyntax, currentDatabaseProvider, supportedDatabaseProviders)
+ public DeleteDefaultConstraintExpression(DatabaseProviders current, DatabaseProviders[] databaseProviders, ISqlSyntaxProvider sqlSyntax)
+ : base(current, databaseProviders, sqlSyntax)
{
}
diff --git a/src/Umbraco.Core/Persistence/Migrations/Syntax/Delete/Expressions/DeleteForeignKeyExpression.cs b/src/Umbraco.Core/Persistence/Migrations/Syntax/Delete/Expressions/DeleteForeignKeyExpression.cs
index 7173beea99..97342e851c 100644
--- a/src/Umbraco.Core/Persistence/Migrations/Syntax/Delete/Expressions/DeleteForeignKeyExpression.cs
+++ b/src/Umbraco.Core/Persistence/Migrations/Syntax/Delete/Expressions/DeleteForeignKeyExpression.cs
@@ -8,13 +8,15 @@ namespace Umbraco.Core.Persistence.Migrations.Syntax.Delete.Expressions
{
public class DeleteForeignKeyExpression : MigrationExpressionBase
{
- public DeleteForeignKeyExpression(ISqlSyntaxProvider sqlSyntax, DatabaseProviders currentDatabaseProvider, DatabaseProviders[] supportedDatabaseProviders = null)
- : base(sqlSyntax, currentDatabaseProvider, supportedDatabaseProviders)
- {
+
+
+ public DeleteForeignKeyExpression(DatabaseProviders current, DatabaseProviders[] databaseProviders, ISqlSyntaxProvider sqlSyntax)
+ : base(current, databaseProviders, sqlSyntax)
+ {
ForeignKey = new ForeignKeyDefinition();
}
- public virtual ForeignKeyDefinition ForeignKey { get; set; }
+ public ForeignKeyDefinition ForeignKey { get; set; }
public override string ToString()
{
@@ -26,7 +28,7 @@ namespace Umbraco.Core.Persistence.Migrations.Syntax.Delete.Expressions
if (CurrentDatabaseProvider == DatabaseProviders.MySql)
{
- //MySql naming "convention" for foreignkeys, which aren't explicitly named
+ //MySql naming "convention" for foreignkeys, which aren't explicitly named
if (string.IsNullOrEmpty(ForeignKey.Name))
ForeignKey.Name = string.Format("{0}_ibfk_1", ForeignKey.ForeignTable.ToLower());
diff --git a/src/Umbraco.Core/Persistence/Migrations/Syntax/Delete/Expressions/DeleteIndexExpression.cs b/src/Umbraco.Core/Persistence/Migrations/Syntax/Delete/Expressions/DeleteIndexExpression.cs
index 203ffa09d1..e504223669 100644
--- a/src/Umbraco.Core/Persistence/Migrations/Syntax/Delete/Expressions/DeleteIndexExpression.cs
+++ b/src/Umbraco.Core/Persistence/Migrations/Syntax/Delete/Expressions/DeleteIndexExpression.cs
@@ -5,13 +5,20 @@ namespace Umbraco.Core.Persistence.Migrations.Syntax.Delete.Expressions
{
public class DeleteIndexExpression : MigrationExpressionBase
{
- public DeleteIndexExpression(ISqlSyntaxProvider sqlSyntax, DatabaseProviders currentDatabaseProvider, DatabaseProviders[] supportedDatabaseProviders = null)
- : base(sqlSyntax, currentDatabaseProvider, supportedDatabaseProviders)
+
+ public DeleteIndexExpression(DatabaseProviders current, DatabaseProviders[] databaseProviders, ISqlSyntaxProvider sqlSyntax)
+ : base(current, databaseProviders, sqlSyntax)
{
Index = new IndexDefinition();
}
- public virtual IndexDefinition Index { get; set; }
+ public DeleteIndexExpression(DatabaseProviders current, DatabaseProviders[] databaseProviders, ISqlSyntaxProvider sqlSyntax, IndexDefinition index)
+ : base(current, databaseProviders, sqlSyntax)
+ {
+ Index = index;
+ }
+
+ public IndexDefinition Index { get; private set; }
public override string ToString()
{
diff --git a/src/Umbraco.Core/Persistence/Migrations/Syntax/Delete/Expressions/DeleteTableExpression.cs b/src/Umbraco.Core/Persistence/Migrations/Syntax/Delete/Expressions/DeleteTableExpression.cs
index 133cc4b7d8..0456091a4b 100644
--- a/src/Umbraco.Core/Persistence/Migrations/Syntax/Delete/Expressions/DeleteTableExpression.cs
+++ b/src/Umbraco.Core/Persistence/Migrations/Syntax/Delete/Expressions/DeleteTableExpression.cs
@@ -4,8 +4,9 @@ namespace Umbraco.Core.Persistence.Migrations.Syntax.Delete.Expressions
{
public class DeleteTableExpression : MigrationExpressionBase
{
- public DeleteTableExpression(ISqlSyntaxProvider sqlSyntax, DatabaseProviders currentDatabaseProvider, DatabaseProviders[] supportedDatabaseProviders = null)
- : base(sqlSyntax, currentDatabaseProvider, supportedDatabaseProviders)
+
+ public DeleteTableExpression(DatabaseProviders current, DatabaseProviders[] databaseProviders, ISqlSyntaxProvider sqlSyntax)
+ : base(current, databaseProviders, sqlSyntax)
{
}
diff --git a/src/Umbraco.Core/Persistence/Migrations/Syntax/Delete/IDeleteBuilder.cs b/src/Umbraco.Core/Persistence/Migrations/Syntax/Delete/IDeleteBuilder.cs
index 6521d043a7..4a31c3bf37 100644
--- a/src/Umbraco.Core/Persistence/Migrations/Syntax/Delete/IDeleteBuilder.cs
+++ b/src/Umbraco.Core/Persistence/Migrations/Syntax/Delete/IDeleteBuilder.cs
@@ -12,7 +12,6 @@ namespace Umbraco.Core.Persistence.Migrations.Syntax.Delete
void Table(string tableName);
IDeleteColumnFromTableSyntax Column(string columnName);
IDeleteForeignKeyFromTableSyntax ForeignKey();
- [Obsolete("Do not use this construct as it does not work with MySql, use the syntax: Delete.ForeignKey().FromTable(\"umbracoUser2app\").ForeignColumn(... instead")]
IDeleteForeignKeyOnTableSyntax ForeignKey(string foreignKeyName);
IDeleteDataSyntax FromTable(string tableName);
IDeleteIndexForTableSyntax Index();
diff --git a/src/Umbraco.Core/Persistence/Migrations/Syntax/Execute/ExecuteBuilder.cs b/src/Umbraco.Core/Persistence/Migrations/Syntax/Execute/ExecuteBuilder.cs
index 3ecffa46ba..549a4a0c66 100644
--- a/src/Umbraco.Core/Persistence/Migrations/Syntax/Execute/ExecuteBuilder.cs
+++ b/src/Umbraco.Core/Persistence/Migrations/Syntax/Execute/ExecuteBuilder.cs
@@ -1,34 +1,31 @@
using System;
using Umbraco.Core.Persistence.Migrations.Syntax.Execute.Expressions;
+using Umbraco.Core.Persistence.SqlSyntax;
namespace Umbraco.Core.Persistence.Migrations.Syntax.Execute
{
public class ExecuteBuilder : IExecuteBuilder
{
private readonly IMigrationContext _context;
+ private readonly ISqlSyntaxProvider _sqlSyntax;
private readonly DatabaseProviders[] _databaseProviders;
- public ExecuteBuilder(IMigrationContext context, params DatabaseProviders[] databaseProviders)
+ public ExecuteBuilder(IMigrationContext context, ISqlSyntaxProvider sqlSyntax, params DatabaseProviders[] databaseProviders)
{
_context = context;
+ _sqlSyntax = sqlSyntax;
_databaseProviders = databaseProviders;
}
public void Sql(string sqlStatement)
{
- var expression = new ExecuteSqlStatementExpression(
- _context.SqlSyntax,
- _context.CurrentDatabaseProvider,
- _databaseProviders) {SqlStatement = sqlStatement};
+ var expression = new ExecuteSqlStatementExpression(_context.CurrentDatabaseProvider, _databaseProviders, _sqlSyntax) {SqlStatement = sqlStatement};
_context.Expressions.Add(expression);
}
public void Code(Func codeStatement)
{
- var expression = new ExecuteCodeStatementExpression(
- _context.SqlSyntax,
- _context.CurrentDatabaseProvider,
- _databaseProviders) {CodeStatement = codeStatement};
+ var expression = new ExecuteCodeStatementExpression(_context.CurrentDatabaseProvider, _databaseProviders, _sqlSyntax) { CodeStatement = codeStatement };
_context.Expressions.Add(expression);
}
}
diff --git a/src/Umbraco.Core/Persistence/Migrations/Syntax/Execute/Expressions/ExecuteCodeStatementExpression.cs b/src/Umbraco.Core/Persistence/Migrations/Syntax/Execute/Expressions/ExecuteCodeStatementExpression.cs
index dc3ace9760..e5f487d15d 100644
--- a/src/Umbraco.Core/Persistence/Migrations/Syntax/Execute/Expressions/ExecuteCodeStatementExpression.cs
+++ b/src/Umbraco.Core/Persistence/Migrations/Syntax/Execute/Expressions/ExecuteCodeStatementExpression.cs
@@ -5,8 +5,8 @@ namespace Umbraco.Core.Persistence.Migrations.Syntax.Execute.Expressions
{
public class ExecuteCodeStatementExpression : MigrationExpressionBase
{
- public ExecuteCodeStatementExpression(ISqlSyntaxProvider sqlSyntax, DatabaseProviders currentDatabaseProvider, DatabaseProviders[] supportedDatabaseProviders = null)
- : base(sqlSyntax, currentDatabaseProvider, supportedDatabaseProviders)
+ public ExecuteCodeStatementExpression(DatabaseProviders current, DatabaseProviders[] databaseProviders, ISqlSyntaxProvider sqlSyntax)
+ : base(current, databaseProviders, sqlSyntax)
{
}
@@ -14,7 +14,7 @@ namespace Umbraco.Core.Persistence.Migrations.Syntax.Execute.Expressions
public override string Process(Database database)
{
- if (CodeStatement != null)
+ if(CodeStatement != null)
return CodeStatement(database);
return base.Process(database);
diff --git a/src/Umbraco.Core/Persistence/Migrations/Syntax/Execute/Expressions/ExecuteSqlStatementExpression.cs b/src/Umbraco.Core/Persistence/Migrations/Syntax/Execute/Expressions/ExecuteSqlStatementExpression.cs
index 152fa749ef..1164ba7caa 100644
--- a/src/Umbraco.Core/Persistence/Migrations/Syntax/Execute/Expressions/ExecuteSqlStatementExpression.cs
+++ b/src/Umbraco.Core/Persistence/Migrations/Syntax/Execute/Expressions/ExecuteSqlStatementExpression.cs
@@ -4,8 +4,9 @@ namespace Umbraco.Core.Persistence.Migrations.Syntax.Execute.Expressions
{
public class ExecuteSqlStatementExpression : MigrationExpressionBase
{
- public ExecuteSqlStatementExpression(ISqlSyntaxProvider sqlSyntax, DatabaseProviders currentDatabaseProvider, DatabaseProviders[] supportedDatabaseProviders = null)
- : base(sqlSyntax, currentDatabaseProvider, supportedDatabaseProviders)
+
+ public ExecuteSqlStatementExpression(DatabaseProviders current, DatabaseProviders[] databaseProviders, ISqlSyntaxProvider sqlSyntax)
+ : base(current, databaseProviders, sqlSyntax)
{
}
diff --git a/src/Umbraco.Core/Persistence/Migrations/Syntax/Expressions/CreateColumnExpression.cs b/src/Umbraco.Core/Persistence/Migrations/Syntax/Expressions/CreateColumnExpression.cs
index 14eb5eeb4d..ad16da5286 100644
--- a/src/Umbraco.Core/Persistence/Migrations/Syntax/Expressions/CreateColumnExpression.cs
+++ b/src/Umbraco.Core/Persistence/Migrations/Syntax/Expressions/CreateColumnExpression.cs
@@ -5,15 +5,16 @@ namespace Umbraco.Core.Persistence.Migrations.Syntax.Expressions
{
public class CreateColumnExpression : MigrationExpressionBase
{
- public CreateColumnExpression(ISqlSyntaxProvider sqlSyntax, DatabaseProviders currentDatabaseProvider, DatabaseProviders[] supportedDatabaseProviders = null)
- : base(sqlSyntax, currentDatabaseProvider, supportedDatabaseProviders)
+
+ public CreateColumnExpression(DatabaseProviders current, DatabaseProviders[] databaseProviders, ISqlSyntaxProvider sqlSyntax)
+ : base(current, databaseProviders, sqlSyntax)
{
Column = new ColumnDefinition { ModificationType = ModificationType.Create };
}
- public virtual string SchemaName { get; set; }
- public virtual string TableName { get; set; }
- public virtual ColumnDefinition Column { get; set; }
+ public string SchemaName { get; set; }
+ public string TableName { get; set; }
+ public ColumnDefinition Column { get; set; }
public override string ToString()
{
diff --git a/src/Umbraco.Core/Persistence/Migrations/Syntax/Expressions/CreateForeignKeyExpression.cs b/src/Umbraco.Core/Persistence/Migrations/Syntax/Expressions/CreateForeignKeyExpression.cs
index 57836281d3..45e40e961f 100644
--- a/src/Umbraco.Core/Persistence/Migrations/Syntax/Expressions/CreateForeignKeyExpression.cs
+++ b/src/Umbraco.Core/Persistence/Migrations/Syntax/Expressions/CreateForeignKeyExpression.cs
@@ -5,13 +5,19 @@ namespace Umbraco.Core.Persistence.Migrations.Syntax.Expressions
{
public class CreateForeignKeyExpression : MigrationExpressionBase
{
- public CreateForeignKeyExpression(ISqlSyntaxProvider sqlSyntax, DatabaseProviders currentDatabaseProvider, DatabaseProviders[] supportedDatabaseProviders = null)
- : base(sqlSyntax, currentDatabaseProvider, supportedDatabaseProviders)
+ public CreateForeignKeyExpression(DatabaseProviders current, DatabaseProviders[] databaseProviders, ISqlSyntaxProvider sqlSyntax, ForeignKeyDefinition fkDef)
+ : base(current, databaseProviders, sqlSyntax)
+ {
+ ForeignKey = fkDef;
+ }
+
+ public CreateForeignKeyExpression(DatabaseProviders current, DatabaseProviders[] databaseProviders, ISqlSyntaxProvider sqlSyntax)
+ : base(current, databaseProviders, sqlSyntax)
{
ForeignKey = new ForeignKeyDefinition();
}
- public virtual ForeignKeyDefinition ForeignKey { get; set; }
+ public ForeignKeyDefinition ForeignKey { get; set; }
public override string ToString()
{
diff --git a/src/Umbraco.Core/Persistence/Migrations/Syntax/Expressions/CreateIndexExpression.cs b/src/Umbraco.Core/Persistence/Migrations/Syntax/Expressions/CreateIndexExpression.cs
index b4ad3dcade..7fa38cd3ae 100644
--- a/src/Umbraco.Core/Persistence/Migrations/Syntax/Expressions/CreateIndexExpression.cs
+++ b/src/Umbraco.Core/Persistence/Migrations/Syntax/Expressions/CreateIndexExpression.cs
@@ -6,13 +6,20 @@ namespace Umbraco.Core.Persistence.Migrations.Syntax.Expressions
{
public class CreateIndexExpression : MigrationExpressionBase
{
- public CreateIndexExpression(ISqlSyntaxProvider sqlSyntax, DatabaseProviders currentDatabaseProvider, DatabaseProviders[] supportedDatabaseProviders = null)
- : base(sqlSyntax, currentDatabaseProvider, supportedDatabaseProviders)
+
+ public CreateIndexExpression(DatabaseProviders current, DatabaseProviders[] databaseProviders, ISqlSyntaxProvider sqlSyntax, IndexDefinition index)
+ : base(current, databaseProviders, sqlSyntax)
+ {
+ Index = index;
+ }
+
+ public CreateIndexExpression(DatabaseProviders current, DatabaseProviders[] databaseProviders, ISqlSyntaxProvider sqlSyntax)
+ : base(current, databaseProviders, sqlSyntax)
{
Index = new IndexDefinition();
}
-
- public virtual IndexDefinition Index { get; set; }
+
+ public IndexDefinition Index { get; set; }
public override string ToString()
{
diff --git a/src/Umbraco.Core/Persistence/Migrations/Syntax/IfDatabase/IfDatabaseBuilder.cs b/src/Umbraco.Core/Persistence/Migrations/Syntax/IfDatabase/IfDatabaseBuilder.cs
index b7105170f1..e0d20c4875 100644
--- a/src/Umbraco.Core/Persistence/Migrations/Syntax/IfDatabase/IfDatabaseBuilder.cs
+++ b/src/Umbraco.Core/Persistence/Migrations/Syntax/IfDatabase/IfDatabaseBuilder.cs
@@ -3,43 +3,46 @@ using Umbraco.Core.Persistence.Migrations.Syntax.Delete;
using Umbraco.Core.Persistence.Migrations.Syntax.Execute;
using Umbraco.Core.Persistence.Migrations.Syntax.Rename;
using Umbraco.Core.Persistence.Migrations.Syntax.Update;
+using Umbraco.Core.Persistence.SqlSyntax;
namespace Umbraco.Core.Persistence.Migrations.Syntax.IfDatabase
{
public class IfDatabaseBuilder : IIfDatabaseBuilder
{
private readonly IMigrationContext _context;
+ private readonly ISqlSyntaxProvider _sqlSyntax;
private readonly DatabaseProviders[] _databaseProviders;
- public IfDatabaseBuilder(IMigrationContext context, params DatabaseProviders[] databaseProviders)
+ public IfDatabaseBuilder(IMigrationContext context, ISqlSyntaxProvider sqlSyntax, params DatabaseProviders[] databaseProviders)
{
_context = context;
+ _sqlSyntax = sqlSyntax;
_databaseProviders = databaseProviders;
}
public ICreateBuilder Create
{
- get { return new CreateBuilder(_context, _databaseProviders); }
+ get { return new CreateBuilder(_context, _sqlSyntax, _databaseProviders); }
}
public IExecuteBuilder Execute
{
- get { return new ExecuteBuilder(_context, _databaseProviders); }
+ get { return new ExecuteBuilder(_context, _sqlSyntax, _databaseProviders); }
}
public IDeleteBuilder Delete
{
- get { return new DeleteBuilder(_context, _databaseProviders); }
+ get { return new DeleteBuilder(_context, _sqlSyntax, _databaseProviders); }
}
public IRenameBuilder Rename
{
- get { return new RenameBuilder(_context, _databaseProviders); }
+ get { return new RenameBuilder(_context, _sqlSyntax, _databaseProviders); }
}
public IUpdateBuilder Update
{
- get { return new UpdateBuilder(_context, _databaseProviders); }
+ get { return new UpdateBuilder(_context, _sqlSyntax, _databaseProviders); }
}
}
}
\ No newline at end of file
diff --git a/src/Umbraco.Core/Persistence/Migrations/Syntax/Insert/Expressions/InsertDataExpression.cs b/src/Umbraco.Core/Persistence/Migrations/Syntax/Insert/Expressions/InsertDataExpression.cs
index f1d739a503..28b8c71d39 100644
--- a/src/Umbraco.Core/Persistence/Migrations/Syntax/Insert/Expressions/InsertDataExpression.cs
+++ b/src/Umbraco.Core/Persistence/Migrations/Syntax/Insert/Expressions/InsertDataExpression.cs
@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
+using System.Text;
using Umbraco.Core.Persistence.DatabaseModelDefinitions;
using Umbraco.Core.Persistence.SqlSyntax;
@@ -8,79 +9,78 @@ namespace Umbraco.Core.Persistence.Migrations.Syntax.Insert.Expressions
public class InsertDataExpression : MigrationExpressionBase
{
private readonly List _rows = new List();
-
- public InsertDataExpression(ISqlSyntaxProvider sqlSyntax, DatabaseProviders currentDatabaseProvider, DatabaseProviders[] supportedDatabaseProviders = null)
- : base(sqlSyntax, currentDatabaseProvider, supportedDatabaseProviders)
+
+ public InsertDataExpression(DatabaseProviders current, DatabaseProviders[] databaseProviders, ISqlSyntaxProvider sqlSyntax)
+ : base(current, databaseProviders, sqlSyntax)
{
}
public string SchemaName { get; set; }
public string TableName { get; set; }
-
+ public bool EnabledIdentityInsert { get; set; }
public List Rows
{
get { return _rows; }
}
-
-
-
+
public override string ToString()
{
- //TODO: This works for single insertion entries, not sure if it is valid for bulk insert operations!!!
-
if (IsExpressionSupported() == false)
return string.Empty;
+
+ var sb = new StringBuilder();
- var insertItems = new List();
-
- foreach (var item in Rows)
+ if (EnabledIdentityInsert && SqlSyntax.SupportsIdentityInsert())
{
- var cols = "";
- var vals = "";
- foreach (var keyVal in item)
+ sb.AppendLine(string.Format("SET IDENTITY_INSERT {0} ON;", SqlSyntax.GetQuotedTableName(TableName)));
+ if (CurrentDatabaseProvider == DatabaseProviders.SqlServer || CurrentDatabaseProvider == DatabaseProviders.SqlServerCE)
{
- cols += keyVal.Key + ",";
- vals += GetQuotedValue(keyVal.Value) + ",";
+ sb.AppendLine("GO");
}
- cols = cols.TrimEnd(',');
- vals = vals.TrimEnd(',');
-
-
- var sql = string.Format(
- SqlSyntax.InsertData,
- SqlSyntax.GetQuotedTableName(TableName),
- cols, vals);
-
- insertItems.Add(sql);
}
- return string.Join(",", insertItems);
- }
-
- private string GetQuotedValue(object val)
- {
- var type = val.GetType();
-
- switch (Type.GetTypeCode(type))
+ try
{
- case TypeCode.Boolean:
- return ((bool)val) ? "1" : "0";
- case TypeCode.Single:
- case TypeCode.Double:
- case TypeCode.Decimal:
- case TypeCode.SByte:
- case TypeCode.Int16:
- case TypeCode.Int32:
- case TypeCode.Int64:
- case TypeCode.Byte:
- case TypeCode.UInt16:
- case TypeCode.UInt32:
- case TypeCode.UInt64:
- return val.ToString();
- default:
- return SqlSyntax.GetQuotedValue(val.ToString());
+ foreach (var item in Rows)
+ {
+ var cols = "";
+ var vals = "";
+ foreach (var keyVal in item)
+ {
+ cols += SqlSyntax.GetQuotedColumnName(keyVal.Key) + ",";
+ vals += GetQuotedValue(keyVal.Value) + ",";
+ }
+ cols = cols.TrimEnd(',');
+ vals = vals.TrimEnd(',');
+
+
+ var sql = string.Format(SqlSyntax.InsertData,
+ SqlSyntax.GetQuotedTableName(TableName),
+ cols, vals);
+
+ sb.AppendLine(string.Format("{0};", sql));
+ if (CurrentDatabaseProvider == DatabaseProviders.SqlServer || CurrentDatabaseProvider == DatabaseProviders.SqlServerCE)
+ {
+ sb.AppendLine("GO");
+ }
+ }
}
+ finally
+ {
+ if (EnabledIdentityInsert && SqlSyntax.SupportsIdentityInsert())
+ {
+ sb.AppendLine(string.Format("SET IDENTITY_INSERT {0} OFF;", SqlSyntax.GetQuotedTableName(TableName)));
+ if (CurrentDatabaseProvider == DatabaseProviders.SqlServer || CurrentDatabaseProvider == DatabaseProviders.SqlServerCE)
+ {
+ sb.AppendLine("GO");
+ }
+ }
+ }
+
+ return sb.ToString();
}
+
+
}
}
\ No newline at end of file
diff --git a/src/Umbraco.Core/Persistence/Migrations/Syntax/Insert/IInsertDataSyntax.cs b/src/Umbraco.Core/Persistence/Migrations/Syntax/Insert/IInsertDataSyntax.cs
index fb0585f0e6..5791f0cb83 100644
--- a/src/Umbraco.Core/Persistence/Migrations/Syntax/Insert/IInsertDataSyntax.cs
+++ b/src/Umbraco.Core/Persistence/Migrations/Syntax/Insert/IInsertDataSyntax.cs
@@ -2,6 +2,7 @@
{
public interface IInsertDataSyntax : IFluentSyntax
{
+ IInsertDataSyntax EnableIdentityInsert();
IInsertDataSyntax Row(object dataAsAnonymousType);
}
}
\ No newline at end of file
diff --git a/src/Umbraco.Core/Persistence/Migrations/Syntax/Insert/InsertBuilder.cs b/src/Umbraco.Core/Persistence/Migrations/Syntax/Insert/InsertBuilder.cs
index d80be1cb39..2d3ce9c64d 100644
--- a/src/Umbraco.Core/Persistence/Migrations/Syntax/Insert/InsertBuilder.cs
+++ b/src/Umbraco.Core/Persistence/Migrations/Syntax/Insert/InsertBuilder.cs
@@ -1,19 +1,24 @@
using Umbraco.Core.Persistence.Migrations.Syntax.Insert.Expressions;
+using Umbraco.Core.Persistence.SqlSyntax;
namespace Umbraco.Core.Persistence.Migrations.Syntax.Insert
{
public class InsertBuilder : IInsertBuilder
{
private readonly IMigrationContext _context;
+ private readonly ISqlSyntaxProvider _sqlSyntax;
+ private readonly DatabaseProviders[] _databaseProviders;
- public InsertBuilder(IMigrationContext context)
+ public InsertBuilder(IMigrationContext context, ISqlSyntaxProvider sqlSyntax, params DatabaseProviders[] databaseProviders)
{
_context = context;
+ _sqlSyntax = sqlSyntax;
+ _databaseProviders = databaseProviders;
}
public IInsertDataSyntax IntoTable(string tableName)
{
- var expression = new InsertDataExpression(_context.SqlSyntax, _context.CurrentDatabaseProvider) { TableName = tableName };
+ var expression = new InsertDataExpression(_context.CurrentDatabaseProvider, _databaseProviders, _sqlSyntax) { TableName = tableName };
_context.Expressions.Add(expression);
return new InsertDataBuilder(expression);
}
diff --git a/src/Umbraco.Core/Persistence/Migrations/Syntax/Insert/InsertDataBuilder.cs b/src/Umbraco.Core/Persistence/Migrations/Syntax/Insert/InsertDataBuilder.cs
index 9b419f8daf..4f8bf2dfcc 100644
--- a/src/Umbraco.Core/Persistence/Migrations/Syntax/Insert/InsertDataBuilder.cs
+++ b/src/Umbraco.Core/Persistence/Migrations/Syntax/Insert/InsertDataBuilder.cs
@@ -14,6 +14,12 @@ namespace Umbraco.Core.Persistence.Migrations.Syntax.Insert
_expression = expression;
}
+ public IInsertDataSyntax EnableIdentityInsert()
+ {
+ _expression.EnabledIdentityInsert = true;
+ return this;
+ }
+
public IInsertDataSyntax Row(object dataAsAnonymousType)
{
_expression.Rows.Add(GetData(dataAsAnonymousType));
diff --git a/src/Umbraco.Core/Persistence/Migrations/Syntax/Rename/Expressions/RenameColumnExpression.cs b/src/Umbraco.Core/Persistence/Migrations/Syntax/Rename/Expressions/RenameColumnExpression.cs
index 445eed65f2..1f84401923 100644
--- a/src/Umbraco.Core/Persistence/Migrations/Syntax/Rename/Expressions/RenameColumnExpression.cs
+++ b/src/Umbraco.Core/Persistence/Migrations/Syntax/Rename/Expressions/RenameColumnExpression.cs
@@ -3,9 +3,9 @@
namespace Umbraco.Core.Persistence.Migrations.Syntax.Rename.Expressions
{
public class RenameColumnExpression : MigrationExpressionBase
- {
- public RenameColumnExpression(ISqlSyntaxProvider sqlSyntax, DatabaseProviders currentDatabaseProvider, DatabaseProviders[] supportedDatabaseProviders = null)
- : base(sqlSyntax, currentDatabaseProvider, supportedDatabaseProviders)
+ {
+ public RenameColumnExpression(DatabaseProviders current, DatabaseProviders[] databaseProviders, ISqlSyntaxProvider sqlSyntax)
+ : base(current, databaseProviders, sqlSyntax)
{
}
diff --git a/src/Umbraco.Core/Persistence/Migrations/Syntax/Rename/Expressions/RenameTableExpression.cs b/src/Umbraco.Core/Persistence/Migrations/Syntax/Rename/Expressions/RenameTableExpression.cs
index 930d6eca8d..ec60117d7e 100644
--- a/src/Umbraco.Core/Persistence/Migrations/Syntax/Rename/Expressions/RenameTableExpression.cs
+++ b/src/Umbraco.Core/Persistence/Migrations/Syntax/Rename/Expressions/RenameTableExpression.cs
@@ -4,8 +4,8 @@ namespace Umbraco.Core.Persistence.Migrations.Syntax.Rename.Expressions
{
public class RenameTableExpression : MigrationExpressionBase
{
- public RenameTableExpression(ISqlSyntaxProvider sqlSyntax, DatabaseProviders currentDatabaseProvider, DatabaseProviders[] supportedDatabaseProviders = null)
- : base(sqlSyntax, currentDatabaseProvider, supportedDatabaseProviders)
+ public RenameTableExpression(DatabaseProviders current, DatabaseProviders[] databaseProviders, ISqlSyntaxProvider sqlSyntax)
+ : base(current, databaseProviders, sqlSyntax)
{
}
@@ -17,7 +17,7 @@ namespace Umbraco.Core.Persistence.Migrations.Syntax.Rename.Expressions
{
if (IsExpressionSupported() == false)
return string.Empty;
-
+
return SqlSyntax.FormatTableRename(OldName, NewName);
}
}
diff --git a/src/Umbraco.Core/Persistence/Migrations/Syntax/Rename/RenameBuilder.cs b/src/Umbraco.Core/Persistence/Migrations/Syntax/Rename/RenameBuilder.cs
index 2e5d7cb55b..ebfb08b862 100644
--- a/src/Umbraco.Core/Persistence/Migrations/Syntax/Rename/RenameBuilder.cs
+++ b/src/Umbraco.Core/Persistence/Migrations/Syntax/Rename/RenameBuilder.cs
@@ -1,34 +1,33 @@
using Umbraco.Core.Persistence.Migrations.Syntax.Rename.Column;
using Umbraco.Core.Persistence.Migrations.Syntax.Rename.Expressions;
using Umbraco.Core.Persistence.Migrations.Syntax.Rename.Table;
+using Umbraco.Core.Persistence.SqlSyntax;
namespace Umbraco.Core.Persistence.Migrations.Syntax.Rename
{
public class RenameBuilder : IRenameBuilder
{
private readonly IMigrationContext _context;
+ private readonly ISqlSyntaxProvider _sqlSyntax;
private readonly DatabaseProviders[] _databaseProviders;
- public RenameBuilder(IMigrationContext context, params DatabaseProviders[] databaseProviders)
+ public RenameBuilder(IMigrationContext context, ISqlSyntaxProvider sqlSyntax, params DatabaseProviders[] databaseProviders)
{
_context = context;
+ _sqlSyntax = sqlSyntax;
_databaseProviders = databaseProviders;
}
public IRenameTableSyntax Table(string oldName)
{
- var expression = new RenameTableExpression(
- _context.SqlSyntax,
- _context.CurrentDatabaseProvider, _databaseProviders) {OldName = oldName};
+ var expression = new RenameTableExpression(_context.CurrentDatabaseProvider, _databaseProviders, _sqlSyntax) { OldName = oldName };
_context.Expressions.Add(expression);
return new RenameTableBuilder(expression);
}
public IRenameColumnTableSyntax Column(string oldName)
{
- var expression = new RenameColumnExpression(
- _context.SqlSyntax,
- _context.CurrentDatabaseProvider, _databaseProviders) {OldName = oldName};
+ var expression = new RenameColumnExpression(_context.CurrentDatabaseProvider, _databaseProviders, _sqlSyntax) { OldName = oldName };
_context.Expressions.Add(expression);
return new RenameColumnBuilder(expression);
}
diff --git a/src/Umbraco.Core/Persistence/Migrations/Syntax/Update/Expressions/UpdateDataExpression.cs b/src/Umbraco.Core/Persistence/Migrations/Syntax/Update/Expressions/UpdateDataExpression.cs
index 37516f8b5b..18bec8b938 100644
--- a/src/Umbraco.Core/Persistence/Migrations/Syntax/Update/Expressions/UpdateDataExpression.cs
+++ b/src/Umbraco.Core/Persistence/Migrations/Syntax/Update/Expressions/UpdateDataExpression.cs
@@ -5,8 +5,8 @@ namespace Umbraco.Core.Persistence.Migrations.Syntax.Update.Expressions
{
public class UpdateDataExpression : MigrationExpressionBase
{
- public UpdateDataExpression(ISqlSyntaxProvider sqlSyntax, DatabaseProviders currentDatabaseProvider, DatabaseProviders[] supportedDatabaseProviders = null)
- : base(sqlSyntax, currentDatabaseProvider, supportedDatabaseProviders)
+ public UpdateDataExpression(DatabaseProviders current, DatabaseProviders[] databaseProviders, ISqlSyntaxProvider sqlSyntax)
+ : base(current, databaseProviders, sqlSyntax)
{
}
@@ -29,7 +29,7 @@ namespace Umbraco.Core.Persistence.Migrations.Syntax.Update.Expressions
{
updateItems.Add(string.Format("{0} = {1}",
SqlSyntax.GetQuotedColumnName(item.Key),
- SqlSyntax.GetQuotedValue(item.Value.ToString())));
+ GetQuotedValue(item.Value)));
}
if (IsAllRows)
@@ -43,12 +43,12 @@ namespace Umbraco.Core.Persistence.Migrations.Syntax.Update.Expressions
whereClauses.Add(string.Format("{0} {1} {2}",
SqlSyntax.GetQuotedColumnName(item.Key),
item.Value == null ? "IS" : "=",
- SqlSyntax.GetQuotedValue(item.Value.ToString())));
+ GetQuotedValue(item.Value)));
}
}
return string.Format(SqlSyntax.UpdateData,
SqlSyntax.GetQuotedTableName(TableName),
- string.Join(", ", updateItems.ToArray()),
+ string.Join(", ", updateItems.ToArray()),
string.Join(" AND ", whereClauses.ToArray()));
}
}
diff --git a/src/Umbraco.Core/Persistence/Migrations/Syntax/Update/UpdateBuilder.cs b/src/Umbraco.Core/Persistence/Migrations/Syntax/Update/UpdateBuilder.cs
index 325a231211..3816e65132 100644
--- a/src/Umbraco.Core/Persistence/Migrations/Syntax/Update/UpdateBuilder.cs
+++ b/src/Umbraco.Core/Persistence/Migrations/Syntax/Update/UpdateBuilder.cs
@@ -1,25 +1,24 @@
using Umbraco.Core.Persistence.Migrations.Syntax.Update.Expressions;
+using Umbraco.Core.Persistence.SqlSyntax;
namespace Umbraco.Core.Persistence.Migrations.Syntax.Update
{
public class UpdateBuilder : IUpdateBuilder
{
private readonly IMigrationContext _context;
+ private readonly ISqlSyntaxProvider _sqlSyntax;
private readonly DatabaseProviders[] _databaseProviders;
- public UpdateBuilder(IMigrationContext context, params DatabaseProviders[] databaseProviders)
+ public UpdateBuilder(IMigrationContext context, ISqlSyntaxProvider sqlSyntax, params DatabaseProviders[] databaseProviders)
{
_context = context;
+ _sqlSyntax = sqlSyntax;
_databaseProviders = databaseProviders;
}
public IUpdateSetSyntax Table(string tableName)
{
- var expression = new UpdateDataExpression(
- _context.SqlSyntax,
- _context.CurrentDatabaseProvider,
- _databaseProviders) {TableName = tableName};
-
+ var expression = new UpdateDataExpression(_context.CurrentDatabaseProvider, _databaseProviders, _sqlSyntax) { TableName = tableName };
_context.Expressions.Add(expression);
return new UpdateDataBuilder(expression, _context);
}
diff --git a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSeven/AddIndexToCmsMacroTable.cs b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSeven/AddIndexToCmsMacroTable.cs
index 0c9bbcc68a..d18cb430c0 100644
--- a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSeven/AddIndexToCmsMacroTable.cs
+++ b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSeven/AddIndexToCmsMacroTable.cs
@@ -3,6 +3,7 @@ using System.CodeDom;
using System.Linq;
using Umbraco.Core.Configuration;
using Umbraco.Core.Logging;
+using Umbraco.Core.Models.Rdbms;
using Umbraco.Core.Persistence.DatabaseModelDefinitions;
using Umbraco.Core.Persistence.SqlSyntax;
@@ -14,12 +15,12 @@ namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSeven
[Migration("7.0.0", 4, GlobalSettings.UmbracoMigrationName)]
public class AddIndexToCmsMacroTable : MigrationBase
{
- private readonly bool _skipIndexCheck;
+ private readonly bool _forTesting;
- internal AddIndexToCmsMacroTable(bool skipIndexCheck, ISqlSyntaxProvider sqlSyntax, ILogger logger)
+ internal AddIndexToCmsMacroTable(bool forTesting, ISqlSyntaxProvider sqlSyntax, ILogger logger)
: base(sqlSyntax, logger)
{
- _skipIndexCheck = skipIndexCheck;
+ _forTesting = forTesting;
}
public AddIndexToCmsMacroTable(ISqlSyntaxProvider sqlSyntax, ILogger logger) : base(sqlSyntax, logger)
@@ -28,7 +29,7 @@ namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSeven
public override void Up()
{
- var dbIndexes = _skipIndexCheck ? new DbIndexDefinition[] { } : SqlSyntax.GetDefinedIndexes(Context.Database)
+ var dbIndexes = _forTesting ? new DbIndexDefinition[] { } : SqlSyntax.GetDefinedIndexes(Context.Database)
.Select(x => new DbIndexDefinition()
{
TableName = x.Item1,
@@ -40,9 +41,33 @@ namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSeven
//make sure it doesn't already exist
if (dbIndexes.Any(x => x.IndexName.InvariantEquals("IX_cmsMacro_Alias")) == false)
{
- Create.Index("IX_cmsMacro_Alias").OnTable("cmsMacro").OnColumn("macroAlias").Unique();
- }
+ //in order to create this index, we need to ensure that there are no duplicates. This could have happened with very old/corrupt umbraco versions.
+ // So we'll remove any duplicates based on alias and only keep the one with the smallest id since I'm pretty sure we'd always choose the 'first' one
+ // when running a query.
+ if (_forTesting == false)
+ {
+ //NOTE: Using full SQL statement here in case the DTO has changed between versions
+ var macros = Context.Database.Fetch("SELECT * FROM cmsMacro")
+ .GroupBy(x => x.Alias)
+ .Where(x => x.Count() > 1);
+
+ foreach (var m in macros)
+ {
+ //get the min id (to keep)
+ var minId = m.Min(x => x.Id);
+ //delete all the others
+ foreach (var macroDto in m.Where(x => x.Id != minId))
+ {
+ Delete.FromTable("cmsMacro").Row(new { id = macroDto.Id });
+ }
+ }
+ }
+
+
+ Create.Index("IX_cmsMacro_Alias").OnTable("cmsMacro").OnColumn("macroAlias").Unique();
+ }
+
}
public override void Down()
diff --git a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSeven/AlterTagRelationsTable.cs b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSeven/AlterTagRelationsTable.cs
index 3370eb763e..922581ee8f 100644
--- a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSeven/AlterTagRelationsTable.cs
+++ b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSeven/AlterTagRelationsTable.cs
@@ -39,15 +39,20 @@ namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSeven
if (Context.CurrentDatabaseProvider == DatabaseProviders.MySql)
{
Delete.ForeignKey().FromTable("cmsTagRelationship").ForeignColumn("nodeId").ToTable("umbracoNode").PrimaryColumn("id");
+ //check for another strange really old one that might have existed
+ if (constraints.Any(x => x.Item1 == "cmsTagRelationship" && x.Item2 == "tagId"))
+ {
+ Delete.ForeignKey().FromTable("cmsTagRelationship").ForeignColumn("tagId").ToTable("cmsTags").PrimaryColumn("id");
+ }
}
else
{
//Before we try to delete this constraint, we'll see if it exists first, some older schemas never had it and some older schema's had this named
// differently than the default.
- var constraint = constraints
- .SingleOrDefault(x => x.Item1 == "cmsTagRelationship" && x.Item2 == "nodeId" && x.Item3.InvariantStartsWith("PK_") == false);
- if (constraint != null)
+ var constraintMatches = constraints.Where(x => x.Item1 == "cmsTagRelationship" && x.Item2 == "nodeId" && x.Item3.InvariantStartsWith("PK_") == false);
+
+ foreach (var constraint in constraintMatches)
{
Delete.ForeignKey(constraint.Item3).OnTable("cmsTagRelationship");
}
diff --git a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSeven/AlterTagsTable.cs b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSeven/AlterTagsTable.cs
index 8d18172341..d832cb936d 100644
--- a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSeven/AlterTagsTable.cs
+++ b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSeven/AlterTagsTable.cs
@@ -28,6 +28,11 @@ namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSeven
}).ToArray();
//add a foreign key to the parent id column too!
+
+ //In some cases in very old corrupted db's this will fail, so it means we need to clean the data first
+ //set the parentID to NULL where it doesn't actually exist in the normal ids
+ Execute.Sql(@"UPDATE cmsTags SET parentId = NULL WHERE parentId IS NOT NULL AND parentId NOT IN (SELECT id FROM cmsTags)");
+
Create.ForeignKey("FK_cmsTags_cmsTags")
.FromTable("cmsTags")
.ForeignColumn("ParentId")
diff --git a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSeven/UpdateRelatedLinksData.cs b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSeven/UpdateRelatedLinksData.cs
index 488faa13d5..5ed60a8de0 100644
--- a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSeven/UpdateRelatedLinksData.cs
+++ b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSeven/UpdateRelatedLinksData.cs
@@ -57,9 +57,11 @@ namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSeven
}
var propertyTypeIds = propertyData.Select(x => x.PropertyTypeId).Distinct();
- var propertyTypes = database.Fetch(
- "WHERE id in (@propertyTypeIds)", new { propertyTypeIds = propertyTypeIds });
+ //NOTE: We are writing the full query because we've added a column to the PropertyTypeDto in later versions so one of the columns
+ // won't exist yet
+ var propertyTypes = database.Fetch("SELECT * FROM cmsPropertyType WHERE id in (@propertyTypeIds)", new { propertyTypeIds = propertyTypeIds });
+
foreach (var data in propertyData)
{
if (string.IsNullOrEmpty(data.Text) == false)
diff --git a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenFourZero/AddDataDecimalColumn.cs b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenFourZero/AddDataDecimalColumn.cs
new file mode 100644
index 0000000000..442b92d2b5
--- /dev/null
+++ b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenFourZero/AddDataDecimalColumn.cs
@@ -0,0 +1,29 @@
+using System.Linq;
+using Umbraco.Core.Configuration;
+using Umbraco.Core.Logging;
+using Umbraco.Core.Persistence.SqlSyntax;
+
+namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSevenFourZero
+{
+ [Migration("7.4.0", 1, GlobalSettings.UmbracoMigrationName)]
+ public class AddDataDecimalColumn : MigrationBase
+ {
+ public AddDataDecimalColumn(ISqlSyntaxProvider sqlSyntax, ILogger logger)
+ : base(sqlSyntax, logger)
+ {
+ }
+
+ public override void Up()
+ {
+ //Don't exeucte if the column is already there
+ var columns = SqlSyntax.GetColumnsInSchema(Context.Database).ToArray();
+
+ if (columns.Any(x => x.TableName.InvariantEquals("cmsPropertyData") && x.ColumnName.InvariantEquals("dataDecimal")) == false)
+ Create.Column("dataDecimal").OnTable("cmsPropertyData").AsDecimal().Nullable();
+ }
+
+ public override void Down()
+ {
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenFourZero/AddUmbracoDeployTables.cs b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenFourZero/AddUmbracoDeployTables.cs
new file mode 100644
index 0000000000..a39cea2ee0
--- /dev/null
+++ b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenFourZero/AddUmbracoDeployTables.cs
@@ -0,0 +1,48 @@
+using System.Linq;
+using Umbraco.Core.Configuration;
+using Umbraco.Core.Logging;
+using Umbraco.Core.Persistence.SqlSyntax;
+
+namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSevenFourZero
+{
+ [Migration("7.4.0", 5, GlobalSettings.UmbracoMigrationName)]
+ public class AddUmbracoDeployTables : MigrationBase
+ {
+ public AddUmbracoDeployTables(ISqlSyntaxProvider sqlSyntax, ILogger logger)
+ : base(sqlSyntax, logger)
+ { }
+
+ public override void Up()
+ {
+ //Don't exeucte if the table is already there
+ var tables = SqlSyntax.GetTablesInSchema(Context.Database).ToArray();
+ if (tables.InvariantContains("umbracoDeployChecksum")) return;
+
+ Create.Table("umbracoDeployChecksum")
+ .WithColumn("id").AsInt32().Identity().PrimaryKey("PK_umbracoDeployChecksum")
+ .WithColumn("entityType").AsString(32).NotNullable()
+ .WithColumn("entityGuid").AsGuid().Nullable()
+ .WithColumn("entityPath").AsString(256).Nullable()
+ .WithColumn("localChecksum").AsString(32).NotNullable()
+ .WithColumn("compositeChecksum").AsString(32).Nullable();
+
+ Create.Table("umbracoDeployDependency")
+ .WithColumn("sourceId").AsInt32().NotNullable().ForeignKey("FK_umbracoDeployDependency_umbracoDeployChecksum_id1", "umbracoDeployChecksum", "id")
+ .WithColumn("targetId").AsInt32().NotNullable().ForeignKey("FK_umbracoDeployDependency_umbracoDeployChecksum_id2", "umbracoDeployChecksum", "id")
+ .WithColumn("mode").AsInt32().NotNullable();
+
+ Create.PrimaryKey("PK_umbracoDeployDependency").OnTable("umbracoDeployDependency").Columns(new[] {"sourceId", "targetId"});
+
+ Create.Index("IX_umbracoDeployChecksum").OnTable("umbracoDeployChecksum")
+ .OnColumn("entityType")
+ .Ascending()
+ .OnColumn("entityGuid")
+ .Ascending()
+ .OnColumn("entityPath")
+ .Unique();
+ }
+
+ public override void Down()
+ { }
+ }
+}
\ No newline at end of file
diff --git a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenFourZero/AddUniqueIdPropertyTypeGroupColumn.cs b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenFourZero/AddUniqueIdPropertyTypeGroupColumn.cs
new file mode 100644
index 0000000000..2a164b6e0d
--- /dev/null
+++ b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenFourZero/AddUniqueIdPropertyTypeGroupColumn.cs
@@ -0,0 +1,80 @@
+using System;
+using System.Linq;
+using Umbraco.Core.Configuration;
+using Umbraco.Core.Logging;
+using Umbraco.Core.Persistence.DatabaseModelDefinitions;
+using Umbraco.Core.Persistence.SqlSyntax;
+
+namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSevenFourZero
+{
+ [Migration("7.4.0", 2, GlobalSettings.UmbracoMigrationName)]
+ public class AddUniqueIdPropertyTypeGroupColumn : MigrationBase
+ {
+ public AddUniqueIdPropertyTypeGroupColumn(ISqlSyntaxProvider sqlSyntax, ILogger logger)
+ : base(sqlSyntax, logger)
+ { }
+
+ public override void Up()
+ {
+ // don't execute if the column is already there
+ var columns = SqlSyntax.GetColumnsInSchema(Context.Database).ToArray();
+
+ if (columns.Any(x => x.TableName.InvariantEquals("cmsPropertyTypeGroup") && x.ColumnName.InvariantEquals("uniqueID")) == false)
+ {
+ Create.Column("uniqueID").OnTable("cmsPropertyTypeGroup").AsGuid().NotNullable().WithDefault(SystemMethods.NewGuid);
+
+ // unique constraint on name + version
+ Create.Index("IX_cmsPropertyTypeGroupUniqueID").OnTable("cmsPropertyTypeGroup")
+ .OnColumn("uniqueID").Ascending()
+ .WithOptions()
+ .NonClustered()
+ .WithOptions()
+ .Unique();
+
+ // fill in the data in a way that is consistent over all environments
+ // (ie cannot use random guids, http://issues.umbraco.org/issue/U4-6942)
+
+ foreach (var data in Context.Database.Query(@"
+SELECT cmsPropertyTypeGroup.id grId, cmsPropertyTypeGroup.text grName, cmsContentType.alias ctAlias, umbracoNode.nodeObjectType nObjType
+FROM cmsPropertyTypeGroup
+INNER JOIN cmsContentType
+ON cmsPropertyTypeGroup.contentTypeNodeId = cmsContentType.nodeId
+INNER JOIN umbracoNode
+ON cmsContentType.nodeId = umbracoNode.id"))
+ {
+ Guid guid;
+ // see BaseDataCreation... built-in groups have their own guids
+ if (data.grId == 3)
+ {
+ guid = new Guid(Constants.PropertyTypeGroups.Image);
+ }
+ else if (data.grId == 4)
+ {
+ guid = new Guid(Constants.PropertyTypeGroups.File);
+ }
+ else if (data.grId == 5)
+ {
+ guid = new Guid(Constants.PropertyTypeGroups.Contents);
+ }
+ else if (data.grId == 11)
+ {
+ guid = new Guid(Constants.PropertyTypeGroups.Membership);
+ }
+ else
+ {
+ // create a consistent guid from
+ // group name + content type alias + object type
+ string guidSource = data.grName + data.ctAlias + data.nObjType;
+ guid = guidSource.ToGuid();
+ }
+
+ // set the Unique Id to the one we've generated
+ Update.Table("cmsPropertyTypeGroup").Set(new { uniqueID = guid }).Where(new { id = data.grId });
+ }
+ }
+ }
+
+ public override void Down()
+ { }
+ }
+}
\ No newline at end of file
diff --git a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenFourZero/EnsureContentTypeUniqueIdsAreConsistent.cs b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenFourZero/EnsureContentTypeUniqueIdsAreConsistent.cs
new file mode 100644
index 0000000000..20200a3230
--- /dev/null
+++ b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenFourZero/EnsureContentTypeUniqueIdsAreConsistent.cs
@@ -0,0 +1,55 @@
+using System;
+using System.Linq;
+using Umbraco.Core.Configuration;
+using Umbraco.Core.Logging;
+using Umbraco.Core.Models.Rdbms;
+using Umbraco.Core.Persistence.SqlSyntax;
+
+namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSevenFourZero
+{
+ ///
+ /// Courier on v. 7.4+ will handle ContentTypes using GUIDs instead of
+ /// alias, so we need to ensure that these are initially consistent on
+ /// all environments (based on the alias).
+ ///
+ [Migration("7.4.0", 3, GlobalSettings.UmbracoMigrationName)]
+ public class EnsureContentTypeUniqueIdsAreConsistent : MigrationBase
+ {
+ public EnsureContentTypeUniqueIdsAreConsistent(ISqlSyntaxProvider sqlSyntax, ILogger logger)
+ : base(sqlSyntax, logger)
+ { }
+
+ public override void Up()
+ {
+ var objectTypes = new[]
+ {
+ Constants.ObjectTypes.DocumentTypeGuid,
+ Constants.ObjectTypes.MediaTypeGuid,
+ Constants.ObjectTypes.MemberTypeGuid,
+ };
+
+ var sql = new Sql()
+ .Select("umbracoNode.id,cmsContentType.alias,umbracoNode.nodeObjectType")
+ .From(SqlSyntax)
+ .InnerJoin(SqlSyntax)
+ .On(SqlSyntax, dto => dto.NodeId, dto => dto.NodeId)
+ .WhereIn(x => x.NodeObjectType, objectTypes);
+
+ var rows = Context.Database.Fetch(sql);
+
+ foreach (var row in rows)
+ {
+ // create a consistent guid from
+ // alias + object type
+ var guidSource = ((string) row.alias) + ((Guid) row.nodeObjectType);
+ var guid = guidSource.ToGuid();
+
+ // set the Unique Id to the one we've generated
+ Update.Table("umbracoNode").Set(new { uniqueID = guid }).Where(new { id = row.id });
+ }
+ }
+
+ public override void Down()
+ { }
+ }
+}
\ No newline at end of file
diff --git a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenFourZero/FixListViewMediaSortOrder.cs b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenFourZero/FixListViewMediaSortOrder.cs
new file mode 100644
index 0000000000..e1fa9e9257
--- /dev/null
+++ b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenFourZero/FixListViewMediaSortOrder.cs
@@ -0,0 +1,34 @@
+using System.Linq;
+using Umbraco.Core.Configuration;
+using Umbraco.Core.Logging;
+using Umbraco.Core.Models.Rdbms;
+using Umbraco.Core.Persistence.SqlSyntax;
+
+namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSevenFourZero
+{
+ [Migration("7.4.0", 4, GlobalSettings.UmbracoMigrationName)]
+ public class FixListViewMediaSortOrder : MigrationBase
+ {
+ public FixListViewMediaSortOrder(ISqlSyntaxProvider sqlSyntax, ILogger logger)
+ : base(sqlSyntax, logger)
+ {
+ }
+
+ public override void Up()
+ {
+ var mediaListviewIncludeProperties = Context.Database.Fetch(new Sql().Select("*").From(SqlSyntax).Where(x => x.Id == -9)).FirstOrDefault();
+ if (mediaListviewIncludeProperties != null)
+ {
+ if (mediaListviewIncludeProperties.Value.Contains("\"alias\":\"sort\""))
+ {
+ mediaListviewIncludeProperties.Value = mediaListviewIncludeProperties.Value.Replace("\"alias\":\"sort\"", "\"alias\":\"sortOrder\"");
+ Context.Database.InsertOrUpdate(mediaListviewIncludeProperties);
+ }
+ }
+ }
+
+ public override void Down()
+ {
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenFourZero/RemoveParentIdPropertyTypeGroupColumn.cs b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenFourZero/RemoveParentIdPropertyTypeGroupColumn.cs
new file mode 100644
index 0000000000..5d5900308a
--- /dev/null
+++ b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenFourZero/RemoveParentIdPropertyTypeGroupColumn.cs
@@ -0,0 +1,30 @@
+using System.Linq;
+using Umbraco.Core.Configuration;
+using Umbraco.Core.Logging;
+using Umbraco.Core.Persistence.SqlSyntax;
+
+namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSevenFourZero
+{
+ [Migration("7.4.0", 4, GlobalSettings.UmbracoMigrationName)]
+ public class RemoveParentIdPropertyTypeGroupColumn : MigrationBase
+ {
+ public RemoveParentIdPropertyTypeGroupColumn(ISqlSyntaxProvider sqlSyntax, ILogger logger)
+ : base(sqlSyntax, logger)
+ { }
+
+ public override void Up()
+ {
+ // don't execute if the column is not there anymore
+ var columns = SqlSyntax.GetColumnsInSchema(Context.Database).ToArray();
+
+ if (columns.Any(x => x.TableName.InvariantEquals("cmsPropertyTypeGroup") && x.ColumnName.InvariantEquals("parentGroupId")) == false)
+ return;
+
+ Delete.ForeignKey("FK_cmsPropertyTypeGroup_cmsPropertyTypeGroup_id").OnTable("cmsPropertyTypeGroup");
+ Delete.Column("parentGroupId").FromTable("cmsPropertyTypeGroup");
+ }
+
+ public override void Down()
+ { }
+ }
+}
diff --git a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenThreeOne/UpdateUserLanguagesToIsoCode.cs b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenThreeOne/UpdateUserLanguagesToIsoCode.cs
new file mode 100644
index 0000000000..50f78ca66d
--- /dev/null
+++ b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenThreeOne/UpdateUserLanguagesToIsoCode.cs
@@ -0,0 +1,38 @@
+using System.Linq;
+using Umbraco.Core.Configuration;
+using Umbraco.Core.Logging;
+using Umbraco.Core.Models.Rdbms;
+using Umbraco.Core.Persistence.SqlSyntax;
+
+namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSevenThreeOne
+{
+ ///
+ /// This fixes the storage of user languages from the old format like en_us to en-US
+ ///
+ [Migration("7.3.1", 0, GlobalSettings.UmbracoMigrationName)]
+ public class UpdateUserLanguagesToIsoCode : MigrationBase
+ {
+ public UpdateUserLanguagesToIsoCode(ISqlSyntaxProvider sqlSyntax, ILogger logger) : base(sqlSyntax, logger)
+ {
+ }
+
+ public override void Up()
+ {
+ var userData = Context.Database.Fetch(new Sql().Select("*").From(SqlSyntax));
+ foreach (var user in userData.Where(x => x.UserLanguage.Contains("_")))
+ {
+ var languageParts = user.UserLanguage.Split('_');
+ if (languageParts.Length == 2)
+ {
+ Update.Table("umbracoUser")
+ .Set(new {userLanguage = languageParts[0] + "-" + languageParts[1].ToUpperInvariant()})
+ .Where(new {id = user.Id});
+ }
+ }
+ }
+
+ public override void Down()
+ {
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenThreeTwo/EnsureMigrationsTableIdentityIsCorrect.cs b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenThreeTwo/EnsureMigrationsTableIdentityIsCorrect.cs
new file mode 100644
index 0000000000..91b4bd6438
--- /dev/null
+++ b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenThreeTwo/EnsureMigrationsTableIdentityIsCorrect.cs
@@ -0,0 +1,50 @@
+using System.Collections.Generic;
+using Umbraco.Core.Configuration;
+using Umbraco.Core.Logging;
+using Umbraco.Core.Models.Rdbms;
+using Umbraco.Core.Persistence.SqlSyntax;
+
+namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSevenThreeTwo
+{
+ ///
+ /// This reinserts all migrations in the migrations table to account for initial rows inserted
+ /// on creation without identity enabled.
+ ///
+ [Migration("7.3.2", 0, GlobalSettings.UmbracoMigrationName)]
+ public class EnsureMigrationsTableIdentityIsCorrect : MigrationBase
+ {
+ public EnsureMigrationsTableIdentityIsCorrect(ISqlSyntaxProvider sqlSyntax, ILogger logger) : base(sqlSyntax, logger)
+ {
+ }
+
+ public override void Up()
+ {
+ // Due to the delayed execution of migrations, we have to wrap this code in Execute.Code to ensure the previous
+ // migration steps (esp. creating the migrations table) have completed before trying to fetch migrations from
+ // this table.
+ List migrations = null;
+ Execute.Code(db =>
+ {
+ migrations = Context.Database.Fetch(new Sql().Select("*").From(SqlSyntax));
+ return string.Empty;
+ });
+ Delete.FromTable("umbracoMigration").AllRows();
+ Execute.Code(database =>
+ {
+ if (migrations != null)
+ {
+ foreach (var migration in migrations)
+ {
+ database.Insert("umbracoMigration", "id", true,
+ new {name = migration.Name, createDate = migration.CreateDate, version = migration.Version});
+ }
+ }
+ return string.Empty;
+ });
+ }
+
+ public override void Down()
+ {
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenThreeZero/AddForeignKeysForLanguageAndDictionaryTables.cs b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenThreeZero/AddForeignKeysForLanguageAndDictionaryTables.cs
new file mode 100644
index 0000000000..eea56031d4
--- /dev/null
+++ b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenThreeZero/AddForeignKeysForLanguageAndDictionaryTables.cs
@@ -0,0 +1,107 @@
+using System;
+using System.Data;
+using System.Linq;
+using Umbraco.Core.Configuration;
+using Umbraco.Core.Logging;
+using Umbraco.Core.Models.Rdbms;
+using Umbraco.Core.Persistence.DatabaseModelDefinitions;
+using Umbraco.Core.Persistence.SqlSyntax;
+
+namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSevenThreeZero
+{
+ [Migration("7.3.0", 14, GlobalSettings.UmbracoMigrationName)]
+ public class AddForeignKeysForLanguageAndDictionaryTables : MigrationBase
+ {
+ public AddForeignKeysForLanguageAndDictionaryTables(ISqlSyntaxProvider sqlSyntax, ILogger logger)
+ : base(sqlSyntax, logger)
+ {
+ }
+
+ public override void Up()
+ {
+ var constraints = SqlSyntax.GetConstraintsPerColumn(Context.Database).Distinct().ToArray();
+
+ //if the FK doesn't exist
+ if (constraints.Any(x => x.Item1.InvariantEquals("cmsLanguageText") && x.Item2.InvariantEquals("languageId") && x.Item3.InvariantEquals("FK_cmsLanguageText_umbracoLanguage_id")) == false)
+ {
+ //Somehow, a language text item might end up with a language Id of zero or one that no longer exists
+ //before we add the foreign key
+ foreach (var pk in Context.Database.Query(
+ "SELECT cmsLanguageText.pk FROM cmsLanguageText WHERE cmsLanguageText.languageId NOT IN (SELECT umbracoLanguage.id FROM umbracoLanguage)"))
+ {
+ Delete.FromTable("cmsLanguageText").Row(new { pk = pk });
+ }
+
+ var columns = SqlSyntax.GetColumnsInSchema(Context.Database).Distinct().ToArray();
+
+ if (columns.Any(x => x.ColumnName.InvariantEquals("id")
+ && x.TableName.InvariantEquals("umbracoLanguage")
+ && x.DataType.InvariantEquals("smallint")))
+ {
+ //Ensure that the umbracoLanguage PK is INT and not SmallInt (which it might be in older db versions)
+ // In order to 'change' this to an INT, we have to run a full migration script which is super annoying
+ Create.Table("umbracoLanguage_TEMP")
+ .WithColumn("id").AsInt32().NotNullable().Identity()
+ .WithColumn("languageISOCode").AsString(10).Nullable()
+ .WithColumn("languageCultureName").AsString(50).Nullable();
+
+ var currentData = this.Context.Database.Fetch(new Sql().Select("*").From(SqlSyntax));
+ foreach (var languageDto in currentData)
+ {
+ Insert.IntoTable("umbracoLanguage_TEMP")
+ .EnableIdentityInsert()
+ .Row(new {id = languageDto.Id, languageISOCode = languageDto.IsoCode, languageCultureName = languageDto.CultureName});
+ }
+
+ //ok, all data has been copied over, drop the old table, rename the temp table and re-add constraints.
+ Delete.Table("umbracoLanguage");
+ Rename.Table("umbracoLanguage_TEMP").To("umbracoLanguage");
+
+ //add the pk
+ Create.PrimaryKey("PK_language").OnTable("umbracoLanguage").Column("id");
+ }
+
+ var dbIndexes = SqlSyntax.GetDefinedIndexes(Context.Database)
+ .Select(x => new DbIndexDefinition
+ {
+ TableName = x.Item1,
+ IndexName = x.Item2,
+ ColumnName = x.Item3,
+ IsUnique = x.Item4
+ }).ToArray();
+
+ //make sure it doesn't already exist
+ if (dbIndexes.Any(x => x.IndexName.InvariantEquals("IX_cmsDictionary_id")) == false)
+ {
+ Create.Index("IX_cmsDictionary_id").OnTable("cmsDictionary")
+ .OnColumn("id").Ascending()
+ .WithOptions().NonClustered()
+ .WithOptions().Unique();
+ }
+
+ //now we need to create a foreign key
+ Create.ForeignKey("FK_cmsLanguageText_umbracoLanguage_id").FromTable("cmsLanguageText").ForeignColumn("languageId")
+ .ToTable("umbracoLanguage").PrimaryColumn("id").OnDeleteOrUpdate(Rule.None);
+
+ Alter.Table("cmsDictionary").AlterColumn("parent").AsGuid().Nullable();
+
+ //set the parent to null if it equals the default dictionary item root id
+ foreach (var pk in Context.Database.Query("SELECT pk FROM cmsDictionary WHERE parent NOT IN (SELECT id FROM cmsDictionary)"))
+ {
+ Update.Table("cmsDictionary").Set(new { parent = (Guid?)null }).Where(new { pk = pk });
+ }
+
+ Create.ForeignKey("FK_cmsDictionary_cmsDictionary_id").FromTable("cmsDictionary").ForeignColumn("parent")
+ .ToTable("cmsDictionary").PrimaryColumn("id").OnDeleteOrUpdate(Rule.None);
+ }
+
+
+
+ }
+
+ public override void Down()
+ {
+ throw new NotImplementedException();
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenThreeZero/AddMigrationTable.cs b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenThreeZero/AddMigrationTable.cs
new file mode 100644
index 0000000000..079dbe1465
--- /dev/null
+++ b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenThreeZero/AddMigrationTable.cs
@@ -0,0 +1,43 @@
+using System.Linq;
+using Umbraco.Core.Configuration;
+using Umbraco.Core.Logging;
+using Umbraco.Core.Persistence.DatabaseModelDefinitions;
+using Umbraco.Core.Persistence.SqlSyntax;
+
+namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSevenThreeZero
+{
+ [Migration("7.3.0", 11, GlobalSettings.UmbracoMigrationName)]
+ public class AddMigrationTable : MigrationBase
+ {
+ public AddMigrationTable(ISqlSyntaxProvider sqlSyntax, ILogger logger)
+ : base(sqlSyntax, logger)
+ {
+ }
+
+ public override void Up()
+ {
+ //Don't exeucte if the table is already there
+ var tables = SqlSyntax.GetTablesInSchema(Context.Database).ToArray();
+ if (tables.InvariantContains("umbracoMigration")) return;
+
+ Create.Table("umbracoMigration")
+ .WithColumn("id").AsInt32().NotNullable().PrimaryKey("PK_umbracoMigrations").Identity()
+ .WithColumn("name").AsString(255).NotNullable()
+ .WithColumn("version").AsString(50).NotNullable()
+ .WithColumn("createDate").AsDateTime().NotNullable().WithDefault(SystemMethods.CurrentDateTime);
+
+ //unique constraint on name + version
+ Create.Index("IX_umbracoMigration").OnTable("umbracoMigration")
+ .OnColumn("name").Ascending()
+ .OnColumn("version").Ascending()
+ .WithOptions()
+ .NonClustered()
+ .WithOptions()
+ .Unique();
+ }
+
+ public override void Down()
+ {
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenThreeZero/AddServerRegistrationColumnsAndLock.cs b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenThreeZero/AddServerRegistrationColumnsAndLock.cs
new file mode 100644
index 0000000000..118dc1fc06
--- /dev/null
+++ b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenThreeZero/AddServerRegistrationColumnsAndLock.cs
@@ -0,0 +1,58 @@
+using System;
+using System.Linq;
+using Umbraco.Core.Configuration;
+using Umbraco.Core.Logging;
+using Umbraco.Core.Models.Rdbms;
+using Umbraco.Core.Persistence.SqlSyntax;
+
+namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSevenThreeZero
+{
+ [Migration("7.3.0", 17, GlobalSettings.UmbracoMigrationName)]
+ public class AddServerRegistrationColumnsAndLock : MigrationBase
+ {
+ public AddServerRegistrationColumnsAndLock(ISqlSyntaxProvider sqlSyntax, ILogger logger)
+ : base(sqlSyntax, logger)
+ { }
+
+ public override void Up()
+ {
+ // don't execute if the column is already there
+ var columns = SqlSyntax.GetColumnsInSchema(Context.Database).ToArray();
+ if (columns.Any(x => x.TableName.InvariantEquals("umbracoServer") && x.ColumnName.InvariantEquals("isMaster")) == false)
+ {
+ Create.Column("isMaster").OnTable("umbracoServer").AsBoolean().NotNullable().WithDefaultValue(0);
+ }
+
+ EnsureLockObject(Constants.System.ServersLock, "0AF5E610-A310-4B6F-925F-E928D5416AF7", "LOCK: Servers");
+ }
+
+ public override void Down()
+ {
+ // not implemented
+ }
+
+ private void EnsureLockObject(int id, string uniqueId, string text)
+ {
+ var exists = Context.Database.Exists(id);
+ if (exists) return;
+
+ Insert
+ .IntoTable("umbracoNode")
+ .EnableIdentityInsert()
+ .Row(new
+ {
+ id = id, // NodeId
+ trashed = false,
+ parentId = -1,
+ nodeUser = 0,
+ level = 1,
+ path = "-1," + id,
+ sortOrder = 0,
+ uniqueId = new Guid(uniqueId),
+ text = text,
+ nodeObjectType = new Guid(Constants.ObjectTypes.LockObject),
+ createDate = DateTime.Now
+ });
+ }
+ }
+}
diff --git a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenThreeZero/AddUniqueIdPropertyTypeColumn.cs b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenThreeZero/AddUniqueIdPropertyTypeColumn.cs
new file mode 100644
index 0000000000..7589c7000d
--- /dev/null
+++ b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenThreeZero/AddUniqueIdPropertyTypeColumn.cs
@@ -0,0 +1,58 @@
+using System.Linq;
+using Umbraco.Core.Configuration;
+using Umbraco.Core.Logging;
+using Umbraco.Core.Persistence.DatabaseModelDefinitions;
+using Umbraco.Core.Persistence.SqlSyntax;
+
+namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSevenThreeZero
+{
+ [Migration("7.3.0", 13, GlobalSettings.UmbracoMigrationName)]
+ public class AddUniqueIdPropertyTypeColumn : MigrationBase
+ {
+ public AddUniqueIdPropertyTypeColumn(ISqlSyntaxProvider sqlSyntax, ILogger logger)
+ : base(sqlSyntax, logger)
+ { }
+
+ public override void Up()
+ {
+ //Don't exeucte if the column is already there
+ var columns = SqlSyntax.GetColumnsInSchema(Context.Database).ToArray();
+
+ if (columns.Any(x => x.TableName.InvariantEquals("cmsPropertyType") && x.ColumnName.InvariantEquals("uniqueID")) == false)
+ {
+ Create.Column("uniqueID").OnTable("cmsPropertyType").AsGuid().NotNullable().WithDefault(SystemMethods.NewGuid);
+
+ // unique constraint on name + version
+ Create.Index("IX_cmsPropertyTypeUniqueID").OnTable("cmsPropertyType")
+ .OnColumn("uniqueID").Ascending()
+ .WithOptions()
+ .NonClustered()
+ .WithOptions()
+ .Unique();
+
+ // fill in the data in a way that is consistent over all environments
+ // (ie cannot use random guids, http://issues.umbraco.org/issue/U4-6942)
+
+ foreach (var data in Context.Database.Query(@"
+SELECT cmsPropertyType.id ptId, cmsPropertyType.Alias ptAlias, cmsContentType.alias ctAlias, umbracoNode.nodeObjectType nObjType
+FROM cmsPropertyType
+INNER JOIN cmsContentType
+ON cmsPropertyType.contentTypeId = cmsContentType.nodeId
+INNER JOIN umbracoNode
+ON cmsContentType.nodeId = umbracoNode.id"))
+ {
+ // create a consistent guid from
+ // property alias + content type alias + object type
+ string guidSource = data.ptAlias + data.ctAlias + data.nObjType;
+ var guid = guidSource.ToGuid();
+
+ // set the Unique Id to the one we've generated
+ Update.Table("cmsPropertyType").Set(new { uniqueID = guid }).Where(new { id = data.ptId });
+ }
+ }
+ }
+
+ public override void Down()
+ { }
+ }
+}
\ No newline at end of file
diff --git a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenThreeZero/AddUserColumns.cs b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenThreeZero/AddUserColumns.cs
index 9e0bb26ab9..46fde95005 100644
--- a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenThreeZero/AddUserColumns.cs
+++ b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenThreeZero/AddUserColumns.cs
@@ -30,7 +30,7 @@ namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSevenThreeZe
if (columns.Any(x => x.TableName.InvariantEquals("umbracoUser") && x.ColumnName.InvariantEquals("lastPasswordChangeDate")) == false)
Create.Column("lastPasswordChangeDate").OnTable("umbracoUser").AsDateTime().Nullable();
- if (columns.Any(x => x.TableName.InvariantEquals("umbracoUser") && x.ColumnName.InvariantEquals("lastLoginDate")) == false);
+ if (columns.Any(x => x.TableName.InvariantEquals("umbracoUser") && x.ColumnName.InvariantEquals("lastLoginDate")) == false)
Create.Column("lastLoginDate").OnTable("umbracoUser").AsDateTime().Nullable();
}
diff --git a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenThreeZero/CleanUpCorruptedPublishedFlags.cs b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenThreeZero/CleanUpCorruptedPublishedFlags.cs
new file mode 100644
index 0000000000..d62ad7645d
--- /dev/null
+++ b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenThreeZero/CleanUpCorruptedPublishedFlags.cs
@@ -0,0 +1,49 @@
+using System.Linq;
+using Umbraco.Core.Configuration;
+using Umbraco.Core.Logging;
+using Umbraco.Core.Models.Rdbms;
+using Umbraco.Core.Persistence.DatabaseModelDefinitions;
+using Umbraco.Core.Persistence.SqlSyntax;
+
+namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSevenThreeZero
+{
+ ///
+ /// Older corrupted dbs might have multiple published flags for a content item, this shouldn't be possible
+ /// so we need to clear the content flag on the older version
+ ///
+ [Migration("7.3.0", 18, GlobalSettings.UmbracoMigrationName)]
+ public class CleanUpCorruptedPublishedFlags : MigrationBase
+ {
+ public CleanUpCorruptedPublishedFlags(ISqlSyntaxProvider sqlSyntax, ILogger logger)
+ : base(sqlSyntax, logger)
+ {
+ }
+
+ public override void Up()
+ {
+ //Get all cmsDocument items that have more than one published date set
+ var sql = @"SELECT * FROM cmsDocument WHERE nodeId IN
+(SELECT nodeId
+FROM cmsDocument
+GROUP BY nodeId
+HAVING SUM(CASE WHEN published = 0 THEN 0 ELSE 1 END) > 1)
+AND published = 1
+ORDER BY nodeId, updateDate";
+
+ var docs = Context.Database.Fetch(sql).GroupBy(x => x.NodeId);
+
+ foreach (var doc in docs)
+ {
+ var latest = doc.OrderByDescending(x => x.UpdateDate).First();
+ foreach (var old in doc.Where(x => x.VersionId != latest.VersionId))
+ {
+ Update.Table("cmsDocument").Set(new {published = 0}).Where(new {nodeId = old.NodeId});
+ }
+ }
+ }
+
+ public override void Down()
+ {
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenThreeZero/CreateCacheInstructionTable.cs b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenThreeZero/CreateCacheInstructionTable.cs
index 44757aed9a..afa5a1e675 100644
--- a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenThreeZero/CreateCacheInstructionTable.cs
+++ b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenThreeZero/CreateCacheInstructionTable.cs
@@ -28,13 +28,11 @@ namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSevenThreeZe
var textType = SqlSyntax.GetSpecialDbType(SpecialDbTypes.NTEXT);
Create.Table("umbracoCacheInstruction")
- .WithColumn("id").AsInt32().Identity().NotNullable()
+ .WithColumn("id").AsInt32().NotNullable().PrimaryKey("PK_umbracoCacheInstruction").Identity()
.WithColumn("utcStamp").AsDateTime().NotNullable()
- .WithColumn("jsonInstruction").AsCustom(textType).NotNullable();
-
- Create.PrimaryKey("PK_umbracoCacheInstruction")
- .OnTable("umbracoCacheInstruction")
- .Column("id");
+ .WithColumn("jsonInstruction").AsCustom(textType).NotNullable()
+ .WithColumn("originated").AsString(500).NotNullable();
+
}
public override void Down()
diff --git a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenThreeZero/MigrateAndRemoveTemplateMasterColumn.cs b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenThreeZero/MigrateAndRemoveTemplateMasterColumn.cs
index dc241f9659..01db36abd9 100644
--- a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenThreeZero/MigrateAndRemoveTemplateMasterColumn.cs
+++ b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenThreeZero/MigrateAndRemoveTemplateMasterColumn.cs
@@ -3,6 +3,7 @@ using System.Linq;
using Umbraco.Core.Configuration;
using Umbraco.Core.Logging;
using Umbraco.Core.Models.Rdbms;
+using Umbraco.Core.Persistence.DatabaseModelDefinitions;
using Umbraco.Core.Persistence.SqlSyntax;
namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSevenThreeZero
@@ -23,46 +24,120 @@ namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSevenThreeZe
{
//Don't execute anything if there is no 'master' column - this might occur if the db is already upgraded
- var cols = SqlSyntax.GetColumnsInSchema(Context.Database);
+ var cols = SqlSyntax.GetColumnsInSchema(Context.Database).ToArray();
if (cols.Any(x => x.ColumnName.InvariantEquals("master") && x.TableName.InvariantEquals("cmsTemplate")) == false)
{
return;
}
+
+ var constraints = SqlSyntax.GetConstraintsPerColumn(Context.Database).Distinct().ToArray();
//update the parentId column for all templates to be correct so it matches the current 'master' template
- //NOTE: we are using dynamic because we need to get the data in a column that no longer exists in the schema
- var templates = Context.Database.Fetch(new Sql().Select("*").From(SqlSyntax));
- foreach (var template in templates)
+
+ //In some old corrupted databases, the information will not be correct in the master column so we need to fix that
+ //first by nulling out the master column where the id doesn't actually exist
+ Execute.Sql(@"UPDATE cmsTemplate SET master = NULL WHERE " +
+ SqlSyntax.GetQuotedColumnName("master") + @" IS NOT NULL AND " +
+ SqlSyntax.GetQuotedColumnName("master") + @" NOT IN (" +
+ //Stupid MySQL... needs this stupid syntax because it can do an update with a sub query of itself,
+ // yet it can do one with a sub sub query
+ // ... this will work in all dbs too
+ @"SELECT nodeId FROM (SELECT * FROM cmsTemplate a) b)");
+
+ //Now we can bulk update the parentId column
+
+ //NOTE: This single statement should be used but stupid SQLCE doesn't support Update with a FROM !!
+ // so now we have to do this by individual rows :(
+ //Execute.Sql(@"UPDATE umbracoNode
+ //SET parentID = COALESCE(t2." + SqlSyntax.GetQuotedColumnName("master") + @", -1)
+ //FROM umbracoNode t1
+ //INNER JOIN cmsTemplate t2
+ //ON t1.id = t2.nodeId");
+ Execute.Code(database =>
{
- Update.Table("umbracoNode").Set(new { parentID = template.master ?? -1 }).Where(new { id = template.nodeId });
+ var templateData = database.Fetch("SELECT * FROM cmsTemplate");
- //now build the correct path for the template
- Update.Table("umbracoNode").Set(new { path = BuildPath(template, templates) }).Where(new { id = template.nodeId });
+ foreach (var template in templateData)
+ {
+ var sql = "SET parentID=@parentId WHERE id=@nodeId";
- }
+ LogHelper.Info("Executing sql statement: UPDATE umbracoNode " + sql);
+
+ database.Update(sql,
+ new {parentId = template.master ?? -1, nodeId = template.nodeId});
+ }
+
+ return string.Empty;
+ });
+
+ //Now we can update the path, but this needs to be done in a delegate callback so that the query runs after the updates just completed
+ Execute.Code(database =>
+ {
+ //NOTE: we are using dynamic because we need to get the data in a column that no longer exists in the schema
+ var templates = database.Fetch(new Sql().Select("*").From());
+ foreach (var template in templates)
+ {
+ var sql = string.Format(SqlSyntax.UpdateData,
+ SqlSyntax.GetQuotedTableName("umbracoNode"),
+ "path=@buildPath",
+ "id=@nodeId");
+
+ LogHelper.Info("Executing sql statement: " + sql);
+
+ //now build the correct path for the template
+ database.Execute(sql, new
+ {
+ buildPath = BuildPath(template, templates),
+ nodeId = template.nodeId
+ });
+ }
+
+ return string.Empty;
+ });
+
+
//now remove the master column and key
if (this.Context.CurrentDatabaseProvider == DatabaseProviders.MySql)
{
- Delete.ForeignKey().FromTable("cmsTemplate").ForeignColumn("master").ToTable("umbracoUser").PrimaryColumn("id");
+ //Because MySQL doesn't name keys with what you want, we need to query for the one that is associated
+ // this is required for this specific case because there are 2 foreign keys on the cmsTemplate table
+ var fkName = constraints.FirstOrDefault(x => x.Item1.InvariantEquals("cmsTemplate") && x.Item2.InvariantEquals("master"));
+ if (fkName != null)
+ {
+ Delete.ForeignKey(fkName.Item3).OnTable("cmsTemplate");
+ }
}
else
{
- //These are the old aliases, before removing them, check they exist
- var constraints = SqlSyntax.GetConstraintsPerColumn(Context.Database).Distinct().ToArray();
-
if (constraints.Any(x => x.Item1.InvariantEquals("cmsTemplate") && x.Item3.InvariantEquals("FK_cmsTemplate_cmsTemplate")))
{
- Delete.ForeignKey("FK_cmsTemplate_cmsTemplate").OnTable("cmsTemplate");
+ Delete.ForeignKey("FK_cmsTemplate_cmsTemplate").OnTable("cmsTemplate");
}
//TODO: Hopefully it's not named something else silly in some crazy old versions
}
- var columns = SqlSyntax.GetColumnsInSchema(Context.Database).Distinct().ToArray();
- if (columns.Any(x => x.ColumnName.InvariantEquals("master") && x.TableName.InvariantEquals("cmsTemplate")))
+
+ var dbIndexes = SqlSyntax.GetDefinedIndexes(Context.Database)
+ .Select(x => new DbIndexDefinition()
+ {
+ TableName = x.Item1,
+ IndexName = x.Item2,
+ ColumnName = x.Item3,
+ IsUnique = x.Item4
+ }).ToArray();
+
+ //in some databases there's an index (IX_Master) on the master column which needs to be dropped first
+ var foundIndex = dbIndexes.FirstOrDefault(x => x.TableName.InvariantEquals("cmsTemplate") && x.ColumnName.InvariantEquals("master"));
+ if (foundIndex != null)
{
- Delete.Column("master").FromTable("cmsTemplate");
+ Delete.Index(foundIndex.IndexName).OnTable("cmsTemplate");
+ }
+
+ if (cols.Any(x => x.ColumnName.InvariantEquals("master") && x.TableName.InvariantEquals("cmsTemplate")))
+ {
+ Delete.Column("master").FromTable("cmsTemplate");
}
}
diff --git a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenThreeZero/MovePublicAccessXmlDataToDb.cs b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenThreeZero/MovePublicAccessXmlDataToDb.cs
index 7dd5f81b3a..d60385926b 100644
--- a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenThreeZero/MovePublicAccessXmlDataToDb.cs
+++ b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenThreeZero/MovePublicAccessXmlDataToDb.cs
@@ -30,6 +30,9 @@ namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSevenThreeZe
}
var xmlFile = IOHelper.MapPath(SystemFiles.AccessXml);
+
+ if (File.Exists(xmlFile) == false) return;
+
using (var fileStream = File.OpenRead(xmlFile))
{
var xml = XDocument.Load(fileStream);
diff --git a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenThreeZero/RemoveUmbracoLoginsTable.cs b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenThreeZero/RemoveUmbracoLoginsTable.cs
new file mode 100644
index 0000000000..b842ec041a
--- /dev/null
+++ b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenThreeZero/RemoveUmbracoLoginsTable.cs
@@ -0,0 +1,29 @@
+using System.Linq;
+using Umbraco.Core.Configuration;
+using Umbraco.Core.Logging;
+using Umbraco.Core.Persistence.SqlSyntax;
+
+namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSevenThreeZero
+{
+ [Migration("7.3.0", 15, GlobalSettings.UmbracoMigrationName)]
+ public class RemoveUmbracoLoginsTable : MigrationBase
+ {
+ public RemoveUmbracoLoginsTable(ISqlSyntaxProvider sqlSyntax, ILogger logger)
+ : base(sqlSyntax, logger)
+ {
+ }
+
+ public override void Up()
+ {
+ if (SqlSyntax.GetColumnsInSchema(Context.Database).Any(x => x.TableName.InvariantEquals("umbracoUserLogins")))
+ {
+ Delete.Table("umbracoUserLogins");
+ }
+ }
+
+ public override void Down()
+ {
+ throw new System.NotImplementedException();
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSixTwoZero/AdditionalIndexesAndKeys.cs b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSixTwoZero/AdditionalIndexesAndKeys.cs
index c629f31b17..e0c462b966 100644
--- a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSixTwoZero/AdditionalIndexesAndKeys.cs
+++ b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSixTwoZero/AdditionalIndexesAndKeys.cs
@@ -48,11 +48,33 @@ namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSixTwoZero
Create.Index("IX_cmsDocument_newest").OnTable("cmsDocument").OnColumn("newest").Ascending().WithOptions().NonClustered();
}
- //TODO: We need to fix this for SQL Azure since it does not let you drop any clustered indexes
- // Issue: http://issues.umbraco.org/issue/U4-5673
- // Some work around notes:
- // http://stackoverflow.com/questions/15872347/alter-clustered-index-column
- // https://social.msdn.microsoft.com/Forums/azure/en-US/5cc4b302-fa42-4c62-956a-bbf79dbbd040/changing-clustered-index-in-azure?forum=ssdsgetstarted
+ //We need to do this for SQL Azure V2 since it does not let you drop any clustered indexes
+ // Issue: http://issues.umbraco.org/issue/U4-5673
+ if (Context.CurrentDatabaseProvider == DatabaseProviders.SqlServer || Context.CurrentDatabaseProvider == DatabaseProviders.SqlAzure)
+ {
+ var version = Context.Database.ExecuteScalar("SELECT @@@@VERSION");
+ if (version.Contains("Microsoft SQL Azure"))
+ {
+ var parts = version.Split(new[] { '-' }, StringSplitOptions.RemoveEmptyEntries).Select(x => x.Trim()).ToArray();
+ if (parts.Length > 1)
+ {
+ if (parts[1].StartsWith("11."))
+ {
+
+ //we want to drop the umbracoUserLogins_Index index since it is named incorrectly and then re-create it so
+ // it follows the standard naming convention
+ if (dbIndexes.Any(x => x.IndexName.InvariantEquals("umbracoUserLogins_Index")))
+ {
+ //It's the old version that doesn't support dropping a clustered index on a table, so we need to do some manual work.
+ ExecuteSqlAzureSqlForChangingIndex();
+ }
+
+ return;
+ }
+ }
+ }
+ }
+
//we want to drop the umbracoUserLogins_Index index since it is named incorrectly and then re-create it so
// it follows the standard naming convention
@@ -73,5 +95,28 @@ namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSixTwoZero
Delete.Index("IX_cmsDocument_published").OnTable("cmsDocument");
Delete.Index("IX_cmsDocument_newest").OnTable("cmsDocument");
}
+
+ private void ExecuteSqlAzureSqlForChangingIndex()
+ {
+ Context.Database.Execute(@"CREATE TABLE ""umbracoUserLogins_temp""
+(
+ contextID uniqueidentifier NOT NULL,
+ userID int NOT NULL,
+ [timeout] bigint NOT NULL
+);
+CREATE CLUSTERED INDEX ""IX_umbracoUserLogins_Index"" ON ""umbracoUserLogins_temp"" (""contextID"");
+INSERT INTO ""umbracoUserLogins_temp"" SELECT * FROM ""umbracoUserLogins""
+DROP TABLE ""umbracoUserLogins""
+CREATE TABLE ""umbracoUserLogins""
+(
+ contextID uniqueidentifier NOT NULL,
+ userID int NOT NULL,
+ [timeout] bigint NOT NULL
+);
+CREATE CLUSTERED INDEX ""IX_umbracoUserLogins_Index"" ON ""umbracoUserLogins"" (""contextID"");
+INSERT INTO ""umbracoUserLogins"" SELECT * FROM ""umbracoUserLogins_temp""
+DROP TABLE ""umbracoUserLogins_temp""");
+
+ }
}
}
\ No newline at end of file
diff --git a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSixZeroOne/UpdatePropertyTypesAndGroups.cs b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSixZeroOne/UpdatePropertyTypesAndGroups.cs
index b4cf0d93fa..28ede71ad1 100644
--- a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSixZeroOne/UpdatePropertyTypesAndGroups.cs
+++ b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSixZeroOne/UpdatePropertyTypesAndGroups.cs
@@ -29,54 +29,52 @@ namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSixZeroOne
{
if (database != null)
{
- //Fetch all PropertyTypes that belongs to a PropertyTypeGroup
- var propertyTypes = database.Fetch("WHERE propertyTypeGroupId > 0");
+ //Fetch all PropertyTypes that belongs to a PropertyTypeGroup
+ //NOTE: We are writing the full query because we've added a column to the PropertyTypeDto in later versions so one of the columns
+ // won't exist yet
+ var propertyTypes = database.Fetch("SELECT * FROM cmsPropertyType WHERE propertyTypeGroupId > 0");
+
var propertyGroups = database.Fetch("WHERE id > 0");
foreach (var propertyType in propertyTypes)
{
- //Get the PropertyTypeGroup that the current PropertyType references
- var parentPropertyTypeGroup = propertyGroups.FirstOrDefault(x => x.Id == propertyType.PropertyTypeGroupId);
- if (parentPropertyTypeGroup != null)
+ // get the PropertyTypeGroup of the current PropertyType, skip if not found
+ var propertyTypeGroup = propertyGroups.FirstOrDefault(x => x.Id == propertyType.propertyTypeGroupId);
+ if (propertyTypeGroup == null) continue;
+
+ // if the PropretyTypeGroup belongs to the same content type as the PropertyType, then fine
+ if (propertyTypeGroup.ContentTypeNodeId == propertyType.contentTypeId) continue;
+
+ // else we want to assign the PropertyType to a proper PropertyTypeGroup
+ // ie one that does belong to the same content - look for it
+ var okPropertyTypeGroup = propertyGroups.FirstOrDefault(x =>
+ x.Text == propertyTypeGroup.Text && // same name
+ x.ContentTypeNodeId == propertyType.contentTypeId); // but for proper content type
+
+ if (okPropertyTypeGroup == null)
{
- //If the ContentType is the same on the PropertyType and the PropertyTypeGroup the group is valid and we skip to the next
- if (parentPropertyTypeGroup.ContentTypeNodeId == propertyType.ContentTypeId) continue;
-
- //Check if the 'new' PropertyTypeGroup has already been created
- var existingPropertyTypeGroup =
- propertyGroups.FirstOrDefault(
- x =>
- x.ParentGroupId == parentPropertyTypeGroup.Id && x.Text == parentPropertyTypeGroup.Text &&
- x.ContentTypeNodeId == propertyType.ContentTypeId);
-
- //This should ensure that we don't create duplicate groups for a single ContentType
- if (existingPropertyTypeGroup == null)
+ // does not exist, create a new PropertyTypeGroup,
+ var propertyGroup = new PropertyTypeGroupDto
{
+ ContentTypeNodeId = propertyType.contentTypeId,
+ Text = propertyTypeGroup.Text,
+ SortOrder = propertyTypeGroup.SortOrder
+ };
- //Create a new PropertyTypeGroup that references the parent group that the PropertyType was referencing pre-6.0.1
- var propertyGroup = new PropertyTypeGroupDto
- {
- ContentTypeNodeId = propertyType.ContentTypeId,
- ParentGroupId = parentPropertyTypeGroup.Id,
- Text = parentPropertyTypeGroup.Text,
- SortOrder = parentPropertyTypeGroup.SortOrder
- };
+ // save + add to list of groups
+ int id = Convert.ToInt16(database.Insert(propertyGroup));
+ propertyGroup.Id = id;
+ propertyGroups.Add(propertyGroup);
- //Save the PropertyTypeGroup in the database and update the list of groups with this new group
- int id = Convert.ToInt16(database.Insert(propertyGroup));
- propertyGroup.Id = id;
- propertyGroups.Add(propertyGroup);
- //Update the reference to the new PropertyTypeGroup on the current PropertyType
- propertyType.PropertyTypeGroupId = id;
- database.Update(propertyType);
- }
- else
- {
- //Update the reference to the existing PropertyTypeGroup on the current PropertyType
- propertyType.PropertyTypeGroupId = existingPropertyTypeGroup.Id;
- database.Update(propertyType);
- }
+ // update the PropertyType to use the new PropertyTypeGroup
+ propertyType.propertyTypeGroupId = id;
}
+ else
+ {
+ // exists, update PropertyType to use the PropertyTypeGroup
+ propertyType.propertyTypeGroupId = okPropertyTypeGroup.Id;
+ }
+ database.Update("cmsPropertyType", "id", propertyType);
}
}
diff --git a/src/Umbraco.Core/Persistence/PetaPoco.cs b/src/Umbraco.Core/Persistence/PetaPoco.cs
index c54e9abc82..234b602a94 100644
--- a/src/Umbraco.Core/Persistence/PetaPoco.cs
+++ b/src/Umbraco.Core/Persistence/PetaPoco.cs
@@ -215,6 +215,10 @@ namespace Umbraco.Core.Persistence
_paramPrefix = "?";
if (_dbType == DBType.Oracle)
_paramPrefix = ":";
+
+ // by default use MSSQL default ReadCommitted level
+ //TODO change to RepeatableRead - but that is breaking
+ _isolationLevel = IsolationLevel.ReadCommitted;
}
// Automatically close one open shared connection
@@ -237,7 +241,27 @@ namespace Umbraco.Core.Persistence
_sharedConnection.ConnectionString = _connectionString;
_sharedConnection.OpenWithRetry();//Changed .Open() => .OpenWithRetry() extension method
- _sharedConnection = OnConnectionOpened(_sharedConnection);
+ // ensure we have the proper isolation level, as levels can leak in pools
+ // read http://stackoverflow.com/questions/9851415/sql-server-isolation-level-leaks-across-pooled-connections
+ // and http://stackoverflow.com/questions/641120/what-exec-sp-reset-connection-shown-in-sql-profiler-means
+ //
+ // NPoco has that code in OpenSharedConnectionImp (commented out?)
+ //using (var cmd = _sharedConnection.CreateCommand())
+ //{
+ // cmd.CommandText = GetSqlForTransactionLevel(_isolationLevel);
+ // cmd.CommandTimeout = CommandTimeout;
+ // cmd.ExecuteNonQuery();
+ //}
+ //
+ // although MSDN documentation for SQL CE clearly states that the above method
+ // should work, it fails & reports an error parsing the query on 'TRANSACTION',
+ // and Google is no help (others have faced the same issue... no solution). So,
+ // switching to another method that does work on all databases.
+ var tr = _sharedConnection.BeginTransaction(_isolationLevel);
+ tr.Commit();
+ tr.Dispose();
+
+ _sharedConnection = OnConnectionOpened(_sharedConnection);
if (KeepConnectionAlive)
_sharedConnectionDepth++; // Make sure you call Dispose
@@ -269,10 +293,20 @@ namespace Umbraco.Core.Persistence
// Helper to create a transaction scope
public Transaction GetTransaction()
{
- return new Transaction(this);
- }
+ return GetTransaction(_isolationLevel);
+ }
- // Use by derived repo generated by T4 templates
+ public Transaction GetTransaction(IsolationLevel isolationLevel)
+ {
+ return new Transaction(this, isolationLevel);
+ }
+
+ public IsolationLevel CurrentTransactionIsolationLevel
+ {
+ get { return _transaction == null ? IsolationLevel.Unspecified : _transaction.IsolationLevel; }
+ }
+
+ // Use by derived repo generated by T4 templates
public virtual void OnBeginTransaction() { }
public virtual void OnEndTransaction() { }
@@ -281,17 +315,23 @@ namespace Umbraco.Core.Persistence
// Use `using (var scope=db.Transaction) { scope.Complete(); }` to ensure correct semantics
public void BeginTransaction()
{
- _transactionDepth++;
+ BeginTransaction(_isolationLevel);
+ }
+
+ public void BeginTransaction(IsolationLevel isolationLevel)
+ {
+ _transactionDepth++;
if (_transactionDepth == 1)
{
OpenSharedConnection();
- _transaction = _sharedConnection.BeginTransaction();
+ _transaction = _sharedConnection.BeginTransaction(isolationLevel);
_transactionCancelled = false;
OnBeginTransaction();
}
-
- }
+ else if (isolationLevel > _transaction.IsolationLevel)
+ throw new Exception("Already in a transaction with a lower isolation level.");
+ }
// Internal helper to cleanup transaction stuff
void CleanupTransaction()
@@ -313,6 +353,7 @@ namespace Umbraco.Core.Persistence
public void AbortTransaction()
{
_transactionCancelled = true;
+ //TODO what shall we do if transactionDepth is already zero?
if ((--_transactionDepth) == 0)
CleanupTransaction();
}
@@ -320,11 +361,32 @@ namespace Umbraco.Core.Persistence
// Complete the transaction
public void CompleteTransaction()
{
- if ((--_transactionDepth) == 0)
+ //TODO what shall we do if transactionDepth is already zero?
+ if ((--_transactionDepth) == 0)
CleanupTransaction();
}
- // Helper to handle named parameters from object properties
+ // in NPoco this is in DatabaseType
+ private static string GetSqlForTransactionLevel(IsolationLevel isolationLevel)
+ {
+ switch (isolationLevel)
+ {
+ case IsolationLevel.ReadCommitted:
+ return "SET TRANSACTION ISOLATION LEVEL READ COMMITTED";
+ case IsolationLevel.ReadUncommitted:
+ return "SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED";
+ case IsolationLevel.RepeatableRead:
+ return "SET TRANSACTION ISOLATION LEVEL REPEATABLE READ";
+ case IsolationLevel.Serializable:
+ return "SET TRANSACTION ISOLATION LEVEL SERIALIZABLE";
+ case IsolationLevel.Snapshot:
+ return "SET TRANSACTION ISOLATION LEVEL SNAPSHOT";
+ default:
+ return "SET TRANSACTION ISOLATION LEVEL READ COMMITTED";
+ }
+ }
+
+ // Helper to handle named parameters from object properties
static Regex rxParams = new Regex(@"(? args_dest)
{
@@ -2264,16 +2326,17 @@ namespace Umbraco.Core.Persistence
string _lastSql;
object[] _lastArgs;
string _paramPrefix = "@";
+ IsolationLevel _isolationLevel;
}
// Transaction object helps maintain transaction depth counts
public class Transaction : IDisposable
{
- public Transaction(Database db)
- {
- _db = db;
- _db.BeginTransaction();
- }
+ public Transaction(Database db, IsolationLevel isolationLevel)
+ {
+ _db = db;
+ _db.BeginTransaction(isolationLevel);
+ }
public virtual void Complete()
{
@@ -2283,6 +2346,7 @@ namespace Umbraco.Core.Persistence
public void Dispose()
{
+ //TODO prevent multiple calls to Dispose
if (_db != null)
_db.AbortTransaction();
}
diff --git a/src/Umbraco.Core/Persistence/Repositories/ContentPreviewRepository.cs b/src/Umbraco.Core/Persistence/Repositories/ContentPreviewRepository.cs
index 6b77c5972f..e40460ee7d 100644
--- a/src/Umbraco.Core/Persistence/Repositories/ContentPreviewRepository.cs
+++ b/src/Umbraco.Core/Persistence/Repositories/ContentPreviewRepository.cs
@@ -83,7 +83,7 @@ namespace Umbraco.Core.Persistence.Repositories
NodeId = entity.Id,
Timestamp = DateTime.Now,
VersionId = entity.Version,
- Xml = entity.Xml.ToString(SaveOptions.None)
+ Xml = entity.Xml.ToDataString()
};
//We need to do a special InsertOrUpdate here because we know that the PreviewXmlDto table has a composite key and thus
diff --git a/src/Umbraco.Core/Persistence/Repositories/ContentRepository.cs b/src/Umbraco.Core/Persistence/Repositories/ContentRepository.cs
index 25c2bb63e9..2b150529b3 100644
--- a/src/Umbraco.Core/Persistence/Repositories/ContentRepository.cs
+++ b/src/Umbraco.Core/Persistence/Repositories/ContentRepository.cs
@@ -38,8 +38,8 @@ namespace Umbraco.Core.Persistence.Repositories
private readonly ContentPreviewRepository _contentPreviewRepository;
private readonly ContentXmlRepository _contentXmlRepository;
- public ContentRepository(IDatabaseUnitOfWork work, CacheHelper cacheHelper, ILogger logger, ISqlSyntaxProvider syntaxProvider, IContentTypeRepository contentTypeRepository, ITemplateRepository templateRepository, ITagRepository tagRepository, IMappingResolver mappingResolver)
- : base(work, cacheHelper, logger, syntaxProvider, mappingResolver)
+ public ContentRepository(IDatabaseUnitOfWork work, CacheHelper cacheHelper, ILogger logger, ISqlSyntaxProvider syntaxProvider, IContentTypeRepository contentTypeRepository, ITemplateRepository templateRepository, ITagRepository tagRepository, IContentSection contentSection)
+ : base(work, cacheHelper, logger, syntaxProvider, contentSection)
{
if (contentTypeRepository == null) throw new ArgumentNullException("contentTypeRepository");
if (templateRepository == null) throw new ArgumentNullException("templateRepository");
@@ -238,7 +238,7 @@ namespace Umbraco.Core.Persistence.Repositories
var xmlItems = (from descendant in descendants
let xml = serializer(descendant)
- select new ContentXmlDto { NodeId = descendant.Id, Xml = xml.ToString(SaveOptions.None) }).ToArray();
+ select new ContentXmlDto { NodeId = descendant.Id, Xml = xml.ToDataString() }).ToArray();
//bulk insert it into the database
Database.BulkInsertRecords(SqlSyntax, xmlItems, tr);
@@ -340,6 +340,12 @@ namespace Umbraco.Core.Persistence.Repositories
{
((Content)entity).AddingEntity();
+ //ensure the default template is assigned
+ if (entity.Template == null)
+ {
+ entity.Template = entity.ContentType.DefaultTemplate;
+ }
+
//Ensure unique name on the same level
entity.Name = EnsureUniqueNodeName(entity.ParentId, entity.Name);
@@ -352,10 +358,11 @@ namespace Umbraco.Core.Persistence.Repositories
//NOTE Should the logic below have some kind of fallback for empty parent ids ?
//Logic for setting Path, Level and SortOrder
var parent = Database.First("WHERE id = @ParentId", new { ParentId = entity.ParentId });
- int level = parent.Level + 1;
- int sortOrder =
- Database.ExecuteScalar("SELECT COUNT(*) FROM umbracoNode WHERE parentID = @ParentId AND nodeObjectType = @NodeObjectType",
- new { ParentId = entity.ParentId, NodeObjectType = NodeObjectTypeId });
+ var level = parent.Level + 1;
+ var maxSortOrder = Database.ExecuteScalar(
+ "SELECT coalesce(max(sortOrder),-1) FROM umbracoNode WHERE parentid = @ParentId AND nodeObjectType = @NodeObjectType",
+ new { /*ParentId =*/ entity.ParentId, NodeObjectType = NodeObjectTypeId });
+ var sortOrder = maxSortOrder + 1;
//Create the (base) node data - umbracoNode
var nodeDto = dto.ContentVersionDto.ContentDto.NodeDto;
@@ -647,8 +654,8 @@ namespace Umbraco.Core.Persistence.Repositories
var translator = new SqlTranslator(sqlClause, query);
var sql = translator.Translate()
.Where(SqlSyntax, x => x.Published)
- .OrderBy(SqlSyntax, x => x.Level)
- .OrderBy(SqlSyntax, x => x.SortOrder);
+ .OrderBy(x => x.Level, SqlSyntax)
+ .OrderBy(x => x.SortOrder, SqlSyntax);
//NOTE: This doesn't allow properties to be part of the query
var dtos = Database.Fetch(sql);
@@ -659,7 +666,7 @@ namespace Umbraco.Core.Persistence.Repositories
// then we can use that entity. Otherwise if it is not published (which can be the case
// because we only store the 'latest' entries in the cache which might not be the published
// version)
- var fromCache = RepositoryCache.RuntimeCache.GetCacheItem(GetCacheIdKey(dto.NodeId));
+ var fromCache = RuntimeCache.GetCacheItem(GetCacheIdKey(dto.NodeId));
//var fromCache = TryGetFromCache(dto.NodeId);
if (fromCache != null && fromCache.Published)
{
@@ -819,12 +826,13 @@ namespace Umbraco.Core.Persistence.Repositories
var contentTypes = _contentTypeRepository.GetAll(dtos.Select(x => x.ContentVersionDto.ContentDto.ContentTypeId).ToArray())
.ToArray();
+
+ var ids = dtos
+ .Where(dto => dto.TemplateId.HasValue && dto.TemplateId.Value > 0)
+ .Select(x => x.TemplateId.Value).ToArray();
+
//NOTE: This should be ok for an SQL 'IN' statement, there shouldn't be an insane amount of content types
- var templates = _templateRepository.GetAll(
- dtos
- .Where(dto => dto.TemplateId.HasValue && dto.TemplateId.Value > 0)
- .Select(x => x.TemplateId.Value).ToArray())
- .ToArray();
+ var templates = ids.Length == 0 ? Enumerable.Empty() : _templateRepository.GetAll(ids).ToArray();
var dtosWithContentTypes = dtos
//This select into and null check are required because we don't have a foreign damn key on the contentType column
@@ -853,7 +861,7 @@ namespace Umbraco.Core.Persistence.Repositories
///
/// Private method to create a content object from a DocumentDto, which is used by Get and GetByVersion.
///
- ///
+ ///
///
///
///
@@ -871,6 +879,11 @@ namespace Umbraco.Core.Persistence.Repositories
{
content.Template = template ?? _templateRepository.Get(dto.TemplateId.Value);
}
+ else
+ {
+ //ensure there isn't one set.
+ content.Template = null;
+ }
content.Properties = propCollection;
@@ -883,7 +896,7 @@ namespace Umbraco.Core.Persistence.Repositories
///
/// Private method to create a content object from a DocumentDto, which is used by Get and GetByVersion.
///
- ///
+ ///
///
///
///
diff --git a/src/Umbraco.Core/Persistence/Repositories/ContentTypeBaseRepository.cs b/src/Umbraco.Core/Persistence/Repositories/ContentTypeBaseRepository.cs
index 3aadcfa5d2..2d4e2cb638 100644
--- a/src/Umbraco.Core/Persistence/Repositories/ContentTypeBaseRepository.cs
+++ b/src/Umbraco.Core/Persistence/Repositories/ContentTypeBaseRepository.cs
@@ -6,6 +6,8 @@ using System.Globalization;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
+using Umbraco.Core.Events;
+using Umbraco.Core.Exceptions;
using Umbraco.Core.Logging;
using Umbraco.Core.Models;
using Umbraco.Core.Models.EntityBase;
@@ -17,6 +19,7 @@ using Umbraco.Core.Persistence.Querying;
using Umbraco.Core.Persistence.Relators;
using Umbraco.Core.Persistence.SqlSyntax;
using Umbraco.Core.Persistence.UnitOfWork;
+using Umbraco.Core.Services;
namespace Umbraco.Core.Persistence.Repositories
{
@@ -25,13 +28,55 @@ namespace Umbraco.Core.Persistence.Repositories
///
/// Exposes shared functionality
///
- internal abstract class ContentTypeBaseRepository : PetaPocoRepositoryBase
+ internal abstract class ContentTypeBaseRepository : PetaPocoRepositoryBase, IReadRepository
where TEntity : class, IContentTypeComposition
{
-
protected ContentTypeBaseRepository(IDatabaseUnitOfWork work, CacheHelper cache, ILogger logger, ISqlSyntaxProvider sqlSyntax, IMappingResolver mappingResolver)
: base(work, cache, logger, sqlSyntax, mappingResolver)
{
+ _guidRepo = new GuidReadOnlyContentTypeBaseRepository(this, work, cache, logger, sqlSyntax);
+ }
+
+ private readonly GuidReadOnlyContentTypeBaseRepository _guidRepo;
+
+ public IEnumerable> Move(TEntity toMove, EntityContainer container)
+ {
+ var parentId = -1;
+ if (container != null)
+ {
+ // Check on paths
+ if ((string.Format(",{0},", container.Path)).IndexOf(string.Format(",{0},", toMove.Id), StringComparison.Ordinal) > -1)
+ {
+ throw new DataOperationException(MoveOperationStatusType.FailedNotAllowedByPath);
+ }
+ parentId = container.Id;
+ }
+
+ //used to track all the moved entities to be given to the event
+ var moveInfo = new List>
+ {
+ new MoveEventInfo(toMove, toMove.Path, parentId)
+ };
+
+ //do the move to a new parent
+ toMove.ParentId = parentId;
+ //schedule it for updating in the transaction
+ AddOrUpdate(toMove);
+
+ //update all descendants
+ var descendants = this.GetByQuery(
+ new Query().Where(type => type.Path.StartsWith(toMove.Path + ",")));
+ foreach (var descendant in descendants)
+ {
+ moveInfo.Add(new MoveEventInfo(descendant, descendant.Path, descendant.ParentId));
+
+ //all we're doing here is setting the parent Id to be dirty so that it resets the path/level/etc...
+ descendant.ParentId = descendant.ParentId;
+ //schedule it for updating in the transaction
+ AddOrUpdate(descendant);
+ }
+
+ return moveInfo;
}
///
@@ -51,7 +96,7 @@ namespace Umbraco.Core.Persistence.Repositories
var translator = new SqlTranslator(sqlClause, query);
var sql = translator.Translate()
- .OrderBy(SqlSyntax, x => x.PropertyTypeGroupId);
+ .OrderBy(x => x.PropertyTypeGroupId, SqlSyntax);
var dtos = Database.Fetch(new GroupPropertyTypeRelator().Map, sql);
@@ -60,14 +105,17 @@ namespace Umbraco.Core.Persistence.Repositories
yield return dto.ContentTypeNodeId;
}
}
-
+
protected virtual PropertyType CreatePropertyType(string propertyEditorAlias, DataTypeDatabaseType dbType, string propertyTypeAlias)
{
return new PropertyType(propertyEditorAlias, dbType, propertyTypeAlias);
}
- protected void PersistNewBaseContentType(ContentTypeDto dto, IContentTypeComposition entity)
+ protected void PersistNewBaseContentType(IContentTypeComposition entity)
{
+ var factory = new ContentTypeFactory();
+ var dto = factory.BuildContentTypeDto(entity);
+
//Cannot add a duplicate content type type
var exists = Database.ExecuteScalar(@"SELECT COUNT(*) FROM cmsContentType
INNER JOIN umbracoNode ON cmsContentType.nodeId = umbracoNode.id
@@ -119,7 +167,7 @@ AND umbracoNode.nodeObjectType = @objectType",
else
{
//Fallback for ContentTypes with no identity
- var contentTypeDto = Database.FirstOrDefault("WHERE alias = @Alias", new {Alias = composition.Alias});
+ var contentTypeDto = Database.FirstOrDefault("WHERE alias = @Alias", new { Alias = composition.Alias });
if (contentTypeDto != null)
{
Database.Insert(new ContentType2ContentTypeDto { ParentId = contentTypeDto.NodeId, ChildId = entity.Id });
@@ -139,7 +187,7 @@ AND umbracoNode.nodeObjectType = @objectType",
}
var propertyFactory = new PropertyGroupFactory(nodeDto.NodeId);
-
+
//Insert Tabs
foreach (var propertyGroup in entity.PropertyGroups)
{
@@ -179,38 +227,35 @@ AND umbracoNode.nodeObjectType = @objectType",
}
}
- protected void PersistUpdatedBaseContentType(ContentTypeDto dto, IContentTypeComposition entity)
+ protected void PersistUpdatedBaseContentType(IContentTypeComposition entity)
{
+ var factory = new ContentTypeFactory();
+ var dto = factory.BuildContentTypeDto(entity);
- //Cannot update to a duplicate alias
+ // ensure the alias is not used already
var exists = Database.ExecuteScalar(@"SELECT COUNT(*) FROM cmsContentType
INNER JOIN umbracoNode ON cmsContentType.nodeId = umbracoNode.id
WHERE cmsContentType." + SqlSyntax.GetQuotedColumnName("alias") + @"= @alias
AND umbracoNode.nodeObjectType = @objectType
AND umbracoNode.id <> @id",
- new { id = dto.NodeId, alias = entity.Alias, objectType = NodeObjectTypeId });
+ new { id = dto.NodeId, alias = dto.Alias, objectType = NodeObjectTypeId });
if (exists > 0)
- {
- throw new DuplicateNameException("An item with the alias " + entity.Alias + " already exists");
- }
-
- var propertyGroupFactory = new PropertyGroupFactory(entity.Id);
+ throw new DuplicateNameException("An item with the alias " + dto.Alias + " already exists");
+ // handle (update) the node
var nodeDto = dto.NodeDto;
- var o = Database.Update(nodeDto);
+ Database.Update(nodeDto);
+ // fixme - why? we are UPDATING so we should ALREADY have a PK!
//Look up ContentType entry to get PrimaryKey for updating the DTO
- var dtoPk = Database.First("WHERE nodeId = @Id", new {Id = entity.Id});
+ var dtoPk = Database.First("WHERE nodeId = @Id", new { Id = entity.Id });
dto.PrimaryKey = dtoPk.PrimaryKey;
Database.Update(dto);
- //Delete the ContentType composition entries before adding the updated collection
- Database.Delete("WHERE childContentTypeId = @Id", new {Id = entity.Id});
- //Update ContentType composition in new table
+ // handle (delete then recreate) compositions
+ Database.Delete("WHERE childContentTypeId = @Id", new { Id = entity.Id });
foreach (var composition in entity.ContentTypeComposition)
- {
- Database.Insert(new ContentType2ContentTypeDto {ParentId = composition.Id, ChildId = entity.Id});
- }
+ Database.Insert(new ContentType2ContentTypeDto { ParentId = composition.Id, ChildId = entity.Id });
//Removing a ContentType from a composition (U4-1690)
//1. Find content based on the current ContentType: entity.Id
@@ -234,7 +279,7 @@ AND umbracoNode.id <> @id",
foreach (var key in compositionBase.RemovedContentTypeKeyTracker)
{
//Find PropertyTypes for the removed ContentType
- var propertyTypes = Database.Fetch("WHERE contentTypeId = @Id", new {Id = key});
+ var propertyTypes = Database.Fetch("WHERE contentTypeId = @Id", new { Id = key });
//Loop through the Content that is based on the current ContentType in order to remove the Properties that are
//based on the PropertyTypes that belong to the removed ContentType.
foreach (var contentDto in contentDtos)
@@ -259,7 +304,7 @@ AND umbracoNode.id <> @id",
}
//Delete the allowed content type entries before adding the updated collection
- Database.Delete("WHERE Id = @Id", new {Id = entity.Id});
+ Database.Delete("WHERE Id = @Id", new { Id = entity.Id });
//Insert collection of allowed content types
foreach (var allowedContentType in entity.AllowedContentTypes)
{
@@ -272,10 +317,10 @@ AND umbracoNode.id <> @id",
}
- if (((ICanBeDirty) entity).IsPropertyDirty("PropertyTypes") || entity.PropertyTypes.Any(x => x.IsDirty()))
+ if (((ICanBeDirty)entity).IsPropertyDirty("PropertyTypes") || entity.PropertyTypes.Any(x => x.IsDirty()))
{
//Delete PropertyTypes by excepting entries from db with entries from collections
- var dbPropertyTypes = Database.Fetch("WHERE contentTypeId = @Id", new {Id = entity.Id});
+ var dbPropertyTypes = Database.Fetch("WHERE contentTypeId = @Id", new { Id = entity.Id });
var dbPropertyTypeAlias = dbPropertyTypes.Select(x => x.Id);
var entityPropertyTypes = entity.PropertyTypes.Where(x => x.HasIdentity).Select(x => x.Id);
var items = dbPropertyTypeAlias.Except(entityPropertyTypes);
@@ -283,60 +328,49 @@ AND umbracoNode.id <> @id",
{
//Before a PropertyType can be deleted, all Properties based on that PropertyType should be deleted.
Database.Delete("WHERE propertyTypeId = @Id", new { Id = item });
- Database.Delete("WHERE propertytypeid = @Id", new {Id = item});
+ Database.Delete("WHERE propertytypeid = @Id", new { Id = item });
Database.Delete("WHERE contentTypeId = @Id AND id = @PropertyTypeId",
- new {Id = entity.Id, PropertyTypeId = item});
+ new { Id = entity.Id, PropertyTypeId = item });
}
}
- if (entity.IsPropertyDirty("PropertyGroups") ||
- entity.PropertyGroups.Any(x => x.IsDirty()))
+ if (entity.IsPropertyDirty("PropertyGroups") || entity.PropertyGroups.Any(x => x.IsDirty()))
{
- //Delete Tabs/Groups by excepting entries from db with entries from collections
- var dbPropertyGroups =
- Database.Fetch("WHERE contenttypeNodeId = @Id", new {Id = entity.Id})
- .Select(x => new Tuple(x.Id, x.Text))
- .ToList();
- var entityPropertyGroups = entity.PropertyGroups.Select(x => new Tuple(x.Id, x.Name)).ToList();
- var tabsToDelete = dbPropertyGroups.Select(x => x.Item1).Except(entityPropertyGroups.Select(x => x.Item1));
- var tabs = dbPropertyGroups.Where(x => tabsToDelete.Any(y => y == x.Item1));
- //Update Tab name downstream to ensure renaming is done properly
- foreach (var propertyGroup in entityPropertyGroups)
+ // todo
+ // we used to try to propagate tabs renaming downstream, relying on ParentId, but
+ // 1) ParentId makes no sense (if a tab can be inherited from multiple composition
+ // types) so we would need to figure things out differently, visiting downstream
+ // content types and looking for tabs with the same name...
+ // 2) It was not deployable as changing a content type changes other content types
+ // that was not deterministic, because it would depend on the order of the changes.
+ // That last point could be fixed if (1) is fixed, but then it still is an issue with
+ // deploy because changing a content type changes other content types that are not
+ // dependencies but dependents, and then what?
+ //
+ // So... for the time being, all renaming propagation is disabled. We just don't do it.
+
+ // (all gone)
+
+ // delete tabs that do not exist anymore
+ // get the tabs that are currently existing (in the db)
+ // get the tabs that we want, now
+ // and derive the tabs that we want to delete
+ var existingPropertyGroups = Database.Fetch("WHERE contentTypeNodeId = @id", new { id = entity.Id })
+ .Select(x => x.Id)
+ .ToList();
+ var newPropertyGroups = entity.PropertyGroups.Select(x => x.Id).ToList();
+ var tabsToDelete = existingPropertyGroups
+ .Except(newPropertyGroups)
+ .ToArray();
+
+ // move properties to generic properties, and delete the tabs
+ if (tabsToDelete.Length > 0)
{
- Database.Update("SET Text = @TabName WHERE parentGroupId = @TabId",
- new { TabName = propertyGroup.Item2, TabId = propertyGroup.Item1 });
-
- var childGroups = Database.Fetch("WHERE parentGroupId = @TabId", new { TabId = propertyGroup.Item1 });
- foreach (var childGroup in childGroups)
- {
- var sibling = Database.Fetch("WHERE contenttypeNodeId = @Id AND text = @Name",
- new { Id = childGroup.ContentTypeNodeId, Name = propertyGroup.Item2 })
- .FirstOrDefault(x => x.ParentGroupId.HasValue == false || x.ParentGroupId.Value.Equals(propertyGroup.Item1) == false);
- //If the child group doesn't have a sibling there is no chance of duplicates and we continue
- if (sibling == null || (sibling.ParentGroupId.HasValue && sibling.ParentGroupId.Value.Equals(propertyGroup.Item1))) continue;
-
- //Since the child group has a sibling with the same name we need to point any PropertyTypes to the sibling
- //as this child group is about to leave the party.
- Database.Update(
- "SET propertyTypeGroupId = @PropertyTypeGroupId WHERE propertyTypeGroupId = @PropertyGroupId AND ContentTypeId = @ContentTypeId",
- new { PropertyTypeGroupId = sibling.Id, PropertyGroupId = childGroup.Id, ContentTypeId = childGroup.ContentTypeNodeId });
-
- //Since the parent group has been renamed and we have duplicates we remove this group
- //and leave our sibling in charge of the part.
- Database.Delete(childGroup);
- }
- }
- //Do Tab updates
- foreach (var tab in tabs)
- {
- Database.Update("SET propertyTypeGroupId = NULL WHERE propertyTypeGroupId = @PropertyGroupId",
- new {PropertyGroupId = tab.Item1});
- Database.Update("SET parentGroupId = NULL WHERE parentGroupId = @TabId",
- new {TabId = tab.Item1});
- Database.Delete("WHERE contenttypeNodeId = @Id AND text = @Name",
- new {Id = entity.Id, Name = tab.Item2});
+ Database.Update("SET propertyTypeGroupId=NULL WHERE propertyTypeGroupId IN (@ids)", new { ids = tabsToDelete });
+ Database.Delete("WHERE id IN (@ids)", new { ids = tabsToDelete });
}
}
+ var propertyGroupFactory = new PropertyGroupFactory(entity.Id);
//Run through all groups to insert or update entries
foreach (var propertyGroup in entity.PropertyGroups)
@@ -380,25 +414,6 @@ AND umbracoNode.id <> @id",
if (propertyType.HasIdentity == false)
propertyType.Id = typePrimaryKey; //Set Id on new PropertyType
}
-
- //If a Composition is removed we need to update/reset references to the PropertyGroups on that ContentType
- if (entity.IsPropertyDirty("ContentTypeComposition") &&
- compositionBase != null &&
- compositionBase.RemovedContentTypeKeyTracker != null &&
- compositionBase.RemovedContentTypeKeyTracker.Any())
- {
- foreach (var compositionId in compositionBase.RemovedContentTypeKeyTracker)
- {
- var dbPropertyGroups =
- Database.Fetch("WHERE contenttypeNodeId = @Id", new { Id = compositionId })
- .Select(x => x.Id);
- foreach (var propertyGroup in dbPropertyGroups)
- {
- Database.Update("SET parentGroupId = NULL WHERE parentGroupId = @TabId AND contenttypeNodeId = @ContentTypeNodeId",
- new { TabId = propertyGroup, ContentTypeNodeId = entity.Id });
- }
- }
- }
}
protected IEnumerable GetAllowedContentTypeIds(int id)
@@ -448,7 +463,7 @@ AND umbracoNode.id <> @id",
var list = new List();
foreach (var dto in dtos.Where(x => (x.PropertyTypeGroupId > 0) == false))
{
- var propType = CreatePropertyType(dto.DataTypeDto.PropertyEditorAlias, dto.DataTypeDto.DbType.EnumParse(true), dto.Alias);
+ var propType = CreatePropertyType(dto.DataTypeDto.PropertyEditorAlias, dto.DataTypeDto.DbType.EnumParse(true), dto.Alias);
propType.DataTypeDefinitionId = dto.DataTypeId;
propType.Description = dto.Description;
propType.Id = dto.Id;
@@ -499,7 +514,7 @@ AND umbracoNode.id <> @id",
throw exception;
});
}
-
+
///
/// Try to set the data type id based on its ControlId
///
@@ -562,28 +577,30 @@ AND umbracoNode.id <> @id",
}
}
- public static IEnumerable GetMediaTypes(
- int[] mediaTypeIds, Database db, ISqlSyntaxProvider sqlSyntax,
+ public static IEnumerable GetMediaTypes(
+ TId[] mediaTypeIds, Database db, ISqlSyntaxProvider sqlSyntax,
TRepo contentTypeRepository)
- where TRepo : IRepositoryQueryable
+ where TRepo : IReadRepository
+ where TId: struct
{
- IDictionary> allParentMediaTypeIds;
+ IDictionary> allParentMediaTypeIds;
var mediaTypes = MapMediaTypes(mediaTypeIds, db, sqlSyntax, out allParentMediaTypeIds)
.ToArray();
MapContentTypeChildren(mediaTypes, db, sqlSyntax, contentTypeRepository, allParentMediaTypeIds);
-
+
return mediaTypes;
}
- public static IEnumerable GetContentTypes(
- int[] contentTypeIds, Database db, ISqlSyntaxProvider sqlSyntax,
+ public static IEnumerable GetContentTypes(
+ TId[] contentTypeIds, Database db, ISqlSyntaxProvider sqlSyntax,
TRepo contentTypeRepository,
ITemplateRepository templateRepository)
- where TRepo : IRepositoryQueryable
+ where TRepo : IReadRepository
+ where TId : struct
{
- IDictionary> allAssociatedTemplates;
- IDictionary> allParentContentTypeIds;
+ IDictionary> allAssociatedTemplates;
+ IDictionary> allParentContentTypeIds;
var contentTypes = MapContentTypes(contentTypeIds, db, sqlSyntax, out allAssociatedTemplates, out allParentContentTypeIds)
.ToArray();
@@ -593,17 +610,18 @@ AND umbracoNode.id <> @id",
contentTypes, db, contentTypeRepository, templateRepository, allAssociatedTemplates);
MapContentTypeChildren(
- contentTypes, db, sqlSyntax, contentTypeRepository, allParentContentTypeIds);
+ contentTypes, db, sqlSyntax, contentTypeRepository, allParentContentTypeIds);
}
return contentTypes;
}
- internal static void MapContentTypeChildren(IContentTypeComposition[] contentTypes,
+ internal static void MapContentTypeChildren(IContentTypeComposition[] contentTypes,
Database db, ISqlSyntaxProvider sqlSyntax,
TRepo contentTypeRepository,
- IDictionary> allParentContentTypeIds)
- where TRepo : IRepositoryQueryable
+ IDictionary> allParentContentTypeIds)
+ where TRepo : IReadRepository
+ where TId : struct
{
//NOTE: SQL call #2
@@ -615,9 +633,9 @@ AND umbracoNode.id <> @id",
foreach (var contentType in contentTypes)
{
contentType.PropertyGroups = allPropGroups[contentType.Id];
- ((ContentTypeBase) contentType).PropertyTypes = allPropTypes[contentType.Id];
+ contentType.NoGroupPropertyTypes = allPropTypes[contentType.Id];
}
-
+
//NOTE: SQL call #3++
if (allParentContentTypeIds != null)
@@ -628,7 +646,18 @@ AND umbracoNode.id <> @id",
var allParentContentTypes = contentTypeRepository.GetAll(allParentIdsAsArray).ToArray();
foreach (var contentType in contentTypes)
{
- var parentContentTypes = allParentContentTypes.Where(x => allParentContentTypeIds[contentType.Id].Contains(x.Id));
+ //TODO: this is pretty hacky right now but i don't have time to refactor/fix running queries based on ints and Guids
+ // (i.e. for v8) but we need queries by GUIDs now so this is how it's gonna have to be
+ var entityId = typeof(TId) == typeof(int) ? contentType.Id : (object)contentType.Key;
+
+ var parentContentTypes = allParentContentTypes.Where(x =>
+ {
+ //TODO: this is pretty hacky right now but i don't have time to refactor/fix running queries based on ints and Guids
+ // (i.e. for v8) but we need queries by GUIDs now so this is how it's gonna have to be
+ var parentEntityId = typeof(TId) == typeof(int) ? x.Id : (object)x.Key;
+
+ return allParentContentTypeIds[(TId)entityId].Contains((TId)parentEntityId);
+ });
foreach (var parentContentType in parentContentTypes)
{
var result = contentType.AddContentType(parentContentType);
@@ -642,15 +671,16 @@ AND umbracoNode.id <> @id",
}
}
-
+
}
- internal static void MapContentTypeTemplates(IContentType[] contentTypes,
+ internal static void MapContentTypeTemplates(IContentType[] contentTypes,
Database db,
TRepo contentTypeRepository,
ITemplateRepository templateRepository,
- IDictionary> associatedTemplates)
- where TRepo : IRepositoryQueryable
+ IDictionary> associatedTemplates)
+ where TRepo : IReadRepository
+ where TId: struct
{
if (associatedTemplates == null || associatedTemplates.Any() == false) return;
@@ -667,7 +697,11 @@ AND umbracoNode.id <> @id",
foreach (var contentType in contentTypes)
{
- var associatedTemplateIds = associatedTemplates[contentType.Id].Select(x => x.TemplateId)
+ //TODO: this is pretty hacky right now but i don't have time to refactor/fix running queries based on ints and Guids
+ // (i.e. for v8) but we need queries by GUIDs now so this is how it's gonna have to be
+ var entityId = typeof(TId) == typeof(int) ? contentType.Id : (object)contentType.Key;
+
+ var associatedTemplateIds = associatedTemplates[(TId)entityId].Select(x => x.TemplateId)
.Distinct()
.ToArray();
@@ -676,11 +710,12 @@ AND umbracoNode.id <> @id",
: Enumerable.Empty()).ToArray();
}
-
+
}
- internal static IEnumerable MapMediaTypes(int[] mediaTypeIds, Database db, ISqlSyntaxProvider sqlSyntax,
- out IDictionary> parentMediaTypeIds)
+ internal static IEnumerable MapMediaTypes(TId[] mediaTypeIds, Database db, ISqlSyntaxProvider sqlSyntax,
+ out IDictionary> parentMediaTypeIds)
+ where TId : struct
{
Mandate.That(mediaTypeIds.Any(), () => new InvalidOperationException("must be at least one content type id specified"));
Mandate.ParameterNotNull(db, "db");
@@ -691,7 +726,7 @@ AND umbracoNode.id <> @id",
var sql = @"SELECT cmsContentType.pk as ctPk, cmsContentType.alias as ctAlias, cmsContentType.allowAtRoot as ctAllowAtRoot, cmsContentType.description as ctDesc,
cmsContentType.icon as ctIcon, cmsContentType.isContainer as ctIsContainer, cmsContentType.nodeId as ctId, cmsContentType.thumbnail as ctThumb,
AllowedTypes.allowedId as ctaAllowedId, AllowedTypes.SortOrder as ctaSortOrder, AllowedTypes.alias as ctaAlias,
- ParentTypes.parentContentTypeId as chtParentId,
+ ParentTypes.parentContentTypeId as chtParentId, ParentTypes.parentContentTypeKey as chtParentKey,
umbracoNode.createDate as nCreateDate, umbracoNode." + sqlSyntax.GetQuotedColumnName("level") + @" as nLevel, umbracoNode.nodeObjectType as nObjectType, umbracoNode.nodeUser as nUser,
umbracoNode.parentID as nParentId, umbracoNode." + sqlSyntax.GetQuotedColumnName("path") + @" as nPath, umbracoNode.sortOrder as nSortOrder, umbracoNode." + sqlSyntax.GetQuotedColumnName("text") + @" as nName, umbracoNode.trashed as nTrashed,
umbracoNode.uniqueID as nUniqueId
@@ -705,10 +740,28 @@ AND umbracoNode.id <> @id",
ON cmsContentTypeAllowedContentType.AllowedId = cmsContentType.nodeId
) AllowedTypes
ON AllowedTypes.Id = cmsContentType.nodeId
- LEFT JOIN cmsContentType2ContentType as ParentTypes
+ LEFT JOIN (
+ SELECT cmsContentType2ContentType.parentContentTypeId, umbracoNode.uniqueID AS parentContentTypeKey, cmsContentType2ContentType.childContentTypeId
+ FROM cmsContentType2ContentType
+ INNER JOIN umbracoNode
+ ON cmsContentType2ContentType.parentContentTypeId = umbracoNode." + sqlSyntax.GetQuotedColumnName("id") + @"
+ ) ParentTypes
ON ParentTypes.childContentTypeId = cmsContentType.nodeId
- WHERE (umbracoNode.nodeObjectType = @nodeObjectType)
- AND (umbracoNode.id IN (@contentTypeIds))";
+ WHERE (umbracoNode.nodeObjectType = @nodeObjectType)";
+
+ if (mediaTypeIds.Any())
+ {
+ //TODO: This is all sorts of hacky but i don't have time to refactor a lot to get both ints and guids working nicely... this will
+ // work for the time being.
+ if (typeof(TId) == typeof(int))
+ {
+ sql = sql + " AND (umbracoNode.id IN (@contentTypeIds))";
+ }
+ else if (typeof(TId) == typeof(Guid))
+ {
+ sql = sql + " AND (umbracoNode.uniqueID IN (@contentTypeIds))";
+ }
+ }
//NOTE: we are going to assume there's not going to be more than 2100 content type ids since that is the max SQL param count!
if ((mediaTypeIds.Length - 1) > 2000)
@@ -722,7 +775,7 @@ AND umbracoNode.id <> @id",
return Enumerable.Empty();
}
- parentMediaTypeIds = new Dictionary>();
+ parentMediaTypeIds = new Dictionary>();
var mappedMediaTypes = new List();
foreach (var contentTypeId in mediaTypeIds)
@@ -734,7 +787,14 @@ AND umbracoNode.id <> @id",
//first we want to get the main content type data this is 1 : 1 with umbraco node data
var ct = result
- .Where(x => x.ctId == currentCtId)
+ .Where(x =>
+ {
+ //TODO: This is a bit hacky right now but don't have time to do a nice refactor to support both GUID and Int queries, so this is
+ // how it is for now.
+ return (typeof (TId) == typeof (int))
+ ? x.ctId == currentCtId
+ : x.nUniqueId == currentCtId;
+ })
.Select(x => new { x.ctPk, x.ctId, x.ctAlias, x.ctAllowAtRoot, x.ctDesc, x.ctIcon, x.ctIsContainer, x.ctThumb, x.nName, x.nCreateDate, x.nLevel, x.nObjectType, x.nUser, x.nParentId, x.nPath, x.nSortOrder, x.nTrashed, x.nUniqueId })
.DistinctBy(x => (int)x.ctId)
.FirstOrDefault();
@@ -758,7 +818,7 @@ AND umbracoNode.id <> @id",
NodeDto = new NodeDto
{
CreateDate = ct.nCreateDate,
- Level = (short) ct.nLevel,
+ Level = (short)ct.nLevel,
NodeId = ct.ctId,
NodeObjectType = ct.nObjectType,
ParentId = ct.nParentId,
@@ -770,11 +830,11 @@ AND umbracoNode.id <> @id",
UserId = ct.nUser
}
};
-
+
//now create the media type object
- var factory = new MediaTypeFactory(new Guid(Constants.ObjectTypes.MediaType));
- var mediaType = factory.BuildEntity(contentTypeDto);
+ var factory = new ContentTypeFactory();
+ var mediaType = factory.BuildMediaTypeEntity(contentTypeDto);
//map the allowed content types
//map the child content type ids
@@ -786,9 +846,10 @@ AND umbracoNode.id <> @id",
return mappedMediaTypes;
}
- internal static IEnumerable MapContentTypes(int[] contentTypeIds, Database db, ISqlSyntaxProvider sqlSyntax,
- out IDictionary> associatedTemplates,
- out IDictionary> parentContentTypeIds)
+ internal static IEnumerable MapContentTypes(TId[] contentTypeIds, Database db, ISqlSyntaxProvider sqlSyntax,
+ out IDictionary> associatedTemplates,
+ out IDictionary> parentContentTypeIds)
+ where TId : struct
{
Mandate.ParameterNotNull(db, "db");
@@ -799,7 +860,7 @@ AND umbracoNode.id <> @id",
cmsContentType.pk as ctPk, cmsContentType.alias as ctAlias, cmsContentType.allowAtRoot as ctAllowAtRoot, cmsContentType.description as ctDesc,
cmsContentType.icon as ctIcon, cmsContentType.isContainer as ctIsContainer, cmsContentType.nodeId as ctId, cmsContentType.thumbnail as ctThumb,
AllowedTypes.allowedId as ctaAllowedId, AllowedTypes.SortOrder as ctaSortOrder, AllowedTypes.alias as ctaAlias,
- ParentTypes.parentContentTypeId as chtParentId,
+ ParentTypes.parentContentTypeId as chtParentId,ParentTypes.parentContentTypeKey as chtParentKey,
umbracoNode.createDate as nCreateDate, umbracoNode." + sqlSyntax.GetQuotedColumnName("level") + @" as nLevel, umbracoNode.nodeObjectType as nObjectType, umbracoNode.nodeUser as nUser,
umbracoNode.parentID as nParentId, umbracoNode." + sqlSyntax.GetQuotedColumnName("path") + @" as nPath, umbracoNode.sortOrder as nSortOrder, umbracoNode." + sqlSyntax.GetQuotedColumnName("text") + @" as nName, umbracoNode.trashed as nTrashed,
umbracoNode.uniqueID as nUniqueId,
@@ -822,11 +883,29 @@ AND umbracoNode.id <> @id",
ON cmsTemplate.nodeId = umbracoNode.id
) as Template
ON Template.nodeId = cmsDocumentType.templateNodeId
- LEFT JOIN cmsContentType2ContentType as ParentTypes
+ LEFT JOIN (
+ SELECT cmsContentType2ContentType.parentContentTypeId, umbracoNode.uniqueID AS parentContentTypeKey, cmsContentType2ContentType.childContentTypeId
+ FROM cmsContentType2ContentType
+ INNER JOIN umbracoNode
+ ON cmsContentType2ContentType.parentContentTypeId = umbracoNode." + sqlSyntax.GetQuotedColumnName("id") + @"
+ ) ParentTypes
ON ParentTypes.childContentTypeId = cmsContentType.nodeId
WHERE (umbracoNode.nodeObjectType = @nodeObjectType)";
- if(contentTypeIds.Any())
- sql = sql + " AND (umbracoNode.id IN (@contentTypeIds))";
+
+ if (contentTypeIds.Any())
+ {
+ //TODO: This is all sorts of hacky but i don't have time to refactor a lot to get both ints and guids working nicely... this will
+ // work for the time being.
+ if (typeof(TId) == typeof(int))
+ {
+ sql = sql + " AND (umbracoNode.id IN (@contentTypeIds))";
+ }
+ else if (typeof(TId) == typeof(Guid))
+ {
+ sql = sql + " AND (umbracoNode.uniqueID IN (@contentTypeIds))";
+ }
+ }
+
//NOTE: we are going to assume there's not going to be more than 2100 content type ids since that is the max SQL param count!
if ((contentTypeIds.Length - 1) > 2000)
@@ -841,8 +920,8 @@ AND umbracoNode.id <> @id",
return Enumerable.Empty();
}
- parentContentTypeIds = new Dictionary>();
- associatedTemplates = new Dictionary>();
+ parentContentTypeIds = new Dictionary>();
+ associatedTemplates = new Dictionary>();
var mappedContentTypes = new List();
foreach (var contentTypeId in contentTypeIds)
@@ -854,7 +933,14 @@ AND umbracoNode.id <> @id",
//first we want to get the main content type data this is 1 : 1 with umbraco node data
var ct = result
- .Where(x => x.ctId == currentCtId)
+ .Where(x =>
+ {
+ //TODO: This is a bit hacky right now but don't have time to do a nice refactor to support both GUID and Int queries, so this is
+ // how it is for now.
+ return (typeof(TId) == typeof(int))
+ ? x.ctId == currentCtId
+ : x.nUniqueId == currentCtId;
+ })
.Select(x => new { x.ctPk, x.ctId, x.ctAlias, x.ctAllowAtRoot, x.ctDesc, x.ctIcon, x.ctIsContainer, x.ctThumb, x.nName, x.nCreateDate, x.nLevel, x.nObjectType, x.nUser, x.nParentId, x.nPath, x.nSortOrder, x.nTrashed, x.nUniqueId })
.DistinctBy(x => (int)x.ctId)
.FirstOrDefault();
@@ -866,7 +952,14 @@ AND umbracoNode.id <> @id",
//get the unique list of associated templates
var defaultTemplates = result
- .Where(x => x.ctId == currentCtId)
+ .Where(x =>
+ {
+ //TODO: This is a bit hacky right now but don't have time to do a nice refactor to support both GUID and Int queries, so this is
+ // how it is for now.
+ return (typeof(TId) == typeof(int))
+ ? x.ctId == currentCtId
+ : x.nUniqueId == currentCtId;
+ })
//use a tuple so that distinct checks both values (in some rare cases the dtIsDefault will not compute as bool?, so we force it with Convert.ToBoolean)
.Select(x => new Tuple(Convert.ToBoolean(x.dtIsDefault), x.dtTemplateId))
.Where(x => x.Item1.HasValue && x.Item2.HasValue)
@@ -877,7 +970,7 @@ AND umbracoNode.id <> @id",
var defaultTemplate = defaultTemplates.FirstOrDefault(x => x.Item1.Value)
?? defaultTemplates.FirstOrDefault();
- var dtDto = new DocumentTypeDto
+ var dtDto = new ContentTypeTemplateDto
{
//create the content type dto
ContentTypeDto = new ContentTypeDto
@@ -914,7 +1007,14 @@ AND umbracoNode.id <> @id",
// We will map a subset of the associated template - alias, id, name
associatedTemplates.Add(currentCtId, result
- .Where(x => x.ctId == currentCtId)
+ .Where(x =>
+ {
+ //TODO: This is a bit hacky right now but don't have time to do a nice refactor to support both GUID and Int queries, so this is
+ // how it is for now.
+ return (typeof(TId) == typeof(int))
+ ? x.ctId == currentCtId
+ : x.nUniqueId == currentCtId;
+ })
.Where(x => x.tId != null)
.Select(x => new AssociatedTemplate(x.tId, x.tAlias, x.tText))
.Distinct()
@@ -922,25 +1022,40 @@ AND umbracoNode.id <> @id",
//now create the content type object
- var factory = new ContentTypeFactory(new Guid(Constants.ObjectTypes.DocumentType));
- var contentType = factory.BuildEntity(dtDto);
+ var factory = new ContentTypeFactory();
+ var contentType = factory.BuildContentTypeEntity(dtDto.ContentTypeDto);
+
+ // NOTE
+ // that was done by the factory but makes little sense, moved here, so
+ // now we have to reset dirty props again (as the factory does it) and yet,
+ // we are not managing allowed templates... the whole thing is weird.
+ ((ContentType) contentType).DefaultTemplateId = dtDto.TemplateNodeId;
+ contentType.ResetDirtyProperties(false);
//map the allowed content types
//map the child content type ids
MapCommonContentTypeObjects(contentType, currentCtId, result, parentContentTypeIds);
-
+
mappedContentTypes.Add(contentType);
}
return mappedContentTypes;
}
- private static void MapCommonContentTypeObjects(T contentType, int currentCtId, List result, IDictionary> parentContentTypeIds)
- where T: IContentTypeBase
+ private static void MapCommonContentTypeObjects(T contentType, TId currentCtId, List result, IDictionary> parentContentTypeIds)
+ where T : IContentTypeBase
+ where TId : struct
{
//map the allowed content types
contentType.AllowedContentTypes = result
- .Where(x => x.ctId == currentCtId)
+ .Where(x =>
+ {
+ //TODO: This is a bit hacky right now but don't have time to do a nice refactor to support both GUID and Int queries, so this is
+ // how it is for now.
+ return (typeof(TId) == typeof(int))
+ ? x.ctId == currentCtId
+ : x.nUniqueId == currentCtId;
+ })
//use tuple so we can use distinct on all vals
.Select(x => new Tuple(x.ctaAllowedId, x.ctaSortOrder, x.ctaAlias))
.Where(x => x.Item1.HasValue && x.Item2.HasValue && x.Item3 != null)
@@ -950,8 +1065,22 @@ AND umbracoNode.id <> @id",
//map the child content type ids
parentContentTypeIds.Add(currentCtId, result
- .Where(x => x.ctId == currentCtId)
- .Select(x => (int?)x.chtParentId)
+ .Where(x =>
+ {
+ //TODO: This is a bit hacky right now but don't have time to do a nice refactor to support both GUID and Int queries, so this is
+ // how it is for now.
+ return (typeof(TId) == typeof(int))
+ ? x.ctId == currentCtId
+ : x.nUniqueId == currentCtId;
+ })
+ .Select(x =>
+ {
+ //TODO: This is a bit hacky right now but don't have time to do a nice refactor to support both GUID and Int queries, so this is
+ // how it is for now.
+ return (typeof(TId) == typeof(int))
+ ? (TId?)x.chtParentId
+ : (TId?)x.chtParentKey;
+ })
.Where(x => x.HasValue)
.Distinct()
.Select(x => x.Value).ToList());
@@ -960,21 +1089,26 @@ AND umbracoNode.id <> @id",
internal static void MapGroupsAndProperties(int[] contentTypeIds, Database db, ISqlSyntaxProvider sqlSyntax,
out IDictionary allPropertyTypeCollection,
out IDictionary allPropertyGroupCollection)
- {
+ {
+ allPropertyGroupCollection = new Dictionary();
+ allPropertyTypeCollection = new Dictionary();
+
+ // query below is not safe + pointless if array is empty
+ if (contentTypeIds.Length == 0) return;
// first part Gets all property groups including property type data even when no property type exists on the group
// second part Gets all property types including ones that are not on a group
// therefore the union of the two contains all of the property type and property group information we need
// NOTE: MySQL requires a SELECT * FROM the inner union in order to be able to sort . lame.
- var sqlBuilder = new StringBuilder(@"SELECT PG.contenttypeNodeId as contentTypeId,
- PT.ptId, PT.ptAlias, PT.ptDesc,PT.ptMandatory,PT.ptName,PT.ptSortOrder,PT.ptRegExp,
+ var sqlBuilder = new StringBuilder(@"SELECT PG.contenttypeNodeId as contentTypeId,
+ PT.ptUniqueId as ptUniqueID, PT.ptId, PT.ptAlias, PT.ptDesc,PT.ptMandatory,PT.ptName,PT.ptSortOrder,PT.ptRegExp,
PT.dtId,PT.dtDbType,PT.dtPropEdAlias,
- PG.id as pgId, PG.parentGroupId as pgParentGroupId, PG.sortorder as pgSortOrder, PG." + sqlSyntax.GetQuotedColumnName("text") + @" as pgText
+ PG.id as pgId, PG.uniqueID as pgKey, PG.sortorder as pgSortOrder, PG." + sqlSyntax.GetQuotedColumnName("text") + @" as pgText
FROM cmsPropertyTypeGroup as PG
LEFT JOIN
(
- SELECT PT.id as ptId, PT.Alias as ptAlias, PT." + sqlSyntax.GetQuotedColumnName("Description") + @" as ptDesc,
+ SELECT PT.uniqueID as ptUniqueId, PT.id as ptId, PT.Alias as ptAlias, PT." + sqlSyntax.GetQuotedColumnName("Description") + @" as ptDesc,
PT.mandatory as ptMandatory, PT.Name as ptName, PT.sortOrder as ptSortOrder, PT.validationRegExp as ptRegExp,
PT.propertyTypeGroupId as ptGroupId,
DT.dbType as dtDbType, DT.nodeId as dtId, DT.propertyEditorAlias as dtPropEdAlias
@@ -988,18 +1122,16 @@ AND umbracoNode.id <> @id",
UNION
SELECT PT.contentTypeId as contentTypeId,
- PT.id as ptId, PT.Alias as ptAlias, PT." + sqlSyntax.GetQuotedColumnName("Description") + @" as ptDesc,
+ PT.uniqueID as ptUniqueID, PT.id as ptId, PT.Alias as ptAlias, PT." + sqlSyntax.GetQuotedColumnName("Description") + @" as ptDesc,
PT.mandatory as ptMandatory, PT.Name as ptName, PT.sortOrder as ptSortOrder, PT.validationRegExp as ptRegExp,
DT.nodeId as dtId, DT.dbType as dtDbType, DT.propertyEditorAlias as dtPropEdAlias,
- PG.id as pgId, PG.parentGroupId as pgParentGroupId, PG.sortorder as pgSortOrder, PG." + sqlSyntax.GetQuotedColumnName("text") + @" as pgText
+ PG.id as pgId, PG.uniqueID as pgKey, PG.sortorder as pgSortOrder, PG." + sqlSyntax.GetQuotedColumnName("text") + @" as pgText
FROM cmsPropertyType as PT
INNER JOIN cmsDataType as DT
ON PT.dataTypeId = DT.nodeId
LEFT JOIN cmsPropertyTypeGroup as PG
- ON PG.id = PT.propertyTypeGroupId");
-
- if(contentTypeIds.Any())
- sqlBuilder.AppendLine(" WHERE (PT.contentTypeId in (@contentTypeIds))");
+ ON PG.id = PT.propertyTypeGroupId
+ WHERE (PT.contentTypeId in (@contentTypeIds))");
sqlBuilder.AppendLine(" ORDER BY (pgId)");
@@ -1010,9 +1142,6 @@ AND umbracoNode.id <> @id",
var result = db.Fetch(sqlBuilder.ToString(), new { contentTypeIds = contentTypeIds });
- allPropertyGroupCollection = new Dictionary();
- allPropertyTypeCollection = new Dictionary();
-
foreach (var contentTypeId in contentTypeIds)
{
//from this we need to make :
@@ -1023,13 +1152,13 @@ AND umbracoNode.id <> @id",
int currId = contentTypeId;
- var propertyGroupCollection = new PropertyGroupCollection(result
+ var propertyGroupCollection = new PropertyGroupCollection(result
//get all rows that have a group id
.Where(x => x.pgId != null)
//filter based on the current content type
.Where(x => x.contentTypeId == currId)
//turn that into a custom object containing only the group info
- .Select(x => new { GroupId = x.pgId, ParentGroupId = x.pgParentGroupId, SortOrder = x.pgSortOrder, Text = x.pgText })
+ .Select(x => new { GroupId = x.pgId, SortOrder = x.pgSortOrder, Text = x.pgText, Key = x.pgKey })
//get distinct data by id
.DistinctBy(x => (int)x.GroupId)
//for each of these groups, create a group object with it's associated properties
@@ -1042,6 +1171,7 @@ AND umbracoNode.id <> @id",
Description = row.ptDesc,
DataTypeDefinitionId = row.dtId,
Id = row.ptId,
+ Key = row.ptUniqueID,
Mandatory = Convert.ToBoolean(row.ptMandatory),
Name = row.ptName,
PropertyGroupId = new Lazy(() => group.GroupId, false),
@@ -1052,15 +1182,15 @@ AND umbracoNode.id <> @id",
//fill in the rest of the group properties
Id = group.GroupId,
Name = group.Text,
- ParentId = group.ParentGroupId,
- SortOrder = group.SortOrder
+ SortOrder = group.SortOrder,
+ Key = group.Key
}).ToArray());
allPropertyGroupCollection[currId] = propertyGroupCollection;
//Create the property type collection now (that don't have groups)
- var propertyTypeCollection = new PropertyTypeCollection(result
+ var propertyTypeCollection = new PropertyTypeCollection(result
.Where(x => x.pgId == null)
//filter based on the current content type
.Where(x => x.contentTypeId == currId)
@@ -1070,6 +1200,7 @@ AND umbracoNode.id <> @id",
Description = row.ptDesc,
DataTypeDefinitionId = row.dtId,
Id = row.ptId,
+ Key = row.ptUniqueID,
Mandatory = Convert.ToBoolean(row.ptMandatory),
Name = row.ptName,
PropertyGroupId = null,
@@ -1080,9 +1211,105 @@ AND umbracoNode.id <> @id",
allPropertyTypeCollection[currId] = propertyTypeCollection;
}
-
+
}
}
+
+ ///