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:
@@ -140,3 +140,4 @@ apidocs/api/*
|
||||
build/docs.zip
|
||||
build/ui-docs.zip
|
||||
build/csharp-docs.zip
|
||||
build/msbuild.log
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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"
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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,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
|
||||
@@ -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
@@ -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()
|
||||
{ }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -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";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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")]
|
||||
|
||||
@@ -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); }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
|
||||
+6
-1
@@ -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);
|
||||
}
|
||||
}
|
||||
+37
-14
@@ -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()
|
||||
|
||||
+54
@@ -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()
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
+71
@@ -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()
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
Reference in New Issue
Block a user