Merge remote-tracking branch 'umbraco/dev-v7' into dev-v7

# Conflicts:
#	src/Umbraco.Web.UI/umbraco/config/lang/fr.xml
This commit is contained in:
michael
2016-09-10 01:45:47 +02:00
451 changed files with 11102 additions and 5880 deletions
+1
View File
@@ -140,3 +140,4 @@ apidocs/api/*
build/docs.zip
build/ui-docs.zip
build/csharp-docs.zip
build/msbuild.log
+4 -2
View File
@@ -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 ##
+23 -17
View File
@@ -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:
+48 -11
View File
@@ -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
+44 -13
View File
@@ -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
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"
+7 -5
View File
@@ -27,15 +27,17 @@
<dependency id="SharpZipLib" version="[0.86.0, 1.0.0)" />
<dependency id="MySql.Data" version="[6.9.8, 7.0.0)" />
<dependency id="xmlrpcnet" version="[2.5.0, 3.0.0)" />
<dependency id="ClientDependency" version="[1.8.4, 2.0.0)" />
<dependency id="ClientDependency" version="[1.9.1, 2.0.0)" />
<dependency id="ClientDependency-Mvc5" version="[1.8.0, 2.0.0)" />
<!-- AutoMapper can not be updated due to: https://github.com/AutoMapper/AutoMapper/issues/373#issuecomment-127644405 -->
<dependency id="AutoMapper" version="[3.0.0, 3.1.0)" />
<dependency id="Newtonsoft.Json" version="[6.0.8, 9.0.0)" />
<dependency id="Examine" version="[0.1.69-beta, 1.0.0)" />
<dependency id="ImageProcessor" version="[2.3.3, 3.0.0)" />
<dependency id="ImageProcessor.Web" version="[4.5.3, 5.0.0)" />
<dependency id="Newtonsoft.Json" version="[6.0.8, 10.0.0)" />
<dependency id="Examine" version="[0.1.69, 1.0.0)" />
<dependency id="ImageProcessor" version="[2.4.4, 3.0.0)" />
<dependency id="ImageProcessor.Web" version="[4.6.4, 5.0.0)" />
<dependency id="semver" version="[1.1.2, 2.0.0)" />
<dependency id="UrlRewritingNet" version="[2.0.7, 3.0.0)" />
<!-- Markdown can not be updated due to: https://github.com/hey-red/markdownsharp/issues/71#issuecomment-233585487 -->
<dependency id="Markdown" version="[1.14.4, 2.0.0)" />
</dependencies>
</metadata>
+4 -3
View File
@@ -16,8 +16,8 @@
<tags>umbraco</tags>
<dependencies>
<dependency id="UmbracoCms.Core" version="[$version$]" />
<dependency id="Newtonsoft.Json" version="[6.0.8, 9.0.0)" />
<dependency id="Umbraco.ModelsBuilder" version="[3.0.2, 4.0.0)" />
<dependency id="Newtonsoft.Json" version="[6.0.8, 10.0.0)" />
<dependency id="Umbraco.ModelsBuilder" version="[3.0.4, 4.0.0)" />
</dependencies>
</metadata>
<files>
@@ -32,6 +32,7 @@
<file src="..\_BuildOutput\WebApp\config\splashes\**" target="UmbracoFiles\Config\splashes" />
<file src="..\_BuildOutput\WebApp\umbraco\**" target="UmbracoFiles\umbraco" />
<file src="..\_BuildOutput\WebApp\umbraco_client\**" target="UmbracoFiles\umbraco_client" />
<file src="..\_BuildOutput\WebApp\Media\Web.config" target="Content\Media\Web.config" />
<file src="tools\install.ps1" target="tools\install.ps1" />
<file src="tools\Readme.txt" target="tools\Readme.txt" />
<file src="tools\ReadmeUpgrade.txt" target="tools\ReadmeUpgrade.txt" />
@@ -44,4 +45,4 @@
<file src="tools\Views.Web.config.install.xdt" target="Views\Web.config.install.xdt" />
<file src="build\**" target="build" />
</files>
</package>
</package>
@@ -21,6 +21,12 @@
</section>
<section alias="StartupDeveloperDashboardSection" xdt:Locator="Match(alias)" xdt:Transform="InsertIfMissing">
<areas xdt:Transform="InsertIfMissing">
<area xdt:Transform="InsertIfMissing">developer</area>
</areas>
</section>
<section alias="StartupDeveloperDashboardSection">
<tab caption="Get Started" xdt:Locator="Match(caption)" xdt:Transform="Replace">
<control showOnce="true" addPanel="true" panelCaption="">
views/dashboard/developer/developerdashboardvideos.html
@@ -30,16 +36,21 @@
</section>
<section alias="StartupDeveloperDashboardSection" xdt:Locator="Match(alias)" xdt:Transform="InsertIfMissing">
<tab caption="Examine Management" xdt:Locator="Match(caption)" xdt:Transform="InsertIfMissing">
<tab caption="Examine Management" xdt:Locator="Match(caption)" xdt:Transform="InsertIfMissing">
<control>
views/dashboard/developer/examinemanagement.html
</control>
</tab>
<tab caption="Xml Data Integrity Report" xdt:Transform="InsertIfMissing">
</tab>
<tab caption="Health Check" xdt:Transform="InsertIfMissing" xdt:Locator="Match(caption)">
<control>
views/dashboard/developer/xmldataintegrityreport.html
views/dashboard/developer/healthcheck.html
</control>
</tab>
</tab>
<tab caption="Redirect URL Management" xdt:Transform="InsertIfMissing" xdt:Locator="Match(caption)">
<control>
views/dashboard/developer/redirecturls.html
</control>
</tab>
</section>
<section alias="StartupMediaDashboardSection" xdt:Locator="Match(alias)" xdt:Transform="InsertIfMissing">
@@ -52,19 +63,6 @@
</tab>
</section>
<section alias="StartupDashboardSection" xdt:Locator="Match(alias)" xdt:Transform="InsertIfMissing">
<tab caption="Get Started" xdt:Locator="Match(caption)" xdt:Transform="Remove" />
<tab caption="Get Started" xdt:Transform="Insert">
<access>
<grant>admin</grant>
</access>
<control showOnce="true" addPanel="true" panelCaption="">
views/dashboard/default/startupdashboardintro.html
</control>
</tab>
<tab caption="Last Edits" xdt:Locator="Match(caption)" xdt:Transform="Remove" />
</section>
<section alias="StartupMemberDashboardSection" xdt:Locator="Match(alias)" xdt:Transform="InsertIfMissing">
<tab caption="Get Started" xdt:Locator="Match(caption)" xdt:Transform="Remove" />
<tab caption="Get Started" xdt:Transform="Insert">
@@ -80,5 +78,6 @@
<section alias="StartupDashboardSection">
<tab caption="Change Password" xdt:Locator="Match(caption)" xdt:Transform="Remove" />
<tab caption="Last Edits" xdt:Locator="Match(caption)" xdt:Transform="Remove" />
</section>
</dashBoard>
@@ -292,6 +292,8 @@
<mimeMap fileExtension=".svg" mimeType="image/svg+xml" xdt:Locator="Match(fileExtension)" xdt:Transform="InsertIfMissing" />
<remove fileExtension=".woff" xdt:Locator="Match(fileExtension)" xdt:Transform="InsertIfMissing" />
<mimeMap fileExtension=".woff" mimeType="application/x-font-woff" xdt:Locator="Match(fileExtension)" xdt:Transform="InsertIfMissing" />
<remove fileExtension=".woff2" xdt:Locator="Match(fileExtension)" xdt:Transform="InsertIfMissing" />
<mimeMap fileExtension=".woff2" mimeType="application/x-font-woff2" xdt:Locator="Match(fileExtension)" xdt:Transform="InsertIfMissing" />
<remove fileExtension=".less" xdt:Locator="Match(fileExtension)" xdt:Transform="InsertIfMissing" />
<mimeMap fileExtension=".less" mimeType="text/css" xdt:Locator="Match(fileExtension)" xdt:Transform="InsertIfMissing" />
</staticContent>
+6 -1
View File
@@ -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) {
+17 -9
View File
@@ -55,21 +55,29 @@
xdt:Transform="SetAttributes()" />
<!--Developer-->
<add alias="datatype" application="developer"
<add alias="packager" application="developer"
xdt:Locator="Match(application,alias)"
xdt:Transform="Remove" />
<add initialize="true" sortOrder="0" alias="datatypes" application="developer" title="Data Types" iconClosed="icon-folder" iconOpen="icon-folder" type="Umbraco.Web.Trees.DataTypeTreeController, umbraco"
<add alias="packagerPackages" application="developer"
xdt:Locator="Match(application,alias)"
xdt:Transform="InsertIfMissing" />
xdt:Transform="Remove" />
<add initialize="true" sortOrder="0" alias="packager" application="developer" iconClosed="icon-folder" iconOpen="icon-folder-open" type="Umbraco.Web.Trees.PackagesTreeController, umbraco"
xdt:Locator="Match(application,alias)"
xdt:Transform="InsertIfMissing" />
<!-- Before 7.4 this tree had the alias 'dataType', without the 's' on the end, this is here to rename it -->
<add sortOrder="1" alias="dataTypes" application="developer" type="Umbraco.Web.Trees.DataTypeTreeController, umbraco"
xdt:Locator="Match(application,type)"
xdt:Transform="SetAttributes(alias,sortOrder)" />
<!-- Yes, set the sortOrder again, like above because.. sometimes apparently we already have a dataTypes node and we can't remove more than one.. if they're equal though (same alias,application and sortOrder) it doesn't throw an error -->
<add sortOrder="1" alias="dataTypes" application="developer"
xdt:Locator="Match(application,alias)"
xdt:Transform="SetAttributes(sortOrder)" />
<add application="developer" alias="macros" title="Macros" type="umbraco.loadMacros, umbraco" iconClosed="icon-folder" iconOpen="icon-folder" sortOrder="2"
xdt:Locator="Match(application,alias)"
xdt:Transform="SetAttributes()" />
<add application="developer" alias="packager" title="Packages" type="umbraco.loadPackager, umbraco" iconClosed="icon-folder" iconOpen="icon-folder" sortOrder="3"
xdt:Locator="Match(application,alias)"
xdt:Transform="SetAttributes()" />
<add application="developer" alias="packagerPackages" title="Packager Packages" type="umbraco.loadPackages, umbraco" iconClosed="icon-folder" iconOpen="icon-folder" initialize="false" sortOrder="3"
xdt:Locator="Match(application,alias)"
xdt:Transform="SetAttributes()" />
<add application="developer" alias="relationTypes" title="Relation Types" type="umbraco.loadRelationTypes, umbraco" iconClosed="icon-folder" iconOpen="icon-folder" sortOrder="4"
xdt:Locator="Match(application,alias)"
xdt:Transform="SetAttributes()" />
+1 -2
View File
@@ -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
7.5.3
+4 -9
View File
@@ -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();
}
}
}
+2 -2
View File
@@ -11,5 +11,5 @@ using System.Resources;
[assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyFileVersion("7.5.0")]
[assembly: AssemblyInformationalVersion("7.5.0-beta")]
[assembly: AssemblyFileVersion("7.5.3")]
[assembly: AssemblyInformationalVersion("7.5.3")]
@@ -9,6 +9,9 @@ namespace Umbraco.Core.Cache
/// <summary>
/// A cache provider that caches items in the HttpContext.Items
/// </summary>
/// <remarks>
/// If the Items collection is null, then this provider has no effect
/// </remarks>
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<DictionaryEntry> GetDictionaryEntries()
{
const string prefix = CacheItemPrefix + "-";
if (HasContextItems == false) return Enumerable.Empty<DictionaryEntry>();
return ContextItems.Cast<DictionaryEntry>()
.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<object> getCacheItem)
{
//no place to cache so just return the callback result
if (HasContextItems == false) return getCacheItem();
cacheKey = GetCacheKey(cacheKey);
Lazy<object> result;
@@ -128,5 +146,10 @@ namespace Umbraco.Core.Cache
#region Insert
#endregion
private class NoopLocker : DisposableObject
{
protected override void DisposeResources()
{ }
}
}
}
+12 -21
View File
@@ -5,38 +5,31 @@ using System.Web.Caching;
namespace Umbraco.Core.Cache
{
internal class NullCacheProvider : IRuntimeCacheProvider
/// <summary>
/// Represents a cache provider that does not cache anything.
/// </summary>
public class NullCacheProvider : IRuntimeCacheProvider
{
public virtual void ClearAllCache()
{
}
{ }
public virtual void ClearCacheItem(string key)
{
}
{ }
public virtual void ClearCacheObjectTypes(string typeName)
{
}
{ }
public virtual void ClearCacheObjectTypes<T>()
{
}
{ }
public virtual void ClearCacheObjectTypes<T>(Func<string, T, bool> predicate)
{
}
{ }
public virtual void ClearCacheByKeySearch(string keyStartsWith)
{
}
{ }
public virtual void ClearCacheByKeyExpression(string regexString)
{
}
{ }
public virtual IEnumerable<object> GetCacheItemsByKeySearch(string keyStartsWith)
{
@@ -64,8 +57,6 @@ namespace Umbraco.Core.Cache
}
public void InsertCacheItem(string cacheKey, Func<object> getCacheItem, TimeSpan? timeout = null, bool isSliding = false, CacheItemPriority priority = CacheItemPriority.Normal, CacheItemRemovedCallback removedCallback = null, string[] dependentFiles = null)
{
}
{ }
}
}
@@ -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
{
/// <summary>
/// Represents a cache provider that caches item in a <see cref="MemoryCache"/>.
/// A cache provider that wraps the logic of a System.Runtime.Caching.ObjectCache
/// </summary>
internal class ObjectCacheRuntimeCacheProvider : IRuntimeCacheProvider
/// <remarks>The <see cref="MemoryCache"/> 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.</remarks>
public class ObjectCacheRuntimeCacheProvider : IRuntimeCacheProvider
{
private readonly ReaderWriterLockSlim _locker = new ReaderWriterLockSlim(LockRecursionPolicy.SupportsRecursion);
internal ObjectCache MemoryCache;
@@ -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
{
/// <summary>
/// A cache provider that statically caches everything in an in memory dictionary
/// Represents a cache provider that statically caches item in a concurrent dictionary.
/// </summary>
internal class StaticCacheProvider : ICacheProvider
public class StaticCacheProvider : ICacheProvider
{
internal readonly ConcurrentDictionary<string, object> StaticCache = new ConcurrentDictionary<string, object>();
@@ -75,7 +74,6 @@ namespace Umbraco.Core.Cache
public virtual object GetCacheItem(string cacheKey, Func<object> getCacheItem)
{
return StaticCache.GetOrAdd(cacheKey, key => getCacheItem());
}
}
}
}
@@ -291,7 +291,19 @@ namespace Umbraco.Core.Configuration.UmbracoSettings
true);
}
}
[ConfigurationProperty("EnableInheritedMediaTypes")]
internal InnerTextConfigurationElement<bool> EnableInheritedMediaTypes
{
get
{
return new OptionalInnerTextConfigurationElement<bool>(
(InnerTextConfigurationElement<bool>)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; }
}
}
}
@@ -61,5 +61,7 @@ namespace Umbraco.Core.Configuration.UmbracoSettings
string DefaultDocumentTypeProperty { get; }
bool EnableInheritedDocumentTypes { get; }
bool EnableInheritedMediaTypes { get; }
}
}
@@ -9,6 +9,8 @@
bool DisableAlternativeTemplates { get; }
bool DisableFindContentByIdPath { get; }
bool DisableRedirectUrlTracking { get; }
string UrlProviderMode { get; }
@@ -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
{
@@ -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");
/// <summary>
/// Gets the current version of Umbraco.
@@ -24,7 +24,7 @@ namespace Umbraco.Core.Configuration
/// Gets the version comment (like beta or RC).
/// </summary>
/// <value>The version comment.</value>
public static string CurrentComment { get { return "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
+20 -7
View File
@@ -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
{
/// <summary>
/// The alias of the internal member indexer
/// </summary>
public const string InternalMemberIndexer = "InternalMemberIndexer";
/// <summary>
/// The alias of the internal content indexer
/// </summary>
public const string InternalIndexer = "InternalIndexer";
/// <summary>
/// The alias of the external content indexer
/// </summary>
public const string ExternalIndexer = "ExternalIndexer";
/// <summary>
/// The alias of the internal member searcher
/// </summary>
@@ -19,6 +27,11 @@ namespace Umbraco.Core
/// The alias of the internal content searcher
/// </summary>
public const string InternalSearcher = "InternalSearcher";
/// <summary>
/// The alias of the external content searcher
/// </summary>
public const string ExternalSearcher = "ExternalSearcher";
}
}
}
+30 -23
View File
@@ -1,33 +1,40 @@
namespace Umbraco.Core
{
public static partial class Constants
{
/// <summary>
/// Defines the identifiers for Umbraco system nodes.
/// </summary>
public static class System
{
/// <summary>
/// The integer identifier for global system root node.
/// </summary>
public const int Root = -1;
public static partial class Constants
{
/// <summary>
/// Defines the identifiers for Umbraco system nodes.
/// </summary>
public static class System
{
/// <summary>
/// The integer identifier for global system root node.
/// </summary>
public const int Root = -1;
/// <summary>
/// The integer identifier for content's recycle bin.
/// </summary>
public const int RecycleBinContent = -20;
/// <summary>
/// The integer identifier for content's recycle bin.
/// </summary>
public const int RecycleBinContent = -20;
/// <summary>
/// The integer identifier for media's recycle bin.
/// </summary>
public const int RecycleBinMedia = -21;
/// <summary>
/// The integer identifier for media's recycle bin.
/// </summary>
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";
}
}
}
+20 -70
View File
@@ -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
/// <param name="databaseName">Name of the database</param>
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);
}
/// <summary>
/// Set the lazy resolution of determining the SQL server version if that is the db type we're using
/// </summary>
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<SqlServerVersionName>(() =>
{
try
{
var database = this._factory.CreateDatabase();
var version = database.ExecuteScalar<string>("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<Result> 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)
{
+23
View File
@@ -0,0 +1,23 @@
namespace Umbraco.Core
{
/// <summary>
/// Provides extension methods for System.Decimal.
/// </summary>
/// <remarks>See System.Decimal on MSDN and also
/// http://stackoverflow.com/questions/4298719/parse-decimal-and-filter-extra-0-on-the-right/4298787#4298787.
/// </remarks>
public static class DecimalExtensions
{
/// <summary>
/// Gets the normalized value.
/// </summary>
/// <param name="value">The value to normalize.</param>
/// <returns>The normalized value.</returns>
/// <remarks>Normalizing changes the scaling factor and removes trailing zeroes,
/// so 1.2500m comes out as 1.25m.</remarks>
public static decimal Normalize(this decimal value)
{
return value / 1.000000000000000000000000000000000m;
}
}
}
@@ -0,0 +1,46 @@
namespace Umbraco.Core.Logging
{
using System;
using System.Runtime.CompilerServices;
using ImageProcessor.Common.Exceptions;
/// <summary>
/// A logger for explicitly logging ImageProcessor exceptions.
/// <remarks>
/// 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.
/// </remarks>
/// </summary>
public sealed class ImageProcessorLogger : ImageProcessor.Common.Exceptions.ILogger
{
/// <summary>
/// Logs the specified message as an error.
/// </summary>
/// <typeparam name="T">The type calling the logger.</typeparam>
/// <param name="text">The message to log.</param>
/// <param name="callerName">The property or method name calling the log.</param>
/// <param name="lineNumber">The line number where the method is called.</param>
public void Log<T>(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<T>(string.Empty, new ImageProcessingException(message));
}
/// <summary>
/// Logs the specified message as an error.
/// </summary>
/// <param name="type">The type calling the logger.</param>
/// <param name="text">The message to log.</param>
/// <param name="callerName">The property or method name calling the log.</param>
/// <param name="lineNumber">The line number where the method is called.</param>
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));
}
}
}
+19
View File
@@ -4,14 +4,33 @@ using Umbraco.Core.Models.EntityBase;
namespace Umbraco.Core.Models
{
/// <summary>
/// Represents a redirect url.
/// </summary>
public interface IRedirectUrl : IAggregateRoot, IRememberBeingDirty
{
/// <summary>
/// Gets or sets the identifier of the content item.
/// </summary>
[DataMember]
int ContentId { get; set; }
/// <summary>
/// Gets or sets the unique key identifying the content item.
/// </summary>
[DataMember]
Guid ContentKey { get; set; }
/// <summary>
/// Gets or sets the redirect url creation date.
/// </summary>
[DataMember]
DateTime CreateDateUtc { get; set; }
/// <summary>
/// Gets or sets the redirect url route.
/// </summary>
/// <remarks>Is a proper Umbraco route eg /path/to/foo or 123/path/tofoo.</remarks>
[DataMember]
string Url { get; set; }
}
@@ -19,7 +19,7 @@ namespace Umbraco.Core.Models.Identity
Culture = Configuration.GlobalSettings.DefaultUILanguage;
}
public virtual async Task<ClaimsIdentity> GenerateUserIdentityAsync(BackOfficeUserManager manager)
public virtual async Task<ClaimsIdentity> GenerateUserIdentityAsync(BackOfficeUserManager<BackOfficeIdentityUser> manager)
{
// NOTE the authenticationType must match the umbraco one
// defined in CookieAuthenticationOptions.AuthenticationType
+47 -45
View File
@@ -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));
}
/// <summary>
/// Gets or Sets the value of the Property
/// </summary>
@@ -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<int>();
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<decimal>();
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<DateTime>();
if (convDateTime == false) ThrowTypeException(value, typeof (DateTime), _propertyType.Alias);
value = convDateTime.Result;
}
break;
}
}
+40 -53
View File
@@ -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)
{
{
}
/// <summary>
@@ -298,18 +298,21 @@ namespace Umbraco.Core.Models
}
/// <summary>
/// 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".
/// </summary>
/// <param name="value"></param>
/// <returns>True if valid, otherwise false</returns>
/// <param name="value">The value.</param>
/// <returns>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.</returns>
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;
}
/// <summary>
/// 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.
/// </summary>
/// <returns></returns>
internal bool CanHaveDataValueTypeChanged()
{
var propertyEditor = PropertyEditorResolver.Current.GetByAlias(_propertyEditorAlias);
return propertyEditor.PreValueEditor.Fields
.SingleOrDefault(x => x.Key == Constants.PropertyEditors.PreValueKeys.DataValueType) != null;
}
/// <summary>
/// Validates the Value from a Property according to the validation settings
/// </summary>
@@ -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<int>(() => PropertyGroupId.Value);
clone._propertyGroupId = new Lazy<int>(() => PropertyGroupId.Value);
}
//this shouldn't really be needed since we're not tracking
clone.ResetDirtyProperties(false);
@@ -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)]
@@ -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; }
}
}
@@ -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")]
@@ -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")]
+22 -12
View File
@@ -5,10 +5,16 @@ using Umbraco.Core.Models.EntityBase;
namespace Umbraco.Core.Models
{
/// <summary>
/// Implements <see cref="IRedirectUrl"/>.
/// </summary>
[Serializable]
[DataContract(IsReference = true)]
public class RedirectUrl : Entity, IRedirectUrl
{
/// <summary>
/// Initializes a new instance of the <see cref="RedirectUrl"/> class.
/// </summary>
public RedirectUrl()
{
CreateDateUtc = DateTime.UtcNow;
@@ -16,42 +22,46 @@ namespace Umbraco.Core.Models
private static readonly Lazy<PropertySelectors> Ps = new Lazy<PropertySelectors>();
// ReSharper disable once ClassNeverInstantiated.Local
private class PropertySelectors
{
public readonly PropertyInfo ContentIdSelector = ExpressionHelper.GetPropertyInfo<RedirectUrl, int>(x => x.ContentId);
public readonly PropertyInfo ContentKeySelector = ExpressionHelper.GetPropertyInfo<RedirectUrl, Guid>(x => x.ContentKey);
public readonly PropertyInfo CreateDateUtcSelector = ExpressionHelper.GetPropertyInfo<RedirectUrl, DateTime>(x => x.CreateDateUtc);
public readonly PropertyInfo UrlSelector = ExpressionHelper.GetPropertyInfo<RedirectUrl, string>(x => x.Url);
}
private int _contentId;
private Guid _contentKey;
private DateTime _createDateUtc;
private string _url;
/// <inheritdoc />
public int ContentId
{
get { return _contentId; }
set
{
SetPropertyValueAndDetectChanges(value, ref _contentId, Ps.Value.ContentIdSelector);
}
set { SetPropertyValueAndDetectChanges(value, ref _contentId, Ps.Value.ContentIdSelector); }
}
/// <inheritdoc />
public Guid ContentKey
{
get { return _contentKey; }
set { SetPropertyValueAndDetectChanges(value, ref _contentKey, Ps.Value.ContentKeySelector); }
}
/// <inheritdoc />
public DateTime CreateDateUtc
{
get { return _createDateUtc; }
set
{
SetPropertyValueAndDetectChanges(value, ref _createDateUtc, Ps.Value.CreateDateUtcSelector);
}
set { SetPropertyValueAndDetectChanges(value, ref _createDateUtc, Ps.Value.CreateDateUtcSelector); }
}
/// <inheritdoc />
public string Url
{
get { return _url; }
set
{
SetPropertyValueAndDetectChanges(value, ref _url, Ps.Value.UrlSelector);
}
set { SetPropertyValueAndDetectChanges(value, ref _url, Ps.Value.UrlSelector); }
}
}
}
+14 -3
View File
@@ -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);
}
/// <summary>
/// Determines whether this user is an admin.
/// </summary>
/// <param name="user"></param>
/// <returns>
/// <c>true</c> if this user is admin; otherwise, <c>false</c>.
/// </returns>
public static bool IsAdmin(this IUser user)
{
if (user == null) throw new ArgumentNullException("user");
return user.UserType.Alias == "admin";
}
}
}
+128 -79
View File
@@ -9,15 +9,28 @@ using System.Xml;
namespace Umbraco.Core
{
/// <summary>
/// Provides object extension methods.
/// </summary>
public static class ObjectExtensions
{
//private static readonly ConcurrentDictionary<Type, Func<object>> ObjectFactoryCache = new ConcurrentDictionary<Type, Func<object>>();
/// <summary>
///
/// </summary>
/// <param name="input"></param>
/// <typeparam name="T"></typeparam>
/// <returns></returns>
public static IEnumerable<T> AsEnumerableOfOne<T>(this T input)
{
return Enumerable.Repeat(input, 1);
}
/// <summary>
///
/// </summary>
/// <param name="input"></param>
public static void DisposeIfDisposable(this object input)
{
var disposable = input as IDisposable;
@@ -25,7 +38,8 @@ namespace Umbraco.Core
}
/// <summary>
/// Provides a shortcut way of safely casting an input when you cannot guarantee the <typeparam name="T"></typeparam> 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 <typeparamref name="T"/> is
/// an instance type (i.e., when the C# AS keyword is not applicable).
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="input">The input.</param>
@@ -63,30 +77,30 @@ namespace Umbraco.Core
}
/// <summary>
/// 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 <see cref="Convert.ChangeType(object,System.Type)"/>.
/// 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 <see cref="Convert.ChangeType(object,System.Type)"/>.
/// </summary>
/// <param name="input">The input.</param>
/// <param name="destinationType">Type of the destination.</param>
/// <returns></returns>
public static Attempt<object> 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<object>.Succeed(null);
// if null...
if (input == null)
{
// nullable is ok
if (destinationType.IsGenericType && destinationType.GetGenericTypeDefinition() == typeof(Nullable<>))
return Attempt<object>.Succeed(null);
// value type is nok, else can be null, so is ok
return Attempt<object>.SucceedIf(destinationType.IsValueType == false, null);
}
//if its not nullable and it is a value type
if (input == null && destinationType.IsValueType) return Attempt<object>.Fail();
//if the type can be null, then no problem
if (input == null && destinationType.IsValueType == false) return Attempt<object>.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<object>.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<object>.Fail();
}
private static Nullable<Attempt<object>> 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<object>? TryConvertToFromString(this string input, Type destinationType)
{
// easy
if (destinationType == typeof(string))
return Attempt<object>.Succeed(input);
if (string.IsNullOrEmpty(input))
// null, empty, whitespaces
if (string.IsNullOrWhiteSpace(input))
{
if (destinationType == typeof(Boolean))
return Attempt<object>.Succeed(false); // special case for booleans, null/empty == false
if (destinationType == typeof(DateTime))
if (destinationType == typeof(bool)) // null/empty = bool false
return Attempt<object>.Succeed(false);
if (destinationType == typeof(DateTime)) // null/empty = min DateTime value
return Attempt<object>.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<object>.Succeed(value) : Attempt<object>.Fail();
}
if (destinationType == typeof(Int64))
{
Int64 value;
return Int64.TryParse(input, out value) ? Attempt<object>.Succeed(value) : Attempt<object>.Fail();
}
if (destinationType == typeof(Boolean))
{
Boolean value;
if (Boolean.TryParse(input, out value))
return Attempt<object>.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<object>.Succeed(value) : Attempt<object>.Fail();
}
else if (destinationType == typeof(Double))
{
Double value;
var input2 = NormalizeNumberDecimalSeparator(input);
return Double.TryParse(input2, out value) ? Attempt<object>.Succeed(value) : Attempt<object>.Fail();
}
else if (destinationType == typeof(Single))
{
Single value;
int value;
if (int.TryParse(input, out value)) return Attempt<object>.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<object>.Succeed(value) : Attempt<object>.Fail();
}
else if (destinationType == typeof(Char))
return Attempt<object>.SucceedIf(decimal.TryParse(input2, out value2), Convert.ToInt32(value2));
}
if (destinationType == typeof(long)) // aka Int64
{
Char value;
return Char.TryParse(input, out value) ? Attempt<object>.Succeed(value) : Attempt<object>.Fail();
long value;
if (long.TryParse(input, out value)) return Attempt<object>.Succeed(value);
// same as int
decimal value2;
var input2 = NormalizeNumberDecimalSeparator(input);
return Attempt<object>.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<object>.Succeed(value) : Attempt<object>.Fail();
bool value;
if (bool.TryParse(input, out value)) return Attempt<object>.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<object>.Succeed(value) : Attempt<object>.Fail();
short value;
return Attempt<object>.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<object>.Succeed(value) : Attempt<object>.Fail();
double value;
var input2 = NormalizeNumberDecimalSeparator(input);
return Attempt<object>.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<object>.Succeed(value) : Attempt<object>.Fail();
float value;
var input2 = NormalizeNumberDecimalSeparator(input);
return Attempt<object>.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<object>.Succeed(value) : Attempt<object>.Fail();
char value;
return Attempt<object>.SucceedIf(char.TryParse(input, out value), value);
}
if (destinationType == typeof(byte)) // aka Byte
{
byte value;
return Attempt<object>.SucceedIf(byte.TryParse(input, out value), value);
}
if (destinationType == typeof(sbyte)) // aka SByte
{
sbyte value;
return Attempt<object>.SucceedIf(sbyte.TryParse(input, out value), value);
}
if (destinationType == typeof(uint)) // aka UInt32
{
uint value;
return Attempt<object>.SucceedIf(uint.TryParse(input, out value), value);
}
if (destinationType == typeof(ushort)) // aka UInt16
{
ushort value;
return Attempt<object>.SucceedIf(ushort.TryParse(input, out value), value);
}
if (destinationType == typeof(ulong)) // aka UInt64
{
ulong value;
return Attempt<object>.SucceedIf(ulong.TryParse(input, out value), value);
}
}
else if (destinationType == typeof(Guid))
{
Guid value;
return Guid.TryParse(input, out value) ? Attempt<object>.Succeed(value) : Attempt<object>.Fail();
return Attempt<object>.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<object>.Succeed(value) : Attempt<object>.Fail();
return Attempt<object>.SucceedIf(DateTimeOffset.TryParse(input, out value), value);
}
else if (destinationType == typeof(TimeSpan))
{
TimeSpan value;
return TimeSpan.TryParse(input, out value) ? Attempt<object>.Succeed(value) : Attempt<object>.Fail();
return Attempt<object>.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<object>.Succeed(value) : Attempt<object>.Fail();
return Attempt<object>.SucceedIf(decimal.TryParse(input2, out value), value);
}
else if (destinationType == typeof(Version))
{
Version value;
return Version.TryParse(input, out value) ? Attempt<object>.Succeed(value) : Attempt<object>.Fail();
return Attempt<object>.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)
{
@@ -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;
}
@@ -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:
@@ -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;
}
}
}
@@ -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)
@@ -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()
@@ -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)},
@@ -43,8 +43,13 @@ namespace Umbraco.Core.Persistence.Migrations.Initial
/// <returns></returns>
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;
@@ -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();
}
}
}
@@ -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;
}
/// <summary>
@@ -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)
@@ -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);
}
}
@@ -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()
@@ -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
{
/// <summary>
/// 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
/// </summary>
[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()
{
}
}
}
@@ -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
{
/// <summary>
/// See: http://issues.umbraco.org/issue/U4-8522
/// </summary>
[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()
{
}
}
}
+99 -84
View File
@@ -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<T>
public class Page<T>
{
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(@"(?<!@)@\w+", RegexOptions.Compiled);
public static string ProcessParams(string _sql, object[] args_src, List<object> 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<T> Fetch<T>(string sql, params object[] args)
public List<T> Fetch<T>(string sql, params object[] args)
{
return Query<T>(sql, args).ToList();
}
public List<T> Fetch<T>(Sql sql)
public List<T> Fetch<T>(Sql sql)
{
return Fetch<T>(sql.SQL, sql.Arguments);
}
@@ -726,7 +726,43 @@ namespace Umbraco.Core.Persistence
return true;
}
public void BuildPageQueries<T>(long skip, long take, string sql, ref object[] args, out string sqlCount, out string sqlPage)
/// <summary>
/// NOTE: This is a custom mod of PetaPoco!! This builds the paging sql for different db providers
/// </summary>
/// <param name="sql"></param>
/// <param name="sqlSelectRemoved"></param>
/// <param name="sqlOrderBy"></param>
/// <param name="args"></param>
/// <param name="sqlPage"></param>
/// <param name="databaseType"></param>
/// <param name="skip"></param>
/// <param name="take"></param>
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<T>(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<T> Page<T>(long page, long itemsPerPage, string sql, params object[] args)
// Fetch a page
public Page<T> Page<T>(long page, long itemsPerPage, string sql, params object[] args)
{
string sqlCount, sqlPage;
BuildPageQueries<T>((page-1)*itemsPerPage, itemsPerPage, sql, ref args, out sqlCount, out sqlPage);
@@ -791,7 +805,7 @@ namespace Umbraco.Core.Persistence
return result;
}
public Page<T> Page<T>(long page, long itemsPerPage, Sql sql)
public Page<T> Page<T>(long page, long itemsPerPage, Sql sql)
{
return Page<T>(page, itemsPerPage, sql.SQL, sql.Arguments);
}
@@ -820,7 +834,7 @@ namespace Umbraco.Core.Persistence
}
// Return an enumerable collection of pocos
public IEnumerable<T> Query<T>(string sql, params object[] args)
public IEnumerable<T> Query<T>(string sql, params object[] args)
{
if (EnableAutoSelect)
sql = AddSelectClause<T>(sql);
@@ -1033,7 +1047,7 @@ namespace Umbraco.Core.Persistence
}
private List<Delegate> Delegates { get; set; }
private Delegate GetItem(int index) { return Delegates[index]; }
/// <summary>
/// Calls the delegate at the specified index and returns its values
/// </summary>
@@ -1068,7 +1082,7 @@ namespace Umbraco.Core.Persistence
// Create a multi-poco factory
Func<IDataReader, Delegate, TRet> CreateMultiPocoFactory<TRet>(Type[] types, string sql, IDataReader r)
{
{
// Call each delegate
var dels = new List<Delegate>();
int pos = 0;
@@ -1088,7 +1102,7 @@ namespace Umbraco.Core.Persistence
static Dictionary<string, Delegate> AutoMappers = new Dictionary<string, Delegate>();
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<IDataReader, Delegate, TRet> GetMultiPocoFactory<TRet>(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<IDataReader, Delegate, TRet>)oFactory;
}
return (Func<IDataReader, Delegate, TRet>)oFactory;
}
}
finally
{
@@ -1130,9 +1144,9 @@ namespace Umbraco.Core.Persistence
if (MultiPocoFactories.TryGetValue(key, out oFactory))
{
return (Func<IDataReader, Delegate, TRet>)oFactory;
}
// Create the factory
}
// Create the factory
var factory = CreateMultiPocoFactory<TRet>(types, sql, r);
MultiPocoFactories.Add(key, factory);
@@ -1207,54 +1221,54 @@ namespace Umbraco.Core.Persistence
}
}
public IEnumerable<T> Query<T>(Sql sql)
public IEnumerable<T> Query<T>(Sql sql)
{
return Query<T>(sql.SQL, sql.Arguments);
}
public bool Exists<T>(object primaryKey)
public bool Exists<T>(object primaryKey)
{
return FirstOrDefault<T>(string.Format("WHERE {0}=@0", EscapeSqlIdentifier(PocoData.ForType(typeof(T)).TableInfo.PrimaryKey)), primaryKey) != null;
}
public T Single<T>(object primaryKey)
public T Single<T>(object primaryKey)
{
return Single<T>(string.Format("WHERE {0}=@0", EscapeSqlIdentifier(PocoData.ForType(typeof(T)).TableInfo.PrimaryKey)), primaryKey);
}
public T SingleOrDefault<T>(object primaryKey)
public T SingleOrDefault<T>(object primaryKey)
{
return SingleOrDefault<T>(string.Format("WHERE {0}=@0", EscapeSqlIdentifier(PocoData.ForType(typeof(T)).TableInfo.PrimaryKey)), primaryKey);
}
public T Single<T>(string sql, params object[] args)
public T Single<T>(string sql, params object[] args)
{
return Query<T>(sql, args).Single();
}
public T SingleOrDefault<T>(string sql, params object[] args)
public T SingleOrDefault<T>(string sql, params object[] args)
{
return Query<T>(sql, args).SingleOrDefault();
}
public T First<T>(string sql, params object[] args)
public T First<T>(string sql, params object[] args)
{
return Query<T>(sql, args).First();
}
public T FirstOrDefault<T>(string sql, params object[] args)
public T FirstOrDefault<T>(string sql, params object[] args)
{
return Query<T>(sql, args).FirstOrDefault();
}
public T Single<T>(Sql sql)
public T Single<T>(Sql sql)
{
return Query<T>(sql).Single();
}
public T SingleOrDefault<T>(Sql sql)
public T SingleOrDefault<T>(Sql sql)
{
return Query<T>(sql).SingleOrDefault();
}
public T First<T>(Sql sql)
public T First<T>(Sql sql)
{
return Query<T>(sql).First();
}
public T FirstOrDefault<T>(Sql sql)
public T FirstOrDefault<T>(Sql sql)
{
return Query<T>(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<string, object>)[ColumnName]=val; }
public override object GetValue(object target)
{
public override object GetValue(object target)
{
object val=null;
(target as IDictionary<string, object>).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<Delegate>)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<string, PocoColumn> Columns { get; private set; }
static System.Threading.ReaderWriterLockSlim InnerLock = new System.Threading.ReaderWriterLockSlim();
/// <summary>
/// Returns a report of the current cache being utilized by PetaPoco
/// </summary>
@@ -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;
}
@@ -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!
@@ -22,8 +22,7 @@ namespace Umbraco.Core.Persistence
public static Sql From<T>(this Sql sql, ISqlSyntaxProvider sqlSyntax)
{
var type = typeof(T);
var tableNameAttribute = type.FirstAttribute<TableNameAttribute>();
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<TColumn>(this Sql sql, Expression<Func<TColumn, object>> columnMember, ISqlSyntaxProvider sqlSyntax)
{
var column = ExpressionHelper.FindProperty(columnMember) as PropertyInfo;
var columnName = column.FirstAttribute<ColumnAttribute>().Name;
var columnName = column.GetColumnName();
var type = typeof(TColumn);
var tableNameAttribute = type.FirstAttribute<TableNameAttribute>();
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<TColumn>(this Sql sql, Expression<Func<TColumn, object>> columnMember, ISqlSyntaxProvider sqlSyntax)
{
var column = ExpressionHelper.FindProperty(columnMember) as PropertyInfo;
var columnName = column.FirstAttribute<ColumnAttribute>().Name;
var columnName = column.GetColumnName();
var type = typeof(TColumn);
var tableNameAttribute = type.FirstAttribute<TableNameAttribute>();
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<TColumn>(this Sql sql, Expression<Func<TColumn, object>> columnMember, ISqlSyntaxProvider sqlProvider)
{
var column = ExpressionHelper.FindProperty(columnMember) as PropertyInfo;
var columnName = column.FirstAttribute<ColumnAttribute>().Name;
var columnName = column.GetColumnName();
return sql.GroupBy(sqlProvider.GetQuotedColumnName(columnName));
}
@@ -110,8 +108,7 @@ namespace Umbraco.Core.Persistence
public static Sql.SqlJoinClause InnerJoin<T>(this Sql sql, ISqlSyntaxProvider sqlSyntax)
{
var type = typeof(T);
var tableNameAttribute = type.FirstAttribute<TableNameAttribute>();
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<T>(this Sql sql, ISqlSyntaxProvider sqlSyntax)
{
var type = typeof(T);
var tableNameAttribute = type.FirstAttribute<TableNameAttribute>();
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<T>(this Sql sql, ISqlSyntaxProvider sqlSyntax)
{
var type = typeof(T);
var tableNameAttribute = type.FirstAttribute<TableNameAttribute>();
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<T>(this Sql sql, ISqlSyntaxProvider sqlSyntax)
{
var type = typeof(T);
var tableNameAttribute = type.FirstAttribute<TableNameAttribute>();
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<TableNameAttribute>().Value;
var rightTableName = rightType.FirstAttribute<TableNameAttribute>().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<ColumnAttribute>().Name;
var rightColumnName = right.FirstAttribute<ColumnAttribute>().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<TableNameAttribute>();
return attr == null || string.IsNullOrWhiteSpace(attr.Value) ? string.Empty : attr.Value;
}
private static string GetColumnName(this PropertyInfo column)
{
var attr = column.FirstAttribute<ColumnAttribute>();
return attr == null || string.IsNullOrWhiteSpace(attr.Name) ? column.Name : attr.Name;
}
}
}
@@ -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<Func<object>>(member);
var getter = lambda.Compile();
searchValue = getter().ToString();
}
else
{
searchValue = methodArgs[0].ToString();
}
if (methodArgs[0].Type != typeof(string) && TypeHelper.IsTypeAssignableFrom<IEnumerable>(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<Func<object>>(member);
var getter = lambda.Compile();
replaceValue = getter().ToString();
}
else
{
replaceValue = methodArgs[1].ToString();
}
if (methodArgs[1].Type != typeof(string) && TypeHelper.IsTypeAssignableFrom<IEnumerable>(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);
@@ -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<DocumentDto>(x => x.Newest)
.OrderByDescending<ContentVersionDto>(x => x.VersionDate);
.OrderByDescending<ContentVersionDto>(x => x.VersionDate, SqlSyntax);
var dto = Database.Fetch<DocumentDto, ContentVersionDto, ContentDto, NodeDto, DocumentPublishedReadOnlyDto>(sql).FirstOrDefault();
var dto = Database.Fetch<DocumentDto, ContentVersionDto, ContentDto, NodeDto, DocumentPublishedReadOnlyDto>(SqlSyntax.SelectTop(sql, 1)).FirstOrDefault();
if (dto == null)
return null;
@@ -143,7 +144,7 @@ namespace Umbraco.Core.Persistence.Repositories
{
var list = new List<string>
{
"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
}
}
/// <summary>
/// This builds the Xml document used for the XML cache
/// </summary>
/// <returns></returns>
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<dynamic>(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<NodeDto>(x => x.Trashed == false)
@@ -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<ContentTypeDto>("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<ContentDto>()
.InnerJoin<NodeDto>()
.On<ContentDto, NodeDto>(left => left.NodeId, right => right.NodeId)
.From<ContentDto>(SqlSyntax)
.InnerJoin<NodeDto>(SqlSyntax).On<ContentDto, NodeDto>(SqlSyntax, left => left.NodeId, right => right.NodeId)
.Where<NodeDto>(x => x.NodeObjectType == new Guid(Constants.ObjectTypes.Document))
.Where<ContentDto>(x => x.ContentTypeId == entity.Id);
var contentDtos = Database.Fetch<ContentDto, NodeDto>(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<PropertyTypeDto>("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<PropertyDataDto>()
.InnerJoin<PropertyTypeDto>()
.On<PropertyDataDto, PropertyTypeDto>(
left => left.PropertyTypeId, right => right.Id)
.Where<PropertyDataDto>(x => x.NodeId == nodeId)
.Where<PropertyTypeDto>(x => x.Id == propertyTypeId);
.From<PropertyDataDto>(SqlSyntax)
.InnerJoin<PropertyTypeDto>(SqlSyntax).On<PropertyDataDto, PropertyTypeDto>(SqlSyntax, left => left.PropertyTypeId, right => right.Id)
.Where<PropertyDataDto>(x => x.NodeId == nodeId)
.Where<PropertyTypeDto>(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<PropertyDataDto>(new Sql("WHERE id IN (" + propertySql.SQL + ")", propertySql.Arguments));
}
}
}
}
//Delete the allowed content type entries before adding the updated collection
Database.Delete<ContentTypeAllowedContentTypeDto>("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<ContentTypeAllowedContentTypeDto>("WHERE Id = @Id", new { entity.Id });
foreach (var allowedContentType in entity.AllowedContentTypes)
{
Database.Insert(new ContentTypeAllowedContentTypeDto
{
Id = entity.Id,
AllowedId = allowedContentType.Id.Value,
SortOrder = allowedContentType.SortOrder
});
{
Id = entity.Id,
AllowedId = allowedContentType.Id.Value,
SortOrder = allowedContentType.SortOrder
});
}
// FIXME below, manage the property types
if (((ICanBeDirty)entity).IsPropertyDirty("PropertyTypes") || entity.PropertyTypes.Any(x => x.IsDirty()))
// delete ??? fixme wtf is this?!
// by excepting entries from db with entries from collections
if (entity.IsPropertyDirty("PropertyTypes") || entity.PropertyTypes.Any(x => x.IsDirty()))
{
//Delete PropertyTypes by excepting entries from db with entries from collections
var dbPropertyTypes = Database.Fetch<PropertyTypeDto>("WHERE contentTypeId = @Id", new { Id = entity.Id });
var dbPropertyTypeAlias = dbPropertyTypes.Select(x => x.Id);
var entityPropertyTypes = entity.PropertyTypes.Where(x => x.HasIdentity).Select(x => x.Id);
var items = dbPropertyTypeAlias.Except(entityPropertyTypes);
foreach (var item in items)
{
//Before a PropertyType can be deleted, all Properties based on that PropertyType should be deleted.
Database.Delete<TagRelationshipDto>("WHERE propertyTypeId = @Id", new { Id = item });
Database.Delete<PropertyDataDto>("WHERE propertytypeid = @Id", new { Id = item });
Database.Delete<PropertyTypeDto>("WHERE contentTypeId = @Id AND id = @PropertyTypeId",
new { Id = entity.Id, PropertyTypeId = item });
}
DeletePropertyType(entity.Id, item);
}
// delete tabs
// by excepting entries from db with entries from collections
List<int> orphanPropertyTypeIds = null;
if (entity.IsPropertyDirty("PropertyGroups") || entity.PropertyGroups.Any(x => x.IsDirty()))
{
// todo
@@ -357,68 +352,97 @@ AND umbracoNode.id <> @id",
// (all gone)
// delete tabs that do not exist anymore
// get the tabs that are currently existing (in the db)
// get the tabs that we want, now
// and derive the tabs that we want to delete
// get the tabs that are currently existing (in the db), get the tabs that we want,
// now, and derive the tabs that we want to delete
var existingPropertyGroups = Database.Fetch<PropertyTypeGroupDto>("WHERE contentTypeNodeId = @id", new { id = entity.Id })
.Select(x => x.Id)
.ToList();
var newPropertyGroups = entity.PropertyGroups.Select(x => x.Id).ToList();
var tabsToDelete = existingPropertyGroups
var groupsToDelete = existingPropertyGroups
.Except(newPropertyGroups)
.ToArray();
// move properties to generic properties, and delete the tabs
if (tabsToDelete.Length > 0)
// delete the tabs
if (groupsToDelete.Length > 0)
{
Database.Update<PropertyTypeDto>("SET propertyTypeGroupId=NULL WHERE propertyTypeGroupId IN (@ids)", new { ids = tabsToDelete });
Database.Delete<PropertyTypeGroupDto>("WHERE id IN (@ids)", new { ids = tabsToDelete });
// if the tab contains properties, take care of them
// - move them to 'generic properties' so they remain consistent
// - keep track of them, later on we'll figure out what to do with them
// see http://issues.umbraco.org/issue/U4-8663
orphanPropertyTypeIds = Database.Fetch<PropertyTypeDto>("WHERE propertyTypeGroupId IN (@ids)", new { ids = groupsToDelete })
.Select(x => x.Id).ToList();
Database.Update<PropertyTypeDto>("SET propertyTypeGroupId=NULL WHERE propertyTypeGroupId IN (@ids)", new { ids = groupsToDelete });
// now we can delete the tabs
Database.Delete<PropertyTypeGroupDto>("WHERE id IN (@ids)", new { ids = groupsToDelete });
}
}
var propertyGroupFactory = new PropertyGroupFactory(entity.Id);
//Run through all groups to insert or update entries
// insert or update groups, assign properties
foreach (var propertyGroup in entity.PropertyGroups)
{
var tabDto = propertyGroupFactory.BuildGroupDto(propertyGroup);
int groupPrimaryKey = propertyGroup.HasIdentity
? Database.Update(tabDto)
: Convert.ToInt32(Database.Insert(tabDto));
// insert or update group
var groupDto = propertyGroupFactory.BuildGroupDto(propertyGroup);
var groupId = propertyGroup.HasIdentity
? Database.Update(groupDto)
: Convert.ToInt32(Database.Insert(groupDto));
if (propertyGroup.HasIdentity == false)
propertyGroup.Id = groupPrimaryKey; //Set Id on new PropertyGroup
propertyGroup.Id = groupId;
else
groupId = propertyGroup.Id;
//Ensure that the PropertyGroup's Id is set on the PropertyTypes within a group
//unless the PropertyGroupId has already been changed.
// assign properties to the group
// (all of them, even those that have .IsPropertyDirty("PropertyGroupId") == true,
// because it should have been set to this group anyways and better be safe)
foreach (var propertyType in propertyGroup.PropertyTypes)
{
if (propertyType.IsPropertyDirty("PropertyGroupId") == false)
{
var tempGroup = propertyGroup;
propertyType.PropertyGroupId = new Lazy<int>(() => tempGroup.Id);
}
}
propertyType.PropertyGroupId = new Lazy<int>(() => groupId);
}
//Run through all PropertyTypes to insert or update entries
// insert or update properties
// all of them, no-group and in groups
foreach (var propertyType in entity.PropertyTypes)
{
var tabId = propertyType.PropertyGroupId != null ? propertyType.PropertyGroupId.Value : default(int);
//If the Id of the DataType is not set, we resolve it from the db by its PropertyEditorAlias
if (propertyType.DataTypeDefinitionId == 0 || propertyType.DataTypeDefinitionId == default(int))
{
AssignDataTypeFromPropertyEditor(propertyType);
}
var groupId = propertyType.PropertyGroupId != null ? propertyType.PropertyGroupId.Value : default(int);
//validate the alias!
// if the Id of the DataType is not set, we resolve it from the db by its PropertyEditorAlias
if (propertyType.DataTypeDefinitionId == 0 || propertyType.DataTypeDefinitionId == default(int))
AssignDataTypeFromPropertyEditor(propertyType);
// validate the alias
ValidateAlias(propertyType);
var propertyTypeDto = propertyGroupFactory.BuildPropertyTypeDto(tabId, propertyType);
int typePrimaryKey = propertyType.HasIdentity
? Database.Update(propertyTypeDto)
: Convert.ToInt32(Database.Insert(propertyTypeDto));
// insert or update property
var propertyTypeDto = propertyGroupFactory.BuildPropertyTypeDto(groupId, propertyType);
var typeId = propertyType.HasIdentity
? Database.Update(propertyTypeDto)
: Convert.ToInt32(Database.Insert(propertyTypeDto));
if (propertyType.HasIdentity == false)
propertyType.Id = typePrimaryKey; //Set Id on new PropertyType
propertyType.Id = typeId;
else
typeId = propertyType.Id;
// not an orphan anymore
if (orphanPropertyTypeIds != null)
orphanPropertyTypeIds.Remove(typeId);
}
// deal with orphan properties: those that were in a deleted tab,
// and have not been re-mapped to another tab or to 'generic properties'
if (orphanPropertyTypeIds != null)
foreach (var id in orphanPropertyTypeIds)
DeletePropertyType(entity.Id, id);
}
private void DeletePropertyType(int contentTypeId, int propertyTypeId)
{
// first clear dependencies
Database.Delete<TagRelationshipDto>("WHERE propertyTypeId = @Id", new { Id = propertyTypeId });
Database.Delete<PropertyDataDto>("WHERE propertytypeid = @Id", new { Id = propertyTypeId });
// then delete the property type
Database.Delete<PropertyTypeDto>("WHERE contentTypeId = @Id AND id = @PropertyTypeId", new { Id = contentTypeId, PropertyTypeId = propertyTypeId });
}
protected IEnumerable<ContentTypeSort> GetAllowedContentTypeIds(int id)
@@ -649,7 +673,7 @@ AND umbracoNode.id <> @id",
var allParentContentTypes = contentTypes.Where(x => allParentIdsAsArray.Contains(x.Id)).ToArray();
foreach (var contentType in contentTypes)
{
{
var entityId = contentType.Id;
var parentContentTypes = allParentContentTypes.Where(x =>
@@ -714,10 +738,10 @@ AND umbracoNode.id <> @id",
out IDictionary<int, List<int>> parentMediaTypeIds)
{
Mandate.ParameterNotNull(db, "db");
var sql = @"SELECT cmsContentType.pk as ctPk, cmsContentType.alias as ctAlias, cmsContentType.allowAtRoot as ctAllowAtRoot, cmsContentType.description as ctDesc,
cmsContentType.icon as ctIcon, cmsContentType.isContainer as ctIsContainer, cmsContentType.nodeId as ctId, cmsContentType.thumbnail as ctThumb,
AllowedTypes.AllowedId as ctaAllowedId, AllowedTypes.SortOrder as ctaSortOrder, AllowedTypes.alias as ctaAlias,
AllowedTypes.AllowedId as ctaAllowedId, AllowedTypes.SortOrder as ctaSortOrder, AllowedTypes.alias as ctaAlias,
ParentTypes.parentContentTypeId as chtParentId, ParentTypes.parentContentTypeKey as chtParentKey,
umbracoNode.createDate as nCreateDate, umbracoNode." + sqlSyntax.GetQuotedColumnName("level") + @" as nLevel, umbracoNode.nodeObjectType as nObjectType, umbracoNode.nodeUser as nUser,
umbracoNode.parentID as nParentId, umbracoNode." + sqlSyntax.GetQuotedColumnName("path") + @" as nPath, umbracoNode.sortOrder as nSortOrder, umbracoNode." + sqlSyntax.GetQuotedColumnName("text") + @" as nName, umbracoNode.trashed as nTrashed,
@@ -741,7 +765,7 @@ AND umbracoNode.id <> @id",
ON ParentTypes.childContentTypeId = cmsContentType.nodeId
WHERE (umbracoNode.nodeObjectType = @nodeObjectType)
ORDER BY ctId";
var result = db.Fetch<dynamic>(sql, new { nodeObjectType = new Guid(Constants.ObjectTypes.MediaType) });
if (result.Any() == false)
@@ -848,16 +872,16 @@ AND umbracoNode.id <> @id",
return mediaType;
}
internal static IEnumerable<IContentType> MapContentTypes(Database db, ISqlSyntaxProvider sqlSyntax,
internal static IEnumerable<IContentType> MapContentTypes(Database db, ISqlSyntaxProvider sqlSyntax,
out IDictionary<int, List<AssociatedTemplate>> associatedTemplates,
out IDictionary<int, List<int>> parentContentTypeIds)
{
Mandate.ParameterNotNull(db, "db");
var sql = @"SELECT cmsDocumentType.IsDefault as dtIsDefault, cmsDocumentType.templateNodeId as dtTemplateId,
cmsContentType.pk as ctPk, cmsContentType.alias as ctAlias, cmsContentType.allowAtRoot as ctAllowAtRoot, cmsContentType.description as ctDesc,
cmsContentType.icon as ctIcon, cmsContentType.isContainer as ctIsContainer, cmsContentType.nodeId as ctId, cmsContentType.thumbnail as ctThumb,
AllowedTypes.AllowedId as ctaAllowedId, AllowedTypes.SortOrder as ctaSortOrder, AllowedTypes.alias as ctaAlias,
AllowedTypes.AllowedId as ctaAllowedId, AllowedTypes.SortOrder as ctaSortOrder, AllowedTypes.alias as ctaAlias,
ParentTypes.parentContentTypeId as chtParentId,ParentTypes.parentContentTypeKey as chtParentKey,
umbracoNode.createDate as nCreateDate, umbracoNode." + sqlSyntax.GetQuotedColumnName("level") + @" as nLevel, umbracoNode.nodeObjectType as nObjectType, umbracoNode.nodeUser as nUser,
umbracoNode.parentID as nParentId, umbracoNode." + sqlSyntax.GetQuotedColumnName("path") + @" as nPath, umbracoNode.sortOrder as nSortOrder, umbracoNode." + sqlSyntax.GetQuotedColumnName("text") + @" as nName, umbracoNode.trashed as nTrashed,
@@ -890,7 +914,7 @@ AND umbracoNode.id <> @id",
ON ParentTypes.childContentTypeId = cmsContentType.nodeId
WHERE (umbracoNode.nodeObjectType = @nodeObjectType)
ORDER BY ctId";
var result = db.Fetch<dynamic>(sql, new { nodeObjectType = new Guid(Constants.ObjectTypes.DocumentType)});
if (result.Any() == false)
@@ -911,7 +935,7 @@ AND umbracoNode.id <> @id",
{
var ct = queue.Dequeue();
//check for default templates
//check for default templates
bool? isDefaultTemplate = Convert.ToBoolean(ct.dtIsDefault);
int? templateId = ct.dtTemplateId;
if (currDefaultTemplate == -1 && isDefaultTemplate.HasValue && isDefaultTemplate.Value && templateId.HasValue)
@@ -41,7 +41,7 @@ namespace Umbraco.Core.Persistence.Repositories
{
var sql = GetBaseQuery(false).Where(GetBaseWhereClause(), new { id = id, NodeObjectType = NodeObjectTypeId });
var nodeDto = Database.Fetch<NodeDto>(sql).FirstOrDefault();
var nodeDto = Database.Fetch<NodeDto>(SqlSyntax.SelectTop(sql, 1)).FirstOrDefault();
return nodeDto == null ? null : CreateEntity(nodeDto);
}
@@ -58,12 +58,12 @@ namespace Umbraco.Core.Persistence.Repositories
var sql = GetBaseQuery(false);
sql.Where(GetBaseWhereClause(), new { Id = id });
var macroDto = Database.Fetch<ExternalLoginDto>(sql).FirstOrDefault();
if (macroDto == null)
var dto = Database.Fetch<ExternalLoginDto>(SqlSyntax.SelectTop(sql, 1)).FirstOrDefault();
if (dto == null)
return null;
var factory = new ExternalLoginFactory();
var entity = factory.BuildEntity(macroDto);
var entity = factory.BuildEntity(dto);
//on initial construction we don't want to have dirty properties tracked
// http://issues.umbraco.org/issue/U4-1946
@@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.Linq.Expressions;
using System.Xml;
using System.Xml.Linq;
using Umbraco.Core.Models;
using Umbraco.Core.Models.Membership;
@@ -11,6 +12,12 @@ namespace Umbraco.Core.Persistence.Repositories
{
public interface IContentRepository : IRepositoryVersionable<int, IContent>, IRecycleBinRepository<IContent>, IDeleteMediaFilesRepository
{
/// <summary>
/// This builds the Xml document used for the XML cache
/// </summary>
/// <returns></returns>
XmlDocument BuildXmlCache();
/// <summary>
/// Get the count of published items
/// </summary>
@@ -1,17 +1,80 @@
using System.Collections.Generic;
using System;
using System.Collections.Generic;
using Umbraco.Core.Models;
namespace Umbraco.Core.Persistence.Repositories
{
public interface IRedirectUrlRepository : IRepositoryQueryable<int, IRedirectUrl>
/// <summary>
/// Defines the <see cref="IRedirectUrl"/> repository.
/// </summary>
public interface IRedirectUrlRepository : IRepositoryQueryable<Guid, IRedirectUrl>
{
IRedirectUrl Get(string url, int contentId);
void Delete(int id);
/// <summary>
/// Gets a redirect url.
/// </summary>
/// <param name="url">The Umbraco redirect url route.</param>
/// <param name="contentKey">The content unique key.</param>
/// <returns></returns>
IRedirectUrl Get(string url, Guid contentKey);
/// <summary>
/// Deletes a redirect url.
/// </summary>
/// <param name="id">The redirect url identifier.</param>
void Delete(Guid id);
/// <summary>
/// Deletes all redirect urls.
/// </summary>
void DeleteAll();
void DeleteContentUrls(int contentId);
/// <summary>
/// Deletes all redirect urls for a given content.
/// </summary>
/// <param name="contentKey">The content unique key.</param>
void DeleteContentUrls(Guid contentKey);
/// <summary>
/// Gets the most recent redirect url corresponding to an Umbraco redirect url route.
/// </summary>
/// <param name="url">The Umbraco redirect url route.</param>
/// <returns>The most recent redirect url corresponding to the route.</returns>
IRedirectUrl GetMostRecentUrl(string url);
IEnumerable<IRedirectUrl> GetContentUrls(int contentId);
/// <summary>
/// Gets all redirect urls for a content item.
/// </summary>
/// <param name="contentKey">The content unique key.</param>
/// <returns>All redirect urls for the content item.</returns>
IEnumerable<IRedirectUrl> GetContentUrls(Guid contentKey);
/// <summary>
/// Gets all redirect urls.
/// </summary>
/// <param name="pageIndex">The page index.</param>
/// <param name="pageSize">The page size.</param>
/// <param name="total">The total count of redirect urls.</param>
/// <returns>The redirect urls.</returns>
IEnumerable<IRedirectUrl> GetAllUrls(long pageIndex, int pageSize, out long total);
/// <summary>
/// Gets all redirect urls below a given content item.
/// </summary>
/// <param name="rootContentId">The content unique identifier.</param>
/// <param name="pageIndex">The page index.</param>
/// <param name="pageSize">The page size.</param>
/// <param name="total">The total count of redirect urls.</param>
/// <returns>The redirect urls.</returns>
IEnumerable<IRedirectUrl> GetAllUrls(int rootContentId, long pageIndex, int pageSize, out long total);
/// <summary>
/// Searches for all redirect urls that contain a given search term in their URL property.
/// </summary>
/// <param name="searchTerm">The term to search for.</param>
/// <param name="pageIndex">The page index.</param>
/// <param name="pageSize">The page size.</param>
/// <param name="total">The total count of redirect urls.</param>
/// <returns>The redirect urls.</returns>
IEnumerable<IRedirectUrl> SearchUrls(string searchTerm, long pageIndex, int pageSize, out long total);
}
}
@@ -52,9 +52,9 @@ namespace Umbraco.Core.Persistence.Repositories
{
var sql = GetBaseQuery(false);
sql.Where(GetBaseWhereClause(), new { Id = id });
sql.OrderByDescending<ContentVersionDto>(x => x.VersionDate);
sql.OrderByDescending<ContentVersionDto>(x => x.VersionDate, SqlSyntax);
var dto = Database.Fetch<ContentVersionDto, ContentDto, NodeDto>(sql).FirstOrDefault();
var dto = Database.Fetch<ContentVersionDto, ContentDto, NodeDto>(SqlSyntax.SelectTop(sql, 1)).FirstOrDefault();
if (dto == null)
return null;
@@ -413,6 +413,8 @@ namespace Umbraco.Core.Persistence.Repositories
{
foreach (var property in entity.Properties)
{
if (keyDictionary.ContainsKey(property.PropertyTypeId) == false) continue;
property.Id = keyDictionary[property.PropertyTypeId];
}
}
@@ -453,8 +455,16 @@ namespace Umbraco.Core.Persistence.Repositories
Func<Tuple<string, object[]>> filterCallback = null;
if (filter.IsNullOrWhiteSpace() == false)
{
sbWhere.Append("AND (umbracoNode." + SqlSyntax.GetQuotedColumnName("text") + " LIKE @" + args.Count + ")");
sbWhere
.Append("AND (")
.Append(SqlSyntax.GetQuotedTableName("umbracoNode"))
.Append(".")
.Append(SqlSyntax.GetQuotedColumnName("text"))
.Append(" LIKE @")
.Append(args.Count)
.Append(")");
args.Add("%" + filter + "%");
filterCallback = () => new Tuple<string, object[]>(sbWhere.ToString().Trim(), args.ToArray());
}
@@ -32,7 +32,7 @@ namespace Umbraco.Core.Persistence.Repositories
var sql = GetBaseQuery(false);
sql.Where(GetBaseWhereClause(), new { Id = id });
var dto = Database.Fetch<NodeDto>(sql).FirstOrDefault();
var dto = Database.Fetch<NodeDto>(SqlSyntax.SelectTop(sql, 1)).FirstOrDefault();
return dto == null ? null : _modelFactory.BuildEntity(dto);
}
@@ -52,9 +52,9 @@ namespace Umbraco.Core.Persistence.Repositories
{
var sql = GetBaseQuery(false);
sql.Where(GetBaseWhereClause(), new { Id = id });
sql.OrderByDescending<ContentVersionDto>(x => x.VersionDate);
sql.OrderByDescending<ContentVersionDto>(x => x.VersionDate, SqlSyntax);
var dto = Database.Fetch<MemberDto, ContentVersionDto, ContentDto, NodeDto>(sql).FirstOrDefault();
var dto = Database.Fetch<MemberDto, ContentVersionDto, ContentDto, NodeDto>(SqlSyntax.SelectTop(sql, 1)).FirstOrDefault();
if (dto == null)
return null;
@@ -365,6 +365,8 @@ namespace Umbraco.Core.Persistence.Repositories
{
foreach (var property in ((Member)entity).Properties)
{
if (keyDictionary.ContainsKey(property.PropertyTypeId) == false) continue;
property.Id = keyDictionary[property.PropertyTypeId];
}
}
@@ -622,9 +624,32 @@ namespace Umbraco.Core.Persistence.Repositories
Func<Tuple<string, object[]>> filterCallback = null;
if (filter.IsNullOrWhiteSpace() == false)
{
sbWhere.Append("AND ((umbracoNode. " + SqlSyntax.GetQuotedColumnName("text") + " LIKE @" + args.Count + ") " +
"OR (cmsMember.LoginName LIKE @0" + args.Count + "))");
args.Add("%" + filter + "%");
//This will build up the where clause - even though the same 'filter' is being
//applied to both columns, the parameters values passed to PetaPoco need to be
//duplicated, otherwise it gets confused :/
var columnFilters = new List<Tuple<string, string>>
{
new Tuple<string, string>("umbracoNode", "text"),
new Tuple<string, string>("cmsMember", "LoginName")
};
sbWhere.Append("AND (");
for (int i = 0; i < columnFilters.Count; i++)
{
sbWhere
.Append("(")
.Append(SqlSyntax.GetQuotedTableName(columnFilters[i].Item1))
.Append(".")
.Append(SqlSyntax.GetQuotedColumnName(columnFilters[i].Item2))
.Append(" LIKE @")
.Append(args.Count)
.Append(") ");
args.Add(string.Format("%{0}%", filter));
if (i < (columnFilters.Count - 1))
{
sbWhere.Append("OR ");
}
}
sbWhere.Append(")");
filterCallback = () => new Tuple<string, object[]>(sbWhere.ToString().Trim(), args.ToArray());
}
@@ -24,7 +24,7 @@ namespace Umbraco.Core.Persistence.Repositories
var sql = GetBaseQuery(false);
sql.Where(GetBaseWhereClause(), new { Id = id });
var dto = Database.First<MigrationDto>(sql);
var dto = Database.Fetch<MigrationDto>(SqlSyntax.SelectTop(sql, 1)).FirstOrDefault();
if (dto == null)
return null;
@@ -17,8 +17,7 @@ namespace Umbraco.Core.Persistence.Repositories
{
public PublicAccessRepository(IDatabaseUnitOfWork work, CacheHelper cache, ILogger logger, ISqlSyntaxProvider sqlSyntax)
: base(work, cache, logger, sqlSyntax)
{
}
{ }
private FullDataSetRepositoryCachePolicyFactory<PublicAccessEntry, Guid> _cachePolicyFactory;
protected override IRepositoryCachePolicyFactory<PublicAccessEntry, Guid> CachePolicyFactory
@@ -46,6 +45,8 @@ namespace Umbraco.Core.Persistence.Repositories
sql.Where("umbracoAccess.id IN (@ids)", new { ids = ids });
}
sql.OrderBy<AccessDto>(x => x.NodeId, SqlSyntax);
var factory = new PublicAccessEntryFactory();
var dtos = Database.Fetch<AccessDto, AccessRuleDto, AccessDto>(new AccessRulesRelator().Map, sql);
return dtos.Select(factory.BuildEntity);
@@ -69,7 +70,7 @@ namespace Umbraco.Core.Persistence.Repositories
.From<AccessDto>(SqlSyntax)
.LeftJoin<AccessRuleDto>(SqlSyntax)
.On<AccessDto, AccessRuleDto>(SqlSyntax, left => left.Id, right => right.AccessId);
return sql;
}
@@ -162,7 +163,5 @@ namespace Umbraco.Core.Persistence.Repositories
{
return entity.Key;
}
}
}
@@ -49,6 +49,10 @@ namespace Umbraco.Core.Persistence.Repositories
INNER JOIN umbracoNode as TB2 ON TB1.nodeId = TB2.id
WHERE TB2.trashed = '1' AND TB2.nodeObjectType = @NodeObjectType)",
FormatDeleteStatement("umbracoAccess", "nodeId"),
@"DELETE FROM umbracoRedirectUrl WHERE umbracoRedirectUrl.id IN(
SELECT TB1.id FROM umbracoRedirectUrl as TB1
INNER JOIN umbracoNode as TB2 ON TB1.contentKey = TB2.uniqueId
WHERE TB2.trashed = '1' AND TB2.nodeObjectType = @NodeObjectType)",
FormatDeleteStatement("umbracoRelation", "parentId"),
FormatDeleteStatement("umbracoRelation", "childId"),
FormatDeleteStatement("cmsTagRelationship", "nodeId"),
@@ -1,6 +1,8 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Cryptography;
using System.Text;
using Umbraco.Core.Logging;
using Umbraco.Core.Models;
using Umbraco.Core.Models.Rdbms;
@@ -10,9 +12,9 @@ using Umbraco.Core.Persistence.UnitOfWork;
namespace Umbraco.Core.Persistence.Repositories
{
internal class RedirectUrlRepository : PetaPocoRepositoryBase<int, IRedirectUrl>, IRedirectUrlRepository
internal class RedirectUrlRepository : PetaPocoRepositoryBase<Guid, IRedirectUrl>, IRedirectUrlRepository
{
public RedirectUrlRepository(IDatabaseUnitOfWork work, CacheHelper cache, ILogger logger, ISqlSyntaxProvider sqlSyntax)
public RedirectUrlRepository(IDatabaseUnitOfWork work, CacheHelper cache, ILogger logger, ISqlSyntaxProvider sqlSyntax)
: base(work, cache, logger, sqlSyntax)
{ }
@@ -21,19 +23,19 @@ namespace Umbraco.Core.Persistence.Repositories
throw new NotSupportedException("This repository does not support this method.");
}
protected override bool PerformExists(int id)
protected override bool PerformExists(Guid id)
{
return PerformGet(id) != null;
}
protected override IRedirectUrl PerformGet(int id)
protected override IRedirectUrl PerformGet(Guid id)
{
var sql = GetBaseQuery(false).Where<RedirectUrlDto>(x => x.Id == id);
var dto = Database.Fetch<RedirectUrlDto>(sql).FirstOrDefault();
var dto = Database.Fetch<RedirectUrlDto>(SqlSyntax.SelectTop(sql, 1)).FirstOrDefault();
return dto == null ? null : Map(dto);
}
protected override IEnumerable<IRedirectUrl> PerformGetAll(params int[] ids)
protected override IEnumerable<IRedirectUrl> PerformGetAll(params Guid[] ids)
{
if (ids.Length > 2000)
throw new NotSupportedException("This repository does not support more than 2000 ids.");
@@ -50,7 +52,14 @@ namespace Umbraco.Core.Persistence.Repositories
protected override Sql GetBaseQuery(bool isCount)
{
var sql = new Sql();
sql.Select(isCount ? "COUNT(*)" : "*").From<RedirectUrlDto>(SqlSyntax);
if (isCount)
sql.Select(@"COUNT(*)
FROM umbracoRedirectUrl
JOIN umbracoNode ON umbracoRedirectUrl.contentKey=umbracoNode.uniqueID");
else
sql.Select(@"umbracoRedirectUrl.*, umbracoNode.id AS contentId
FROM umbracoRedirectUrl
JOIN umbracoNode ON umbracoRedirectUrl.contentKey=umbracoNode.uniqueID");
return sql;
}
@@ -77,7 +86,7 @@ namespace Umbraco.Core.Persistence.Repositories
{
var dto = Map(entity);
Database.Insert(dto);
entity.Id = dto.Id;
entity.Id = entity.Key.GetHashCode();
}
protected override void PersistUpdatedItem(IRedirectUrl entity)
@@ -92,10 +101,11 @@ namespace Umbraco.Core.Persistence.Repositories
return new RedirectUrlDto
{
Id = redirectUrl.Id,
ContentId = redirectUrl.ContentId,
Id = redirectUrl.Key,
ContentKey = redirectUrl.ContentKey,
CreateDateUtc = redirectUrl.CreateDateUtc,
Url = redirectUrl.Url
Url = redirectUrl.Url,
UrlHash = redirectUrl.Url.ToSHA1()
};
}
@@ -107,8 +117,10 @@ namespace Umbraco.Core.Persistence.Repositories
try
{
url.DisableChangeTracking();
url.Id = dto.Id;
url.Key = dto.Id;
url.Id = dto.Id.GetHashCode();
url.ContentId = dto.ContentId;
url.ContentKey = dto.ContentKey;
url.CreateDateUtc = dto.CreateDateUtc;
url.Url = dto.Url;
return url;
@@ -119,9 +131,10 @@ namespace Umbraco.Core.Persistence.Repositories
}
}
public IRedirectUrl Get(string url, int contentId)
public IRedirectUrl Get(string url, Guid contentKey)
{
var sql = GetBaseQuery(false).Where<RedirectUrlDto>(x => x.Url == url && x.ContentId == contentId);
var urlHash = url.ToSHA1();
var sql = GetBaseQuery(false).Where<RedirectUrlDto>(x => x.Url == url && x.UrlHash == urlHash && x.ContentKey == contentKey);
var dto = Database.Fetch<RedirectUrlDto>(sql).FirstOrDefault();
return dto == null ? null : Map(dto);
}
@@ -131,34 +144,40 @@ namespace Umbraco.Core.Persistence.Repositories
Database.Execute("DELETE FROM umbracoRedirectUrl");
}
public void DeleteContentUrls(int contentId)
public void DeleteContentUrls(Guid contentKey)
{
Database.Execute("DELETE FROM umbracoRedirectUrl WHERE contentId=@contentId", new { contentId });
Database.Execute("DELETE FROM umbracoRedirectUrl WHERE contentKey=@contentKey", new { contentKey });
}
public void Delete(int id)
public void Delete(Guid id)
{
Database.Delete<RedirectUrlDto>(id);
}
public IRedirectUrl GetMostRecentUrl(string url)
{
var dtos = Database.Fetch<RedirectUrlDto>("SELECT * FROM umbracoRedirectUrl WHERE url=@url ORDER BY createDateUtc DESC;",
new { url });
var urlHash = url.ToSHA1();
var sql = GetBaseQuery(false)
.Where<RedirectUrlDto>(x => x.Url == url && x.UrlHash == urlHash)
.OrderByDescending<RedirectUrlDto>(x => x.CreateDateUtc, SqlSyntax);
var dtos = Database.Fetch<RedirectUrlDto>(sql);
var dto = dtos.FirstOrDefault();
return dto == null ? null : Map(dto);
}
public IEnumerable<IRedirectUrl> GetContentUrls(int contentId)
public IEnumerable<IRedirectUrl> GetContentUrls(Guid contentKey)
{
var dtos = Database.Fetch<RedirectUrlDto>("SELECT * FROM umbracoRedirectUrl WHERE contentId=@id ORDER BY createDateUtc DESC;",
new { id = contentId });
var sql = GetBaseQuery(false)
.Where<RedirectUrlDto>(x => x.ContentKey == contentKey)
.OrderByDescending<RedirectUrlDto>(x => x.CreateDateUtc, SqlSyntax);
var dtos = Database.Fetch<RedirectUrlDto>(sql);
return dtos.Select(Map);
}
public IEnumerable<IRedirectUrl> GetAllUrls(long pageIndex, int pageSize, out long total)
{
var sql = GetBaseQuery(false).OrderByDescending<RedirectUrlDto>(x => x.CreateDateUtc, SqlSyntax);
var sql = GetBaseQuery(false)
.OrderByDescending<RedirectUrlDto>(x => x.CreateDateUtc, SqlSyntax);
var result = Database.Page<RedirectUrlDto>(pageIndex + 1, pageSize, sql);
total = Convert.ToInt32(result.TotalItems);
return result.Items.Select(Map);
@@ -167,8 +186,7 @@ namespace Umbraco.Core.Persistence.Repositories
public IEnumerable<IRedirectUrl> GetAllUrls(int rootContentId, long pageIndex, int pageSize, out long total)
{
var sql = GetBaseQuery(false)
.InnerJoin<NodeDto>(SqlSyntax).On<NodeDto, RedirectUrlDto>(SqlSyntax, left => left.NodeId, right => right.ContentId)
.Where("umbracoNode.path LIKE @path", new { path = "%," + rootContentId + ",%" })
.Where(string.Format("{0}.{1} LIKE @path", SqlSyntax.GetQuotedTableName("umbracoNode"), SqlSyntax.GetQuotedColumnName("path")), new { path = "%," + rootContentId + ",%" })
.OrderByDescending<RedirectUrlDto>(x => x.CreateDateUtc, SqlSyntax);
var result = Database.Page<RedirectUrlDto>(pageIndex + 1, pageSize, sql);
total = Convert.ToInt32(result.TotalItems);
@@ -176,5 +194,17 @@ namespace Umbraco.Core.Persistence.Repositories
var rules = result.Items.Select(Map);
return rules;
}
public IEnumerable<IRedirectUrl> SearchUrls(string searchTerm, long pageIndex, int pageSize, out long total)
{
var sql = GetBaseQuery(false)
.Where(string.Format("{0}.{1} LIKE @url", SqlSyntax.GetQuotedTableName("umbracoRedirectUrl"), SqlSyntax.GetQuotedColumnName("Url")), new { url = "%" + searchTerm.Trim().ToLowerInvariant() + "%" })
.OrderByDescending<RedirectUrlDto>(x => x.CreateDateUtc, SqlSyntax);
var result = Database.Page<RedirectUrlDto>(pageIndex + 1, pageSize, sql);
total = Convert.ToInt32(result.TotalItems);
var rules = result.Items.Select(Map);
return rules;
}
}
}
@@ -33,7 +33,7 @@ namespace Umbraco.Core.Persistence.Repositories
var sql = GetBaseQuery(false);
sql.Where(GetBaseWhereClause(), new { Id = id });
var dto = Database.FirstOrDefault<RelationDto>(sql);
var dto = Database.Fetch<RelationDto>(SqlSyntax.SelectTop(sql, 1)).FirstOrDefault();
if (dto == null)
return null;
@@ -31,7 +31,7 @@ namespace Umbraco.Core.Persistence.Repositories
var sql = GetBaseQuery(false);
sql.Where(GetBaseWhereClause(), new { Id = id });
var dto = Database.FirstOrDefault<RelationTypeDto>(sql);
var dto = Database.Fetch<RelationTypeDto>(SqlSyntax.SelectTop(sql, 1)).FirstOrDefault();
if (dto == null)
return null;
@@ -1,7 +1,6 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Umbraco.Core.Logging;
using Umbraco.Core.Models;
using Umbraco.Core.Models.EntityBase;
@@ -26,7 +25,7 @@ namespace Umbraco.Core.Persistence.Repositories
var sql = GetBaseQuery(false);
sql.Where(GetBaseWhereClause(), new { Id = id });
var tagDto = Database.Fetch<TagDto>(sql).FirstOrDefault();
var tagDto = Database.Fetch<TagDto>(SqlSyntax.SelectTop(sql, 1)).FirstOrDefault();
if (tagDto == null)
return null;
@@ -25,7 +25,7 @@ namespace Umbraco.Core.Persistence.Repositories
var sql = GetBaseQuery(false);
sql.Where(GetBaseWhereClause(), new { Id = id });
var taskDto = Database.Fetch<TaskDto, TaskTypeDto>(sql).FirstOrDefault();
var taskDto = Database.Fetch<TaskDto, TaskTypeDto>(SqlSyntax.SelectTop(sql, 1)).FirstOrDefault();
if (taskDto == null)
return null;
@@ -23,7 +23,7 @@ namespace Umbraco.Core.Persistence.Repositories
var sql = GetBaseQuery(false);
sql.Where(GetBaseWhereClause(), new { Id = id });
var taskDto = Database.Fetch<TaskTypeDto>(sql).FirstOrDefault();
var taskDto = Database.Fetch<TaskTypeDto>(SqlSyntax.SelectTop(sql, 1)).FirstOrDefault();
if (taskDto == null)
return null;
@@ -236,7 +236,7 @@ namespace Umbraco.Core.Persistence.Repositories
private Sql GetFilteredSqlForPagedResults(Sql sql, Func<Tuple<string, object[]>> defaultFilter = null)
{
Sql filteredSql;
// Apply filter
if (defaultFilter != null)
{
@@ -262,7 +262,7 @@ namespace Umbraco.Core.Persistence.Repositories
return filteredSql;
}
private Sql GetSortedSqlForPagedResults(Sql sql, Direction orderDirection, string orderBy, bool orderBySystemField)
private Sql GetSortedSqlForPagedResults(Sql sql, Direction orderDirection, string orderBy, bool orderBySystemField, Tuple<string, string> nodeIdSelect)
{
//copy to var so that the original isn't changed
@@ -286,44 +286,71 @@ namespace Umbraco.Core.Persistence.Repositories
}
else
{
// Sorting by a custom field, so set-up sub-query for ORDER BY clause to pull through valie
// Sorting by a custom field, so set-up sub-query for ORDER BY clause to pull through value
// from most recent content version for the given order by field
var sortedInt = string.Format(SqlSyntax.ConvertIntegerToOrderableString, "dataInt");
var sortedDate = string.Format(SqlSyntax.ConvertDateToOrderableString, "dataDate");
var sortedString = string.Format("COALESCE({0},'')", "dataNvarchar");
var sortedDecimal = string.Format(SqlSyntax.ConvertDecimalToOrderableString, "dataDecimal");
var innerJoinTempTable = string.Format(@"INNER JOIN (
SELECT CASE
WHEN dataInt Is Not Null THEN {0}
WHEN dataDecimal Is Not Null THEN {1}
WHEN dataDate Is Not Null THEN {2}
ELSE {3}
END AS CustomPropVal,
cd.nodeId AS CustomPropValContentId
FROM cmsDocument cd
INNER JOIN cmsPropertyData cpd ON cpd.contentNodeId = cd.nodeId AND cpd.versionId = cd.versionId
INNER JOIN cmsPropertyType cpt ON cpt.Id = cpd.propertytypeId
WHERE cpt.Alias = @{4} AND cd.newest = 1) AS CustomPropData
ON CustomPropData.CustomPropValContentId = umbracoNode.id
", sortedInt, sortedDecimal, sortedDate, sortedString, sortedSql.Arguments.Length);
//these are defaults that will be used in the query - they can be overridden for non-versioned entities or document entities
var versionQuery = " AND cpd.versionId = cd.versionId";
var newestQuery = string.Empty;
//cmsDocument needs to filter by the 'newest' parameter in the query
if (nodeIdSelect.Item1 == "cmsDocument")
newestQuery = " AND cd.newest = 1";
//members do not use versions so clear the versionQuery string
if (nodeIdSelect.Item1 == "cmsMember")
versionQuery = string.Empty;
//needs to be an outer join since there's no guarantee that any of the nodes have values for this property
var outerJoinTempTable = string.Format(@"LEFT OUTER JOIN (
SELECT CASE
WHEN dataInt Is Not Null THEN {0}
WHEN dataDecimal Is Not Null THEN {1}
WHEN dataDate Is Not Null THEN {2}
ELSE {3}
END AS CustomPropVal,
cd.{4} AS CustomPropValContentId
FROM {5} cd
INNER JOIN cmsPropertyData cpd ON cpd.contentNodeId = cd.{4}{6}
INNER JOIN cmsPropertyType cpt ON cpt.Id = cpd.propertytypeId
WHERE cpt.Alias = @{7}{8}) AS CustomPropData
ON CustomPropData.CustomPropValContentId = umbracoNode.id
", sortedInt, sortedDecimal, sortedDate, sortedString, nodeIdSelect.Item2, nodeIdSelect.Item1, versionQuery, sortedSql.Arguments.Length, newestQuery);
//insert this just above the first LEFT OUTER JOIN (for cmsDocument) or the last WHERE (everything else)
string newSql;
if (nodeIdSelect.Item1 == "cmsDocument")
newSql = sortedSql.SQL.Insert(sortedSql.SQL.IndexOf("LEFT OUTER JOIN"), outerJoinTempTable);
else
newSql = sortedSql.SQL.Insert(sortedSql.SQL.LastIndexOf("WHERE"), outerJoinTempTable);
//insert this just above the LEFT OUTER JOIN
var newSql = sortedSql.SQL.Insert(sortedSql.SQL.IndexOf("LEFT OUTER JOIN"), innerJoinTempTable);
var newArgs = sortedSql.Arguments.ToList();
newArgs.Add(orderBy);
sortedSql = new Sql(newSql, newArgs.ToArray());
sortedSql.OrderBy("CustomPropData.CustomPropVal");
if (orderDirection == Direction.Descending)
{
sortedSql.Append(" DESC");
}
sortedSql.OrderByDescending("CustomPropData.CustomPropVal");
}
else
{
sortedSql.OrderBy("CustomPropData.CustomPropVal");
}
}
//no matter what we always MUST order the result also by umbracoNode.id to ensure that all records being ordered by are unique.
// if we do not do this then we end up with issues where we are ordering by a field that has duplicate values (i.e. the 'text' column
// is empty for many nodes)
// see: http://issues.umbraco.org/issue/U4-8831
sortedSql.OrderBy("umbracoNode.id");
return sortedSql;
}
/// <summary>
@@ -371,7 +398,7 @@ namespace Umbraco.Core.Persistence.Repositories
//get sorted and filtered sql
var sqlNodeIdsWithSort = GetSortedSqlForPagedResults(
GetFilteredSqlForPagedResults(sqlNodeIds, defaultFilter),
orderDirection, orderBy, orderBySystemField);
orderDirection, orderBy, orderBySystemField, nodeIdSelect);
// Get page of results and total count
IEnumerable<TEntity> result;
@@ -409,7 +436,7 @@ namespace Umbraco.Core.Persistence.Repositories
//get sorted and filtered sql
var fullQuery = GetSortedSqlForPagedResults(
GetFilteredSqlForPagedResults(withInnerJoinSql, defaultFilter),
orderDirection, orderBy, orderBySystemField);
orderDirection, orderBy, orderBySystemField, nodeIdSelect);
return processQuery(fullQuery);
}
else
@@ -483,17 +510,6 @@ WHERE EXISTS(
var propertyFactory = new PropertyFactory(compositionProperties, def.Version, def.Id, def.CreateDate, def.VersionDate);
var properties = propertyFactory.BuildEntity(propertyDataDtos.ToArray()).ToArray();
var newProperties = properties.Where(x => x.HasIdentity == false && x.PropertyType.HasIdentity);
foreach (var property in newProperties)
{
var propertyDataDto = new PropertyDataDto { NodeId = def.Id, PropertyTypeId = property.PropertyTypeId, VersionId = def.Version };
int primaryKey = Convert.ToInt32(Database.Insert(propertyDataDto));
property.Version = def.Version;
property.Id = primaryKey;
}
foreach (var property in properties)
{
//NOTE: The benchmarks run with and without the following code show very little change so this is not a perf bottleneck
@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
using Umbraco.Core.Models.Rdbms;
using Umbraco.Core.Persistence.DatabaseAnnotations;
using Umbraco.Core.Persistence.DatabaseModelDefinitions;
using Umbraco.Core.Persistence.Querying;
@@ -66,6 +67,7 @@ namespace Umbraco.Core.Persistence.SqlSyntax
string Format(ForeignKeyDefinition foreignKey);
string FormatColumnRename(string tableName, string oldName, string newName);
string FormatTableRename(string oldName, string newName);
Sql SelectTop(Sql sql, int top);
bool SupportsClustered();
bool SupportsIdentityInsert();
bool? SupportsCaseInsensitiveQueries(Database db);
@@ -29,7 +29,11 @@ namespace Umbraco.Core.Persistence.SqlSyntax
public override string GetQuotedTableName(string tableName)
{
return string.Format("[{0}]", tableName);
if (tableName.Contains(".") == false)
return string.Format("[{0}]", tableName);
var tableNameParts = tableName.Split(new[] { '.' }, 2);
return string.Format("[{0}].[{1}]", tableNameParts[0], tableNameParts[1]);
}
public override string GetQuotedColumnName(string columnName)
@@ -10,7 +10,7 @@ namespace Umbraco.Core.Persistence.SqlSyntax
/// <summary>
/// Represents an SqlSyntaxProvider for MySql
/// </summary>
[SqlSyntaxProviderAttribute("MySql.Data.MySqlClient")]
[SqlSyntaxProvider(Constants.DatabaseProviders.MySql)]
public class MySqlSyntaxProvider : SqlSyntaxProviderBase<MySqlSyntaxProvider>
{
private readonly ILogger _logger;
@@ -178,6 +178,11 @@ ORDER BY TABLE_NAME, INDEX_NAME",
return result > 0;
}
public override Sql SelectTop(Sql sql, int top)
{
return new Sql(string.Concat(sql.SQL, " LIMIT ", top), sql.Arguments);
}
public override bool SupportsClustered()
{
return true;
@@ -10,7 +10,7 @@ namespace Umbraco.Core.Persistence.SqlSyntax
/// <summary>
/// Represents an SqlSyntaxProvider for Sql Ce
/// </summary>
[SqlSyntaxProviderAttribute("System.Data.SqlServerCe.4.0")]
[SqlSyntaxProviderAttribute(Constants.DatabaseProviders.SqlCe)]
public class SqlCeSyntaxProvider : MicrosoftSqlSyntaxProviderBase<SqlCeSyntaxProvider>
{
public SqlCeSyntaxProvider()
@@ -18,6 +18,11 @@ namespace Umbraco.Core.Persistence.SqlSyntax
}
public override Sql SelectTop(Sql sql, int top)
{
return new Sql(sql.SQL.Insert(sql.SQL.IndexOf(' '), " TOP " + top), sql.Arguments);
}
public override bool SupportsClustered()
{
return false;
@@ -8,18 +8,63 @@ namespace Umbraco.Core.Persistence.SqlSyntax
/// <summary>
/// Represents an SqlSyntaxProvider for Sql Server
/// </summary>
[SqlSyntaxProviderAttribute("System.Data.SqlClient")]
[SqlSyntaxProviderAttribute(Constants.DatabaseProviders.SqlServer)]
public class SqlServerSyntaxProvider : MicrosoftSqlSyntaxProviderBase<SqlServerSyntaxProvider>
{
public SqlServerSyntaxProvider()
{
}
}
/// <summary>
/// Gets/sets the version of the current SQL server instance
/// </summary>
internal Lazy<SqlServerVersionName> VersionName { get; set; }
internal SqlServerVersionName GetVersionName(Database database)
{
if (_versionName.HasValue)
return _versionName.Value;
try
{
var version = database.ExecuteScalar<string>("SELECT SERVERPROPERTY('productversion')");
var firstPart = version.Split('.')[0];
switch (firstPart)
{
case "13":
_versionName = SqlServerVersionName.V2014;
break;
case "12":
_versionName = SqlServerVersionName.V2014;
break;
case "11":
_versionName = SqlServerVersionName.V2012;
break;
case "10":
_versionName = SqlServerVersionName.V2008;
break;
case "9":
_versionName = SqlServerVersionName.V2005;
break;
case "8":
_versionName = SqlServerVersionName.V2000;
break;
case "7":
_versionName = SqlServerVersionName.V7;
break;
default:
_versionName = SqlServerVersionName.Other;
break;
}
}
catch (Exception)
{
_versionName = SqlServerVersionName.Invalid;
}
return _versionName.Value;
}
private SqlServerVersionName? _versionName;
/// <summary>
/// SQL Server stores default values assigned to columns as constraints, it also stores them with named values, this is the only
@@ -104,6 +149,11 @@ order by T.name, I.name");
return column.IsIdentity ? GetIdentityString(column) : string.Empty;
}
public override Sql SelectTop(Sql sql, int top)
{
return new Sql(sql.SQL.Insert(sql.SQL.IndexOf(' '), " TOP " + top), sql.Arguments);
}
private static string GetIdentityString(ColumnDefinition column)
{
return "IDENTITY(1,1)";
@@ -3,6 +3,9 @@
/// <summary>
/// Represents the version name of SQL server (i.e. the year 2008, 2005, etc...)
/// </summary>
/// <remarks>
/// see: https://support.microsoft.com/en-us/kb/321185
/// </remarks>
internal enum SqlServerVersionName
{
Invalid = -1,
@@ -11,6 +14,8 @@
V2005 = 2,
V2008 = 3,
V2012 = 4,
Other = 5
V2014 = 5,
V2016 = 6,
Other = 100
}
}
@@ -503,6 +503,8 @@ namespace Umbraco.Core.Persistence.SqlSyntax
protected abstract string FormatIdentity(ColumnDefinition column);
public abstract Sql SelectTop(Sql sql, int top);
public virtual string DeleteDefaultConstraint
{
get
@@ -22,12 +22,23 @@
/// <remarks>
/// See: http://issues.umbraco.org/issue/U4-3876
/// </remarks>
public static Sql GetDeleteSubquery(this ISqlSyntaxProvider sqlProvider, string tableName, string columnName, Sql subQuery)
public static Sql GetDeleteSubquery(this ISqlSyntaxProvider sqlProvider, string tableName, string columnName, Sql subQuery, WhereInType whereInType = WhereInType.In)
{
return new Sql(string.Format(@"DELETE FROM {0} WHERE {1} IN (SELECT {1} FROM ({2}) x)",
return
new Sql(string.Format(
whereInType == WhereInType.In
? @"DELETE FROM {0} WHERE {1} IN (SELECT {1} FROM ({2}) x)"
: @"DELETE FROM {0} WHERE {1} NOT IN (SELECT {1} FROM ({2}) x)",
sqlProvider.GetQuotedTableName(tableName),
sqlProvider.GetQuotedColumnName(columnName),
subQuery.SQL), subQuery.Arguments);
}
}
internal enum WhereInType
{
In,
NotIn
}
}
@@ -6,6 +6,7 @@ using System.Linq;
using System.Text;
using StackExchange.Profiling;
using Umbraco.Core.Logging;
using Umbraco.Core.Persistence.SqlSyntax;
namespace Umbraco.Core.Persistence
{
@@ -154,5 +155,41 @@ namespace Umbraco.Core.Persistence
}
base.OnExecutedCommand(cmd);
}
/// <summary>
/// We are overriding this in the case that we are using SQL Server 2012+ so we can make paging more efficient than the default PetaPoco paging
/// see: http://issues.umbraco.org/issue/U4-8837
/// </summary>
/// <param name="sql"></param>
/// <param name="sqlSelectRemoved"></param>
/// <param name="sqlOrderBy"></param>
/// <param name="args"></param>
/// <param name="sqlPage"></param>
/// <param name="databaseType"></param>
/// <param name="skip"></param>
/// <param name="take"></param>
internal override void BuildSqlDbSpecificPagingQuery(DBType databaseType, long skip, long take, string sql, string sqlSelectRemoved, string sqlOrderBy, ref object[] args, out string sqlPage)
{
if (databaseType == DBType.SqlServer)
{
//we need to check it's version to see what kind of paging format we can use
//TODO: This is a hack, but we don't have access to the SqlSyntaxProvider here, we can in v8 but not now otherwise
// this would be a breaking change.
var sqlServerSyntax = SqlSyntaxContext.SqlSyntaxProvider as SqlServerSyntaxProvider;
if (sqlServerSyntax != null)
{
if ((int) sqlServerSyntax.GetVersionName(this) >= (int) SqlServerVersionName.V2012)
{
//we can use the good paging! to do that we are going to change the databaseType to SQLCE since
//it also uses the good paging syntax.
base.BuildSqlDbSpecificPagingQuery(DBType.SqlServerCE, skip, take, sql, sqlSelectRemoved, sqlOrderBy, ref args, out sqlPage);
return;
}
}
}
//use the defaults
base.BuildSqlDbSpecificPagingQuery(databaseType, skip, take, sql, sqlSelectRemoved, sqlOrderBy, ref args, out sqlPage);
}
}
}
@@ -28,7 +28,7 @@ namespace Umbraco.Core.PropertyEditors.ValueConverters
// </values>
// </keyFeatureList>
var sourceString = source.ToString();
var sourceString = source != null ? source.ToString() : null;
if (string.IsNullOrWhiteSpace(sourceString)) return Enumerable.Empty<string>();
//SD: I have no idea why this logic is here, I'm pretty sure we've never saved the multiple txt string
@@ -17,8 +17,15 @@ namespace Umbraco.Core.Publishing
_contentService = contentService;
}
public void CheckPendingAndProcess()
/// <summary>
/// Processes scheduled operations
/// </summary>
/// <returns>
/// Returns the number of items successfully completed
/// </returns>
public int CheckPendingAndProcess()
{
var counter = 0;
foreach (var d in _contentService.GetContentForRelease())
{
try
@@ -32,10 +39,14 @@ namespace Umbraco.Core.Publishing
LogHelper.Error<ScheduledPublisher>("Could not published the document (" + d.Id + ") based on it's scheduled release, status result: " + result.Result.StatusType, result.Exception);
}
else
{
{
LogHelper.Warn<ScheduledPublisher>("Could not published the document (" + d.Id + ") based on it's scheduled release. Status result: " + result.Result.StatusType);
}
}
else
{
counter++;
}
}
catch (Exception ee)
{
@@ -48,7 +59,11 @@ namespace Umbraco.Core.Publishing
try
{
d.ExpireDate = null;
_contentService.UnPublish(d, (int)d.GetWriterProfile().Id);
var result = _contentService.UnPublish(d, (int)d.GetWriterProfile().Id);
if (result)
{
counter++;
}
}
catch (Exception ee)
{
@@ -56,6 +71,8 @@ namespace Umbraco.Core.Publishing
throw;
}
}
return counter;
}
}
}
@@ -0,0 +1,31 @@
using System.Configuration;
using System.DirectoryServices.AccountManagement;
using System.Threading.Tasks;
using Umbraco.Core.Models.Identity;
namespace Umbraco.Core.Security
{
public class ActiveDirectoryBackOfficeUserPasswordChecker : IBackOfficeUserPasswordChecker
{
public virtual string ActiveDirectoryDomain {
get {
return ConfigurationManager.AppSettings["ActiveDirectoryDomain"];
}
}
public Task<BackOfficeUserPasswordCheckerResult> CheckPasswordAsync(BackOfficeIdentityUser user, string password)
{
bool isValid;
using (var pc = new PrincipalContext(ContextType.Domain, ActiveDirectoryDomain))
{
isValid = pc.ValidateCredentials(user.UserName, password);
}
var result = isValid
? BackOfficeUserPasswordCheckerResult.ValidCredentials
: BackOfficeUserPasswordCheckerResult.InvalidCredentials;
return Task.FromResult(result);
}
}
}
@@ -307,11 +307,9 @@ namespace Umbraco.Core.Security
}
catch (Exception)
{
//TODO: Do we need to do more here?? need to make sure that the forms cookie is gone, but is that
// taken care of in our custom middleware somehow?
ctx.Authentication.SignOut(
Core.Constants.Security.BackOfficeAuthenticationType,
Core.Constants.Security.BackOfficeExternalAuthenticationType);
Constants.Security.BackOfficeAuthenticationType,
Constants.Security.BackOfficeExternalAuthenticationType);
return null;
}
}
@@ -7,7 +7,8 @@ using Umbraco.Core.Models.Identity;
namespace Umbraco.Core.Security
{
public class BackOfficeClaimsIdentityFactory : ClaimsIdentityFactory<BackOfficeIdentityUser, int>
public class BackOfficeClaimsIdentityFactory<T> : ClaimsIdentityFactory<T, int>
where T: BackOfficeIdentityUser
{
public BackOfficeClaimsIdentityFactory()
{
@@ -20,7 +21,7 @@ namespace Umbraco.Core.Security
/// </summary>
/// <param name="manager"/><param name="user"/><param name="authenticationType"/>
/// <returns/>
public override async Task<ClaimsIdentity> CreateAsync(UserManager<BackOfficeIdentityUser, int> manager, BackOfficeIdentityUser user, string authenticationType)
public override async Task<ClaimsIdentity> CreateAsync(UserManager<T, int> manager, T user, string authenticationType)
{
var baseIdentity = await base.CreateAsync(manager, user, authenticationType);
@@ -42,4 +43,9 @@ namespace Umbraco.Core.Security
return umbracoIdentity;
}
}
public class BackOfficeClaimsIdentityFactory : BackOfficeClaimsIdentityFactory<BackOfficeIdentityUser>
{
}
}
@@ -17,7 +17,7 @@ namespace Umbraco.Core.Security
private readonly ILogger _logger;
private readonly IOwinRequest _request;
public BackOfficeSignInManager(BackOfficeUserManager userManager, IAuthenticationManager authenticationManager, ILogger logger, IOwinRequest request)
public BackOfficeSignInManager(UserManager<BackOfficeIdentityUser, int> userManager, IAuthenticationManager authenticationManager, ILogger logger, IOwinRequest request)
: base(userManager, authenticationManager)
{
if (logger == null) throw new ArgumentNullException("logger");
@@ -29,13 +29,13 @@ namespace Umbraco.Core.Security
public override Task<ClaimsIdentity> CreateUserIdentityAsync(BackOfficeIdentityUser user)
{
return user.GenerateUserIdentityAsync((BackOfficeUserManager)UserManager);
return user.GenerateUserIdentityAsync((BackOfficeUserManager<BackOfficeIdentityUser>)UserManager);
}
public static BackOfficeSignInManager Create(IdentityFactoryOptions<BackOfficeSignInManager> options, IOwinContext context, ILogger logger)
{
return new BackOfficeSignInManager(
context.GetUserManager<BackOfficeUserManager>(),
context.GetBackOfficeUserManager(),
context.Authentication,
logger,
context.Request);
@@ -46,7 +46,7 @@ namespace Umbraco.Core.Security
/// </summary>
/// <param name="userName"/><param name="password"/><param name="isPersistent"/><param name="shouldLockout"/>
/// <returns/>
public async override Task<SignInStatus> PasswordSignInAsync(string userName, string password, bool isPersistent, bool shouldLockout)
public override async Task<SignInStatus> PasswordSignInAsync(string userName, string password, bool isPersistent, bool shouldLockout)
{
var result = await base.PasswordSignInAsync(userName, password, isPersistent, shouldLockout);
@@ -5,6 +5,7 @@ using System.Threading.Tasks;
using System.Web.Security;
using Microsoft.AspNet.Identity;
using Microsoft.AspNet.Identity.Owin;
using Microsoft.Owin.Security.DataProtection;
using Umbraco.Core.Models.Identity;
using Umbraco.Core.Services;
@@ -15,6 +16,8 @@ namespace Umbraco.Core.Security
/// </summary>
public class BackOfficeUserManager : BackOfficeUserManager<BackOfficeIdentityUser>
{
public const string OwinMarkerKey = "Umbraco.Web.Security.Identity.BackOfficeUserManagerMarker";
public BackOfficeUserManager(IUserStore<BackOfficeIdentityUser, int> store)
: base(store)
{
@@ -26,9 +29,8 @@ namespace Umbraco.Core.Security
MembershipProviderBase membershipProvider)
: base(store)
{
if (options == null) throw new ArgumentNullException("options");
var manager = new BackOfficeUserManager(store);
InitUserManager(manager, membershipProvider, options);
if (options == null) throw new ArgumentNullException("options");;
InitUserManager(this, membershipProvider, options);
}
#region Static Create methods
@@ -69,7 +71,7 @@ namespace Umbraco.Core.Security
{
var manager = new BackOfficeUserManager(customUserStore, options, membershipProvider);
return manager;
}
}
#endregion
/// <summary>
@@ -79,65 +81,15 @@ namespace Umbraco.Core.Security
/// <param name="membershipProvider"></param>
/// <param name="options"></param>
/// <returns></returns>
protected void InitUserManager(BackOfficeUserManager manager, MembershipProviderBase membershipProvider, IdentityFactoryOptions<BackOfficeUserManager> options)
protected void InitUserManager(
BackOfficeUserManager manager,
MembershipProviderBase membershipProvider,
IdentityFactoryOptions<BackOfficeUserManager> options)
{
// Configure validation logic for usernames
manager.UserValidator = new UserValidator<BackOfficeIdentityUser, int>(manager)
{
AllowOnlyAlphanumericUserNames = false,
RequireUniqueEmail = true
};
// Configure validation logic for passwords
manager.PasswordValidator = new PasswordValidator
{
RequiredLength = membershipProvider.MinRequiredPasswordLength,
RequireNonLetterOrDigit = membershipProvider.MinRequiredNonAlphanumericCharacters > 0,
RequireDigit = false,
RequireLowercase = false,
RequireUppercase = false
//TODO: Do we support the old regex match thing that membership providers used?
};
//use a custom hasher based on our membership provider
manager.PasswordHasher = new MembershipPasswordHasher(membershipProvider);
var dataProtectionProvider = options.DataProtectionProvider;
if (dataProtectionProvider != null)
{
manager.UserTokenProvider = new DataProtectorTokenProvider<BackOfficeIdentityUser, int>(dataProtectionProvider.Create("ASP.NET Identity"));
}
manager.UserLockoutEnabledByDefault = true;
manager.MaxFailedAccessAttemptsBeforeLockout = membershipProvider.MaxInvalidPasswordAttempts;
//NOTE: This just needs to be in the future, we currently don't support a lockout timespan, it's either they are locked
// or they are not locked, but this determines what is set on the account lockout date which corresponds to whether they are
// locked out or not.
manager.DefaultAccountLockoutTimeSpan = TimeSpan.FromDays(30);
//custom identity factory for creating the identity object for which we auth against in the back office
manager.ClaimsIdentityFactory = new BackOfficeClaimsIdentityFactory();
manager.EmailService = new EmailService();
//NOTE: Not implementing these, if people need custom 2 factor auth, they'll need to implement their own UserStore to suport it
//// Register two factor authentication providers. This application uses Phone and Emails as a step of receiving a code for verifying the user
//// You can write your own provider and plug in here.
//manager.RegisterTwoFactorProvider("PhoneCode", new PhoneNumberTokenProvider<ApplicationUser>
//{
// MessageFormat = "Your security code is: {0}"
//});
//manager.RegisterTwoFactorProvider("EmailCode", new EmailTokenProvider<ApplicationUser>
//{
// Subject = "Security Code",
// BodyFormat = "Your security code is: {0}"
//});
//manager.SmsService = new SmsService();
//NOTE: This method is mostly here for backwards compat
base.InitUserManager(manager, membershipProvider, options.DataProtectionProvider);
}
}
/// <summary>
@@ -180,6 +132,73 @@ namespace Umbraco.Core.Security
}
#endregion
/// <summary>
/// Initializes the user manager with the correct options
/// </summary>
/// <param name="manager"></param>
/// <param name="membershipProvider"></param>
/// <param name="dataProtectionProvider"></param>
/// <returns></returns>
protected void InitUserManager(
BackOfficeUserManager<T> manager,
MembershipProviderBase membershipProvider,
IDataProtectionProvider dataProtectionProvider)
{
// Configure validation logic for usernames
manager.UserValidator = new UserValidator<T, int>(manager)
{
AllowOnlyAlphanumericUserNames = false,
RequireUniqueEmail = true
};
// Configure validation logic for passwords
manager.PasswordValidator = new PasswordValidator
{
RequiredLength = membershipProvider.MinRequiredPasswordLength,
RequireNonLetterOrDigit = membershipProvider.MinRequiredNonAlphanumericCharacters > 0,
RequireDigit = false,
RequireLowercase = false,
RequireUppercase = false
//TODO: Do we support the old regex match thing that membership providers used?
};
//use a custom hasher based on our membership provider
manager.PasswordHasher = new MembershipPasswordHasher(membershipProvider);
if (dataProtectionProvider != null)
{
manager.UserTokenProvider = new DataProtectorTokenProvider<T, int>(dataProtectionProvider.Create("ASP.NET Identity"));
}
manager.UserLockoutEnabledByDefault = true;
manager.MaxFailedAccessAttemptsBeforeLockout = membershipProvider.MaxInvalidPasswordAttempts;
//NOTE: This just needs to be in the future, we currently don't support a lockout timespan, it's either they are locked
// or they are not locked, but this determines what is set on the account lockout date which corresponds to whether they are
// locked out or not.
manager.DefaultAccountLockoutTimeSpan = TimeSpan.FromDays(30);
//custom identity factory for creating the identity object for which we auth against in the back office
manager.ClaimsIdentityFactory = new BackOfficeClaimsIdentityFactory<T>();
manager.EmailService = new EmailService();
//NOTE: Not implementing these, if people need custom 2 factor auth, they'll need to implement their own UserStore to suport it
//// Register two factor authentication providers. This application uses Phone and Emails as a step of receiving a code for verifying the user
//// You can write your own provider and plug in here.
//manager.RegisterTwoFactorProvider("PhoneCode", new PhoneNumberTokenProvider<ApplicationUser>
//{
// MessageFormat = "Your security code is: {0}"
//});
//manager.RegisterTwoFactorProvider("EmailCode", new EmailTokenProvider<ApplicationUser>
//{
// Subject = "Security Code",
// BodyFormat = "Your security code is: {0}"
//});
//manager.SmsService = new SmsService();
}
/// <summary>
/// Logic used to validate a username and password
/// </summary>
@@ -200,7 +219,7 @@ namespace Umbraco.Core.Security
/// We've allowed this check to be overridden with a simple callback so that developers don't actually
/// have to implement/override this class.
/// </remarks>
public async override Task<bool> CheckPasswordAsync(T user, string password)
public override async Task<bool> CheckPasswordAsync(T user, string password)
{
if (BackOfficeUserPasswordChecker != null)
{
@@ -0,0 +1,26 @@
using System;
using Microsoft.AspNet.Identity.Owin;
using Microsoft.Owin;
using Umbraco.Core.Models.Identity;
namespace Umbraco.Core.Security
{
/// <summary>
/// This class is only here due to the fact that IOwinContext Get / Set only work in generics, if they worked
/// with regular 'object' then we wouldn't have to use this work around but because of that we have to use this
/// class to resolve the 'real' type of the registered user manager
/// </summary>
/// <typeparam name="TManager"></typeparam>
/// <typeparam name="TUser"></typeparam>
internal class BackOfficeUserManagerMarker<TManager, TUser> : IBackOfficeUserManagerMarker
where TManager : BackOfficeUserManager<TUser>
where TUser : BackOfficeIdentityUser
{
public BackOfficeUserManager<BackOfficeIdentityUser> GetManager(IOwinContext owin)
{
var mgr = owin.Get<TManager>() as BackOfficeUserManager<BackOfficeIdentityUser>;
if (mgr == null) throw new InvalidOperationException("Could not cast the registered back office user of type " + typeof(TManager) + " to " + typeof(BackOfficeUserManager<BackOfficeIdentityUser>));
return mgr;
}
}
}
+1 -1
View File
@@ -17,7 +17,7 @@ namespace Umbraco.Core.Security
//TODO: This check could be nicer but that is the way it is currently
mailMessage.IsBodyHtml = message.Body.IsNullOrWhiteSpace() == false
&& message.Body.Contains("<") && message.Body.Contains("/>");
&& message.Body.Contains("<") && message.Body.Contains("</");
await client.SendMailAsync(mailMessage);
}
@@ -0,0 +1,15 @@
using Microsoft.Owin;
using Umbraco.Core.Models.Identity;
namespace Umbraco.Core.Security
{
/// <summary>
/// This interface is only here due to the fact that IOwinContext Get / Set only work in generics, if they worked
/// with regular 'object' then we wouldn't have to use this work around but because of that we have to use this
/// class to resolve the 'real' type of the registered user manager
/// </summary>
internal interface IBackOfficeUserManagerMarker
{
BackOfficeUserManager<BackOfficeIdentityUser> GetManager(IOwinContext owin);
}
}
@@ -0,0 +1,47 @@
using System;
using Microsoft.AspNet.Identity.Owin;
using Microsoft.Owin;
using Umbraco.Core.Models.Identity;
namespace Umbraco.Core.Security
{
public static class OwinExtensions
{
/// <summary>
/// Gets the back office sign in manager out of OWIN
/// </summary>
/// <param name="owinContext"></param>
/// <returns></returns>
public static BackOfficeSignInManager GetBackOfficeSignInManager(this IOwinContext owinContext)
{
var mgr = owinContext.Get<BackOfficeSignInManager>();
if (mgr == null)
{
throw new NullReferenceException("Could not resolve an instance of " + typeof(BackOfficeSignInManager) + " from the " + typeof(IOwinContext));
}
return mgr;
}
/// <summary>
/// Gets the back office user manager out of OWIN
/// </summary>
/// <param name="owinContext"></param>
/// <returns></returns>
/// <remarks>
/// This is required because to extract the user manager we need to user a custom service since owin only deals in generics and
/// developers could register their own user manager types
/// </remarks>
public static BackOfficeUserManager<BackOfficeIdentityUser> GetBackOfficeUserManager(this IOwinContext owinContext)
{
var marker = owinContext.Get<IBackOfficeUserManagerMarker>(BackOfficeUserManager.OwinMarkerKey);
if (marker == null) throw new NullReferenceException("No " + typeof(IBackOfficeUserManagerMarker) + " has been registered with Owin which means that no Umbraco back office user manager has been registered");
var mgr = marker.GetManager(owinContext);
if (mgr == null)
{
throw new NullReferenceException("Could not resolve an instance of " + typeof(BackOfficeUserManager<BackOfficeIdentityUser>));
}
return mgr;
}
}
}
+16 -1
View File
@@ -4,6 +4,7 @@ using System.ComponentModel;
using System.Globalization;
using System.Linq;
using System.Threading;
using System.Xml;
using System.Xml.Linq;
using Umbraco.Core.Auditing;
using Umbraco.Core.Configuration;
@@ -636,7 +637,7 @@ namespace Umbraco.Core.Services
query.Where(x => x.Path.SqlContains(string.Format(",{0},", id), TextColumnType.NVarchar));
}
var contents = repository.GetPagedResultsByQuery(query, pageIndex, pageSize, out totalChildren, orderBy, orderDirection, orderBySystemField, filter);
return contents;
}
}
@@ -1714,6 +1715,20 @@ namespace Umbraco.Core.Services
return true;
}
/// <summary>
/// This builds the Xml document used for the XML cache
/// </summary>
/// <returns></returns>
public XmlDocument BuildXmlCache()
{
var uow = UowProvider.GetUnitOfWork();
using (var repository = RepositoryFactory.CreateContentRepository(uow))
{
var result = repository.BuildXmlCache();
return result;
}
}
/// <summary>
/// Rebuilds all xml content in the cmsContentXml table for all documents
/// </summary>
@@ -803,10 +803,13 @@ namespace Umbraco.Core.Services
var uow = UowProvider.GetUnitOfWork();
using (var repository = RepositoryFactory.CreateContentTypeRepository(uow))
{
var deletedContentTypes = new List<IContentType>() {contentType};
deletedContentTypes.AddRange(contentType.Descendants().OfType<IContentType>());
repository.Delete(contentType);
uow.Commit();
DeletedContentType.RaiseEvent(new DeleteEventArgs<IContentType>(contentType, false), this);
DeletedContentType.RaiseEvent(new DeleteEventArgs<IContentType>(deletedContentTypes.DistinctBy(x => x.Id), false), this);
}
Audit(AuditType.Delete, string.Format("Delete ContentType performed by user"), userId, contentType.Id);
@@ -838,14 +841,18 @@ namespace Umbraco.Core.Services
var uow = UowProvider.GetUnitOfWork();
using (var repository = RepositoryFactory.CreateContentTypeRepository(uow))
{
var deletedContentTypes = new List<IContentType>();
deletedContentTypes.AddRange(asArray);
foreach (var contentType in asArray)
{
deletedContentTypes.AddRange(contentType.Descendants().OfType<IContentType>());
repository.Delete(contentType);
}
uow.Commit();
DeletedContentType.RaiseEvent(new DeleteEventArgs<IContentType>(asArray, false), this);
DeletedContentType.RaiseEvent(new DeleteEventArgs<IContentType>(deletedContentTypes.DistinctBy(x => x.Id), false), this);
}
Audit(AuditType.Delete, string.Format("Delete ContentTypes performed by user"), userId, -1);
@@ -1238,11 +1245,13 @@ namespace Umbraco.Core.Services
var uow = UowProvider.GetUnitOfWork();
using (var repository = RepositoryFactory.CreateMediaTypeRepository(uow))
{
var deletedMediaTypes = new List<IMediaType>() {mediaType};
deletedMediaTypes.AddRange(mediaType.Descendants().OfType<IMediaType>());
repository.Delete(mediaType);
uow.Commit();
DeletedMediaType.RaiseEvent(new DeleteEventArgs<IMediaType>(mediaType, false), this);
DeletedMediaType.RaiseEvent(new DeleteEventArgs<IMediaType>(deletedMediaTypes.DistinctBy(x => x.Id), false), this);
}
Audit(AuditType.Delete, string.Format("Delete MediaType performed by user"), userId, mediaType.Id);
@@ -1271,13 +1280,17 @@ namespace Umbraco.Core.Services
var uow = UowProvider.GetUnitOfWork();
using (var repository = RepositoryFactory.CreateMediaTypeRepository(uow))
{
var deletedMediaTypes = new List<IMediaType>();
deletedMediaTypes.AddRange(asArray);
foreach (var mediaType in asArray)
{
deletedMediaTypes.AddRange(mediaType.Descendants().OfType<IMediaType>());
repository.Delete(mediaType);
}
uow.Commit();
DeletedMediaType.RaiseEvent(new DeleteEventArgs<IMediaType>(asArray, false), this);
DeletedMediaType.RaiseEvent(new DeleteEventArgs<IMediaType>(deletedMediaTypes.DistinctBy(x => x.Id), false), this);
}
Audit(AuditType.Delete, string.Format("Delete MediaTypes performed by user"), userId, -1);
@@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Xml;
using Umbraco.Core.Models;
using Umbraco.Core.Models.Membership;
using Umbraco.Core.Persistence.DatabaseModelDefinitions;
@@ -94,6 +95,12 @@ namespace Umbraco.Core.Services
/// </summary>
public interface IContentService : IService
{
/// <summary>
/// This builds the Xml document used for the XML cache
/// </summary>
/// <returns></returns>
XmlDocument BuildXmlCache();
/// <summary>
/// Rebuilds all xml content in the cmsContentXml table for all documents
/// </summary>
@@ -29,6 +29,20 @@ namespace Umbraco.Core.Services
Func<IUser, string[], string> createSubject,
Func<IUser, string[], string> createBody);
/// <summary>
/// Sends the notifications for the specified user regarding the specified nodes and action.
/// </summary>
/// <param name="entities"></param>
/// <param name="operatingUser"></param>
/// <param name="action"></param>
/// <param name="actionName"></param>
/// <param name="http"></param>
/// <param name="createSubject"></param>
/// <param name="createBody"></param>
void SendNotifications(IUser operatingUser, IEnumerable<IUmbracoEntity> entities, string action, string actionName, HttpContextBase http,
Func<IUser, string[], string> createSubject,
Func<IUser, string[], string> createBody);
/// <summary>
/// Gets the notifications for the user
/// </summary>

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