diff --git a/.gitignore b/.gitignore
index a6c4639191..5b5e7660c5 100644
--- a/.gitignore
+++ b/.gitignore
@@ -140,3 +140,4 @@ apidocs/api/*
build/docs.zip
build/ui-docs.zip
build/csharp-docs.zip
+build/msbuild.log
diff --git a/README.md b/README.md
index 53070b6917..3184d2024d 100644
--- a/README.md
+++ b/README.md
@@ -3,9 +3,11 @@ Umbraco CMS
Umbraco is a free open source Content Management System built on the ASP.NET platform.
## Building Umbraco from source ##
-The easiest way to get started is to run `build/build.bat` which will build both the backoffice (also known as "Belle") and the Umbraco core. You can then easily start debugging from Visual Studio, or if you need to debug Belle you can run `grunt dev` in `src\Umbraco.Web.UI.Client`.
+The easiest way to get started is to run `build/build.bat` which will build both the backoffice (also known as "Belle") and the Umbraco core. You can then easily start debugging from Visual Studio, or if you need to debug Belle you can run `grunt vs` in `src\Umbraco.Web.UI.Client`.
-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.
+If you're interested in making changes to Belle without running Visual Studio 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/?container=umbraco-750) so you don't have to build the code yourself.
## Watch a introduction video ##
diff --git a/appveyor.yml b/appveyor.yml
index db69da4978..3044444730 100644
--- a/appveyor.yml
+++ b/appveyor.yml
@@ -8,39 +8,45 @@ build_script:
FOR /F "skip=1 delims=" %%i IN (UmbracoVersion.txt) DO IF NOT DEFINED release SET "release=%%i"
- SET PATH=C:\Program Files (x86)\MSBuild\14.0\Bin;%PATH%
-
- ECHO %PATH%
-
-
- ECHO Building Release %release% build%APPVEYOR_BUILD_NUMBER%
-
-
- SET nuGetFolder=%CD%\..\src\packages\
+ SET nuGetFolder=C:\Users\appveyor\.nuget\packages
..\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% -Verbosity quiet
-
IF EXIST ..\src\umbraco.businesslogic\packages.config ..\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 Building Release %release% build%APPVEYOR_BUILD_NUMBER%
+
+ SET PATH=C:\Program Files (x86)\MSBuild\14.0\Bin;%PATH%
+
SET MSBUILD="C:\Program Files (x86)\MSBuild\14.0\Bin\MsBuild.exe"
- %MSBUILD% "../src/Umbraco.Tests/Umbraco.Tests.csproj" /verbosity:minimal
-
-
- build.bat %release% build%APPVEYOR_BUILD_NUMBER%
-
-
+ XCOPY "..\src\Umbraco.Tests\unit-test-log4net.CI.config" "..\src\Umbraco.Tests\unit-test-log4net.config" /Y
+
+ %MSBUILD% "..\src\Umbraco.Tests\Umbraco.Tests.csproj" /consoleloggerparameters:Summary;ErrorsOnly
+
+ build.bat nopause %release% build%APPVEYOR_BUILD_NUMBER%
+
ECHO %PATH%
test:
assemblies: src\Umbraco.Tests\bin\Debug\Umbraco.Tests.dll
artifacts:
- path: build\UmbracoCms.*
+ name: UmbracoFiles
+- path: build\msbuild.log
+ name: BuildLog
+deploy:
+- provider: AzureBlob
+ storage_account_name: umbraconightlies
+ storage_access_key:
+ secure: bmEMml2SF7QLHULiePa/a01XOeIa2SxJeXuaZ+1R27b+Vb2nNUQVYiPlUyF2cZAFSHI/zO/LekRsVU1rTescGhJjF7SSjKybymI3p+F/OWpwqiu2WfFee1ofXBFx8QHw
+ container: umbraco-750
+ artifact: UmbracoFiles
+ on:
+ branch: dev-v7
notifications:
- provider: Slack
auth_token:
diff --git a/build/Build.bat b/build/Build.bat
index 1167d9fcb6..fc0d8a69ea 100644
--- a/build/Build.bat
+++ b/build/Build.bat
@@ -11,20 +11,28 @@ FOR /F "skip=1 delims=" %%i IN (UmbracoVersion.txt) DO IF NOT DEFINED release SE
FOR /F "skip=2 delims=" %%i IN (UmbracoVersion.txt) DO IF NOT DEFINED comment SET "comment=%%i"
REM If there's arguments on the command line overrule UmbracoVersion.txt and use that as the version
-IF [%1] NEQ [] (SET release=%1)
-IF [%2] NEQ [] (SET comment=%2) ELSE (IF [%1] NEQ [] (SET "comment="))
+IF [%2] NEQ [] (SET release=%2)
+IF [%3] NEQ [] (SET comment=%3) ELSE (IF [%2] NEQ [] (SET "comment="))
+
+REM Get the "is continuous integration" from the parameters
+SET "isci=0"
+IF [%1] NEQ [] (SET isci=1)
SET version=%release%
-
IF [%comment%] EQU [] (SET version=%release%) ELSE (SET version=%release%-%comment%)
+
+ECHO.
ECHO Building Umbraco %version%
+ECHO.
ReplaceIISExpressPortNumber.exe ..\src\Umbraco.Web.UI\Umbraco.Web.UI.csproj %release%
+ECHO.
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
RD ..\src\Umbraco.Web.UI.Client\bower_components /Q /S
+ECHO.
ECHO Removing existing built files to make sure everything is clean as a whistle
RMDIR /Q /S _BuildOutput
DEL /F /Q UmbracoCms.*.zip
@@ -32,27 +40,56 @@ DEL /F /Q UmbracoExamine.*.zip
DEL /F /Q UmbracoCms.*.nupkg
DEL /F /Q webpihash.txt
+ECHO.
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% /verbosity:minimal
+REM Adding the default Git path so that if it's installed it can actually be found
+REM This is necessary because SETLOCAL is on in InstallGit.cmd so that one might find Git,
+REM but the path setting is lost due to SETLOCAL
+path=C:\Program Files (x86)\Git\cmd;C:\Program Files\Git\cmd;%PATH%
+
+ECHO.
+ECHO Making sure we have a web.config
+IF NOT EXIST %CD%\..\src\Umbraco.Web.UI\web.config COPY %CD%\..\src\Umbraco.Web.UI\web.Template.config %CD%\..\src\Umbraco.Web.UI\web.config
+
+ECHO.
+ECHO.
+ECHO Performing MSBuild and producing Umbraco binaries zip files
+ECHO This takes a few minutes and logging is set to report warnings
+ECHO and errors only so it might seems like nothing is happening for a while.
+ECHO You can check the msbuild.log file for progress.
+ECHO.
+%windir%\Microsoft.NET\Framework\v4.0.30319\msbuild.exe "Build.proj" /p:BUILD_RELEASE=%release% /p:BUILD_COMMENT=%comment% /p:NugetPackagesDirectory=%nuGetFolder% /consoleloggerparameters:Summary;ErrorsOnly;WarningsOnly /fileLogger
+IF ERRORLEVEL 1 GOTO :error
+
+ECHO.
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
+ECHO.
ECHO Adding Web.config transform files to the NuGet package
REN .\_BuildOutput\WebApp\Views\Web.config Web.config.transform
REN .\_BuildOutput\WebApp\Xslt\Web.config Web.config.transform
+ECHO.
ECHO Packing the NuGet release files
..\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
+IF ERRORLEVEL 1 GOTO :error
-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.
+:success
+ECHO.
+ECHO No errors were detected!
+ECHO There may still be some in the output, which you would need to investigate.
+ECHO Warnings are usually normal.
GOTO :EOF
-:showerror
-PAUSE
+:error
+
+ECHO.
+ECHO Errors were detected!
+
+REM don't pause if continuous integration else the build server waits forever
+REM before cancelling the build (and, there is noone to read the output anyways)
+IF isci NEQ 1 PAUSE
diff --git a/build/InstallGit.cmd b/build/InstallGit.cmd
index b6ba71df9b..e009e2594e 100644
--- a/build/InstallGit.cmd
+++ b/build/InstallGit.cmd
@@ -1,23 +1,33 @@
@ECHO OFF
SETLOCAL
-REM SETLOCAL is on, so changes to the path not persist to the actual user's path
+ :: SETLOCAL is on, so changes to the path not persist to the actual user's path
-git.exe 2> NUL
-if %ERRORLEVEL%==9009 GOTO :trydefaultpath
+git.exe --version
+IF %ERRORLEVEL%==9009 GOTO :trydefaultpath
GOTO :EOF
+ :: Git is installed, no need to to anything else
:trydefaultpath
-path=C:\Program Files (x86)\Git\cmd;C:\Program Files\Git\cmd;%PATH%
-git.exe 2> NUL
-if %ERRORLEVEL%==9009 GOTO :showerror
+PATH=C:\Program Files (x86)\Git\cmd;C:\Program Files\Git\cmd;%PATH%
+git.exe --version
+IF %ERRORLEVEL%==9009 GOTO :showerror
GOTO :EOF
+ :: Git is installed, no need to to anything else
:showerror
ECHO Git is not in your path and could not be found in C:\Program Files (x86)\Git\cmd nor in C:\Program Files\Git\cmd
-set /p install=" Do you want to install Git through Chocolatey [y/n]? " %=%
-if %install%==y (
+SET /p install=" Do you want to install Git through Chocolatey [y/n]? " %=%
+IF %install%==y (
+ :: Create a temporary batch file to execute either after elevating to admin or as-is when the user is already admin
+ ECHO @ECHO OFF > "%temp%\ChocoInstallGit.cmd"
+ ECHO SETLOCAL >> "%temp%\ChocoInstallGit.cmd"
+ ECHO ECHO Installing Chocolatey first >> "%temp%\ChocoInstallGit.cmd"
+ ECHO @powershell -NoProfile -ExecutionPolicy Bypass -Command "iex ((new-object net.webclient).DownloadString('https://chocolatey.org/install.ps1'))" >> "%temp%\ChocoInstallGit.cmd"
+ ECHO SET PATH=%%PATH%%;%%ALLUSERSPROFILE%%\chocolatey\bin >> "%temp%\ChocoInstallGit.cmd"
+ ECHO choco install git -y >> "%temp%\ChocoInstallGit.cmd"
+
GOTO :installgit
-) else (
+) ELSE (
GOTO :cantcontinue
)
@@ -26,7 +36,28 @@ ECHO Can't complete the build without Git being in the path. Please add it to be
GOTO :EOF
:installgit
-ECHO Installing Chocolatey first
-@powershell -NoProfile -ExecutionPolicy unrestricted -Command "iex ((new-object net.webclient).DownloadString('https://chocolatey.org/install.ps1'))" && SET PATH=%PATH%;%ALLUSERSPROFILE%\chocolatey\bin
-ECHO Installing Git through Chocolatey
-choco install git
\ No newline at end of file
+pushd %~dp0
+ :: Running prompt elevated
+
+:: --> Check for permissions
+>nul 2>&1 "%SYSTEMROOT%\system32\cacls.exe" "%SYSTEMROOT%\system32\config\system"
+
+:: --> If error flag set, we do not have admin.
+IF '%errorlevel%' NEQ '0' (
+ GOTO UACPrompt
+) ELSE ( GOTO gotAdmin )
+
+:UACPrompt
+ ECHO You're not currently running this with admin privileges, we'll now try to execute the install of Git through Chocolatey after elevating to admin privileges
+ ECHO Set UAC = CreateObject^("Shell.Application"^) > "%temp%\getadmin.vbs"
+ ECHO UAC.ShellExecute "%temp%\ChocoInstallGit.cmd", "", "", "runas", 1 >> "%temp%\getadmin.vbs"
+
+ "%temp%\getadmin.vbs"
+ EXIT /B
+
+:gotAdmin
+ IF EXIST "%temp%\getadmin.vbs" ( DEL "%temp%\getadmin.vbs" )
+ pushd "%CD%"
+ CD /D "%~dp0"
+
+ CALL "%temp%\ChocoInstallGit.cmd"
\ No newline at end of file
diff --git a/build/NuSpecs/UmbracoCms.Core.nuspec b/build/NuSpecs/UmbracoCms.Core.nuspec
index a486f8a58b..dbc7be00e4 100644
--- a/build/NuSpecs/UmbracoCms.Core.nuspec
+++ b/build/NuSpecs/UmbracoCms.Core.nuspec
@@ -27,15 +27,17 @@
-
+
+
-
-
-
-
+
+
+
+
+
diff --git a/build/NuSpecs/UmbracoCms.nuspec b/build/NuSpecs/UmbracoCms.nuspec
index 66e82ac488..54d12f6844 100644
--- a/build/NuSpecs/UmbracoCms.nuspec
+++ b/build/NuSpecs/UmbracoCms.nuspec
@@ -16,8 +16,8 @@
umbraco
-
-
+
+
@@ -32,6 +32,7 @@
+
@@ -44,4 +45,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 a77632926c..8368870186 100644
--- a/build/NuSpecs/tools/Dashboard.config.install.xdt
+++ b/build/NuSpecs/tools/Dashboard.config.install.xdt
@@ -21,6 +21,12 @@
+
+
views/dashboard/developer/developerdashboardvideos.html
@@ -30,16 +36,21 @@
-
+
views/dashboard/developer/examinemanagement.html
-
-
+
+
- views/dashboard/developer/xmldataintegrityreport.html
+ views/dashboard/developer/healthcheck.html
-
+
+
+
+ views/dashboard/developer/redirecturls.html
+
+
-
-
-
-
- admin
-
-
- views/dashboard/default/startupdashboardintro.html
-
-
-
-
-
@@ -80,5 +78,6 @@
\ 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 dfb9925840..987db6c3d6 100644
--- a/build/NuSpecs/tools/Web.config.install.xdt
+++ b/build/NuSpecs/tools/Web.config.install.xdt
@@ -292,6 +292,8 @@
+
+
diff --git a/build/NuSpecs/tools/install.ps1 b/build/NuSpecs/tools/install.ps1
index 5ebaa6d02a..0e62fb0749 100644
--- a/build/NuSpecs/tools/install.ps1
+++ b/build/NuSpecs/tools/install.ps1
@@ -97,7 +97,12 @@ if ($project) {
$umbracoUIXMLSource = Join-Path $installPath "UmbracoFiles\Umbraco\Config\Create\UI.xml"
$umbracoUIXMLDestination = Join-Path $projectPath "Umbraco\Config\Create\UI.xml"
Copy-Item $umbracoUIXMLSource $umbracoUIXMLDestination -Force
- }
+ } else {
+ $upgradeViewSource = Join-Path $umbracoFolderSource "Views\install\*"
+ $upgradeView = Join-Path $umbracoFolder "Views\install\"
+ Write-Host "Copying2 ${upgradeViewSource} to ${upgradeView}"
+ Copy-Item $upgradeViewSource $upgradeView -Force
+ }
$installFolder = Join-Path $projectPath "Install"
if(Test-Path $installFolder) {
diff --git a/build/NuSpecs/tools/trees.config.install.xdt b/build/NuSpecs/tools/trees.config.install.xdt
index 580c619547..a9fbf07762 100644
--- a/build/NuSpecs/tools/trees.config.install.xdt
+++ b/build/NuSpecs/tools/trees.config.install.xdt
@@ -55,21 +55,29 @@
xdt:Transform="SetAttributes()" />
-
-
+ xdt:Transform="Remove" />
+
+
+
+
+
+
+
+
-
-
diff --git a/build/UmbracoVersion.txt b/build/UmbracoVersion.txt
index 74b38086d9..df444cdd7b 100644
--- a/build/UmbracoVersion.txt
+++ b/build/UmbracoVersion.txt
@@ -1,3 +1,2 @@
# Usage: on line 2 put the release version, on line 3 put the version comment (example: beta)
-7.5.0
-beta
\ No newline at end of file
+7.5.3
\ No newline at end of file
diff --git a/src/SQLCE4Umbraco/SqlCEHelper.cs b/src/SQLCE4Umbraco/SqlCEHelper.cs
index 26a781360e..ab6f686c21 100644
--- a/src/SQLCE4Umbraco/SqlCEHelper.cs
+++ b/src/SQLCE4Umbraco/SqlCEHelper.cs
@@ -40,15 +40,10 @@ namespace SqlCE4Umbraco
var localConnection = new SqlCeConnection(ConnectionString);
if (!System.IO.File.Exists(ReplaceDataDirectory(localConnection.Database)))
{
- var sqlCeEngine = new SqlCeEngine(ConnectionString);
- sqlCeEngine.CreateDatabase();
-
- // SD: Pretty sure this should be in a using clause but i don't want to cause unknown side-effects here
- // since it's been like this for quite some time
- //using (var sqlCeEngine = new SqlCeEngine(ConnectionString))
- //{
- // sqlCeEngine.CreateDatabase();
- //}
+ using (var sqlCeEngine = new SqlCeEngine(ConnectionString))
+ {
+ sqlCeEngine.CreateDatabase();
+ }
}
}
diff --git a/src/SolutionInfo.cs b/src/SolutionInfo.cs
index 16a91ab12a..a0a32912c9 100644
--- a/src/SolutionInfo.cs
+++ b/src/SolutionInfo.cs
@@ -11,5 +11,5 @@ using System.Resources;
[assembly: AssemblyVersion("1.0.*")]
-[assembly: AssemblyFileVersion("7.5.0")]
-[assembly: AssemblyInformationalVersion("7.5.0-beta")]
\ No newline at end of file
+[assembly: AssemblyFileVersion("7.5.3")]
+[assembly: AssemblyInformationalVersion("7.5.3")]
\ No newline at end of file
diff --git a/src/Umbraco.Core/Cache/HttpRequestCacheProvider.cs b/src/Umbraco.Core/Cache/HttpRequestCacheProvider.cs
index ca1f4e85a0..792b5982b1 100644
--- a/src/Umbraco.Core/Cache/HttpRequestCacheProvider.cs
+++ b/src/Umbraco.Core/Cache/HttpRequestCacheProvider.cs
@@ -9,6 +9,9 @@ namespace Umbraco.Core.Cache
///
/// A cache provider that caches items in the HttpContext.Items
///
+ ///
+ /// If the Items collection is null, then this provider has no effect
+ ///
internal class HttpRequestCacheProvider : DictionaryCacheProviderBase
{
// context provider
@@ -34,6 +37,11 @@ namespace Umbraco.Core.Cache
get { return _context != null ? _context.Items : HttpContext.Current.Items; }
}
+ private bool HasContextItems
+ {
+ get { return (_context != null && _context.Items != null) || HttpContext.Current != null; }
+ }
+
// for unit tests
public HttpRequestCacheProvider(HttpContextBase context)
{
@@ -50,18 +58,23 @@ namespace Umbraco.Core.Cache
protected override IEnumerable GetDictionaryEntries()
{
const string prefix = CacheItemPrefix + "-";
+
+ if (HasContextItems == false) return Enumerable.Empty();
+
return ContextItems.Cast()
.Where(x => x.Key is string && ((string)x.Key).StartsWith(prefix));
}
protected override void RemoveEntry(string key)
{
+ if (HasContextItems == false) return;
+
ContextItems.Remove(key);
}
protected override object GetEntry(string key)
{
- return ContextItems[key];
+ return HasContextItems ? ContextItems[key] : null;
}
#region Lock
@@ -81,7 +94,9 @@ namespace Umbraco.Core.Cache
get
{
- return new MonitorLock(ContextItems.SyncRoot);
+ return HasContextItems
+ ? (IDisposable) new MonitorLock(ContextItems.SyncRoot)
+ : new NoopLocker();
}
}
@@ -91,6 +106,9 @@ namespace Umbraco.Core.Cache
public override object GetCacheItem(string cacheKey, Func getCacheItem)
{
+ //no place to cache so just return the callback result
+ if (HasContextItems == false) return getCacheItem();
+
cacheKey = GetCacheKey(cacheKey);
Lazy result;
@@ -128,5 +146,10 @@ namespace Umbraco.Core.Cache
#region Insert
#endregion
+ private class NoopLocker : DisposableObject
+ {
+ protected override void DisposeResources()
+ { }
+ }
}
}
\ No newline at end of file
diff --git a/src/Umbraco.Core/Cache/NullCacheProvider.cs b/src/Umbraco.Core/Cache/NullCacheProvider.cs
index f4e449499b..6b797307ac 100644
--- a/src/Umbraco.Core/Cache/NullCacheProvider.cs
+++ b/src/Umbraco.Core/Cache/NullCacheProvider.cs
@@ -5,38 +5,31 @@ using System.Web.Caching;
namespace Umbraco.Core.Cache
{
- internal class NullCacheProvider : IRuntimeCacheProvider
+ ///
+ /// Represents a cache provider that does not cache anything.
+ ///
+ public class NullCacheProvider : IRuntimeCacheProvider
{
public virtual void ClearAllCache()
- {
- }
+ { }
public virtual void ClearCacheItem(string key)
- {
- }
+ { }
public virtual void ClearCacheObjectTypes(string typeName)
- {
- }
+ { }
public virtual void ClearCacheObjectTypes()
- {
- }
+ { }
public virtual void ClearCacheObjectTypes(Func predicate)
- {
- }
-
-
-
+ { }
public virtual void ClearCacheByKeySearch(string keyStartsWith)
- {
- }
+ { }
public virtual void ClearCacheByKeyExpression(string regexString)
- {
- }
+ { }
public virtual IEnumerable GetCacheItemsByKeySearch(string keyStartsWith)
{
@@ -64,8 +57,6 @@ namespace Umbraco.Core.Cache
}
public void InsertCacheItem(string cacheKey, Func getCacheItem, TimeSpan? timeout = null, bool isSliding = false, CacheItemPriority priority = CacheItemPriority.Normal, CacheItemRemovedCallback removedCallback = null, string[] dependentFiles = null)
- {
-
- }
+ { }
}
}
\ No newline at end of file
diff --git a/src/Umbraco.Core/Cache/ObjectCacheRuntimeCacheProvider.cs b/src/Umbraco.Core/Cache/ObjectCacheRuntimeCacheProvider.cs
index 8afa5639f1..feccba03b4 100644
--- a/src/Umbraco.Core/Cache/ObjectCacheRuntimeCacheProvider.cs
+++ b/src/Umbraco.Core/Cache/ObjectCacheRuntimeCacheProvider.cs
@@ -1,23 +1,23 @@
using System;
using System.Collections.Generic;
-using System.ComponentModel;
using System.Linq;
-using System.Reflection;
using System.Runtime.Caching;
using System.Text.RegularExpressions;
using System.Threading;
using System.Web.Caching;
-using Umbraco.Core.Logging;
using CacheItemPriority = System.Web.Caching.CacheItemPriority;
namespace Umbraco.Core.Cache
{
///
+ /// Represents a cache provider that caches item in a .
/// A cache provider that wraps the logic of a System.Runtime.Caching.ObjectCache
///
- internal class ObjectCacheRuntimeCacheProvider : IRuntimeCacheProvider
+ /// The is created with name "in-memory". That name is
+ /// used to retrieve configuration options. It does not identify the memory cache, i.e.
+ /// each instance of this class has its own, independent, memory cache.
+ public class ObjectCacheRuntimeCacheProvider : IRuntimeCacheProvider
{
-
private readonly ReaderWriterLockSlim _locker = new ReaderWriterLockSlim(LockRecursionPolicy.SupportsRecursion);
internal ObjectCache MemoryCache;
diff --git a/src/Umbraco.Core/Cache/StaticCacheProvider.cs b/src/Umbraco.Core/Cache/StaticCacheProvider.cs
index 9c448efa6a..c7fd00d39a 100644
--- a/src/Umbraco.Core/Cache/StaticCacheProvider.cs
+++ b/src/Umbraco.Core/Cache/StaticCacheProvider.cs
@@ -3,14 +3,13 @@ using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Text.RegularExpressions;
-using System.Web.Caching;
namespace Umbraco.Core.Cache
{
///
- /// A cache provider that statically caches everything in an in memory dictionary
+ /// Represents a cache provider that statically caches item in a concurrent dictionary.
///
- internal class StaticCacheProvider : ICacheProvider
+ public class StaticCacheProvider : ICacheProvider
{
internal readonly ConcurrentDictionary StaticCache = new ConcurrentDictionary();
@@ -75,7 +74,6 @@ namespace Umbraco.Core.Cache
public virtual object GetCacheItem(string cacheKey, Func getCacheItem)
{
return StaticCache.GetOrAdd(cacheKey, key => getCacheItem());
- }
-
+ }
}
}
\ No newline at end of file
diff --git a/src/Umbraco.Core/Configuration/UmbracoSettings/ContentElement.cs b/src/Umbraco.Core/Configuration/UmbracoSettings/ContentElement.cs
index 1b1404a897..d743daca6a 100644
--- a/src/Umbraco.Core/Configuration/UmbracoSettings/ContentElement.cs
+++ b/src/Umbraco.Core/Configuration/UmbracoSettings/ContentElement.cs
@@ -291,7 +291,19 @@ namespace Umbraco.Core.Configuration.UmbracoSettings
true);
}
}
-
+
+ [ConfigurationProperty("EnableInheritedMediaTypes")]
+ internal InnerTextConfigurationElement EnableInheritedMediaTypes
+ {
+ get
+ {
+ return new OptionalInnerTextConfigurationElement(
+ (InnerTextConfigurationElement)this["EnableInheritedMediaTypes"],
+ //set the default
+ true);
+ }
+ }
+
string IContentSection.NotificationEmailAddress
{
get { return Notifications.NotificationEmailAddress; }
@@ -431,5 +443,10 @@ namespace Umbraco.Core.Configuration.UmbracoSettings
{
get { return EnableInheritedDocumentTypes; }
}
+
+ bool IContentSection.EnableInheritedMediaTypes
+ {
+ get { return EnableInheritedMediaTypes; }
+ }
}
}
\ No newline at end of file
diff --git a/src/Umbraco.Core/Configuration/UmbracoSettings/IContentSection.cs b/src/Umbraco.Core/Configuration/UmbracoSettings/IContentSection.cs
index ebdd9ae637..3d5e4435b6 100644
--- a/src/Umbraco.Core/Configuration/UmbracoSettings/IContentSection.cs
+++ b/src/Umbraco.Core/Configuration/UmbracoSettings/IContentSection.cs
@@ -61,5 +61,7 @@ namespace Umbraco.Core.Configuration.UmbracoSettings
string DefaultDocumentTypeProperty { get; }
bool EnableInheritedDocumentTypes { get; }
+
+ bool EnableInheritedMediaTypes { get; }
}
}
\ No newline at end of file
diff --git a/src/Umbraco.Core/Configuration/UmbracoSettings/IWebRoutingSection.cs b/src/Umbraco.Core/Configuration/UmbracoSettings/IWebRoutingSection.cs
index 2998fc2f78..9eb6d02aa7 100644
--- a/src/Umbraco.Core/Configuration/UmbracoSettings/IWebRoutingSection.cs
+++ b/src/Umbraco.Core/Configuration/UmbracoSettings/IWebRoutingSection.cs
@@ -9,6 +9,8 @@
bool DisableAlternativeTemplates { get; }
bool DisableFindContentByIdPath { get; }
+
+ bool DisableRedirectUrlTracking { get; }
string UrlProviderMode { get; }
diff --git a/src/Umbraco.Core/Configuration/UmbracoSettings/WebRoutingElement.cs b/src/Umbraco.Core/Configuration/UmbracoSettings/WebRoutingElement.cs
index 1ed9bc034c..82f5d46b28 100644
--- a/src/Umbraco.Core/Configuration/UmbracoSettings/WebRoutingElement.cs
+++ b/src/Umbraco.Core/Configuration/UmbracoSettings/WebRoutingElement.cs
@@ -27,6 +27,12 @@ namespace Umbraco.Core.Configuration.UmbracoSettings
get { return (bool) base["disableFindContentByIdPath"]; }
}
+ [ConfigurationProperty("disableRedirectUrlTracking", DefaultValue = "false")]
+ public bool DisableRedirectUrlTracking
+ {
+ get { return (bool) base["disableRedirectUrlTracking"]; }
+ }
+
[ConfigurationProperty("urlProviderMode", DefaultValue = "AutoLegacy")]
public string UrlProviderMode
{
diff --git a/src/Umbraco.Core/Configuration/UmbracoVersion.cs b/src/Umbraco.Core/Configuration/UmbracoVersion.cs
index 8c2fe08dfd..72e7187960 100644
--- a/src/Umbraco.Core/Configuration/UmbracoVersion.cs
+++ b/src/Umbraco.Core/Configuration/UmbracoVersion.cs
@@ -6,7 +6,7 @@ namespace Umbraco.Core.Configuration
{
public class UmbracoVersion
{
- private static readonly Version Version = new Version("7.5.0");
+ private static readonly Version Version = new Version("7.5.3");
///
/// Gets the current version of Umbraco.
@@ -24,7 +24,7 @@ namespace Umbraco.Core.Configuration
/// Gets the version comment (like beta or RC).
///
/// The version comment.
- public static string CurrentComment { get { return "beta"; } }
+ public static string CurrentComment { get { return ""; } }
// 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
diff --git a/src/Umbraco.Core/Constants-Examine.cs b/src/Umbraco.Core/Constants-Examine.cs
index 4ff6115749..9b1de812f7 100644
--- a/src/Umbraco.Core/Constants-Examine.cs
+++ b/src/Umbraco.Core/Constants-Examine.cs
@@ -1,15 +1,23 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
-
-namespace Umbraco.Core
+namespace Umbraco.Core
{
public static partial class Constants
{
public static class Examine
{
+ ///
+ /// The alias of the internal member indexer
+ ///
+ public const string InternalMemberIndexer = "InternalMemberIndexer";
+
+ ///
+ /// The alias of the internal content indexer
+ ///
+ public const string InternalIndexer = "InternalIndexer";
+
+ ///
+ /// The alias of the external content indexer
+ ///
+ public const string ExternalIndexer = "ExternalIndexer";
///
/// The alias of the internal member searcher
///
@@ -19,6 +27,11 @@ namespace Umbraco.Core
/// The alias of the internal content searcher
///
public const string InternalSearcher = "InternalSearcher";
+
+ ///
+ /// The alias of the external content searcher
+ ///
+ public const string ExternalSearcher = "ExternalSearcher";
}
}
}
diff --git a/src/Umbraco.Core/Constants-System.cs b/src/Umbraco.Core/Constants-System.cs
index 82e3a1ff3f..4a30db9cd8 100644
--- a/src/Umbraco.Core/Constants-System.cs
+++ b/src/Umbraco.Core/Constants-System.cs
@@ -1,33 +1,40 @@
namespace Umbraco.Core
{
- public static partial class Constants
- {
- ///
- /// Defines the identifiers for Umbraco system nodes.
- ///
- public static class System
- {
- ///
- /// The integer identifier for global system root node.
- ///
- public const int Root = -1;
+ public static partial class Constants
+ {
+ ///
+ /// Defines the identifiers for Umbraco system nodes.
+ ///
+ public static class System
+ {
+ ///
+ /// The integer identifier for global system root node.
+ ///
+ public const int Root = -1;
- ///
- /// The integer identifier for content's recycle bin.
- ///
- public const int RecycleBinContent = -20;
+ ///
+ /// The integer identifier for content's recycle bin.
+ ///
+ public const int RecycleBinContent = -20;
- ///
- /// The integer identifier for media's recycle bin.
- ///
- public const int RecycleBinMedia = -21;
+ ///
+ /// The integer identifier for media's recycle bin.
+ ///
+ public const int RecycleBinMedia = -21;
- public const int DefaultContentListViewDataTypeId = -95;
+ public const int DefaultContentListViewDataTypeId = -95;
public const int DefaultMediaListViewDataTypeId = -96;
public const int DefaultMembersListViewDataTypeId = -97;
// identifiers for lock objects
- public const int ServersLock = -331;
- }
- }
+ public const int ServersLock = -331;
+ }
+
+ public static class DatabaseProviders
+ {
+ public const string SqlCe = "System.Data.SqlServerCe.4.0";
+ public const string SqlServer = "System.Data.SqlClient";
+ public const string MySql = "MySql.Data.MySqlClient";
+ }
+ }
}
\ No newline at end of file
diff --git a/src/Umbraco.Core/DatabaseContext.cs b/src/Umbraco.Core/DatabaseContext.cs
index 7f8cbbf0f7..05aba6b97d 100644
--- a/src/Umbraco.Core/DatabaseContext.cs
+++ b/src/Umbraco.Core/DatabaseContext.cs
@@ -135,7 +135,7 @@ namespace Umbraco.Core
if (string.IsNullOrEmpty(_providerName) == false)
return _providerName;
- _providerName = "System.Data.SqlClient";
+ _providerName = Constants.DatabaseProviders.SqlServer;
if (ConfigurationManager.ConnectionStrings[GlobalSettings.UmbracoConnectionName] != null)
{
if (string.IsNullOrEmpty(ConfigurationManager.ConnectionStrings[GlobalSettings.UmbracoConnectionName].ProviderName) == false)
@@ -212,10 +212,10 @@ namespace Umbraco.Core
{
var provider = DbConnectionExtensions.DetectProviderFromConnectionString(connectionString);
var databaseProvider = provider.ToString();
- var providerName = "System.Data.SqlClient";
+ var providerName = Constants.DatabaseProviders.SqlServer;
if (databaseProvider.ToLower().Contains("mysql"))
{
- providerName = "MySql.Data.MySqlClient";
+ providerName = Constants.DatabaseProviders.MySql;
}
SaveConnectionString(connectionString, providerName);
Initialize(string.Empty);
@@ -240,10 +240,10 @@ namespace Umbraco.Core
public string GetDatabaseConnectionString(string server, string databaseName, string user, string password, string databaseProvider, out string providerName)
{
- providerName = "System.Data.SqlClient";
+ providerName = Constants.DatabaseProviders.SqlServer;
if (databaseProvider.ToLower().Contains("mysql"))
{
- providerName = "MySql.Data.MySqlClient";
+ providerName = Constants.DatabaseProviders.MySql;
return string.Format("Server={0}; Database={1};Uid={2};Pwd={3}", server, databaseName, user, password);
}
if (databaseProvider.ToLower().Contains("azure"))
@@ -260,7 +260,7 @@ namespace Umbraco.Core
/// Name of the database
public void ConfigureIntegratedSecurityDatabaseConnection(string server, string databaseName)
{
- const string providerName = "System.Data.SqlClient";
+ const string providerName = Constants.DatabaseProviders.SqlServer;
var connectionString = GetIntegratedSecurityDatabaseConnectionString(server, databaseName);
SaveConnectionString(connectionString, providerName);
Initialize(providerName);
@@ -373,7 +373,7 @@ namespace Umbraco.Core
var databaseSettings = ConfigurationManager.ConnectionStrings[GlobalSettings.UmbracoConnectionName];
if (databaseSettings != null && string.IsNullOrWhiteSpace(databaseSettings.ConnectionString) == false && string.IsNullOrWhiteSpace(databaseSettings.ProviderName) == false)
{
- var providerName = "System.Data.SqlClient";
+ var providerName = Constants.DatabaseProviders.SqlServer;
string connString = null;
if (!string.IsNullOrEmpty(ConfigurationManager.ConnectionStrings[GlobalSettings.UmbracoConnectionName].ProviderName))
{
@@ -381,8 +381,7 @@ namespace Umbraco.Core
connString = ConfigurationManager.ConnectionStrings[GlobalSettings.UmbracoConnectionName].ConnectionString;
}
Initialize(providerName, connString);
-
- DetermineSqlServerVersion();
+
}
else if (ConfigurationManager.AppSettings.ContainsKey(GlobalSettings.UmbracoConnectionName) && string.IsNullOrEmpty(ConfigurationManager.AppSettings[GlobalSettings.UmbracoConnectionName]) == false)
{
@@ -395,8 +394,8 @@ namespace Umbraco.Core
else if (legacyConnString.ToLowerInvariant().Contains("tcp:"))
{
//Must be sql azure
- SaveConnectionString(legacyConnString, "System.Data.SqlClient");
- Initialize("System.Data.SqlClient");
+ SaveConnectionString(legacyConnString, Constants.DatabaseProviders.SqlServer);
+ Initialize(Constants.DatabaseProviders.SqlServer);
}
else if (legacyConnString.ToLowerInvariant().Contains("datalayer=mysql"))
{
@@ -407,20 +406,19 @@ namespace Umbraco.Core
foreach (var variable in legacyConnString.Split(';').Where(x => x.ToLowerInvariant().StartsWith("datalayer") == false))
connectionStringWithoutDatalayer = string.Format("{0}{1};", connectionStringWithoutDatalayer, variable);
- SaveConnectionString(connectionStringWithoutDatalayer, "MySql.Data.MySqlClient");
- Initialize("MySql.Data.MySqlClient");
+ SaveConnectionString(connectionStringWithoutDatalayer, Constants.DatabaseProviders.MySql);
+ Initialize(Constants.DatabaseProviders.MySql);
}
else
{
//Must be sql
- SaveConnectionString(legacyConnString, "System.Data.SqlClient");
- Initialize("System.Data.SqlClient");
+ SaveConnectionString(legacyConnString, Constants.DatabaseProviders.SqlServer);
+ Initialize(Constants.DatabaseProviders.SqlServer);
}
//Remove the legacy connection string, so we don't end up in a loop if something goes wrong.
GlobalSettings.RemoveSetting(GlobalSettings.UmbracoConnectionName);
-
- DetermineSqlServerVersion();
+
}
else
{
@@ -465,49 +463,6 @@ namespace Umbraco.Core
Initialize(providerName);
}
- ///
- /// Set the lazy resolution of determining the SQL server version if that is the db type we're using
- ///
- private void DetermineSqlServerVersion()
- {
-
- var sqlServerSyntax = SqlSyntax as SqlServerSyntaxProvider;
- if (sqlServerSyntax != null)
- {
- //this will not execute now, it is lazy so will only execute when we need to actually know
- // the sql server version.
- sqlServerSyntax.VersionName = new Lazy(() =>
- {
- try
- {
- var database = this._factory.CreateDatabase();
-
- var version = database.ExecuteScalar("SELECT SERVERPROPERTY('productversion')");
- var firstPart = version.Split('.')[0];
- switch (firstPart)
- {
- case "11":
- return SqlServerVersionName.V2012;
- case "10":
- return SqlServerVersionName.V2008;
- case "9":
- return SqlServerVersionName.V2005;
- case "8":
- return SqlServerVersionName.V2000;
- case "7":
- return SqlServerVersionName.V7;
- default:
- return SqlServerVersionName.Other;
- }
- }
- catch (Exception)
- {
- return SqlServerVersionName.Invalid;
- }
- });
- }
- }
-
internal DatabaseSchemaResult ValidateDatabaseSchema()
{
if (_configured == false || (string.IsNullOrEmpty(_connectionString) || string.IsNullOrEmpty(ProviderName)))
@@ -517,7 +472,7 @@ namespace Umbraco.Core
{
if (SystemUtilities.GetCurrentTrustLevel() != AspNetHostingPermissionLevel.Unrestricted
- && ProviderName == "MySql.Data.MySqlClient")
+ && ProviderName == Constants.DatabaseProviders.MySql)
{
throw new InvalidOperationException("Cannot use MySql in Medium Trust configuration");
}
@@ -625,13 +580,8 @@ namespace Umbraco.Core
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 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
@@ -731,7 +681,7 @@ namespace Umbraco.Core
private Attempt CheckReadyForInstall()
{
if (SystemUtilities.GetCurrentTrustLevel() != AspNetHostingPermissionLevel.Unrestricted
- && ProviderName == "MySql.Data.MySqlClient")
+ && ProviderName == Constants.DatabaseProviders.MySql)
{
throw new InvalidOperationException("Cannot use MySql in Medium Trust configuration");
}
@@ -780,7 +730,7 @@ namespace Umbraco.Core
{
var dbIsSqlCe = false;
if (databaseSettings != null && databaseSettings.ProviderName != null)
- dbIsSqlCe = databaseSettings.ProviderName == "System.Data.SqlServerCe.4.0";
+ dbIsSqlCe = databaseSettings.ProviderName == Constants.DatabaseProviders.SqlCe;
var sqlCeDatabaseExists = false;
if (dbIsSqlCe)
{
diff --git a/src/Umbraco.Core/DecimalExtensions.cs b/src/Umbraco.Core/DecimalExtensions.cs
new file mode 100644
index 0000000000..b4d74fdb2e
--- /dev/null
+++ b/src/Umbraco.Core/DecimalExtensions.cs
@@ -0,0 +1,23 @@
+namespace Umbraco.Core
+{
+ ///
+ /// Provides extension methods for System.Decimal.
+ ///
+ /// See System.Decimal on MSDN and also
+ /// http://stackoverflow.com/questions/4298719/parse-decimal-and-filter-extra-0-on-the-right/4298787#4298787.
+ ///
+ public static class DecimalExtensions
+ {
+ ///
+ /// Gets the normalized value.
+ ///
+ /// The value to normalize.
+ /// The normalized value.
+ /// Normalizing changes the scaling factor and removes trailing zeroes,
+ /// so 1.2500m comes out as 1.25m.
+ public static decimal Normalize(this decimal value)
+ {
+ return value / 1.000000000000000000000000000000000m;
+ }
+ }
+}
diff --git a/src/Umbraco.Core/Logging/ImageProcessorLogger.cs b/src/Umbraco.Core/Logging/ImageProcessorLogger.cs
new file mode 100644
index 0000000000..02ca9c2949
--- /dev/null
+++ b/src/Umbraco.Core/Logging/ImageProcessorLogger.cs
@@ -0,0 +1,46 @@
+namespace Umbraco.Core.Logging
+{
+ using System;
+ using System.Runtime.CompilerServices;
+
+ using ImageProcessor.Common.Exceptions;
+
+ ///
+ /// A logger for explicitly logging ImageProcessor exceptions.
+ ///
+ /// Creating this logger is enough for ImageProcessor to find and replace its in-built debug logger
+ /// without any additional configuration required. This class currently has to be public in order
+ /// to do so.
+ ///
+ ///
+ public sealed class ImageProcessorLogger : ImageProcessor.Common.Exceptions.ILogger
+ {
+ ///
+ /// Logs the specified message as an error.
+ ///
+ /// The type calling the logger.
+ /// The message to log.
+ /// The property or method name calling the log.
+ /// The line number where the method is called.
+ public void Log(string text, [CallerMemberName] string callerName = null, [CallerLineNumber] int lineNumber = 0)
+ {
+ // Using LogHelper since the ImageProcessor logger expects a parameterless constructor.
+ var message = string.Format("{0} {1} : {2}", callerName, lineNumber, text);
+ LogHelper.Error(string.Empty, new ImageProcessingException(message));
+ }
+
+ ///
+ /// Logs the specified message as an error.
+ ///
+ /// The type calling the logger.
+ /// The message to log.
+ /// The property or method name calling the log.
+ /// The line number where the method is called.
+ public void Log(Type type, string text, [CallerMemberName] string callerName = null, [CallerLineNumber] int lineNumber = 0)
+ {
+ // Using LogHelper since the ImageProcessor logger expects a parameterless constructor.
+ var message = string.Format("{0} {1} : {2}", callerName, lineNumber, text);
+ LogHelper.Error(type, string.Empty, new ImageProcessingException(message));
+ }
+ }
+}
diff --git a/src/Umbraco.Core/Models/IRedirectUrl.cs b/src/Umbraco.Core/Models/IRedirectUrl.cs
index 4f15b1e082..419389f60d 100644
--- a/src/Umbraco.Core/Models/IRedirectUrl.cs
+++ b/src/Umbraco.Core/Models/IRedirectUrl.cs
@@ -4,14 +4,33 @@ using Umbraco.Core.Models.EntityBase;
namespace Umbraco.Core.Models
{
+ ///
+ /// Represents a redirect url.
+ ///
public interface IRedirectUrl : IAggregateRoot, IRememberBeingDirty
{
+ ///
+ /// Gets or sets the identifier of the content item.
+ ///
[DataMember]
int ContentId { get; set; }
+ ///
+ /// Gets or sets the unique key identifying the content item.
+ ///
+ [DataMember]
+ Guid ContentKey { get; set; }
+
+ ///
+ /// Gets or sets the redirect url creation date.
+ ///
[DataMember]
DateTime CreateDateUtc { get; set; }
+ ///
+ /// Gets or sets the redirect url route.
+ ///
+ /// Is a proper Umbraco route eg /path/to/foo or 123/path/tofoo.
[DataMember]
string Url { get; set; }
}
diff --git a/src/Umbraco.Core/Models/Identity/BackOfficeIdentityUser.cs b/src/Umbraco.Core/Models/Identity/BackOfficeIdentityUser.cs
index 0d803c26e5..50dc7d06f8 100644
--- a/src/Umbraco.Core/Models/Identity/BackOfficeIdentityUser.cs
+++ b/src/Umbraco.Core/Models/Identity/BackOfficeIdentityUser.cs
@@ -19,7 +19,7 @@ namespace Umbraco.Core.Models.Identity
Culture = Configuration.GlobalSettings.DefaultUILanguage;
}
- public virtual async Task GenerateUserIdentityAsync(BackOfficeUserManager manager)
+ public virtual async Task GenerateUserIdentityAsync(BackOfficeUserManager manager)
{
// NOTE the authenticationType must match the umbraco one
// defined in CookieAuthenticationOptions.AuthenticationType
diff --git a/src/Umbraco.Core/Models/Property.cs b/src/Umbraco.Core/Models/Property.cs
index 6d7cafe7c9..dff6a712c7 100644
--- a/src/Umbraco.Core/Models/Property.cs
+++ b/src/Umbraco.Core/Models/Property.cs
@@ -133,6 +133,13 @@ namespace Umbraco.Core.Models
set { SetPropertyValueAndDetectChanges(value, ref _version, Ps.Value.VersionSelector); }
}
+ private static void ThrowTypeException(object value, Type expected, string alias)
+ {
+ throw new InvalidOperationException(string.Format("Value \"{0}\" of type \"{1}\" could not be converted"
+ + " to type \"{2}\" which is expected by property type \"{3}\".",
+ value, value.GetType(), expected, alias));
+ }
+
///
/// Gets or Sets the value of the Property
///
@@ -146,55 +153,50 @@ namespace Umbraco.Core.Models
get { return _value; }
set
{
- bool typeValidation = _propertyType.IsPropertyTypeValid(value);
+ var isOfExpectedType = _propertyType.IsPropertyTypeValid(value);
- if (typeValidation == false)
+ if (isOfExpectedType == false) // isOfExpectedType is true if value is null - so if false, value is *not* null
{
- // Normally we'll throw an exception here. However if the property is of a type that can have it's data field (dataInt, dataVarchar etc.)
- // changed, we might have a value of the now "wrong" type. As of May 2016 Label is the only built-in property editor that supports this.
- // In that case we should try to parse the value and return null if that's not possible rather than throwing an exception.
- if (value != null && _propertyType.CanHaveDataValueTypeChanged())
- {
- var stringValue = value.ToString();
- switch (_propertyType.DataTypeDatabaseType)
- {
- case DataTypeDatabaseType.Nvarchar:
- case DataTypeDatabaseType.Ntext:
- value = stringValue;
- break;
- case DataTypeDatabaseType.Integer:
- int integerValue;
- if (int.TryParse(stringValue, out integerValue) == false)
- {
- // Edge case, but if changed from decimal --> integer, the above tryparse will fail. So we'll try going
- // via decimal too to return the integer value rather than zero.
- decimal decimalForIntegerValue;
- if (decimal.TryParse(stringValue, out decimalForIntegerValue))
- {
- integerValue = (int)decimalForIntegerValue;
- }
- }
+ // "garbage-in", accept what we can & convert
+ // throw only if conversion is not possible
- value = integerValue;
- break;
- case DataTypeDatabaseType.Decimal:
- decimal decimalValue;
- decimal.TryParse(stringValue, out decimalValue);
- value = decimalValue;
- break;
- case DataTypeDatabaseType.Date:
- DateTime dateValue;
- DateTime.TryParse(stringValue, out dateValue);
- value = dateValue;
- break;
- }
- }
- else
+ var s = value.ToString();
+
+ switch (_propertyType.DataTypeDatabaseType)
{
- throw new Exception(
- string.Format(
- "Type validation failed. The value type: '{0}' does not match the DataType in PropertyType with alias: '{1}'",
- value == null ? "null" : value.GetType().Name, Alias));
+ case DataTypeDatabaseType.Nvarchar:
+ case DataTypeDatabaseType.Ntext:
+ value = s;
+ break;
+ case DataTypeDatabaseType.Integer:
+ if (s.IsNullOrWhiteSpace()) value = null; // assume empty means null
+ else
+ {
+ var convInt = value.TryConvertTo();
+ if (convInt == false) ThrowTypeException(value, typeof(int), _propertyType.Alias);
+ value = convInt.Result;
+ }
+ break;
+ case DataTypeDatabaseType.Decimal:
+ if (s.IsNullOrWhiteSpace()) value = null; // assume empty means null
+ else
+ {
+ var convDecimal = value.TryConvertTo();
+ if (convDecimal == false) ThrowTypeException(value, typeof (decimal), _propertyType.Alias);
+ // need to normalize the value (change the scaling factor and remove trailing zeroes)
+ // because the underlying database is going to mess with the scaling factor anyways.
+ value = convDecimal.Result.Normalize();
+ }
+ break;
+ case DataTypeDatabaseType.Date:
+ if (s.IsNullOrWhiteSpace()) value = null; // assume empty means null
+ else
+ {
+ var convDateTime = value.TryConvertTo();
+ if (convDateTime == false) ThrowTypeException(value, typeof (DateTime), _propertyType.Alias);
+ value = convDateTime.Result;
+ }
+ break;
}
}
diff --git a/src/Umbraco.Core/Models/PropertyType.cs b/src/Umbraco.Core/Models/PropertyType.cs
index cab2861e28..0aebcb6544 100644
--- a/src/Umbraco.Core/Models/PropertyType.cs
+++ b/src/Umbraco.Core/Models/PropertyType.cs
@@ -48,7 +48,7 @@ namespace Umbraco.Core.Models
{
_alias = GetAlias(propertyTypeAlias);
}
-
+
public PropertyType(string propertyEditorAlias, DataTypeDatabaseType dataTypeDatabaseType)
: this(propertyEditorAlias, dataTypeDatabaseType, false)
{
@@ -56,7 +56,7 @@ namespace Umbraco.Core.Models
public PropertyType(string propertyEditorAlias, DataTypeDatabaseType dataTypeDatabaseType, string propertyTypeAlias)
: this(propertyEditorAlias, dataTypeDatabaseType, false, propertyTypeAlias)
- {
+ {
}
///
@@ -298,18 +298,21 @@ namespace Umbraco.Core.Models
}
///
- /// Validates the Value from a Property according to its type
+ /// Gets a value indicating whether the value is of the expected type
+ /// for the property, and can be assigned to the property "as is".
///
- ///
- /// True if valid, otherwise false
+ /// The value.
+ /// True if the value is of the expected type for the property,
+ /// and can be assigned to the property "as is". Otherwise, false, to indicate
+ /// that some conversion is required.
public bool IsPropertyTypeValid(object value)
{
- //Can't validate null values, so just allow it to pass the current validation
+ // null values are assumed to be ok
if (value == null)
return true;
- //Check type if the type of the value match the type from the DataType/PropertyEditor
- Type type = value.GetType();
+ // check if the type of the value matches the type from the DataType/PropertyEditor
+ var valueType = value.GetType();
//TODO Add PropertyEditor Type validation when its relevant to introduce
/*bool isEditorModel = value is IEditorModel;
@@ -326,50 +329,34 @@ namespace Umbraco.Core.Models
return argument == type;
}*/
- if (PropertyEditorAlias.IsNullOrWhiteSpace() == false)
+ if (PropertyEditorAlias.IsNullOrWhiteSpace() == false) // fixme - always true?
{
- //Find DataType by Id
- //IDataType dataType = DataTypesResolver.Current.GetById(DataTypeControlId);
- //Check if dataType is null (meaning that the ControlId is valid) ?
- //Possibly cast to BaseDataType and get the DbType from there (which might not be possible because it lives in umbraco.cms.businesslogic.datatype) ?
-
- //Simple validation using the DatabaseType from the DataTypeDefinition and Type of the passed in value
- 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;
-
- if (DataTypeDatabaseType == DataTypeDatabaseType.Nvarchar && type == typeof(string))
- return true;
-
- if (DataTypeDatabaseType == DataTypeDatabaseType.Ntext && type == typeof(string))
- return true;
+ // simple validation using the DatabaseType from the DataTypeDefinition
+ // and the Type of the passed in value
+ switch (DataTypeDatabaseType)
+ {
+ // fixme breaking!
+ case DataTypeDatabaseType.Integer:
+ return valueType == typeof(int);
+ case DataTypeDatabaseType.Decimal:
+ return valueType == typeof(decimal);
+ case DataTypeDatabaseType.Date:
+ return valueType == typeof(DateTime);
+ case DataTypeDatabaseType.Nvarchar:
+ return valueType == typeof(string);
+ case DataTypeDatabaseType.Ntext:
+ return valueType == typeof(string);
+ }
}
- //Fallback for simple value types when no Control Id or Database Type is set
- if (type.IsPrimitive || value is string)
+ // fixme - never reached + makes no sense?
+ // fallback for simple value types when no Control Id or Database Type is set
+ if (valueType.IsPrimitive || value is string)
return true;
return false;
}
- ///
- /// Checks the underlying property editor prevalues to see if the one that allows changing of the database field
- /// to which data is saved (dataInt, dataVarchar etc.) is included. If so that means the field could be changed when the data
- /// type is saved.
- ///
- ///
- internal bool CanHaveDataValueTypeChanged()
- {
- var propertyEditor = PropertyEditorResolver.Current.GetByAlias(_propertyEditorAlias);
- return propertyEditor.PreValueEditor.Fields
- .SingleOrDefault(x => x.Key == Constants.PropertyEditors.PreValueKeys.DataValueType) != null;
- }
-
///
/// Validates the Value from a Property according to the validation settings
///
@@ -389,15 +376,15 @@ namespace Umbraco.Core.Models
var regexPattern = new Regex(ValidationRegExp);
return regexPattern.IsMatch(value.ToString());
}
- catch
+ catch
{
throw new Exception(string .Format("Invalid validation expression on property {0}",this.Alias));
}
-
+
}
-
+
//TODO: We must ensure that the property value can actually be saved based on the specified database type
-
+
//TODO Add PropertyEditor validation when its relevant to introduce
/*if (value is IEditorModel && DataTypeControlId != Guid.Empty)
{
@@ -415,19 +402,19 @@ namespace Umbraco.Core.Models
{
if (base.Equals(other)) return true;
- //Check whether the PropertyType's properties are equal.
+ //Check whether the PropertyType's properties are equal.
return Alias.InvariantEquals(other.Alias);
}
public override int GetHashCode()
{
- //Get hash code for the Name field if it is not null.
+ //Get hash code for the Name field if it is not null.
int baseHash = base.GetHashCode();
- //Get hash code for the Alias field.
+ //Get hash code for the Alias field.
int hashAlias = Alias.ToLowerInvariant().GetHashCode();
- //Calculate the hash code for the product.
+ //Calculate the hash code for the product.
return baseHash ^ hashAlias;
}
@@ -439,7 +426,7 @@ namespace Umbraco.Core.Models
//need to manually assign the Lazy value as it will not be automatically mapped
if (PropertyGroupId != null)
{
- clone._propertyGroupId = new Lazy(() => PropertyGroupId.Value);
+ clone._propertyGroupId = new Lazy(() => PropertyGroupId.Value);
}
//this shouldn't really be needed since we're not tracking
clone.ResetDirtyProperties(false);
diff --git a/src/Umbraco.Core/Models/Rdbms/PropertyDataDto.cs b/src/Umbraco.Core/Models/Rdbms/PropertyDataDto.cs
index 63e3104d12..c4cd28f6e0 100644
--- a/src/Umbraco.Core/Models/Rdbms/PropertyDataDto.cs
+++ b/src/Umbraco.Core/Models/Rdbms/PropertyDataDto.cs
@@ -16,7 +16,7 @@ namespace Umbraco.Core.Models.Rdbms
[Column("contentNodeId")]
[ForeignKey(typeof(NodeDto))]
- [Index(IndexTypes.NonClustered, Name = "IX_cmsPropertyData_1")]
+ [Index(IndexTypes.UniqueNonClustered, Name = "IX_cmsPropertyData_1", ForColumns = "contentNodeId,versionId,propertytypeid")]
public int NodeId { get; set; }
[Column("versionId")]
@@ -33,9 +33,23 @@ namespace Umbraco.Core.Models.Rdbms
[NullSetting(NullSetting = NullSettings.Null)]
public int? Integer { get; set; }
+ private decimal? _decimalValue;
+
[Column("dataDecimal")]
[NullSetting(NullSetting = NullSettings.Null)]
- public decimal? Decimal { get; set; }
+ public decimal? Decimal
+ {
+ get
+ {
+ return _decimalValue;
+ }
+ set
+ {
+ // need to normalize the value (change the scaling factor and remove trailing zeroes)
+ // because the underlying database probably has messed with the scaling factor.
+ _decimalValue = value.HasValue ? (decimal?) value.Value.Normalize() : null;
+ }
+ }
[Column("dataDate")]
[NullSetting(NullSetting = NullSettings.Null)]
diff --git a/src/Umbraco.Core/Models/Rdbms/RedirectUrlDto.cs b/src/Umbraco.Core/Models/Rdbms/RedirectUrlDto.cs
index 6ada1dca74..ca65e4b9a1 100644
--- a/src/Umbraco.Core/Models/Rdbms/RedirectUrlDto.cs
+++ b/src/Umbraco.Core/Models/Rdbms/RedirectUrlDto.cs
@@ -5,7 +5,7 @@ using Umbraco.Core.Persistence.DatabaseAnnotations;
namespace Umbraco.Core.Models.Rdbms
{
[TableName("umbracoRedirectUrl")]
- [PrimaryKey("id")]
+ [PrimaryKey("id", autoIncrement = false)]
[ExplicitColumns]
class RedirectUrlDto
{
@@ -14,22 +14,37 @@ namespace Umbraco.Core.Models.Rdbms
CreateDateUtc = DateTime.UtcNow;
}
- [Column("id")]
- [PrimaryKeyColumn(IdentitySeed = 1, Name = "PK_umbracoRedirectUrl")]
- public int Id { get; set; }
+ // notes
+ //
+ // we want a unique, non-clustered index on (url ASC, contentId ASC, createDate DESC) but the
+ // problem is that the index key must be 900 bytes max. should we run without an index? done
+ // some perfs comparisons, and running with an index on a hash is only slightly slower on
+ // inserts, and much faster on reads, so... we have an index on a hash.
- [Column("contentId")]
- [NullSetting(NullSetting = NullSettings.NotNull)]
- [ForeignKey(typeof(NodeDto), Column = "id")]
+ [Column("id")]
+ [PrimaryKeyColumn(Name = "PK_umbracoRedirectUrl", AutoIncrement = false)]
+ public Guid Id { get; set; }
+
+ [ResultColumn]
public int ContentId { get; set; }
+ [Column("contentKey")]
+ [NullSetting(NullSetting = NullSettings.NotNull)]
+ [ForeignKey(typeof(NodeDto), Column = "uniqueID")]
+ public Guid ContentKey { get; set; }
+
[Column("createDateUtc")]
[NullSetting(NullSetting = NullSettings.NotNull)]
public DateTime CreateDateUtc { get; set; }
[Column("url")]
[NullSetting(NullSetting = NullSettings.NotNull)]
- [Index(IndexTypes.UniqueNonClustered, Name = "IX_umbracoRedirectUrl", ForColumns = "url, createDateUtc")]
public string Url { get; set; }
+
+ [Column("urlHash")]
+ [NullSetting(NullSetting = NullSettings.NotNull)]
+ [Index(IndexTypes.UniqueNonClustered, Name = "IX_umbracoRedirectUrl", ForColumns = "urlHash, contentKey, createDateUtc")]
+ [Length(40)]
+ public string UrlHash { get; set; }
}
}
diff --git a/src/Umbraco.Core/Models/Rdbms/StylesheetDto.cs b/src/Umbraco.Core/Models/Rdbms/StylesheetDto.cs
index 3580fcf918..bbc63d950b 100644
--- a/src/Umbraco.Core/Models/Rdbms/StylesheetDto.cs
+++ b/src/Umbraco.Core/Models/Rdbms/StylesheetDto.cs
@@ -1,11 +1,10 @@
-using Umbraco.Core.Persistence;
+using System;
+using Umbraco.Core.Persistence;
using Umbraco.Core.Persistence.DatabaseAnnotations;
namespace Umbraco.Core.Models.Rdbms
{
- [TableName("cmsStylesheet")]
- [PrimaryKey("nodeId", autoIncrement = false)]
- [ExplicitColumns]
+ [Obsolete("This is no longer used and will be removed from Umbraco in future versions")]
internal class StylesheetDto
{
[Column("nodeId")]
diff --git a/src/Umbraco.Core/Models/Rdbms/StylesheetPropertyDto.cs b/src/Umbraco.Core/Models/Rdbms/StylesheetPropertyDto.cs
index cfe3148764..bff327d286 100644
--- a/src/Umbraco.Core/Models/Rdbms/StylesheetPropertyDto.cs
+++ b/src/Umbraco.Core/Models/Rdbms/StylesheetPropertyDto.cs
@@ -1,11 +1,10 @@
-using Umbraco.Core.Persistence;
+using System;
+using Umbraco.Core.Persistence;
using Umbraco.Core.Persistence.DatabaseAnnotations;
namespace Umbraco.Core.Models.Rdbms
{
- [TableName("cmsStylesheetProperty")]
- [PrimaryKey("nodeId", autoIncrement = false)]
- [ExplicitColumns]
+ [Obsolete("This is no longer used and will be removed from Umbraco in future versions")]
internal class StylesheetPropertyDto
{
[Column("nodeId")]
diff --git a/src/Umbraco.Core/Models/RedirectUrl.cs b/src/Umbraco.Core/Models/RedirectUrl.cs
index 1706dd38cf..ff7746d0cf 100644
--- a/src/Umbraco.Core/Models/RedirectUrl.cs
+++ b/src/Umbraco.Core/Models/RedirectUrl.cs
@@ -5,10 +5,16 @@ using Umbraco.Core.Models.EntityBase;
namespace Umbraco.Core.Models
{
+ ///
+ /// Implements .
+ ///
[Serializable]
[DataContract(IsReference = true)]
public class RedirectUrl : Entity, IRedirectUrl
{
+ ///
+ /// Initializes a new instance of the class.
+ ///
public RedirectUrl()
{
CreateDateUtc = DateTime.UtcNow;
@@ -16,42 +22,46 @@ namespace Umbraco.Core.Models
private static readonly Lazy Ps = new Lazy();
+ // ReSharper disable once ClassNeverInstantiated.Local
private class PropertySelectors
{
public readonly PropertyInfo ContentIdSelector = ExpressionHelper.GetPropertyInfo(x => x.ContentId);
+ public readonly PropertyInfo ContentKeySelector = ExpressionHelper.GetPropertyInfo(x => x.ContentKey);
public readonly PropertyInfo CreateDateUtcSelector = ExpressionHelper.GetPropertyInfo(x => x.CreateDateUtc);
public readonly PropertyInfo UrlSelector = ExpressionHelper.GetPropertyInfo(x => x.Url);
}
private int _contentId;
+ private Guid _contentKey;
private DateTime _createDateUtc;
private string _url;
+ ///
public int ContentId
{
get { return _contentId; }
- set
- {
- SetPropertyValueAndDetectChanges(value, ref _contentId, Ps.Value.ContentIdSelector);
- }
+ set { SetPropertyValueAndDetectChanges(value, ref _contentId, Ps.Value.ContentIdSelector); }
}
+ ///
+ public Guid ContentKey
+ {
+ get { return _contentKey; }
+ set { SetPropertyValueAndDetectChanges(value, ref _contentKey, Ps.Value.ContentKeySelector); }
+ }
+
+ ///
public DateTime CreateDateUtc
{
get { return _createDateUtc; }
- set
- {
- SetPropertyValueAndDetectChanges(value, ref _createDateUtc, Ps.Value.CreateDateUtcSelector);
- }
+ set { SetPropertyValueAndDetectChanges(value, ref _createDateUtc, Ps.Value.CreateDateUtcSelector); }
}
+ ///
public string Url
{
get { return _url; }
- set
- {
- SetPropertyValueAndDetectChanges(value, ref _url, Ps.Value.UrlSelector);
- }
+ set { SetPropertyValueAndDetectChanges(value, ref _url, Ps.Value.UrlSelector); }
}
}
}
\ No newline at end of file
diff --git a/src/Umbraco.Core/Models/UserExtensions.cs b/src/Umbraco.Core/Models/UserExtensions.cs
index 5b9f63cf48..ece63b4889 100644
--- a/src/Umbraco.Core/Models/UserExtensions.cs
+++ b/src/Umbraco.Core/Models/UserExtensions.cs
@@ -1,8 +1,5 @@
using System;
using System.Globalization;
-using System.Linq;
-using System.Threading;
-using Umbraco.Core.Models.Identity;
using Umbraco.Core.Models.Membership;
using Umbraco.Core.Services;
@@ -83,5 +80,19 @@ namespace Umbraco.Core.Models
if (media == null) throw new ArgumentNullException("media");
return HasPathAccess(media.Path, user.StartMediaId, Constants.System.RecycleBinMedia);
}
+
+
+ ///
+ /// Determines whether this user is an admin.
+ ///
+ ///
+ ///
+ /// true if this user is admin; otherwise, false .
+ ///
+ public static bool IsAdmin(this IUser user)
+ {
+ if (user == null) throw new ArgumentNullException("user");
+ return user.UserType.Alias == "admin";
+ }
}
}
\ No newline at end of file
diff --git a/src/Umbraco.Core/ObjectExtensions.cs b/src/Umbraco.Core/ObjectExtensions.cs
index a8a1e80eb7..6ed424c232 100644
--- a/src/Umbraco.Core/ObjectExtensions.cs
+++ b/src/Umbraco.Core/ObjectExtensions.cs
@@ -9,15 +9,28 @@ using System.Xml;
namespace Umbraco.Core
{
+ ///
+ /// Provides object extension methods.
+ ///
public static class ObjectExtensions
{
//private static readonly ConcurrentDictionary> ObjectFactoryCache = new ConcurrentDictionary>();
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
public static IEnumerable AsEnumerableOfOne(this T input)
{
return Enumerable.Repeat(input, 1);
}
+ ///
+ ///
+ ///
+ ///
public static void DisposeIfDisposable(this object input)
{
var disposable = input as IDisposable;
@@ -25,7 +38,8 @@ namespace Umbraco.Core
}
///
- /// Provides a shortcut way of safely casting an input when you cannot guarantee the is an instance type (i.e., when the C# AS keyword is not applicable)
+ /// Provides a shortcut way of safely casting an input when you cannot guarantee the is
+ /// an instance type (i.e., when the C# AS keyword is not applicable).
///
///
/// The input.
@@ -63,30 +77,30 @@ namespace Umbraco.Core
}
///
- /// Tries to convert the input object to the output type using TypeConverters. If the destination type is a superclass of the input type,
- /// if will use .
+ /// Tries to convert the input object to the output type using TypeConverters. If the destination
+ /// type is a superclass of the input type, if will use .
///
/// The input.
/// Type of the destination.
///
public static Attempt TryConvertTo(this object input, Type destinationType)
{
- //if it is null and it is nullable, then return success with null
- if (input == null && destinationType.IsGenericType && destinationType.GetGenericTypeDefinition() == typeof (Nullable<>))
- {
- return Attempt.Succeed(null);
+ // if null...
+ if (input == null)
+ {
+ // nullable is ok
+ if (destinationType.IsGenericType && destinationType.GetGenericTypeDefinition() == typeof(Nullable<>))
+ return Attempt.Succeed(null);
+
+ // value type is nok, else can be null, so is ok
+ return Attempt.SucceedIf(destinationType.IsValueType == false, null);
}
-
- //if its not nullable and it is a value type
- if (input == null && destinationType.IsValueType) return Attempt.Fail();
- //if the type can be null, then no problem
- if (input == null && destinationType.IsValueType == false) return Attempt.Succeed(null);
+ // easy
if (destinationType == typeof(object)) return Attempt.Succeed(input);
-
if (input.GetType() == destinationType) return Attempt.Succeed(input);
- //check for string so that overloaders of ToString() can take advantage of the conversion.
+ // check for string so that overloaders of ToString() can take advantage of the conversion.
if (destinationType == typeof(string)) return Attempt.Succeed(input.ToString());
// if we've got a nullable of something, we try to convert directly to that thing.
@@ -116,9 +130,10 @@ namespace Umbraco.Core
{
if (input is string)
{
+ // try convert from string, returns an Attempt if the string could be
+ // processed (either succeeded or failed), else null if we need to try
+ // other methods
var result = TryConvertToFromString(input as string, destinationType);
-
- // if we processed the string (succeed or fail), we're done
if (result.HasValue) return result.Value;
}
@@ -201,90 +216,124 @@ namespace Umbraco.Core
return Attempt.Fail();
}
- private static Nullable> TryConvertToFromString(this string input, Type destinationType)
+ // returns an attempt if the string has been processed (either succeeded or failed)
+ // returns null if we need to try other methods
+ private static Attempt? TryConvertToFromString(this string input, Type destinationType)
{
+ // easy
if (destinationType == typeof(string))
return Attempt.Succeed(input);
- if (string.IsNullOrEmpty(input))
+ // null, empty, whitespaces
+ if (string.IsNullOrWhiteSpace(input))
{
- if (destinationType == typeof(Boolean))
- return Attempt.Succeed(false); // special case for booleans, null/empty == false
- if (destinationType == typeof(DateTime))
+ if (destinationType == typeof(bool)) // null/empty = bool false
+ return Attempt.Succeed(false);
+ if (destinationType == typeof(DateTime)) // null/empty = min DateTime value
return Attempt.Succeed(DateTime.MinValue);
+
+ // cannot decide here,
+ // any of the types below will fail parsing and will return a failed attempt
+ // but anything else will not be processed and will return null
+ // so even though the string is null/empty we have to proceed
}
- // we have a non-empty string, look for type conversions in the expected order of frequency of use...
+ // look for type conversions in the expected order of frequency of use...
if (destinationType.IsPrimitive)
{
- if (destinationType == typeof(Int32))
+ if (destinationType == typeof(int)) // aka Int32
{
- Int32 value;
- return Int32.TryParse(input, out value) ? Attempt.Succeed(value) : Attempt.Fail();
- }
- if (destinationType == typeof(Int64))
- {
- Int64 value;
- return Int64.TryParse(input, out value) ? Attempt.Succeed(value) : Attempt.Fail();
- }
- if (destinationType == typeof(Boolean))
- {
- Boolean value;
- if (Boolean.TryParse(input, out value))
- return Attempt.Succeed(value); // don't declare failure so the CustomBooleanTypeConverter can try
- }
- else if (destinationType == typeof(Int16))
- {
- Int16 value;
- return Int16.TryParse(input, out value) ? Attempt.Succeed(value) : Attempt.Fail();
- }
- else if (destinationType == typeof(Double))
- {
- Double value;
- var input2 = NormalizeNumberDecimalSeparator(input);
- return Double.TryParse(input2, out value) ? Attempt.Succeed(value) : Attempt.Fail();
- }
- else if (destinationType == typeof(Single))
- {
- Single value;
+ int value;
+ if (int.TryParse(input, out value)) return Attempt.Succeed(value);
+
+ // because decimal 100.01m will happily convert to integer 100, it
+ // makes sense that string "100.01" *also* converts to integer 100.
+ decimal value2;
var input2 = NormalizeNumberDecimalSeparator(input);
- return Single.TryParse(input2, out value) ? Attempt.Succeed(value) : Attempt.Fail();
- }
- else if (destinationType == typeof(Char))
+ return Attempt.SucceedIf(decimal.TryParse(input2, out value2), Convert.ToInt32(value2));
+ }
+
+ if (destinationType == typeof(long)) // aka Int64
{
- Char value;
- return Char.TryParse(input, out value) ? Attempt.Succeed(value) : Attempt.Fail();
+ long value;
+ if (long.TryParse(input, out value)) return Attempt.Succeed(value);
+
+ // same as int
+ decimal value2;
+ var input2 = NormalizeNumberDecimalSeparator(input);
+ return Attempt.SucceedIf(decimal.TryParse(input2, out value2), Convert.ToInt64(value2));
}
- else if (destinationType == typeof(Byte))
+
+ // fixme - should we do the decimal trick for short, byte, unsigned?
+
+ if (destinationType == typeof(bool)) // aka Boolean
{
- Byte value;
- return Byte.TryParse(input, out value) ? Attempt.Succeed(value) : Attempt.Fail();
+ bool value;
+ if (bool.TryParse(input, out value)) return Attempt.Succeed(value);
+ // don't declare failure so the CustomBooleanTypeConverter can try
+ return null;
}
- else if (destinationType == typeof(SByte))
+
+ if (destinationType == typeof(short)) // aka Int16
{
- SByte value;
- return SByte.TryParse(input, out value) ? Attempt.Succeed(value) : Attempt.Fail();
+ short value;
+ return Attempt.SucceedIf(short.TryParse(input, out value), value);
}
- else if (destinationType == typeof(UInt32))
+
+ if (destinationType == typeof(double)) // aka Double
{
- UInt32 value;
- return UInt32.TryParse(input, out value) ? Attempt.Succeed(value) : Attempt.Fail();
+ double value;
+ var input2 = NormalizeNumberDecimalSeparator(input);
+ return Attempt.SucceedIf(double.TryParse(input2, out value), value);
}
- else if (destinationType == typeof(UInt16))
+
+ if (destinationType == typeof(float)) // aka Single
{
- UInt16 value;
- return UInt16.TryParse(input, out value) ? Attempt.Succeed(value) : Attempt.Fail();
+ float value;
+ var input2 = NormalizeNumberDecimalSeparator(input);
+ return Attempt.SucceedIf(float.TryParse(input2, out value), value);
}
- else if (destinationType == typeof(UInt64))
+
+ if (destinationType == typeof(char)) // aka Char
{
- UInt64 value;
- return UInt64.TryParse(input, out value) ? Attempt.Succeed(value) : Attempt.Fail();
+ char value;
+ return Attempt.SucceedIf(char.TryParse(input, out value), value);
+ }
+
+ if (destinationType == typeof(byte)) // aka Byte
+ {
+ byte value;
+ return Attempt.SucceedIf(byte.TryParse(input, out value), value);
+ }
+
+ if (destinationType == typeof(sbyte)) // aka SByte
+ {
+ sbyte value;
+ return Attempt.SucceedIf(sbyte.TryParse(input, out value), value);
+ }
+
+ if (destinationType == typeof(uint)) // aka UInt32
+ {
+ uint value;
+ return Attempt.SucceedIf(uint.TryParse(input, out value), value);
+ }
+
+ if (destinationType == typeof(ushort)) // aka UInt16
+ {
+ ushort value;
+ return Attempt.SucceedIf(ushort.TryParse(input, out value), value);
+ }
+
+ if (destinationType == typeof(ulong)) // aka UInt64
+ {
+ ulong value;
+ return Attempt.SucceedIf(ulong.TryParse(input, out value), value);
}
}
else if (destinationType == typeof(Guid))
{
Guid value;
- return Guid.TryParse(input, out value) ? Attempt.Succeed(value) : Attempt.Fail();
+ return Attempt.SucceedIf(Guid.TryParse(input, out value), value);
}
else if (destinationType == typeof(DateTime))
{
@@ -307,30 +356,30 @@ namespace Umbraco.Core
else if (destinationType == typeof(DateTimeOffset))
{
DateTimeOffset value;
- return DateTimeOffset.TryParse(input, out value) ? Attempt.Succeed(value) : Attempt.Fail();
+ return Attempt.SucceedIf(DateTimeOffset.TryParse(input, out value), value);
}
else if (destinationType == typeof(TimeSpan))
{
TimeSpan value;
- return TimeSpan.TryParse(input, out value) ? Attempt.Succeed(value) : Attempt.Fail();
+ return Attempt.SucceedIf(TimeSpan.TryParse(input, out value), value);
}
- else if (destinationType == typeof(Decimal))
+ else if (destinationType == typeof(decimal)) // aka Decimal
{
- Decimal value;
+ decimal value;
var input2 = NormalizeNumberDecimalSeparator(input);
- return Decimal.TryParse(input2, out value) ? Attempt.Succeed(value) : Attempt.Fail();
+ return Attempt.SucceedIf(decimal.TryParse(input2, out value), value);
}
else if (destinationType == typeof(Version))
{
Version value;
- return Version.TryParse(input, out value) ? Attempt.Succeed(value) : Attempt.Fail();
+ return Attempt.SucceedIf(Version.TryParse(input, out value), value);
}
// E_NOTIMPL IPAddress, BigInteger
return null; // we can't decide...
}
- private static readonly char[] NumberDecimalSeparatorsToNormalize = new[] {'.', ','};
+ private static readonly char[] NumberDecimalSeparatorsToNormalize = {'.', ','};
private static string NormalizeNumberDecimalSeparator(string s)
{
diff --git a/src/Umbraco.Core/Persistence/DatabaseSchemaHelper.cs b/src/Umbraco.Core/Persistence/DatabaseSchemaHelper.cs
index dc3b2f3e8b..bcc1528d3f 100644
--- a/src/Umbraco.Core/Persistence/DatabaseSchemaHelper.cs
+++ b/src/Umbraco.Core/Persistence/DatabaseSchemaHelper.cs
@@ -107,10 +107,10 @@ namespace Umbraco.Core.Persistence
var foreignSql = _syntaxProvider.Format(tableDefinition.ForeignKeys);
var indexSql = _syntaxProvider.Format(tableDefinition.Indexes);
- var tableExist = _db.TableExist(tableName);
+ var tableExist = TableExist(tableName);
if (overwrite && tableExist)
{
- _db.DropTable(tableName);
+ DropTable(tableName);
tableExist = false;
}
diff --git a/src/Umbraco.Core/Persistence/DbConnectionExtensions.cs b/src/Umbraco.Core/Persistence/DbConnectionExtensions.cs
index 5145ec95c5..c0c7b2a777 100644
--- a/src/Umbraco.Core/Persistence/DbConnectionExtensions.cs
+++ b/src/Umbraco.Core/Persistence/DbConnectionExtensions.cs
@@ -42,13 +42,13 @@ namespace Umbraco.Core.Persistence
{
case DatabaseProviders.SqlServer:
case DatabaseProviders.SqlAzure:
- factory = DbProviderFactories.GetFactory("System.Data.SqlClient");
+ factory = DbProviderFactories.GetFactory(Constants.DatabaseProviders.SqlServer);
break;
case DatabaseProviders.SqlServerCE:
factory = DbProviderFactories.GetFactory("System.Data.SqlServerCe.4.0");
break;
case DatabaseProviders.MySql:
- factory = DbProviderFactories.GetFactory("MySql.Data.MySqlClient");
+ factory = DbProviderFactories.GetFactory(Constants.DatabaseProviders.MySql);
break;
case DatabaseProviders.PostgreSQL:
case DatabaseProviders.Oracle:
diff --git a/src/Umbraco.Core/Persistence/DefaultDatabaseFactory.cs b/src/Umbraco.Core/Persistence/DefaultDatabaseFactory.cs
index 72c4275541..658b8ebabe 100644
--- a/src/Umbraco.Core/Persistence/DefaultDatabaseFactory.cs
+++ b/src/Umbraco.Core/Persistence/DefaultDatabaseFactory.cs
@@ -102,5 +102,14 @@ namespace Umbraco.Core.Persistence
}
}
}
+
+ // during tests, the thread static var can leak between tests
+ // this method provides a way to force-reset the variable
+ internal void ResetForTests()
+ {
+ if (_nonHttpInstance == null) return;
+ _nonHttpInstance.Dispose();
+ _nonHttpInstance = null;
+ }
}
}
\ 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 a69539e9ea..446bd426ad 100644
--- a/src/Umbraco.Core/Persistence/Factories/PropertyFactory.cs
+++ b/src/Umbraco.Core/Persistence/Factories/PropertyFactory.cs
@@ -99,7 +99,7 @@ namespace Umbraco.Core.Persistence.Factories
decimal val;
if (decimal.TryParse(property.Value.ToString(), out val))
{
- dto.Decimal = val;
+ dto.Decimal = val; // property value should be normalized already
}
}
else if (property.DataTypeDatabaseType == DataTypeDatabaseType.Date && property.Value != null && string.IsNullOrWhiteSpace(property.Value.ToString()) == false)
diff --git a/src/Umbraco.Core/Persistence/Migrations/Initial/BaseDataCreation.cs b/src/Umbraco.Core/Persistence/Migrations/Initial/BaseDataCreation.cs
index 8046de454b..9570024b09 100644
--- a/src/Umbraco.Core/Persistence/Migrations/Initial/BaseDataCreation.cs
+++ b/src/Umbraco.Core/Persistence/Migrations/Initial/BaseDataCreation.cs
@@ -266,9 +266,9 @@ namespace Umbraco.Core.Persistence.Migrations.Initial
//defaults for the member list
_database.Insert("cmsDataTypePreValues", "id", false, new DataTypePreValueDto { Id = -1, Alias = "pageSize", SortOrder = 1, DataTypeNodeId = Constants.System.DefaultMembersListViewDataTypeId, Value = "10" });
- _database.Insert("cmsDataTypePreValues", "id", false, new DataTypePreValueDto { Id = -2, Alias = "orderBy", SortOrder = 2, DataTypeNodeId = Constants.System.DefaultMembersListViewDataTypeId, Value = "Name" });
+ _database.Insert("cmsDataTypePreValues", "id", false, new DataTypePreValueDto { Id = -2, Alias = "orderBy", SortOrder = 2, DataTypeNodeId = Constants.System.DefaultMembersListViewDataTypeId, Value = "username" });
_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}]" });
+ _database.Insert("cmsDataTypePreValues", "id", false, new DataTypePreValueDto { Id = -4, Alias = "includeProperties", SortOrder = 4, DataTypeNodeId = Constants.System.DefaultMembersListViewDataTypeId, Value = "[{\"alias\":\"username\",\"isSystem\":1},{\"alias\":\"email\",\"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}";
@@ -276,10 +276,10 @@ namespace Umbraco.Core.Persistence.Migrations.Initial
//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 = -6, Alias = "orderBy", SortOrder = 2, DataTypeNodeId = Constants.System.DefaultMediaListViewDataTypeId, Value = "updateDate" });
_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}]" });
+ _database.Insert("cmsDataTypePreValues", "id", false, new DataTypePreValueDto { Id = -9, Alias = "includeProperties", SortOrder = 5, DataTypeNodeId = Constants.System.DefaultMediaListViewDataTypeId, Value = "[{\"alias\":\"updateDate\",\"header\":\"Last edited\",\"isSystem\":1},{\"alias\":\"owner\",\"header\":\"Updated by\",\"isSystem\":1}]" });
}
private void CreateUmbracoRelationTypeData()
diff --git a/src/Umbraco.Core/Persistence/Migrations/Initial/DatabaseSchemaCreation.cs b/src/Umbraco.Core/Persistence/Migrations/Initial/DatabaseSchemaCreation.cs
index de9177cf8f..423c847c47 100644
--- a/src/Umbraco.Core/Persistence/Migrations/Initial/DatabaseSchemaCreation.cs
+++ b/src/Umbraco.Core/Persistence/Migrations/Initial/DatabaseSchemaCreation.cs
@@ -64,8 +64,7 @@ namespace Umbraco.Core.Persistence.Migrations.Initial
{23, typeof (PropertyDataDto)},
{24, typeof (RelationTypeDto)},
{25, typeof (RelationDto)},
- {26, typeof (StylesheetDto)},
- {27, typeof (StylesheetPropertyDto)},
+
{28, typeof (TagDto)},
{29, typeof (TagRelationshipDto)},
{31, typeof (UserTypeDto)},
diff --git a/src/Umbraco.Core/Persistence/Migrations/Initial/DatabaseSchemaResult.cs b/src/Umbraco.Core/Persistence/Migrations/Initial/DatabaseSchemaResult.cs
index 9af6d46fbb..47772c5c15 100644
--- a/src/Umbraco.Core/Persistence/Migrations/Initial/DatabaseSchemaResult.cs
+++ b/src/Umbraco.Core/Persistence/Migrations/Initial/DatabaseSchemaResult.cs
@@ -43,8 +43,13 @@ namespace Umbraco.Core.Persistence.Migrations.Initial
///
public SemVersion DetermineInstalledVersionByMigrations(IMigrationEntryService migrationEntryService)
{
- var allMigrations = migrationEntryService.GetAll(GlobalSettings.UmbracoMigrationName);
- var mostrecent = allMigrations.OrderByDescending(x => x.Version).Select(x => x.Version).FirstOrDefault();
+ SemVersion mostrecent = null;
+
+ if (ValidTables.Any(x => x.InvariantEquals("umbracoMigration")))
+ {
+ var allMigrations = migrationEntryService.GetAll(GlobalSettings.UmbracoMigrationName);
+ mostrecent = allMigrations.OrderByDescending(x => x.Version).Select(x => x.Version).FirstOrDefault();
+ }
return mostrecent ?? new SemVersion(new Version(0, 0, 0));
}
@@ -116,13 +121,19 @@ namespace Umbraco.Core.Persistence.Migrations.Initial
//if the error is for umbracoAccess it must be the previous version to 7.3 since that is when it is added
if (Errors.Any(x => x.Item1.Equals("Table") && (x.Item2.InvariantEquals("umbracoAccess"))))
{
- return new Version(7, 2, 5);
+ return new Version(7, 2, 0);
}
//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 new Version(7, 3, 0);
+ }
+
+ //if the error is for umbracoRedirectUrl it must be the previous version to 7.5 since that is when it is added
+ if (Errors.Any(x => x.Item1.Equals("Table") && (x.Item2.InvariantEquals("umbracoRedirectUrl"))))
+ {
+ return new Version(7, 4, 0);
}
return UmbracoVersion.Current;
diff --git a/src/Umbraco.Core/Persistence/Migrations/LocalMigrationContext.cs b/src/Umbraco.Core/Persistence/Migrations/LocalMigrationContext.cs
new file mode 100644
index 0000000000..f6a603c70f
--- /dev/null
+++ b/src/Umbraco.Core/Persistence/Migrations/LocalMigrationContext.cs
@@ -0,0 +1,54 @@
+using System.Linq;
+using System.Text;
+using Umbraco.Core.Logging;
+using Umbraco.Core.Persistence.Migrations.Syntax.Alter;
+using Umbraco.Core.Persistence.Migrations.Syntax.Create;
+using Umbraco.Core.Persistence.Migrations.Syntax.Delete;
+using Umbraco.Core.Persistence.Migrations.Syntax.Execute;
+using Umbraco.Core.Persistence.SqlSyntax;
+
+namespace Umbraco.Core.Persistence.Migrations
+{
+ internal class LocalMigrationContext : MigrationContext
+ {
+ private readonly ISqlSyntaxProvider _sqlSyntax;
+
+ public LocalMigrationContext(DatabaseProviders databaseProvider, Database database, ISqlSyntaxProvider sqlSyntax, ILogger logger)
+ : base(databaseProvider, database, logger)
+ {
+ _sqlSyntax = sqlSyntax;
+ }
+
+ public IExecuteBuilder Execute
+ {
+ get { return new ExecuteBuilder(this, _sqlSyntax); }
+ }
+
+ public IDeleteBuilder Delete
+ {
+ get { return new DeleteBuilder(this, _sqlSyntax); }
+ }
+
+ public IAlterSyntaxBuilder Alter
+ {
+ get { return new AlterSyntaxBuilder(this, _sqlSyntax); }
+ }
+
+ public ICreateBuilder Create
+ {
+ get { return new CreateBuilder(this, _sqlSyntax); }
+ }
+
+ public string GetSql()
+ {
+ var sb = new StringBuilder();
+ foreach (var sql in Expressions.Select(x => x.Process(Database)))
+ {
+ sb.Append(sql);
+ sb.AppendLine();
+ sb.AppendLine("GO");
+ }
+ return sb.ToString();
+ }
+ }
+}
diff --git a/src/Umbraco.Core/Persistence/Migrations/MigrationRunner.cs b/src/Umbraco.Core/Persistence/Migrations/MigrationRunner.cs
index 081865b9a1..51751b07ad 100644
--- a/src/Umbraco.Core/Persistence/Migrations/MigrationRunner.cs
+++ b/src/Umbraco.Core/Persistence/Migrations/MigrationRunner.cs
@@ -63,7 +63,7 @@ namespace Umbraco.Core.Persistence.Migrations
_targetVersion = targetVersion;
_productName = productName;
//ensure this is null if there aren't any
- _migrations = migrations.Length == 0 ? null : migrations;
+ _migrations = migrations == null || migrations.Length == 0 ? null : migrations;
}
///
diff --git a/src/Umbraco.Core/Persistence/Migrations/Syntax/Delete/Index/DeleteIndexBuilder.cs b/src/Umbraco.Core/Persistence/Migrations/Syntax/Delete/Index/DeleteIndexBuilder.cs
index 72250877db..93661c9ece 100644
--- a/src/Umbraco.Core/Persistence/Migrations/Syntax/Delete/Index/DeleteIndexBuilder.cs
+++ b/src/Umbraco.Core/Persistence/Migrations/Syntax/Delete/Index/DeleteIndexBuilder.cs
@@ -1,4 +1,5 @@
-using Umbraco.Core.Persistence.DatabaseModelDefinitions;
+using System;
+using Umbraco.Core.Persistence.DatabaseModelDefinitions;
using Umbraco.Core.Persistence.Migrations.Syntax.Delete.Expressions;
namespace Umbraco.Core.Persistence.Migrations.Syntax.Delete.Index
@@ -17,12 +18,14 @@ namespace Umbraco.Core.Persistence.Migrations.Syntax.Delete.Index
return this;
}
+ [Obsolete("I don't think this would ever be used when dropping an index, see DeleteIndexExpression.ToString")]
public void OnColumn(string columnName)
{
var column = new IndexColumnDefinition { Name = columnName };
Expression.Index.Columns.Add(column);
}
+ [Obsolete("I don't think this would ever be used when dropping an index, see DeleteIndexExpression.ToString")]
public void OnColumns(params string[] columnNames)
{
foreach (string columnName in columnNames)
diff --git a/src/Umbraco.Core/Persistence/Migrations/Syntax/Delete/Index/IDeleteIndexOnColumnSyntax.cs b/src/Umbraco.Core/Persistence/Migrations/Syntax/Delete/Index/IDeleteIndexOnColumnSyntax.cs
index f2f4280f23..fcf5038a86 100644
--- a/src/Umbraco.Core/Persistence/Migrations/Syntax/Delete/Index/IDeleteIndexOnColumnSyntax.cs
+++ b/src/Umbraco.Core/Persistence/Migrations/Syntax/Delete/Index/IDeleteIndexOnColumnSyntax.cs
@@ -1,8 +1,13 @@
-namespace Umbraco.Core.Persistence.Migrations.Syntax.Delete.Index
+using System;
+
+namespace Umbraco.Core.Persistence.Migrations.Syntax.Delete.Index
{
public interface IDeleteIndexOnColumnSyntax : IFluentSyntax
{
+ [Obsolete("I don't think this would ever be used when dropping an index, see DeleteIndexExpression.ToString")]
void OnColumn(string columnName);
+
+ [Obsolete("I don't think this would ever be used when dropping an index, see DeleteIndexExpression.ToString")]
void OnColumns(params string[] columnNames);
}
}
\ No newline at end of file
diff --git a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenFiveZero/AddRedirectUrlTable.cs b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenFiveZero/AddRedirectUrlTable.cs
index 56e423ebaf..508c0f284b 100644
--- a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenFiveZero/AddRedirectUrlTable.cs
+++ b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenFiveZero/AddRedirectUrlTable.cs
@@ -14,24 +14,47 @@ namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSevenFiveZer
public override void Up()
{
- // don't exeucte if the table is already there
- var tables = SqlSyntax.GetTablesInSchema(Context.Database).ToArray();
- if (tables.InvariantContains("umbracoRedirectUrl")) return;
+ // defer, because we are making decisions based upon what's in the database
+ Execute.Code(MigrationCode);
+ }
- Create.Table("umbracoRedirectUrl")
- .WithColumn("id").AsInt32().Identity().PrimaryKey("PK_umbracoRedirectUrl")
- .WithColumn("contentId").AsInt32().NotNullable()
+ private string MigrationCode(Database database)
+ {
+ var umbracoRedirectUrlTableName = "umbracoRedirectUrl";
+
+ var localContext = new LocalMigrationContext(Context.CurrentDatabaseProvider, database, SqlSyntax, Logger);
+
+ var tables = SqlSyntax.GetTablesInSchema(database).ToArray();
+
+ if (tables.InvariantContains(umbracoRedirectUrlTableName))
+ {
+ var columns = SqlSyntax.GetColumnsInSchema(database).ToArray();
+ if (columns.Any(x => x.TableName.InvariantEquals(umbracoRedirectUrlTableName) && x.ColumnName.InvariantEquals("id") && x.DataType == "uniqueidentifier"))
+ return null;
+ localContext.Delete.Table(umbracoRedirectUrlTableName);
+ }
+
+ localContext.Create.Table(umbracoRedirectUrlTableName)
+ .WithColumn("id").AsGuid().NotNullable().PrimaryKey("PK_" + umbracoRedirectUrlTableName)
.WithColumn("createDateUtc").AsDateTime().NotNullable()
- .WithColumn("url").AsString(2048).NotNullable();
+ .WithColumn("url").AsString(2048).NotNullable()
+ .WithColumn("contentKey").AsGuid().NotNullable()
+ .WithColumn("urlHash").AsString(40).NotNullable();
- //Create.PrimaryKey("PK_umbracoRedirectUrl").OnTable("umbracoRedirectUrl").Columns(new[] { "id" });
+ localContext.Create.Index("IX_" + umbracoRedirectUrlTableName).OnTable(umbracoRedirectUrlTableName)
+ .OnColumn("urlHash")
+ .Ascending()
+ .OnColumn("contentKey")
+ .Ascending()
+ .OnColumn("createDateUtc")
+ .Descending()
+ .WithOptions().NonClustered();
- Create.Index("IX_umbracoRedirectUrl").OnTable("umbracoRedirectUrl")
- .OnColumn("url")
- .Ascending()
- .OnColumn("createDateUtc")
- .Ascending()
- .WithOptions().NonClustered();
+ localContext.Create.ForeignKey("FK_" + umbracoRedirectUrlTableName)
+ .FromTable(umbracoRedirectUrlTableName).ForeignColumn("contentKey")
+ .ToTable("umbracoNode").PrimaryColumn("uniqueID");
+
+ return localContext.GetSql();
}
public override void Down()
diff --git a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenFiveZero/RemoveStylesheetDataAndTablesAgain.cs b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenFiveZero/RemoveStylesheetDataAndTablesAgain.cs
new file mode 100644
index 0000000000..96523e25e8
--- /dev/null
+++ b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenFiveZero/RemoveStylesheetDataAndTablesAgain.cs
@@ -0,0 +1,54 @@
+using System;
+using System.Linq;
+using Umbraco.Core.Configuration;
+using Umbraco.Core.Logging;
+using Umbraco.Core.Persistence.SqlSyntax;
+
+namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSevenFiveZero
+{
+ ///
+ /// This is here to re-remove these tables, we dropped them in 7.3 but new installs created them again so we're going to re-drop them
+ ///
+ [Migration("7.5.0", 1, GlobalSettings.UmbracoMigrationName)]
+ public class RemoveStylesheetDataAndTablesAgain : MigrationBase
+ {
+ public RemoveStylesheetDataAndTablesAgain(ISqlSyntaxProvider sqlSyntax, ILogger logger)
+ : base(sqlSyntax, logger)
+ {
+ }
+
+ public override void Up()
+ {
+ // defer, because we are making decisions based upon what's in the database
+ Execute.Code(MigrationCode);
+ }
+
+ private string MigrationCode(Database database)
+ {
+ var localContext = new LocalMigrationContext(Context.CurrentDatabaseProvider, database, SqlSyntax, Logger);
+
+ //Clear all stylesheet data if the tables exist
+ var tables = SqlSyntax.GetTablesInSchema(Context.Database).ToArray();
+ if (tables.InvariantContains("cmsStylesheetProperty"))
+ {
+ localContext.Delete.FromTable("cmsStylesheetProperty").AllRows();
+ localContext.Delete.FromTable("umbracoNode").Row(new { nodeObjectType = new Guid(Constants.ObjectTypes.StylesheetProperty) });
+
+ localContext.Delete.Table("cmsStylesheetProperty");
+ }
+ if (tables.InvariantContains("cmsStylesheet"))
+ {
+ localContext.Delete.FromTable("cmsStylesheet").AllRows();
+ localContext.Delete.FromTable("umbracoNode").Row(new { nodeObjectType = new Guid(Constants.ObjectTypes.Stylesheet) });
+
+ localContext.Delete.Table("cmsStylesheet");
+ }
+
+ return localContext.GetSql();
+ }
+
+ public override void Down()
+ {
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenFiveZero/UpdateUniqueIndexOnCmsPropertyData.cs b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenFiveZero/UpdateUniqueIndexOnCmsPropertyData.cs
new file mode 100644
index 0000000000..f8e6abe42e
--- /dev/null
+++ b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenFiveZero/UpdateUniqueIndexOnCmsPropertyData.cs
@@ -0,0 +1,71 @@
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+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.TargetVersionSevenFiveZero
+{
+ ///
+ /// See: http://issues.umbraco.org/issue/U4-8522
+ ///
+ [Migration("7.5.0", 2, GlobalSettings.UmbracoMigrationName)]
+ public class UpdateUniqueIndexOnCmsPropertyData : MigrationBase
+ {
+ public UpdateUniqueIndexOnCmsPropertyData(ISqlSyntaxProvider sqlSyntax, ILogger logger)
+ : base(sqlSyntax, logger)
+ {
+ }
+
+ public override void Up()
+ {
+ //Clear all stylesheet data if the tables exist
+ //tuple = tablename, indexname, columnname, unique
+ var indexes = SqlSyntax.GetDefinedIndexes(Context.Database).ToArray();
+ var found = indexes.FirstOrDefault(
+ x => x.Item1.InvariantEquals("cmsPropertyData")
+ && x.Item2.InvariantEquals("IX_cmsPropertyData_1")
+ //we're searching for the old index which is not unique
+ && x.Item4 == false);
+
+ if (found != null)
+ {
+ //Check for MySQL
+ if (Context.CurrentDatabaseProvider == DatabaseProviders.MySql)
+ {
+ //Use the special double nested sub query for MySQL since that is the only
+ //way delete sub queries works
+ var delPropQry = SqlSyntax.GetDeleteSubquery(
+ "cmsPropertyData",
+ "id",
+ new Sql("SELECT MIN(id) FROM cmsPropertyData GROUP BY contentNodeId, versionId, propertytypeid HAVING MIN(id) IS NOT NULL"),
+ WhereInType.NotIn);
+ Execute.Sql(delPropQry.SQL);
+ }
+ else
+ {
+ //NOTE: Even though the above will work for MSSQL, we are not going to execute the
+ // nested delete sub query logic since it will be slower and there could be a ton of property
+ // data here so needs to be as fast as possible.
+ Execute.Sql("DELETE FROM cmsPropertyData WHERE id NOT IN (SELECT MIN(id) FROM cmsPropertyData GROUP BY contentNodeId, versionId, propertytypeid HAVING MIN(id) IS NOT NULL)");
+ }
+
+ //we need to re create this index
+ Delete.Index("IX_cmsPropertyData_1").OnTable("cmsPropertyData");
+ Create.Index("IX_cmsPropertyData_1").OnTable("cmsPropertyData")
+ .OnColumn("contentNodeId").Ascending()
+ .OnColumn("versionId").Ascending()
+ .OnColumn("propertytypeid").Ascending()
+ .WithOptions().NonClustered()
+ .WithOptions().Unique();
+ }
+ }
+
+ public override void Down()
+ {
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Umbraco.Core/Persistence/PetaPoco.cs b/src/Umbraco.Core/Persistence/PetaPoco.cs
index 88f90639d1..63278889a2 100644
--- a/src/Umbraco.Core/Persistence/PetaPoco.cs
+++ b/src/Umbraco.Core/Persistence/PetaPoco.cs
@@ -1,10 +1,10 @@
/* PetaPoco v4.0.3 - A Tiny ORMish thing for your POCO's.
* Copyright © 2011 Topten Software. All Rights Reserved.
- *
+ *
* Apache License 2.0 - http://www.toptensoftware.com/petapoco/license
- *
- * Special thanks to Rob Conery (@robconery) for original inspiration (ie:Massive) and for
- * use of Subsonic's T4 templates, Rob Sullivan (@DataChomp) for hard core DBA advice
+ *
+ * Special thanks to Rob Conery (@robconery) for original inspiration (ie:Massive) and for
+ * use of Subsonic's T4 templates, Rob Sullivan (@DataChomp) for hard core DBA advice
* and Adam Schroder (@schotime) for lots of suggestions, improvements and Oracle support
*/
@@ -88,7 +88,7 @@ namespace Umbraco.Core.Persistence
}
// Results from paged request
- public class Page
+ public class Page
{
public long CurrentPage { get; set; }
public long TotalPages { get; set; }
@@ -164,7 +164,7 @@ namespace Umbraco.Core.Persistence
connectionStringName = ConfigurationManager.ConnectionStrings[0].Name;
// Work out connection string and provider name
- var providerName = "System.Data.SqlClient";
+ var providerName = Constants.DatabaseProviders.SqlServer;
if (ConfigurationManager.ConnectionStrings[connectionStringName] != null)
{
if (!string.IsNullOrEmpty(ConfigurationManager.ConnectionStrings[connectionStringName].ProviderName))
@@ -181,7 +181,7 @@ namespace Umbraco.Core.Persistence
CommonConstruct();
}
- enum DBType
+ internal enum DBType
{
SqlServer,
SqlServerCE,
@@ -202,7 +202,7 @@ namespace Umbraco.Core.Persistence
if (_providerName != null)
_factory = DbProviderFactories.GetFactory(_providerName);
-
+
string dbtype = (_factory == null ? _sharedConnection.GetType() : _factory.GetType()).Name;
if (dbtype.StartsWith("MySql")) _dbType = DBType.MySql;
@@ -385,7 +385,7 @@ namespace Umbraco.Core.Persistence
return "SET TRANSACTION ISOLATION LEVEL READ COMMITTED";
}
}
-
+
// Helper to handle named parameters from object properties
static Regex rxParams = new Regex(@"(? args_dest)
@@ -425,8 +425,8 @@ namespace Umbraco.Core.Persistence
}
// Expand collections to parameter lists
- if ((arg_val as System.Collections.IEnumerable) != null &&
- (arg_val as string) == null &&
+ if ((arg_val as System.Collections.IEnumerable) != null &&
+ (arg_val as string) == null &&
(arg_val as byte[]) == null)
{
var sb = new StringBuilder();
@@ -487,10 +487,10 @@ namespace Umbraco.Core.Persistence
}
else if (t == typeof(string))
{
- // out of memory exception occurs if trying to save more than 4000 characters to SQL Server CE NText column.
+ // out of memory exception occurs if trying to save more than 4000 characters to SQL Server CE NText column.
//Set before attempting to set Size, or Size will always max out at 4000
if ((item as string).Length + 1 > 4000 && p.GetType().Name == "SqlCeParameter")
- p.GetType().GetProperty("SqlDbType").SetValue(p, SqlDbType.NText, null);
+ p.GetType().GetProperty("SqlDbType").SetValue(p, SqlDbType.NText, null);
p.Size = (item as string).Length + 1;
if(p.Size < 4000)
@@ -676,12 +676,12 @@ namespace Umbraco.Core.Persistence
public bool ForceDateTimesToUtc { get; set; }
// Return a typed list of pocos
- public List Fetch(string sql, params object[] args)
+ public List Fetch(string sql, params object[] args)
{
return Query(sql, args).ToList();
}
- public List Fetch(Sql sql)
+ public List Fetch(Sql sql)
{
return Fetch(sql.SQL, sql.Arguments);
}
@@ -726,7 +726,43 @@ namespace Umbraco.Core.Persistence
return true;
}
- public void BuildPageQueries(long skip, long take, string sql, ref object[] args, out string sqlCount, out string sqlPage)
+ ///
+ /// NOTE: This is a custom mod of PetaPoco!! This builds the paging sql for different db providers
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ internal virtual void BuildSqlDbSpecificPagingQuery(DBType databaseType, long skip, long take, string sql, string sqlSelectRemoved, string sqlOrderBy, ref object[] args, out string sqlPage)
+ {
+ if (databaseType == DBType.SqlServer || databaseType == DBType.Oracle)
+ {
+ sqlSelectRemoved = rxOrderBy.Replace(sqlSelectRemoved, "");
+ if (rxDistinct.IsMatch(sqlSelectRemoved))
+ {
+ sqlSelectRemoved = "peta_inner.* FROM (SELECT " + sqlSelectRemoved + ") peta_inner";
+ }
+ sqlPage = string.Format("SELECT * FROM (SELECT ROW_NUMBER() OVER ({0}) peta_rn, {1}) peta_paged WHERE peta_rn>@{2} AND peta_rn<=@{3}",
+ sqlOrderBy == null ? "ORDER BY (SELECT NULL)" : sqlOrderBy, sqlSelectRemoved, args.Length, args.Length + 1);
+ args = args.Concat(new object[] { skip, skip + take }).ToArray();
+ }
+ else if (databaseType == DBType.SqlServerCE)
+ {
+ sqlPage = string.Format("{0}\nOFFSET @{1} ROWS FETCH NEXT @{2} ROWS ONLY", sql, args.Length, args.Length + 1);
+ args = args.Concat(new object[] { skip, take }).ToArray();
+ }
+ else
+ {
+ sqlPage = string.Format("{0}\nLIMIT @{1} OFFSET @{2}", sql, args.Length, args.Length + 1);
+ args = args.Concat(new object[] { take, skip }).ToArray();
+ }
+ }
+
+ public void BuildPageQueries(long skip, long take, string sql, ref object[] args, out string sqlCount, out string sqlPage)
{
// Add auto select clause
if (EnableAutoSelect)
@@ -734,38 +770,16 @@ namespace Umbraco.Core.Persistence
// Split the SQL into the bits we need
string sqlSelectRemoved, sqlOrderBy;
- if (!SplitSqlForPaging(sql, out sqlCount, out sqlSelectRemoved, out sqlOrderBy))
+ if (SplitSqlForPaging(sql, out sqlCount, out sqlSelectRemoved, out sqlOrderBy) == false)
throw new Exception("Unable to parse SQL statement for paged query");
if (_dbType == DBType.Oracle && sqlSelectRemoved.StartsWith("*"))
throw new Exception("Query must alias '*' when performing a paged query.\neg. select t.* from table t order by t.id");
-
- // Build the SQL for the actual final result
- if (_dbType == DBType.SqlServer || _dbType == DBType.Oracle)
- {
- sqlSelectRemoved = rxOrderBy.Replace(sqlSelectRemoved, "");
- if (rxDistinct.IsMatch(sqlSelectRemoved))
- {
- sqlSelectRemoved = "peta_inner.* FROM (SELECT " + sqlSelectRemoved + ") peta_inner";
- }
- sqlPage = string.Format("SELECT * FROM (SELECT ROW_NUMBER() OVER ({0}) peta_rn, {1}) peta_paged WHERE peta_rn>@{2} AND peta_rn<=@{3}",
- sqlOrderBy==null ? "ORDER BY (SELECT NULL)" : sqlOrderBy, sqlSelectRemoved, args.Length, args.Length + 1);
- args = args.Concat(new object[] { skip, skip+take }).ToArray();
- }
- else if (_dbType == DBType.SqlServerCE)
- {
- sqlPage = string.Format("{0}\nOFFSET @{1} ROWS FETCH NEXT @{2} ROWS ONLY", sql, args.Length, args.Length + 1);
- args = args.Concat(new object[] { skip, take }).ToArray();
- }
- else
- {
- sqlPage = string.Format("{0}\nLIMIT @{1} OFFSET @{2}", sql, args.Length, args.Length + 1);
- args = args.Concat(new object[] { take, skip }).ToArray();
- }
-
+
+ BuildSqlDbSpecificPagingQuery(_dbType, skip, take, sql, sqlSelectRemoved, sqlOrderBy, ref args, out sqlPage);
}
- // Fetch a page
- public Page Page(long page, long itemsPerPage, string sql, params object[] args)
+ // Fetch a page
+ public Page Page(long page, long itemsPerPage, string sql, params object[] args)
{
string sqlCount, sqlPage;
BuildPageQueries((page-1)*itemsPerPage, itemsPerPage, sql, ref args, out sqlCount, out sqlPage);
@@ -791,7 +805,7 @@ namespace Umbraco.Core.Persistence
return result;
}
- public Page Page(long page, long itemsPerPage, Sql sql)
+ public Page Page(long page, long itemsPerPage, Sql sql)
{
return Page(page, itemsPerPage, sql.SQL, sql.Arguments);
}
@@ -820,7 +834,7 @@ namespace Umbraco.Core.Persistence
}
// Return an enumerable collection of pocos
- public IEnumerable Query(string sql, params object[] args)
+ public IEnumerable Query(string sql, params object[] args)
{
if (EnableAutoSelect)
sql = AddSelectClause(sql);
@@ -1033,7 +1047,7 @@ namespace Umbraco.Core.Persistence
}
private List Delegates { get; set; }
private Delegate GetItem(int index) { return Delegates[index]; }
-
+
///
/// Calls the delegate at the specified index and returns its values
///
@@ -1068,7 +1082,7 @@ namespace Umbraco.Core.Persistence
// Create a multi-poco factory
Func CreateMultiPocoFactory(Type[] types, string sql, IDataReader r)
- {
+ {
// Call each delegate
var dels = new List();
int pos = 0;
@@ -1088,7 +1102,7 @@ namespace Umbraco.Core.Persistence
static Dictionary AutoMappers = new Dictionary();
static System.Threading.ReaderWriterLockSlim RWLock = new System.Threading.ReaderWriterLockSlim();
- // Get (or create) the multi-poco factory for a query
+ // Get (or create) the multi-poco factory for a query
Func GetMultiPocoFactory(Type[] types, string sql, IDataReader r)
{
// Build a key string (this is crap, should address this at some point)
@@ -1113,8 +1127,8 @@ namespace Umbraco.Core.Persistence
if (MultiPocoFactories.TryGetValue(key, out oFactory))
{
//mpFactory = oFactory;
- return (Func)oFactory;
- }
+ return (Func)oFactory;
+ }
}
finally
{
@@ -1130,9 +1144,9 @@ namespace Umbraco.Core.Persistence
if (MultiPocoFactories.TryGetValue(key, out oFactory))
{
return (Func)oFactory;
- }
-
- // Create the factory
+ }
+
+ // Create the factory
var factory = CreateMultiPocoFactory(types, sql, r);
MultiPocoFactories.Add(key, factory);
@@ -1207,54 +1221,54 @@ namespace Umbraco.Core.Persistence
}
}
-
- public IEnumerable Query(Sql sql)
+
+ public IEnumerable Query(Sql sql)
{
return Query(sql.SQL, sql.Arguments);
}
- public bool Exists(object primaryKey)
+ public bool Exists(object primaryKey)
{
return FirstOrDefault(string.Format("WHERE {0}=@0", EscapeSqlIdentifier(PocoData.ForType(typeof(T)).TableInfo.PrimaryKey)), primaryKey) != null;
}
- public T Single(object primaryKey)
+ public T Single(object primaryKey)
{
return Single(string.Format("WHERE {0}=@0", EscapeSqlIdentifier(PocoData.ForType(typeof(T)).TableInfo.PrimaryKey)), primaryKey);
}
- public T SingleOrDefault(object primaryKey)
+ public T SingleOrDefault(object primaryKey)
{
return SingleOrDefault(string.Format("WHERE {0}=@0", EscapeSqlIdentifier(PocoData.ForType(typeof(T)).TableInfo.PrimaryKey)), primaryKey);
}
- public T Single(string sql, params object[] args)
+ public T Single(string sql, params object[] args)
{
return Query(sql, args).Single();
}
- public T SingleOrDefault(string sql, params object[] args)
+ public T SingleOrDefault(string sql, params object[] args)
{
return Query(sql, args).SingleOrDefault();
}
- public T First(string sql, params object[] args)
+ public T First(string sql, params object[] args)
{
return Query(sql, args).First();
}
- public T FirstOrDefault(string sql, params object[] args)
+ public T FirstOrDefault(string sql, params object[] args)
{
return Query(sql, args).FirstOrDefault();
}
- public T Single(Sql sql)
+ public T Single(Sql sql)
{
return Query(sql).Single();
}
- public T SingleOrDefault(Sql sql)
+ public T SingleOrDefault(Sql sql)
{
return Query(sql).SingleOrDefault();
}
- public T First(Sql sql)
+ public T First(Sql sql)
{
return Query(sql).First();
}
- public T FirstOrDefault(Sql sql)
+ public T FirstOrDefault(Sql sql)
{
return Query(sql).FirstOrDefault();
}
@@ -1285,7 +1299,7 @@ namespace Umbraco.Core.Persistence
return Insert(tableName, primaryKeyName, true, poco);
}
- // Insert a poco into a table. If the poco has a property with the same name
+ // Insert a poco into a table. If the poco has a property with the same name
// as the primary key the id of the new record is assigned to it. Either way,
// the new id is returned.
public object Insert(string tableName, string primaryKeyName, bool autoIncrement, object poco)
@@ -1721,7 +1735,7 @@ namespace Umbraco.Core.Persistence
{
cmd.CommandTimeout = CommandTimeout;
}
-
+
// Call hook
OnExecutingCommand(cmd);
@@ -1779,8 +1793,8 @@ namespace Umbraco.Core.Persistence
public class ExpandoColumn : PocoColumn
{
public override void SetValue(object target, object val) { (target as IDictionary)[ColumnName]=val; }
- public override object GetValue(object target)
- {
+ public override object GetValue(object target)
+ {
object val=null;
(target as IDictionary).TryGetValue(ColumnName, out val);
return val;
@@ -1803,7 +1817,7 @@ namespace Umbraco.Core.Persistence
}
static readonly ObjectCache ObjectCache = new MemoryCache("NPoco");
-
+
}
public class PocoData
@@ -1812,7 +1826,7 @@ namespace Umbraco.Core.Persistence
internal static bool UseLongKeys = false;
//USE ONLY FOR TESTING - default is one hr
internal static int SlidingExpirationSeconds = 3600;
-
+
public static PocoData ForObject(object o, string primaryKeyName)
{
var t = o.GetType();
@@ -1836,7 +1850,7 @@ namespace Umbraco.Core.Persistence
#endif
return ForType(t);
}
-
+
public static PocoData ForType(Type t)
{
#if !PETAPOCO_NO_DYNAMIC
@@ -1856,7 +1870,7 @@ namespace Umbraco.Core.Persistence
InnerLock.ExitReadLock();
}
-
+
// Cache it
InnerLock.EnterWriteLock();
try
@@ -1959,7 +1973,7 @@ namespace Umbraco.Core.Persistence
public Delegate GetFactory(string sql, string connString, bool ForceDateTimesToUtc, int firstColumn, int countColumns, IDataReader r)
{
- //TODO: It would be nice to remove the irrelevant SQL parts - for a mapping operation anything after the SELECT clause isn't required.
+ //TODO: It would be nice to remove the irrelevant SQL parts - for a mapping operation anything after the SELECT clause isn't required.
// This would ensure less duplicate entries that get cached, currently both of these queries would be cached even though they are
// returning the same structured data:
// SELECT * FROM MyTable ORDER BY MyColumn
@@ -1981,7 +1995,7 @@ namespace Umbraco.Core.Persistence
combiner.AddInt(countColumns);
key = combiner.GetCombinedHashCode();
}
-
+
var objectCache = _managedCache.GetCache();
@@ -2029,7 +2043,7 @@ namespace Umbraco.Core.Persistence
il.Emit(OpCodes.Brfalse_S, lblNotNull); // obj, obj, fieldname, converter?, value
il.Emit(OpCodes.Pop); // obj, obj, fieldname, converter?
if (converter != null)
- il.Emit(OpCodes.Pop); // obj, obj, fieldname,
+ il.Emit(OpCodes.Pop); // obj, obj, fieldname,
il.Emit(OpCodes.Ldnull); // obj, obj, fieldname, null
if (converter != null)
{
@@ -2169,7 +2183,7 @@ namespace Umbraco.Core.Persistence
// return it
var del = m.CreateDelegate(Expression.GetFuncType(typeof(IDataReader), type));
-
+
return del;
};
@@ -2178,7 +2192,7 @@ namespace Umbraco.Core.Persistence
// the line belows returns existing item or adds the new value if it doesn't exist
var value = (Lazy)objectCache.AddOrGetExisting(key, newValue, new CacheItemPolicy
{
- //sliding expiration of 1 hr, if the same key isn't used in this
+ //sliding expiration of 1 hr, if the same key isn't used in this
// timeframe it will be removed from the cache
SlidingExpiration = new TimeSpan(0, 0, SlidingExpirationSeconds)
});
@@ -2272,7 +2286,7 @@ namespace Umbraco.Core.Persistence
public TableInfo TableInfo { get; private set; }
public Dictionary Columns { get; private set; }
static System.Threading.ReaderWriterLockSlim InnerLock = new System.Threading.ReaderWriterLockSlim();
-
+
///
/// Returns a report of the current cache being utilized by PetaPoco
///
@@ -2286,7 +2300,7 @@ namespace Umbraco.Core.Persistence
foreach (var pocoData in m_PocoDatas)
{
sb.AppendFormat("\t{0}\n", pocoData.Key);
- sb.AppendFormat("\t\tTable:{0} - Col count:{1}\n", pocoData.Value.TableInfo.TableName, pocoData.Value.QueryColumns.Length);
+ sb.AppendFormat("\t\tTable:{0} - Col count:{1}\n", pocoData.Value.TableInfo.TableName, pocoData.Value.QueryColumns.Length);
}
var cache = managedCache.GetCache();
@@ -2299,7 +2313,7 @@ namespace Umbraco.Core.Persistence
totalBytes = Encoding.Unicode.GetByteCount(keys);
sb.AppendFormat("\tTotal byte for keys:{0}\n", totalBytes);
-
+
sb.AppendLine("\tAll Poco cache items:");
foreach (var item in cache)
@@ -2315,8 +2329,8 @@ namespace Umbraco.Core.Persistence
// Member variables
- string _connectionString;
- string _providerName;
+ readonly string _connectionString;
+ readonly string _providerName;
DbProviderFactory _factory;
IDbConnection _sharedConnection;
IDbTransaction _transaction;
@@ -2417,6 +2431,7 @@ namespace Umbraco.Core.Persistence
else
_rhs = sql;
+ _sqlFinal = null;
return this;
}
diff --git a/src/Umbraco.Core/Persistence/PetaPocoExtensions.cs b/src/Umbraco.Core/Persistence/PetaPocoExtensions.cs
index 6b8e7b46ff..a72621d1a5 100644
--- a/src/Umbraco.Core/Persistence/PetaPocoExtensions.cs
+++ b/src/Umbraco.Core/Persistence/PetaPocoExtensions.cs
@@ -203,7 +203,8 @@ namespace Umbraco.Core.Persistence
{
//if it is sql ce or it is a sql server version less than 2008, we need to do individual inserts.
var sqlServerSyntax = SqlSyntaxContext.SqlSyntaxProvider as SqlServerSyntaxProvider;
- if ((sqlServerSyntax != null && (int)sqlServerSyntax.VersionName.Value < (int)SqlServerVersionName.V2008)
+
+ if ((sqlServerSyntax != null && (int)sqlServerSyntax.GetVersionName(db) < (int)SqlServerVersionName.V2008)
|| SqlSyntaxContext.SqlSyntaxProvider is SqlCeSyntaxProvider)
{
//SqlCe doesn't support bulk insert statements!
diff --git a/src/Umbraco.Core/Persistence/PetaPocoSqlExtensions.cs b/src/Umbraco.Core/Persistence/PetaPocoSqlExtensions.cs
index 3cbd70803d..04f4147155 100644
--- a/src/Umbraco.Core/Persistence/PetaPocoSqlExtensions.cs
+++ b/src/Umbraco.Core/Persistence/PetaPocoSqlExtensions.cs
@@ -22,8 +22,7 @@ namespace Umbraco.Core.Persistence
public static Sql From(this Sql sql, ISqlSyntaxProvider sqlSyntax)
{
var type = typeof(T);
- var tableNameAttribute = type.FirstAttribute();
- string tableName = tableNameAttribute == null ? string.Empty : tableNameAttribute.Value;
+ var tableName = type.GetTableName();
return sql.From(sqlSyntax.GetQuotedTableName(tableName));
}
@@ -51,11 +50,10 @@ namespace Umbraco.Core.Persistence
public static Sql OrderBy(this Sql sql, Expression> columnMember, ISqlSyntaxProvider sqlSyntax)
{
var column = ExpressionHelper.FindProperty(columnMember) as PropertyInfo;
- var columnName = column.FirstAttribute().Name;
+ var columnName = column.GetColumnName();
var type = typeof(TColumn);
- var tableNameAttribute = type.FirstAttribute();
- string tableName = tableNameAttribute == null ? string.Empty : tableNameAttribute.Value;
+ var tableName = type.GetTableName();
//need to ensure the order by is in brackets, see: https://github.com/toptensoftware/PetaPoco/issues/177
var syntax = string.Format("({0}.{1})",
@@ -74,13 +72,13 @@ namespace Umbraco.Core.Persistence
public static Sql OrderByDescending(this Sql sql, Expression> columnMember, ISqlSyntaxProvider sqlSyntax)
{
var column = ExpressionHelper.FindProperty(columnMember) as PropertyInfo;
- var columnName = column.FirstAttribute().Name;
+ var columnName = column.GetColumnName();
var type = typeof(TColumn);
- var tableNameAttribute = type.FirstAttribute();
- string tableName = tableNameAttribute == null ? string.Empty : tableNameAttribute.Value;
+ var tableName = type.GetTableName();
- var syntax = string.Format("{0}.{1} DESC",
+ //need to ensure the order by is in brackets, see: https://github.com/toptensoftware/PetaPoco/issues/177
+ var syntax = string.Format("({0}.{1}) DESC",
sqlSyntax.GetQuotedTableName(tableName),
sqlSyntax.GetQuotedColumnName(columnName));
@@ -96,7 +94,7 @@ namespace Umbraco.Core.Persistence
public static Sql GroupBy(this Sql sql, Expression> columnMember, ISqlSyntaxProvider sqlProvider)
{
var column = ExpressionHelper.FindProperty(columnMember) as PropertyInfo;
- var columnName = column.FirstAttribute().Name;
+ var columnName = column.GetColumnName();
return sql.GroupBy(sqlProvider.GetQuotedColumnName(columnName));
}
@@ -110,8 +108,7 @@ namespace Umbraco.Core.Persistence
public static Sql.SqlJoinClause InnerJoin(this Sql sql, ISqlSyntaxProvider sqlSyntax)
{
var type = typeof(T);
- var tableNameAttribute = type.FirstAttribute();
- string tableName = tableNameAttribute == null ? string.Empty : tableNameAttribute.Value;
+ var tableName = type.GetTableName();
return sql.InnerJoin(sqlSyntax.GetQuotedTableName(tableName));
}
@@ -125,8 +122,7 @@ namespace Umbraco.Core.Persistence
public static Sql.SqlJoinClause LeftJoin(this Sql sql, ISqlSyntaxProvider sqlSyntax)
{
var type = typeof(T);
- var tableNameAttribute = type.FirstAttribute();
- string tableName = tableNameAttribute == null ? string.Empty : tableNameAttribute.Value;
+ var tableName = type.GetTableName();
return sql.LeftJoin(sqlSyntax.GetQuotedTableName(tableName));
}
@@ -140,8 +136,7 @@ namespace Umbraco.Core.Persistence
public static Sql.SqlJoinClause LeftOuterJoin(this Sql sql, ISqlSyntaxProvider sqlSyntax)
{
var type = typeof(T);
- var tableNameAttribute = type.FirstAttribute();
- string tableName = tableNameAttribute == null ? string.Empty : tableNameAttribute.Value;
+ var tableName = type.GetTableName();
return sql.LeftOuterJoin(sqlSyntax.GetQuotedTableName(tableName));
}
@@ -155,8 +150,7 @@ namespace Umbraco.Core.Persistence
public static Sql.SqlJoinClause RightJoin(this Sql sql, ISqlSyntaxProvider sqlSyntax)
{
var type = typeof(T);
- var tableNameAttribute = type.FirstAttribute();
- string tableName = tableNameAttribute == null ? string.Empty : tableNameAttribute.Value;
+ var tableName = type.GetTableName();
return sql.RightJoin(sqlSyntax.GetQuotedTableName(tableName));
}
@@ -173,13 +167,14 @@ namespace Umbraco.Core.Persistence
{
var leftType = typeof(TLeft);
var rightType = typeof(TRight);
- var leftTableName = leftType.FirstAttribute().Value;
- var rightTableName = rightType.FirstAttribute().Value;
+ var leftTableName = leftType.GetTableName();
+ var rightTableName = rightType.GetTableName();
- var left = ExpressionHelper.FindProperty(leftMember) as PropertyInfo;
- var right = ExpressionHelper.FindProperty(rightMember) as PropertyInfo;
- var leftColumnName = left.FirstAttribute().Name;
- var rightColumnName = right.FirstAttribute().Name;
+ var leftColumn = ExpressionHelper.FindProperty(leftMember) as PropertyInfo;
+ var rightColumn = ExpressionHelper.FindProperty(rightMember) as PropertyInfo;
+
+ var leftColumnName = leftColumn.GetColumnName();
+ var rightColumnName = rightColumn.GetColumnName();
string onClause = string.Format("{0}.{1} = {2}.{3}",
sqlSyntax.GetQuotedTableName(leftTableName),
@@ -193,5 +188,20 @@ namespace Umbraco.Core.Persistence
{
return sql.Append(new Sql("ORDER BY " + String.Join(", ", (from x in columns select x + " DESC").ToArray())));
}
+
+ private static string GetTableName(this Type type)
+ {
+ // todo: returning string.Empty for now
+ // BUT the code bits that calls this method cannot deal with string.Empty so we
+ // should either throw, or fix these code bits...
+ var attr = type.FirstAttribute();
+ return attr == null || string.IsNullOrWhiteSpace(attr.Value) ? string.Empty : attr.Value;
+ }
+
+ private static string GetColumnName(this PropertyInfo column)
+ {
+ var attr = column.FirstAttribute();
+ return attr == null || string.IsNullOrWhiteSpace(attr.Name) ? column.Name : attr.Name;
+ }
}
}
\ No newline at end of file
diff --git a/src/Umbraco.Core/Persistence/Querying/BaseExpressionHelper.cs b/src/Umbraco.Core/Persistence/Querying/BaseExpressionHelper.cs
index 4679b9b2ad..0960acc9e5 100644
--- a/src/Umbraco.Core/Persistence/Querying/BaseExpressionHelper.cs
+++ b/src/Umbraco.Core/Persistence/Querying/BaseExpressionHelper.cs
@@ -440,6 +440,55 @@ namespace Umbraco.Core.Persistence.Querying
}
return HandleStringComparison(visitedObjectForMethod, compareValue, m.Method.Name, colType);
+
+ case "Replace":
+ string searchValue;
+
+ if (methodArgs[0].NodeType != ExpressionType.Constant)
+ {
+ //This occurs when we are getting a value from a non constant such as: x => x.Path.StartsWith(content.Path)
+ // So we'll go get the value:
+ var member = Expression.Convert(methodArgs[0], typeof(object));
+ var lambda = Expression.Lambda>(member);
+ var getter = lambda.Compile();
+ searchValue = getter().ToString();
+ }
+ else
+ {
+ searchValue = methodArgs[0].ToString();
+ }
+
+ if (methodArgs[0].Type != typeof(string) && TypeHelper.IsTypeAssignableFrom(methodArgs[0].Type))
+ {
+ throw new NotSupportedException("An array Contains method is not supported");
+ }
+
+ string replaceValue;
+
+ if (methodArgs[1].NodeType != ExpressionType.Constant)
+ {
+ //This occurs when we are getting a value from a non constant such as: x => x.Path.StartsWith(content.Path)
+ // So we'll go get the value:
+ var member = Expression.Convert(methodArgs[1], typeof(object));
+ var lambda = Expression.Lambda>(member);
+ var getter = lambda.Compile();
+ replaceValue = getter().ToString();
+ }
+ else
+ {
+ replaceValue = methodArgs[1].ToString();
+ }
+
+ if (methodArgs[1].Type != typeof(string) && TypeHelper.IsTypeAssignableFrom(methodArgs[1].Type))
+ {
+ throw new NotSupportedException("An array Contains method is not supported");
+ }
+
+ SqlParameters.Add(RemoveQuote(searchValue));
+
+ SqlParameters.Add(RemoveQuote(replaceValue));
+
+ return string.Format("replace({0}, @{1}, @{2})", visitedObjectForMethod, SqlParameters.Count - 2, SqlParameters.Count - 1);
//case "Substring":
// var startIndex = Int32.Parse(args[0].ToString()) + 1;
// if (args.Count == 2)
@@ -501,7 +550,7 @@ namespace Umbraco.Core.Persistence.Querying
//case "As":
// return string.Format("{0} As {1}", r,
// GetQuotedColumnName(RemoveQuoteFromAlias(RemoveQuote(args[0].ToString()))));
-
+
default:
throw new ArgumentOutOfRangeException("No logic supported for " + m.Method.Name);
diff --git a/src/Umbraco.Core/Persistence/Repositories/ContentRepository.cs b/src/Umbraco.Core/Persistence/Repositories/ContentRepository.cs
index 8d4ebecc53..9222769247 100644
--- a/src/Umbraco.Core/Persistence/Repositories/ContentRepository.cs
+++ b/src/Umbraco.Core/Persistence/Repositories/ContentRepository.cs
@@ -6,6 +6,7 @@ using System.Linq;
using System.Linq.Expressions;
using System.Net.Http.Headers;
using System.Text;
+using System.Xml;
using System.Xml.Linq;
using Umbraco.Core.Configuration;
using Umbraco.Core.Dynamics;
@@ -63,9 +64,9 @@ namespace Umbraco.Core.Persistence.Repositories
var sql = GetBaseQuery(false)
.Where(GetBaseWhereClause(), new { Id = id })
.Where(x => x.Newest)
- .OrderByDescending(x => x.VersionDate);
+ .OrderByDescending(x => x.VersionDate, SqlSyntax);
- var dto = Database.Fetch(sql).FirstOrDefault();
+ var dto = Database.Fetch(SqlSyntax.SelectTop(sql, 1)).FirstOrDefault();
if (dto == null)
return null;
@@ -143,7 +144,7 @@ namespace Umbraco.Core.Persistence.Repositories
{
var list = new List
{
- "DELETE FROM umbracoRedirectUrl WHERE contentId = @Id",
+ "DELETE FROM umbracoRedirectUrl WHERE contentKey IN (SELECT uniqueID FROM umbracoNode WHERE id = @Id)",
"DELETE FROM cmsTask WHERE nodeId = @Id",
"DELETE FROM umbracoUser2NodeNotify WHERE nodeId = @Id",
"DELETE FROM umbracoUser2NodePermission WHERE nodeId = @Id",
@@ -688,6 +689,77 @@ namespace Umbraco.Core.Persistence.Repositories
}
}
+
+ ///
+ /// This builds the Xml document used for the XML cache
+ ///
+ ///
+ public XmlDocument BuildXmlCache()
+ {
+ //TODO: This is what we should do , but converting to use XDocument would be breaking unless we convert
+ // to XmlDocument at the end of this, but again, this would be bad for memory... though still not nearly as
+ // bad as what is happening before!
+ // We'll keep using XmlDocument for now though, but XDocument xml generation is much faster:
+ // https://blogs.msdn.microsoft.com/codejunkie/2008/10/08/xmldocument-vs-xelement-performance/
+ // I think we already have code in here to convert XDocument to XmlDocument but in case we don't here
+ // it is: https://blogs.msdn.microsoft.com/marcelolr/2009/03/13/fast-way-to-convert-xmldocument-into-xdocument/
+
+ //// Prepare an XmlDocument with an appropriate inline DTD to match
+ //// the expected content
+ //var parent = new XElement("root", new XAttribute("id", "-1"));
+ //var xmlDoc = new XDocument(
+ // new XDocumentType("root", null, null, DocumentType.GenerateDtd()),
+ // parent);
+
+ var xmlDoc = new XmlDocument();
+ var doctype = xmlDoc.CreateDocumentType("root", null, null,
+ ApplicationContext.Current.Services.ContentTypeService.GetContentTypesDtd());
+ xmlDoc.AppendChild(doctype);
+ var parent = xmlDoc.CreateElement("root");
+ var pIdAtt = xmlDoc.CreateAttribute("id");
+ pIdAtt.Value = "-1";
+ parent.Attributes.Append(pIdAtt);
+ xmlDoc.AppendChild(parent);
+
+ const string sql = @"select umbracoNode.id, umbracoNode.parentID, umbracoNode.sortOrder, cmsContentXml.xml, umbracoNode.level from umbracoNode
+inner join cmsContentXml on cmsContentXml.nodeId = umbracoNode.id and umbracoNode.nodeObjectType = @type
+where umbracoNode.id in (select cmsDocument.nodeId from cmsDocument where cmsDocument.published = 1)
+order by umbracoNode.level, umbracoNode.parentID, umbracoNode.sortOrder";
+
+ XmlElement last = null;
+
+ //NOTE: Query creates a reader - does not load all into memory
+ foreach (var row in Database.Query(sql, new { type = new Guid(Constants.ObjectTypes.Document) }))
+ {
+ string parentId = ((int)row.parentID).ToInvariantString();
+ string xml = row.xml;
+ int sortOrder = row.sortOrder;
+
+ //if the parentid is changing
+ if (last != null && last.GetAttribute("parentID") != parentId)
+ {
+ parent = xmlDoc.GetElementById(parentId);
+ if (parent == null)
+ {
+ //Need to short circuit here, if the parent is not there it means that the parent is unpublished
+ // and therefore the child is not published either so cannot be included in the xml cache
+ continue;
+ }
+ }
+
+ var xmlDocFragment = xmlDoc.CreateDocumentFragment();
+ xmlDocFragment.InnerXml = xml;
+
+ last = (XmlElement)parent.AppendChild(xmlDocFragment);
+
+ // fix sortOrder - see notes in UpdateSortOrder
+ last.Attributes["sortOrder"].Value = sortOrder.ToInvariantString();
+ }
+
+ return xmlDoc;
+
+ }
+
public int CountPublished()
{
var sql = GetBaseQuery(true).Where(x => x.Trashed == false)
diff --git a/src/Umbraco.Core/Persistence/Repositories/ContentTypeBaseRepository.cs b/src/Umbraco.Core/Persistence/Repositories/ContentTypeBaseRepository.cs
index dc85ad3a33..d86f77168e 100644
--- a/src/Umbraco.Core/Persistence/Repositories/ContentTypeBaseRepository.cs
+++ b/src/Umbraco.Core/Persistence/Repositories/ContentTypeBaseRepository.cs
@@ -251,8 +251,8 @@ AND umbracoNode.id <> @id",
var nodeDto = dto.NodeDto;
Database.Update(nodeDto);
+ // look up ContentType entry to get PrimaryKey for updating the DTO
// 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 });
dto.PrimaryKey = dtoPk.PrimaryKey;
Database.Update(dto);
@@ -262,31 +262,30 @@ AND umbracoNode.id <> @id",
foreach (var composition in entity.ContentTypeComposition)
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
- //2. Find all PropertyTypes on the ContentType that was removed - tracked id (key)
- //3. Remove properties based on property types from the removed content type where the content ids correspond to those found in step one
+ // removing a ContentType from a composition (U4-1690)
+ // 1. Find content based on the current ContentType: entity.Id
+ // 2. Find all PropertyTypes on the ContentType that was removed - tracked id (key)
+ // 3. Remove properties based on property types from the removed content type where the content ids correspond to those found in step one
var compositionBase = entity as ContentTypeCompositionBase;
if (compositionBase != null && compositionBase.RemovedContentTypeKeyTracker != null &&
compositionBase.RemovedContentTypeKeyTracker.Any())
{
- //Find Content based on the current ContentType
+ // find Content based on the current ContentType
var sql = new Sql();
sql.Select("*")
- .From()
- .InnerJoin()
- .On(left => left.NodeId, right => right.NodeId)
+ .From(SqlSyntax)
+ .InnerJoin(SqlSyntax).On(SqlSyntax, left => left.NodeId, right => right.NodeId)
.Where(x => x.NodeObjectType == new Guid(Constants.ObjectTypes.Document))
.Where(x => x.ContentTypeId == entity.Id);
-
var contentDtos = Database.Fetch(sql);
- //Loop through all tracked keys, which corresponds to the ContentTypes that has been removed from the composition
+
+ // loop through all tracked keys, which corresponds to the ContentTypes that has been removed from the composition
foreach (var key in compositionBase.RemovedContentTypeKeyTracker)
{
- //Find PropertyTypes for the removed ContentType
+ // find PropertyTypes for the removed ContentType
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.
+ // 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)
{
foreach (var propertyType in propertyTypes)
@@ -294,51 +293,47 @@ AND umbracoNode.id <> @id",
var nodeId = contentDto.NodeId;
var propertyTypeId = propertyType.Id;
var propertySql = new Sql().Select("cmsPropertyData.id")
- .From()
- .InnerJoin()
- .On(
- left => left.PropertyTypeId, right => right.Id)
- .Where(x => x.NodeId == nodeId)
- .Where(x => x.Id == propertyTypeId);
+ .From(SqlSyntax)
+ .InnerJoin(SqlSyntax).On(SqlSyntax, left => left.PropertyTypeId, right => right.Id)
+ .Where(x => x.NodeId == nodeId)
+ .Where(x => x.Id == propertyTypeId);
- //Finally delete the properties that match our criteria for removing a ContentType from the composition
+ // finally delete the properties that match our criteria for removing a ContentType from the composition
Database.Delete(new Sql("WHERE id IN (" + propertySql.SQL + ")", propertySql.Arguments));
}
}
}
}
- //Delete the allowed content type entries before adding the updated collection
- Database.Delete("WHERE Id = @Id", new { Id = entity.Id });
- //Insert collection of allowed content types
+ // delete the allowed content type entries before re-inserting the collectino of allowed content types
+ Database.Delete