Compare commits
414 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 7ee510ed38 | |||
| 136e9e66d2 | |||
| 55a2eb5b6c | |||
| 0c2c12fdd9 | |||
| ec427afe14 | |||
| 155a304ce3 | |||
| 161a19a82b | |||
| e40ed5e858 | |||
| 1ffbd734c8 | |||
| fd5bc49c87 | |||
| 2301a00cd1 | |||
| 313f7967cb | |||
| 6adaa1f70c | |||
| 8960b0828e | |||
| 397ab0e979 | |||
| 15c803b975 | |||
| d62b36ae5b | |||
| 34506664df | |||
| 2557b9a7fb | |||
| ac66dc9892 | |||
| 11e3d8c761 | |||
| 22b9941822 | |||
| b5b17a466b | |||
| 2c5b37a842 | |||
| ae864a3b19 | |||
| 449e464a14 | |||
| 5f72eb74db | |||
| fa85ce39c7 | |||
| bdea519770 | |||
| 78657f34d6 | |||
| e8fd7e2373 | |||
| 3da9fddd64 | |||
| e1821697d4 | |||
| a7756afae6 | |||
| fcd3bb2eee | |||
| cf5dac0ddd | |||
| 1cdf5c042e | |||
| 2281cd8291 | |||
| fa68f7fafe | |||
| 898e39dac5 | |||
| f6045a49b5 | |||
| 849cd4d315 | |||
| 10141bf4f6 | |||
| 11fcc8cd65 | |||
| 95aaf40281 | |||
| e25a39ec5d | |||
| c165a34ed7 | |||
| dc616bb6bf | |||
| cd6c7e51fd | |||
| 2caad3f2d0 | |||
| 4850b01097 | |||
| 92c0cfb9df | |||
| 8c5ee547b1 | |||
| b9c9a232cd | |||
| b565513052 | |||
| cff24e6ccf | |||
| 8ef3ecd714 | |||
| 1f66ebff1f | |||
| 47f8b483a9 | |||
| 1c56cd48ae | |||
| 6e9a9d6444 | |||
| c4850199f7 | |||
| 679333b9c9 | |||
| 02c609747d | |||
| 413e70db7d | |||
| c2cbac7ef9 | |||
| a4a079cd1b | |||
| 22fc9c617c | |||
| 0bcdfc8820 | |||
| b868c06e79 | |||
| f0dd32ea19 | |||
| 97607bc83b | |||
| 5b41262f41 | |||
| e8e1cd8323 | |||
| 1e17d6a527 | |||
| 0d05d0bf34 | |||
| 94184238a2 | |||
| f21d1f879e | |||
| 9c1b2499d5 | |||
| 60a315f964 | |||
| 1870d5444d | |||
| a927d5fb84 | |||
| e433daf5ff | |||
| af476e1536 | |||
| fcfb9afc22 | |||
| 05818f6262 | |||
| accf8ba5f2 | |||
| 1788682af9 | |||
| a41833c137 | |||
| 7abc6d3d4f | |||
| a551881ce2 | |||
| 4a5a8497b4 | |||
| 567c0977d0 | |||
| 9ec4247350 | |||
| 2f31175db5 | |||
| c2722d8120 | |||
| 325cefa349 | |||
| e43ecfd42c | |||
| 76725aa0d8 | |||
| d4cf4cd700 | |||
| 48c0b95707 | |||
| a5caf361e0 | |||
| 74c3aaade5 | |||
| cf94da2b8f | |||
| 826d6d5b15 | |||
| 56f4904fc0 | |||
| 9178e7afcf | |||
| 7f3733d4b5 | |||
| 3df23c3a34 | |||
| 7746c4a014 | |||
| b18945e630 | |||
| 4640cf56d1 | |||
| eae1ce7012 | |||
| e900711180 | |||
| 9f4875c8a7 | |||
| 46902d4335 | |||
| 5eb22b0fd9 | |||
| 6a18965946 | |||
| be1ec05072 | |||
| bb2caf85cb | |||
| 8ee8bda953 | |||
| b42f75df17 | |||
| caa3a3319f | |||
| 5caf95331a | |||
| 040e45ca70 | |||
| bd3d626147 | |||
| 47d08e69a4 | |||
| df182674b4 | |||
| 6ec9a2ae45 | |||
| bf00f7a663 | |||
| 74d8341620 | |||
| 70207e5374 | |||
| f4c038c64c | |||
| c4fa3aa649 | |||
| d51cda7843 | |||
| 372ffee42b | |||
| c1cfea7ebd | |||
| 3cdd8a0c43 | |||
| bf130584a6 | |||
| c3e67705ba | |||
| 7a80d689aa | |||
| eabc009f0c | |||
| d3a2d88347 | |||
| 93f4d65227 | |||
| a4c0342bda | |||
| 6c328d729d | |||
| 095633ba27 | |||
| 633d1bb0ae | |||
| fb89c3da3c | |||
| 3097a46ab3 | |||
| c63156fcd8 | |||
| 29690df567 | |||
| 84a90884e3 | |||
| d29b7311fa | |||
| 17b59cc20f | |||
| 4423571eaa | |||
| 750600b202 | |||
| 28eb4ff03a | |||
| 79517f171c | |||
| e76c42f874 | |||
| 151c35bc47 | |||
| 84265a9716 | |||
| 6618388973 | |||
| 61ce533a08 | |||
| 0a44ad3ab6 | |||
| 7c34255abe | |||
| d52216103b | |||
| a26cd625e7 | |||
| b280bf73aa | |||
| a6d57a5f6d | |||
| 1f22191bbc | |||
| 25088042a9 | |||
| 6b7a83c6c9 | |||
| eb5f2e29a3 | |||
| 02397fd90c | |||
| cf86409e3f | |||
| 134f5e2601 | |||
| e0ded863cc | |||
| bd55594be1 | |||
| 0e82696082 | |||
| 37ac524e8e | |||
| 43af2f8e55 | |||
| a7a6854f9b | |||
| a20584e3de | |||
| 19ec26fef9 | |||
| 23d225c14f | |||
| 527f98189a | |||
| 1a289be999 | |||
| 8a0485e093 | |||
| 647492fbfb | |||
| 78c289a559 | |||
| 646abb3aea | |||
| 2af59471a2 | |||
| fc43d3cdee | |||
| b76b09026b | |||
| 971d7cf933 | |||
| 118a83e56c | |||
| 1f6dea7177 | |||
| f2667c3360 | |||
| f331736384 | |||
| 090f89e5b6 | |||
| a6bc94185c | |||
| 4870afd2d3 | |||
| 80257b6684 | |||
| 8f3a0b6919 | |||
| 9fd2f34343 | |||
| e774717346 | |||
| 5f5759c8a3 | |||
| 0b90097aad | |||
| 58533ca17e | |||
| 427e99dd29 | |||
| 1ec09e15cf | |||
| a48568d1ba | |||
| 5aa1b81cd4 | |||
| 7cd540c37e | |||
| 83595ecaf5 | |||
| e07c5e6ff8 | |||
| 67ff827d67 | |||
| e1208e5019 | |||
| 3f7159797f | |||
| 995f644501 | |||
| f7eec8dafb | |||
| e039265fab | |||
| f08192a4ab | |||
| a16ac1f0ba | |||
| 469d6f9ca3 | |||
| 54cc80f4e8 | |||
| 64628c0a9d | |||
| 6f81b7fe24 | |||
| f2819f61e5 | |||
| 2107e9d9e3 | |||
| c623b448ba | |||
| 1e8bdc8f0c | |||
| 8bb49f57e8 | |||
| da0b1c2f23 | |||
| a288fc62eb | |||
| 82e50ccd3a | |||
| 15b0dc9f29 | |||
| 8057d09fc2 | |||
| 8877100bf0 | |||
| 41c96e6e26 | |||
| abdd117264 | |||
| 6272dac698 | |||
| 7a4b5fa796 | |||
| c962132ee0 | |||
| 0780979469 | |||
| a42e568fdf | |||
| e62661cc6a | |||
| 6841b2ac61 | |||
| f7f4276485 | |||
| bb9e23ca3e | |||
| 10db9363f4 | |||
| 7d5d69e2e3 | |||
| 06897aa8c6 | |||
| ee75158dd0 | |||
| b49db0e941 | |||
| c022397d86 | |||
| e25f6e82ed | |||
| d104525a50 | |||
| b0a64aa4b5 | |||
| ca86385269 | |||
| fb8368267e | |||
| ef4322a9b3 | |||
| 7032e137b8 | |||
| e99643ecfd | |||
| a325529eb8 | |||
| c68f832be6 | |||
| 4ab1c51327 | |||
| 6f39bac32a | |||
| e007a37c36 | |||
| ab9e029525 | |||
| d298388a7b | |||
| 4463abe9bb | |||
| d1db38182f | |||
| e808360653 | |||
| c5df712471 | |||
| 2f67008987 | |||
| 1672e284c5 | |||
| 60ce36a7f2 | |||
| 05c12d1f27 | |||
| 72e2cacc87 | |||
| 4285102422 | |||
| 5ffdf4dd60 | |||
| abbcf0e26b | |||
| 347b25472f | |||
| b745948dc6 | |||
| 8007c8869f | |||
| b51a328ead | |||
| f61437cc7c | |||
| f3b8f2eb5f | |||
| de4628f450 | |||
| b0a64655b4 | |||
| 48d88ec8fa | |||
| 7ca151a3fa | |||
| 363c8fab2c | |||
| 8d4fd9d1cf | |||
| 24071eb3b1 | |||
| 62ffbe7cf0 | |||
| f2d6b47e47 | |||
| f3541908c7 | |||
| 4e57e1cc18 | |||
| 410afffaa9 | |||
| 936e94789d | |||
| 468a56e5de | |||
| 468ec8824a | |||
| cad913b7a1 | |||
| 382103d99d | |||
| cbb6b9bca0 | |||
| a1b90a50cd | |||
| 9ea5855073 | |||
| 4a37433042 | |||
| 91469812d3 | |||
| 68a8eb7f3f | |||
| 2e7baabb94 | |||
| 57745fe71d | |||
| de6f9236e7 | |||
| a00c59e8b5 | |||
| 05c2da807f | |||
| 0fd8ca43d6 | |||
| 87f09b2ee6 | |||
| e466878925 | |||
| 7adf917570 | |||
| 7c41db4bcc | |||
| 5caaef7795 | |||
| 3b72945b1f | |||
| 71eb22d0e8 | |||
| 9d780a2e8f | |||
| 55b00069cb | |||
| b304728f4e | |||
| 319acc199e | |||
| 2a982f21a7 | |||
| 3ff455f11d | |||
| 41ad1bf27b | |||
| f49d939a2d | |||
| c7e80fc532 | |||
| b47d691841 | |||
| 7e1e83b493 | |||
| f6971185f8 | |||
| 244429b972 | |||
| 80ceba36dc | |||
| d33b617851 | |||
| 2b5f370c92 | |||
| 4a0cf81d7d | |||
| fef743517f | |||
| b3cbcebf5b | |||
| 3d90c2b83f | |||
| 3b8b5b6dfa | |||
| 85f95cf067 | |||
| 23be0134e7 | |||
| 355fb64d8b | |||
| 42959a1db5 | |||
| 5b754b3958 | |||
| cba31986fe | |||
| 07b0267668 | |||
| a83291cc6e | |||
| a1cd22208b | |||
| 59d75277c2 | |||
| 69e193e887 | |||
| c3116d5df7 | |||
| b0b874212e | |||
| 19086ab3a1 | |||
| 069594c3ac | |||
| 0bc4c7e2aa | |||
| 7f12cff3bd | |||
| 56adab43d6 | |||
| 83294b6594 | |||
| e0c89eb1ea | |||
| 9ad8e6a876 | |||
| 14b58386e5 | |||
| e26cbccfe0 | |||
| fa058f348d | |||
| 62d9a1a132 | |||
| 9284269428 | |||
| f8c4481eb1 | |||
| 9f6e6e4701 | |||
| 34ba3f3351 | |||
| dcfb8b94d8 | |||
| 0876004d9a | |||
| 700b71f9fb | |||
| bba58ea0c0 | |||
| d0d4885812 | |||
| 3642ff72a4 | |||
| 6e830c1171 | |||
| ab523baf4f | |||
| 2dec03f441 | |||
| 9ada0d9c97 | |||
| df553140e8 | |||
| 0b898f2e32 | |||
| f5f3676a56 | |||
| bc828fc6f3 | |||
| dc7b83ca6c | |||
| 8188e4ff55 | |||
| fcf9dcd803 | |||
| 3556043b55 | |||
| fef9e3fde0 | |||
| e849d2d0d0 | |||
| 9a6123579f | |||
| 591a881283 | |||
| 1554d52ea9 | |||
| 60924d93fb | |||
| 50335723b0 | |||
| 58697eaa04 | |||
| ce58ad91b5 | |||
| 5678be0db9 | |||
| e14aba3875 | |||
| 77162e4b51 | |||
| 0df7767f4a | |||
| 464a227af1 | |||
| 6c172702be | |||
| 7566546fd7 | |||
| 59b8b397b7 | |||
| 7c72e70967 | |||
| 75b74a6687 | |||
| 7dcb4435d8 |
+11
-5
@@ -1,10 +1,16 @@
|
||||
root=true
|
||||
# editorconfig.org
|
||||
|
||||
# top-most EditorConfig file
|
||||
root = true
|
||||
|
||||
# Default settings:
|
||||
# A newline ending every file
|
||||
# Use 4 spaces as indentation
|
||||
[*]
|
||||
end_of_line = lf
|
||||
insert_final_newline = true
|
||||
indent_style = space
|
||||
indent_size = 4
|
||||
trim_trailing_whitespace = true
|
||||
|
||||
[*.{cs,cshtml,csx,vb,vbx,vbhtml,fs,fsx,txt,ps1,sql}]
|
||||
indent_size = 4
|
||||
# Trim trailing whitespace, limited support.
|
||||
# https://github.com/editorconfig/editorconfig/wiki/Property-research:-Trim-trailing-spaces
|
||||
trim_trailing_whitespace = true
|
||||
@@ -0,0 +1,49 @@
|
||||
*.doc diff=astextplain
|
||||
*.DOC diff=astextplain
|
||||
*.docx diff=astextplain
|
||||
*.DOCX diff=astextplain
|
||||
*.dot diff=astextplain
|
||||
*.DOT diff=astextplain
|
||||
*.pdf diff=astextplain
|
||||
*.PDF diff=astextplain
|
||||
*.rtf diff=astextplain
|
||||
*.RTF diff=astextplain
|
||||
|
||||
*.jpg binary
|
||||
*.png binary
|
||||
*.gif binary
|
||||
|
||||
*.cs text=auto diff=csharp
|
||||
*.vb text=auto
|
||||
*.c text=auto
|
||||
*.cpp text=auto
|
||||
*.cxx text=auto
|
||||
*.h text=auto
|
||||
*.hxx text=auto
|
||||
*.py text=auto
|
||||
*.rb text=auto
|
||||
*.java text=auto
|
||||
*.html text=auto
|
||||
*.htm text=auto
|
||||
*.css text=auto
|
||||
*.scss text=auto
|
||||
*.sass text=auto
|
||||
*.less text=auto
|
||||
*.js text=auto
|
||||
*.lisp text=auto
|
||||
*.clj text=auto
|
||||
*.sql text=auto
|
||||
*.php text=auto
|
||||
*.lua text=auto
|
||||
*.m text=auto
|
||||
*.asm text=auto
|
||||
*.erl text=auto
|
||||
*.fs text=auto
|
||||
*.fsx text=auto
|
||||
*.hs text=auto
|
||||
|
||||
*.csproj text=auto merge=union
|
||||
*.vbproj text=auto merge=union
|
||||
*.fsproj text=auto merge=union
|
||||
*.dbproj text=auto merge=union
|
||||
*.sln text=auto eol=crlf merge=union
|
||||
@@ -44,7 +44,7 @@ function Build-UmbracoDocs
|
||||
# change baseUrl
|
||||
$baseUrl = "https://our.umbraco.org/apidocs/ui/"
|
||||
$indexPath = "$src/Umbraco.Web.UI.Client/docs/api/index.html"
|
||||
(Get-Content $indexPath).Replace("location.href.replace(rUrl, indexFile)", "'$baseUrl'") `
|
||||
(Get-Content $indexPath).Replace("origin + location.href.substr(origin.length).replace(rUrl, indexFile)", "'$baseUrl'") `
|
||||
| Set-Content $indexPath
|
||||
|
||||
# restore
|
||||
@@ -100,8 +100,9 @@ function Get-DocFx($uenv, $buildTemp)
|
||||
$docFx = "$buildTemp\docfx"
|
||||
if (-not (test-path $docFx))
|
||||
{
|
||||
Write-Host "Download DocFx..."
|
||||
$source = "https://github.com/dotnet/docfx/releases/download/v2.19.2/docfx.zip"
|
||||
Write-Host "Download DocFx from $source"
|
||||
|
||||
Invoke-WebRequest $source -OutFile "$buildTemp\docfx.zip"
|
||||
|
||||
&$uenv.Zip x "$buildTemp\docfx.zip" -o"$buildTemp\docfx" -aos > $nul
|
||||
|
||||
@@ -239,18 +239,18 @@ function Prepare-Tests
|
||||
|
||||
# data
|
||||
Write-Host "Copy data files"
|
||||
if( -Not (Test-Path -Path "$tmp\tests\Packaging" ) )
|
||||
if (-Not (Test-Path -Path "$tmp\tests\Packaging" ) )
|
||||
{
|
||||
Write-Host "Create packaging directory"
|
||||
New-Item -ItemType directory -Path "$tmp\tests\Packaging"
|
||||
mkdir "$tmp\tests\Packaging" > $null
|
||||
}
|
||||
Copy-Files "$src\Umbraco.Tests\Packaging\Packages" "*" "$tmp\tests\Packaging\Packages"
|
||||
|
||||
# required for package install tests
|
||||
if( -Not (Test-Path -Path "$tmp\tests\bin" ) )
|
||||
if (-Not (Test-Path -Path "$tmp\tests\bin" ) )
|
||||
{
|
||||
Write-Host "Create bin directory"
|
||||
New-Item -ItemType directory -Path "$tmp\tests\bin"
|
||||
mkdir "$tmp\tests\bin" > $null
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -28,7 +28,7 @@
|
||||
<dependency id="HtmlAgilityPack" version="[1.4.9.5, 2.0.0)" />
|
||||
<dependency id="Lucene.Net" version="[2.9.4.1, 3.0.0.0)" />
|
||||
<dependency id="MySql.Data" version="[6.9.9, 7.0.0)" />
|
||||
<dependency id="ClientDependency" version="[1.9.2, 2.0.0)" />
|
||||
<dependency id="ClientDependency" version="[1.9.6, 2.0.0)" />
|
||||
<dependency id="ClientDependency-Mvc5" version="[1.8.0.0, 2.0.0)" />
|
||||
<dependency id="AutoMapper" version="[3.3.1, 4.0.0)" />
|
||||
<dependency id="Newtonsoft.Json" version="[10.0.2, 11.0.0)" />
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
<dependencies>
|
||||
<dependency id="UmbracoCms.Core" version="[$version$]" />
|
||||
<dependency id="Newtonsoft.Json" version="[10.0.2, 11.0.0)" />
|
||||
<dependency id="Umbraco.ModelsBuilder" version="[3.0.8, 4.0.0)" />
|
||||
<dependency id="Umbraco.ModelsBuilder" version="[3.0.10, 4.0.0)" />
|
||||
<dependency id="Microsoft.AspNet.SignalR.Core" version="[2.2.1, 3.0.0)" />
|
||||
<dependency id="ImageProcessor.Web.Config" version="[2.3.1, 3.0.0)" />
|
||||
</dependencies>
|
||||
@@ -32,7 +32,7 @@
|
||||
<file src="$BuildTmp$\WebApp\bin\amd64\**" target="UmbracoFiles\bin\amd64" />
|
||||
<file src="$BuildTmp$\WebApp\bin\x86\**" target="UmbracoFiles\bin\x86" />
|
||||
<file src="$BuildTmp$\WebApp\config\splashes\**" target="UmbracoFiles\Config\splashes" />
|
||||
<file src="$BuildTmp$\WebApp\config\BackOfficeTours\**" target="UmbracoFiles\Config\BackOfficeTours" />
|
||||
<file src="$BuildTmp$\WebApp\config\BackOfficeTours\**" target="Content\Config\BackOfficeTours" />
|
||||
<file src="$BuildTmp$\WebApp\umbraco\**" target="UmbracoFiles\umbraco" />
|
||||
<file src="$BuildTmp$\WebApp\umbraco_client\**" target="UmbracoFiles\umbraco_client" />
|
||||
<file src="$BuildTmp$\WebApp\Media\Web.config" target="Content\Media\Web.config" />
|
||||
|
||||
@@ -98,10 +98,51 @@ if ($project) {
|
||||
$umbracoUIXMLDestination = Join-Path $projectPath "Umbraco\Config\Create\UI.xml"
|
||||
Copy-Item $umbracoUIXMLSource $umbracoUIXMLDestination -Force
|
||||
} else {
|
||||
# This part only runs for upgrades
|
||||
|
||||
$upgradeViewSource = Join-Path $umbracoFolderSource "Views\install\*"
|
||||
$upgradeView = Join-Path $umbracoFolder "Views\install\"
|
||||
Write-Host "Copying2 ${upgradeViewSource} to ${upgradeView}"
|
||||
Copy-Item $upgradeViewSource $upgradeView -Force
|
||||
|
||||
Try
|
||||
{
|
||||
# Disable tours for upgrades, presumably Umbraco experience is already available
|
||||
$umbracoSettingsConfigPath = Join-Path $configFolder "umbracoSettings.config"
|
||||
$content = (Get-Content $umbracoSettingsConfigPath).Replace('<tours enable="true">','<tours enable="false">')
|
||||
# Saves with UTF-8 encoding without BOM which makes sure Umbraco can still read it
|
||||
# Reference: https://stackoverflow.com/a/32951824/5018
|
||||
[IO.File]::WriteAllLines($umbracoSettingsConfigPath, $content)
|
||||
}
|
||||
Catch
|
||||
{
|
||||
# Not a big problem if this fails, let it go
|
||||
}
|
||||
|
||||
Try
|
||||
{
|
||||
$uiXmlConfigPath = Join-Path $umbracoFolder -ChildPath "Config" | Join-Path -ChildPath "create" | Join-Path -ChildPath "UI.xml"
|
||||
$uiXmlFile = Join-Path $umbracoFolder -ChildPath "Config" | Join-Path -ChildPath "create" | Join-Path -ChildPath "UI.xml"
|
||||
|
||||
$uiXml = New-Object System.Xml.XmlDocument
|
||||
$uiXml.PreserveWhitespace = $true
|
||||
|
||||
$uiXml.Load($uiXmlFile)
|
||||
$createExists = $uiXml.SelectNodes("//nodeType[@alias='macros']/tasks/create")
|
||||
|
||||
if($createExists.Count -eq 0)
|
||||
{
|
||||
$macrosTasksNode = $uiXml.SelectNodes("//nodeType[@alias='macros']/tasks")
|
||||
|
||||
#Creating: <create assembly="umbraco" type="macroTasks" />
|
||||
$createNode = $uiXml.CreateElement("create")
|
||||
$createNode.SetAttribute("assembly", "umbraco")
|
||||
$createNode.SetAttribute("type", "macroTasks")
|
||||
$macrosTasksNode.AppendChild($createNode)
|
||||
$uiXml.Save($uiXmlFile)
|
||||
}
|
||||
}
|
||||
Catch { }
|
||||
}
|
||||
|
||||
$installFolder = Join-Path $projectPath "Install"
|
||||
|
||||
+1
-1
@@ -2,6 +2,6 @@
|
||||
<configuration>
|
||||
<packageSources>
|
||||
<add key="nuget.org" value="https://www.nuget.org/api/v2/" />
|
||||
<add key="umbracocore" value="http://www.myget.org/f/umbracocore/" />
|
||||
<add key="umbracocore" value="https://www.myget.org/F/umbracocore/api/v3/index.json" />
|
||||
</packageSources>
|
||||
</configuration>
|
||||
+2
-2
@@ -11,5 +11,5 @@ using System.Resources;
|
||||
|
||||
[assembly: AssemblyVersion("1.0.*")]
|
||||
|
||||
[assembly: AssemblyFileVersion("7.8.0")]
|
||||
[assembly: AssemblyInformationalVersion("7.8.0-beta004")]
|
||||
[assembly: AssemblyFileVersion("7.9.0")]
|
||||
[assembly: AssemblyInformationalVersion("7.9.0")]
|
||||
@@ -0,0 +1,351 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Web;
|
||||
using Umbraco.Core.Events;
|
||||
using Umbraco.Core.Models;
|
||||
using Umbraco.Core.Models.Membership;
|
||||
using Umbraco.Core.Security;
|
||||
using Umbraco.Core.Services;
|
||||
|
||||
namespace Umbraco.Core.Auditing
|
||||
{
|
||||
public sealed class AuditEventHandler : ApplicationEventHandler
|
||||
{
|
||||
private IAuditService _auditServiceInstance;
|
||||
private IUserService _userServiceInstance;
|
||||
private IEntityService _entityServiceInstance;
|
||||
|
||||
private IUser CurrentPerformingUser
|
||||
{
|
||||
get
|
||||
{
|
||||
var identity = Thread.CurrentPrincipal?.GetUmbracoIdentity();
|
||||
return identity == null
|
||||
? new User { Id = 0, Name = "SYSTEM", Email = "" }
|
||||
: _userServiceInstance.GetUserById(Convert.ToInt32(identity.Id));
|
||||
}
|
||||
}
|
||||
|
||||
private IUser GetPerformingUser(int userId)
|
||||
{
|
||||
var found = userId >= 0 ? _userServiceInstance.GetUserById(userId) : null;
|
||||
return found ?? new User {Id = 0, Name = "SYSTEM", Email = ""};
|
||||
}
|
||||
|
||||
private string PerformingIp
|
||||
{
|
||||
get
|
||||
{
|
||||
var httpContext = HttpContext.Current == null ? (HttpContextBase) null : new HttpContextWrapper(HttpContext.Current);
|
||||
var ip = httpContext.GetCurrentRequestIpAddress();
|
||||
if (ip.ToLowerInvariant().StartsWith("unknown")) ip = "";
|
||||
return ip;
|
||||
}
|
||||
}
|
||||
|
||||
protected override void ApplicationStarted(UmbracoApplicationBase umbracoApplication, ApplicationContext applicationContext)
|
||||
{
|
||||
_auditServiceInstance = applicationContext.Services.AuditService;
|
||||
_userServiceInstance = applicationContext.Services.UserService;
|
||||
_entityServiceInstance = applicationContext.Services.EntityService;
|
||||
|
||||
//BackOfficeUserManager.AccountLocked += ;
|
||||
//BackOfficeUserManager.AccountUnlocked += ;
|
||||
BackOfficeUserManager.ForgotPasswordRequested += OnForgotPasswordRequest;
|
||||
BackOfficeUserManager.ForgotPasswordChangedSuccess += OnForgotPasswordChange;
|
||||
BackOfficeUserManager.LoginFailed += OnLoginFailed;
|
||||
//BackOfficeUserManager.LoginRequiresVerification += ;
|
||||
BackOfficeUserManager.LoginSuccess += OnLoginSuccess;
|
||||
BackOfficeUserManager.LogoutSuccess += OnLogoutSuccess;
|
||||
BackOfficeUserManager.PasswordChanged += OnPasswordChanged;
|
||||
BackOfficeUserManager.PasswordReset += OnPasswordReset;
|
||||
//BackOfficeUserManager.ResetAccessFailedCount += ;
|
||||
|
||||
UserService.SavedUserGroup2 += OnSavedUserGroupWithUsers;
|
||||
|
||||
UserService.SavedUser += OnSavedUser;
|
||||
UserService.DeletedUser += OnDeletedUser;
|
||||
UserService.UserGroupPermissionsAssigned += UserGroupPermissionAssigned;
|
||||
|
||||
MemberService.Saved += OnSavedMember;
|
||||
MemberService.Deleted += OnDeletedMember;
|
||||
MemberService.AssignedRoles += OnAssignedRoles;
|
||||
MemberService.RemovedRoles += OnRemovedRoles;
|
||||
MemberService.Exported += OnMemberExported;
|
||||
}
|
||||
|
||||
private string FormatEmail(IMember member)
|
||||
{
|
||||
return member == null ? string.Empty : member.Email.IsNullOrWhiteSpace() ? "" : $"<{member.Email}>";
|
||||
}
|
||||
|
||||
private string FormatEmail(IUser user)
|
||||
{
|
||||
return user == null ? string.Empty : user.Email.IsNullOrWhiteSpace() ? "" : $"<{user.Email}>";
|
||||
}
|
||||
|
||||
private void OnRemovedRoles(IMemberService sender, RolesEventArgs args)
|
||||
{
|
||||
var performingUser = CurrentPerformingUser;
|
||||
var roles = string.Join(", ", args.Roles);
|
||||
var members = sender.GetAllMembers(args.MemberIds).ToDictionary(x => x.Id, x => x);
|
||||
foreach (var id in args.MemberIds)
|
||||
{
|
||||
members.TryGetValue(id, out var member);
|
||||
_auditServiceInstance.Write(performingUser.Id, $"User \"{performingUser.Name}\" {FormatEmail(performingUser)}", PerformingIp,
|
||||
DateTime.UtcNow,
|
||||
-1, $"Member {id} \"{member?.Name ?? "(unknown)"}\" {FormatEmail(member)}",
|
||||
"umbraco/member/roles/removed", $"roles modified, removed {roles}");
|
||||
}
|
||||
}
|
||||
|
||||
private void OnAssignedRoles(IMemberService sender, RolesEventArgs args)
|
||||
{
|
||||
var performingUser = CurrentPerformingUser;
|
||||
var roles = string.Join(", ", args.Roles);
|
||||
var members = sender.GetAllMembers(args.MemberIds).ToDictionary(x => x.Id, x => x);
|
||||
foreach (var id in args.MemberIds)
|
||||
{
|
||||
members.TryGetValue(id, out var member);
|
||||
_auditServiceInstance.Write(performingUser.Id, $"User \"{performingUser.Name}\" {FormatEmail(performingUser)}", PerformingIp,
|
||||
DateTime.UtcNow,
|
||||
-1, $"Member {id} \"{member?.Name ?? "(unknown)"}\" {FormatEmail(member)}",
|
||||
"umbraco/member/roles/assigned", $"roles modified, assigned {roles}");
|
||||
}
|
||||
}
|
||||
|
||||
private void OnMemberExported(IMemberService sender, ExportedMemberEventArgs exportedMemberEventArgs)
|
||||
{
|
||||
var performingUser = CurrentPerformingUser;
|
||||
var member = exportedMemberEventArgs.Member;
|
||||
|
||||
_auditServiceInstance.Write(performingUser.Id, $"User \"{performingUser.Name}\" {FormatEmail(performingUser)}", PerformingIp,
|
||||
DateTime.UtcNow,
|
||||
-1, $"Member {member.Id} \"{member.Name}\" {FormatEmail(member)}",
|
||||
"umbraco/member/exported", "exported member data");
|
||||
}
|
||||
|
||||
private void OnSavedUserGroupWithUsers(IUserService sender, SaveEventArgs<UserGroupWithUsers> saveEventArgs)
|
||||
{
|
||||
var performingUser = CurrentPerformingUser;
|
||||
foreach (var groupWithUser in saveEventArgs.SavedEntities)
|
||||
{
|
||||
var group = groupWithUser.UserGroup;
|
||||
|
||||
var dp = string.Join(", ", ((UserGroup)group).GetPreviouslyDirtyProperties());
|
||||
var sections = ((UserGroup)group).WasPropertyDirty("AllowedSections")
|
||||
? string.Join(", ", group.AllowedSections)
|
||||
: null;
|
||||
var perms = ((UserGroup)group).WasPropertyDirty("Permissions")
|
||||
? string.Join(", ", group.Permissions)
|
||||
: null;
|
||||
|
||||
var sb = new StringBuilder();
|
||||
sb.Append($"updating {(string.IsNullOrWhiteSpace(dp) ? "(nothing)" : dp)};");
|
||||
if (sections != null)
|
||||
sb.Append($", assigned sections: {sections}");
|
||||
if (perms != null)
|
||||
{
|
||||
if (sections != null)
|
||||
sb.Append(", ");
|
||||
sb.Append($"default perms: {perms}");
|
||||
}
|
||||
|
||||
_auditServiceInstance.Write(performingUser.Id, $"User \"{performingUser.Name}\" {FormatEmail(performingUser)}", PerformingIp,
|
||||
DateTime.UtcNow,
|
||||
-1, $"User Group {group.Id} \"{group.Name}\" ({group.Alias})",
|
||||
"umbraco/user-group/save", $"{sb}");
|
||||
|
||||
// now audit the users that have changed
|
||||
|
||||
foreach (var user in groupWithUser.RemovedUsers)
|
||||
{
|
||||
_auditServiceInstance.Write(performingUser.Id, $"User \"{performingUser.Name}\" {FormatEmail(performingUser)}", PerformingIp,
|
||||
DateTime.UtcNow,
|
||||
user.Id, $"User \"{user.Name}\" {FormatEmail(user)}",
|
||||
"umbraco/user-group/save", $"Removed user \"{user.Name}\" {FormatEmail(user)} from group {group.Id} \"{group.Name}\" ({group.Alias})");
|
||||
}
|
||||
|
||||
foreach (var user in groupWithUser.AddedUsers)
|
||||
{
|
||||
_auditServiceInstance.Write(performingUser.Id, $"User \"{performingUser.Name}\" {FormatEmail(performingUser)}", PerformingIp,
|
||||
DateTime.UtcNow,
|
||||
user.Id, $"User \"{user.Name}\" {FormatEmail(user)}",
|
||||
"umbraco/user-group/save", $"Added user \"{user.Name}\" {FormatEmail(user)} to group {group.Id} \"{group.Name}\" ({group.Alias})");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void UserGroupPermissionAssigned(IUserService sender, SaveEventArgs<EntityPermission> saveEventArgs)
|
||||
{
|
||||
var performingUser = CurrentPerformingUser;
|
||||
var perms = saveEventArgs.SavedEntities;
|
||||
foreach (var perm in perms)
|
||||
{
|
||||
var group = sender.GetUserGroupById(perm.UserGroupId);
|
||||
var assigned = string.Join(", ", perm.AssignedPermissions);
|
||||
var entity = _entityServiceInstance.Get(perm.EntityId);
|
||||
|
||||
_auditServiceInstance.Write(performingUser.Id, $"User \"{performingUser.Name}\" {FormatEmail(performingUser)}", PerformingIp,
|
||||
DateTime.UtcNow,
|
||||
-1, $"User Group {group.Id} \"{group.Name}\" ({group.Alias})",
|
||||
"umbraco/user-group/permissions-change", $"assigning {(string.IsNullOrWhiteSpace(assigned) ? "(nothing)" : assigned)} on id:{perm.EntityId} \"{entity.Name}\"");
|
||||
}
|
||||
}
|
||||
|
||||
private void OnSavedMember(IMemberService sender, SaveEventArgs<IMember> saveEventArgs)
|
||||
{
|
||||
var performingUser = CurrentPerformingUser;
|
||||
var members = saveEventArgs.SavedEntities;
|
||||
foreach (var member in members)
|
||||
{
|
||||
var dp = string.Join(", ", ((Member) member).GetPreviouslyDirtyProperties());
|
||||
|
||||
_auditServiceInstance.Write(performingUser.Id, $"User \"{performingUser.Name}\" {FormatEmail(performingUser)}", PerformingIp,
|
||||
DateTime.UtcNow,
|
||||
-1, $"Member {member.Id} \"{member.Name}\" {FormatEmail(member)}",
|
||||
"umbraco/member/save", $"updating {(string.IsNullOrWhiteSpace(dp) ? "(nothing)" : dp)}");
|
||||
}
|
||||
}
|
||||
|
||||
private void OnDeletedMember(IMemberService sender, DeleteEventArgs<IMember> deleteEventArgs)
|
||||
{
|
||||
var performingUser = CurrentPerformingUser;
|
||||
var members = deleteEventArgs.DeletedEntities;
|
||||
foreach (var member in members)
|
||||
{
|
||||
_auditServiceInstance.Write(performingUser.Id, $"User \"{performingUser.Name}\" {FormatEmail(performingUser)}", PerformingIp,
|
||||
DateTime.UtcNow,
|
||||
-1, $"Member {member.Id} \"{member.Name}\" {FormatEmail(member)}",
|
||||
"umbraco/member/delete", $"delete member id:{member.Id} \"{member.Name}\" {FormatEmail(member)}");
|
||||
}
|
||||
}
|
||||
|
||||
private void OnSavedUser(IUserService sender, SaveEventArgs<IUser> saveEventArgs)
|
||||
{
|
||||
var performingUser = CurrentPerformingUser;
|
||||
var affectedUsers = saveEventArgs.SavedEntities;
|
||||
foreach (var affectedUser in affectedUsers)
|
||||
{
|
||||
var groups = affectedUser.WasPropertyDirty("Groups")
|
||||
? string.Join(", ", affectedUser.Groups.Select(x => x.Alias))
|
||||
: null;
|
||||
|
||||
var dp = string.Join(", ", ((User)affectedUser).GetPreviouslyDirtyProperties());
|
||||
|
||||
_auditServiceInstance.Write(performingUser.Id, $"User \"{performingUser.Name}\" {FormatEmail(performingUser)}", PerformingIp,
|
||||
DateTime.UtcNow,
|
||||
affectedUser.Id, $"User \"{affectedUser.Name}\" {FormatEmail(affectedUser)}",
|
||||
"umbraco/user/save", $"updating {(string.IsNullOrWhiteSpace(dp) ? "(nothing)" : dp)}{(groups == null ? "" : "; groups assigned: " + groups)}");
|
||||
}
|
||||
}
|
||||
|
||||
private void OnDeletedUser(IUserService sender, DeleteEventArgs<IUser> deleteEventArgs)
|
||||
{
|
||||
var performingUser = CurrentPerformingUser;
|
||||
var affectedUsers = deleteEventArgs.DeletedEntities;
|
||||
foreach (var affectedUser in affectedUsers)
|
||||
_auditServiceInstance.Write(performingUser.Id, $"User \"{performingUser.Name}\" {FormatEmail(performingUser)}", PerformingIp,
|
||||
DateTime.UtcNow,
|
||||
affectedUser.Id, $"User \"{affectedUser.Name}\" {FormatEmail(affectedUser)}",
|
||||
"umbraco/user/delete", "delete user");
|
||||
}
|
||||
|
||||
private void OnLoginSuccess(object sender, EventArgs args)
|
||||
{
|
||||
if (args is IdentityAuditEventArgs identityArgs)
|
||||
{
|
||||
var performingUser = GetPerformingUser(identityArgs.PerformingUser);
|
||||
WriteAudit(performingUser, identityArgs.AffectedUser, identityArgs.IpAddress, "umbraco/user/sign-in/login", "login success");
|
||||
}
|
||||
}
|
||||
|
||||
private void OnLogoutSuccess(object sender, EventArgs args)
|
||||
{
|
||||
if (args is IdentityAuditEventArgs identityArgs)
|
||||
{
|
||||
var performingUser = GetPerformingUser(identityArgs.PerformingUser);
|
||||
WriteAudit(performingUser, identityArgs.AffectedUser, identityArgs.IpAddress, "umbraco/user/sign-in/logout", "logout success");
|
||||
}
|
||||
}
|
||||
|
||||
private void OnPasswordReset(object sender, EventArgs args)
|
||||
{
|
||||
if (args is IdentityAuditEventArgs identityArgs && identityArgs.PerformingUser >= 0)
|
||||
{
|
||||
WriteAudit(identityArgs.PerformingUser, identityArgs.AffectedUser, identityArgs.IpAddress, "umbraco/user/password/reset", "password reset");
|
||||
}
|
||||
}
|
||||
|
||||
private void OnPasswordChanged(object sender, EventArgs args)
|
||||
{
|
||||
if (args is IdentityAuditEventArgs identityArgs && identityArgs.PerformingUser >= 0)
|
||||
{
|
||||
WriteAudit(identityArgs.PerformingUser, identityArgs.AffectedUser, identityArgs.IpAddress, "umbraco/user/password/change", "password change");
|
||||
}
|
||||
}
|
||||
|
||||
private void OnLoginFailed(object sender, EventArgs args)
|
||||
{
|
||||
if (args is IdentityAuditEventArgs identityArgs && identityArgs.PerformingUser >= 0)
|
||||
{
|
||||
WriteAudit(identityArgs.PerformingUser, 0, identityArgs.IpAddress, "umbraco/user/sign-in/failed", "login failed", affectedDetails: "");
|
||||
}
|
||||
}
|
||||
|
||||
private void OnForgotPasswordChange(object sender, EventArgs args)
|
||||
{
|
||||
if (args is IdentityAuditEventArgs identityArgs && identityArgs.PerformingUser >= 0)
|
||||
{
|
||||
WriteAudit(identityArgs.PerformingUser, identityArgs.AffectedUser, identityArgs.IpAddress, "umbraco/user/password/forgot/change", "password forgot/change");
|
||||
}
|
||||
}
|
||||
|
||||
private void OnForgotPasswordRequest(object sender, EventArgs args)
|
||||
{
|
||||
if (args is IdentityAuditEventArgs identityArgs && identityArgs.PerformingUser >= 0)
|
||||
{
|
||||
WriteAudit(identityArgs.PerformingUser, identityArgs.AffectedUser, identityArgs.IpAddress, "umbraco/user/password/forgot/request", "password forgot/request");
|
||||
}
|
||||
}
|
||||
|
||||
private void WriteAudit(int performingId, int affectedId, string ipAddress, string eventType, string eventDetails, string affectedDetails = null)
|
||||
{
|
||||
var performingUser = _userServiceInstance.GetUserById(performingId);
|
||||
|
||||
var performingDetails = performingUser == null
|
||||
? $"User UNKNOWN:{performingId}"
|
||||
: $"User \"{performingUser.Name}\" {FormatEmail(performingUser)}";
|
||||
|
||||
WriteAudit(performingId, performingDetails, affectedId, ipAddress, eventType, eventDetails, affectedDetails);
|
||||
}
|
||||
|
||||
private void WriteAudit(IUser performingUser, int affectedId, string ipAddress, string eventType, string eventDetails)
|
||||
{
|
||||
var performingDetails = performingUser == null
|
||||
? $"User UNKNOWN"
|
||||
: $"User \"{performingUser.Name}\" {FormatEmail(performingUser)}";
|
||||
|
||||
WriteAudit(performingUser?.Id ?? 0, performingDetails, affectedId, ipAddress, eventType, eventDetails);
|
||||
}
|
||||
|
||||
private void WriteAudit(int performingId, string performingDetails, int affectedId, string ipAddress, string eventType, string eventDetails, string affectedDetails = null)
|
||||
{
|
||||
if (affectedDetails == null)
|
||||
{
|
||||
var affectedUser = _userServiceInstance.GetUserById(affectedId);
|
||||
affectedDetails = affectedUser == null
|
||||
? $"User UNKNOWN:{affectedId}"
|
||||
: $"User \"{affectedUser.Name}\" {FormatEmail(affectedUser)}";
|
||||
}
|
||||
|
||||
_auditServiceInstance.Write(performingId, performingDetails,
|
||||
ipAddress,
|
||||
DateTime.UtcNow,
|
||||
affectedId, affectedDetails,
|
||||
eventType, eventDetails);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using System.ComponentModel;
|
||||
using System.Threading;
|
||||
using System.Web;
|
||||
using Umbraco.Core.Security;
|
||||
@@ -45,12 +46,8 @@ namespace Umbraco.Core.Auditing
|
||||
/// </summary>
|
||||
public string Username { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Sets the properties on the event being raised, all parameters are optional except for the action being performed
|
||||
/// </summary>
|
||||
/// <param name="action">An action based on the AuditEvent enum</param>
|
||||
/// <param name="ipAddress">The client's IP address. This is usually automatically set but could be overridden if necessary</param>
|
||||
/// <param name="performingUser">The Id of the user performing the action (if different from the user affected by the action)</param>
|
||||
[Obsolete("Use the method that has the affectedUser parameter instead")]
|
||||
[EditorBrowsable(EditorBrowsableState.Never)]
|
||||
public IdentityAuditEventArgs(AuditEvent action, string ipAddress, int performingUser = -1)
|
||||
{
|
||||
DateTimeUtc = DateTime.UtcNow;
|
||||
@@ -63,6 +60,35 @@ namespace Umbraco.Core.Auditing
|
||||
: performingUser;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Default constructor
|
||||
/// </summary>
|
||||
/// <param name="action"></param>
|
||||
/// <param name="ipAddress"></param>
|
||||
/// <param name="comment"></param>
|
||||
/// <param name="performingUser"></param>
|
||||
/// <param name="affectedUser"></param>
|
||||
public IdentityAuditEventArgs(AuditEvent action, string ipAddress, string comment = null, int performingUser = -1, int affectedUser = -1)
|
||||
{
|
||||
DateTimeUtc = DateTime.UtcNow;
|
||||
Action = action;
|
||||
|
||||
IpAddress = ipAddress;
|
||||
Comment = comment;
|
||||
AffectedUser = affectedUser;
|
||||
|
||||
PerformingUser = performingUser == -1
|
||||
? GetCurrentRequestBackofficeUserId()
|
||||
: performingUser;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates an instance without a performing or affected user (the id will be set to -1)
|
||||
/// </summary>
|
||||
/// <param name="action"></param>
|
||||
/// <param name="ipAddress"></param>
|
||||
/// <param name="username"></param>
|
||||
/// <param name="comment"></param>
|
||||
public IdentityAuditEventArgs(AuditEvent action, string ipAddress, string username, string comment)
|
||||
{
|
||||
DateTimeUtc = DateTime.UtcNow;
|
||||
@@ -71,6 +97,22 @@ namespace Umbraco.Core.Auditing
|
||||
IpAddress = ipAddress;
|
||||
Username = username;
|
||||
Comment = comment;
|
||||
|
||||
PerformingUser = -1;
|
||||
}
|
||||
|
||||
public IdentityAuditEventArgs(AuditEvent action, string ipAddress, string username, string comment, int performingUser)
|
||||
{
|
||||
DateTimeUtc = DateTime.UtcNow;
|
||||
Action = action;
|
||||
|
||||
IpAddress = ipAddress;
|
||||
Username = username;
|
||||
Comment = comment;
|
||||
|
||||
PerformingUser = performingUser == -1
|
||||
? GetCurrentRequestBackofficeUserId()
|
||||
: performingUser;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -146,7 +146,7 @@ namespace Umbraco.Core.Cache
|
||||
#region Insert
|
||||
#endregion
|
||||
|
||||
private class NoopLocker : DisposableObject
|
||||
private class NoopLocker : DisposableObjectSlim
|
||||
{
|
||||
protected override void DisposeResources()
|
||||
{ }
|
||||
|
||||
@@ -0,0 +1,61 @@
|
||||
using System;
|
||||
|
||||
namespace Umbraco.Core.Collections
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a composite key of (Type, Type) for fast dictionaries.
|
||||
/// </summary>
|
||||
internal struct CompositeTypeTypeKey : IEquatable<CompositeTypeTypeKey>
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="CompositeTypeTypeKey"/> struct.
|
||||
/// </summary>
|
||||
public CompositeTypeTypeKey(Type type1, Type type2) : this()
|
||||
{
|
||||
Type1 = type1;
|
||||
Type2 = type2;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the first type.
|
||||
/// </summary>
|
||||
public Type Type1 { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the second type.
|
||||
/// </summary>
|
||||
public Type Type2 { get; private set; }
|
||||
|
||||
/// <inheritdoc/>
|
||||
public bool Equals(CompositeTypeTypeKey other)
|
||||
{
|
||||
return Type1 == other.Type1 && Type2 == other.Type2;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
var other = obj is CompositeTypeTypeKey ? (CompositeTypeTypeKey)obj : default(CompositeTypeTypeKey);
|
||||
return Type1 == other.Type1 && Type2 == other.Type2;
|
||||
}
|
||||
|
||||
public static bool operator ==(CompositeTypeTypeKey key1, CompositeTypeTypeKey key2)
|
||||
{
|
||||
return key1.Type1 == key2.Type1 && key1.Type2 == key2.Type2;
|
||||
}
|
||||
|
||||
public static bool operator !=(CompositeTypeTypeKey key1, CompositeTypeTypeKey key2)
|
||||
{
|
||||
return key1.Type1 != key2.Type1 || key1.Type2 != key2.Type2;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override int GetHashCode()
|
||||
{
|
||||
unchecked
|
||||
{
|
||||
return (Type1.GetHashCode() * 397) ^ Type2.GetHashCode();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,10 +1,14 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
using System.Web;
|
||||
using System.Xml.Linq;
|
||||
using ClientDependency.Core.CompositeFiles.Providers;
|
||||
using ClientDependency.Core.Config;
|
||||
using Semver;
|
||||
using Umbraco.Core.IO;
|
||||
using Umbraco.Core.Logging;
|
||||
|
||||
@@ -24,10 +28,74 @@ namespace Umbraco.Core.Configuration
|
||||
_logger = logger;
|
||||
_fileName = IOHelper.MapPath(string.Format("{0}/ClientDependency.config", SystemDirectories.Config));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Changes the version number in ClientDependency.config to a hashed value for the version and the DateTime.Day
|
||||
/// </summary>
|
||||
/// <param name="version">The <see cref="SemVersion">version</see> of Umbraco we're upgrading to</param>
|
||||
/// <param name="date">A <see cref="DateTime">date</see> value to use in the hash to prevent this method from updating the version on each startup</param>
|
||||
/// <param name="dateFormat">Allows the developer to specify the <see cref="string">date precision</see> for the hash (i.e. "yyyyMMdd" would be a precision for the day)</param>
|
||||
/// <returns>Boolean to indicate succesful update of the ClientDependency.config file</returns>
|
||||
public bool UpdateVersionNumber(SemVersion version, DateTime date, string dateFormat)
|
||||
{
|
||||
var byteContents = Encoding.Unicode.GetBytes(version + date.ToString(dateFormat));
|
||||
|
||||
//This is a way to convert a string to a long
|
||||
//see https://www.codeproject.com/Articles/34309/Convert-String-to-bit-Integer
|
||||
//We could much more easily use MD5 which would create us an INT but since that is not compliant with
|
||||
//hashing standards we have to use SHA
|
||||
int intHash;
|
||||
using (var hash = SHA256.Create())
|
||||
{
|
||||
var bytes = hash.ComputeHash(byteContents);
|
||||
|
||||
var longResult = new[] { 0, 8, 16, 24 }
|
||||
.Select(i => BitConverter.ToInt64(bytes, i))
|
||||
.Aggregate((x, y) => x ^ y);
|
||||
|
||||
//CDF requires an INT, and although this isn't fail safe, it will work for our purposes. We are not hashing for crypto purposes
|
||||
//so there could be some collisions with this conversion but it's not a problem for our purposes
|
||||
//It's also important to note that the long.GetHashCode() implementation in .NET is this: return (int) this ^ (int) (this >> 32);
|
||||
//which means that this value will not change per appdomain like some GetHashCode implementations.
|
||||
intHash = longResult.GetHashCode();
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
var clientDependencyConfigXml = XDocument.Load(_fileName, LoadOptions.PreserveWhitespace);
|
||||
if (clientDependencyConfigXml.Root != null)
|
||||
{
|
||||
|
||||
var versionAttribute = clientDependencyConfigXml.Root.Attribute("version");
|
||||
|
||||
//Set the new version to the hashcode of now
|
||||
var oldVersion = versionAttribute.Value;
|
||||
var newVersion = Math.Abs(intHash).ToString();
|
||||
|
||||
//don't update if it's the same version
|
||||
if (oldVersion == newVersion)
|
||||
return false;
|
||||
|
||||
versionAttribute.SetValue(newVersion);
|
||||
clientDependencyConfigXml.Save(_fileName, SaveOptions.DisableFormatting);
|
||||
|
||||
_logger.Info<ClientDependencyConfiguration>(string.Format("Updated version number from {0} to {1}", oldVersion, newVersion));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.Error<ClientDependencyConfiguration>("Couldn't update ClientDependency version number", ex);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Changes the version number in ClientDependency.config to a random value to avoid stale caches
|
||||
/// </summary>
|
||||
/// <seealso cref="UpdateVersionNumber(SemVersion, DateTime, string)" />
|
||||
[Obsolete("Use the UpdateVersionNumber method specifying the version, date and dateFormat instead")]
|
||||
public bool IncreaseVersionNumber()
|
||||
{
|
||||
try
|
||||
@@ -107,4 +175,4 @@ namespace Umbraco.Core.Configuration
|
||||
return success;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@ namespace Umbraco.Core.Configuration
|
||||
{
|
||||
public class UmbracoVersion
|
||||
{
|
||||
private static readonly Version Version = new Version("7.8.0");
|
||||
private static readonly Version Version = new Version("7.9.0");
|
||||
|
||||
/// <summary>
|
||||
/// Gets the current version of Umbraco.
|
||||
|
||||
@@ -9,6 +9,7 @@ namespace Umbraco.Core
|
||||
{
|
||||
|
||||
public const string AdminGroupAlias = "admin";
|
||||
public const string SensitiveDataGroupAlias = "sensitiveData";
|
||||
public const string TranslatorGroupAlias = "translator";
|
||||
|
||||
public const string BackOfficeAuthenticationType = "UmbracoBackOffice";
|
||||
@@ -36,4 +37,4 @@ namespace Umbraco.Core
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,17 +11,53 @@
|
||||
/// The integer identifier for global system root node.
|
||||
/// </summary>
|
||||
public const int Root = -1;
|
||||
|
||||
/// <summary>
|
||||
/// The string identifier for global system root node.
|
||||
/// </summary>
|
||||
/// <remarks>Use this instead of re-creating the string everywhere.</remarks>
|
||||
public const string RootString = "-1";
|
||||
|
||||
/// <summary>
|
||||
/// The integer identifier for content's recycle bin.
|
||||
/// </summary>
|
||||
public const int RecycleBinContent = -20;
|
||||
|
||||
/// <summary>
|
||||
/// The string identifier for content's recycle bin.
|
||||
/// </summary>
|
||||
/// <remarks>Use this instead of re-creating the string everywhere.</remarks>
|
||||
public const string RecycleBinContentString = "-20";
|
||||
|
||||
/// <summary>
|
||||
/// The string path prefix of the content's recycle bin.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>Everything that is in the content recycle bin, has a path that starts with the prefix.</para>
|
||||
/// <para>Use this instead of re-creating the string everywhere.</para>
|
||||
/// </remarks>
|
||||
public const string RecycleBinContentPathPrefix = "-1,-20,";
|
||||
|
||||
/// <summary>
|
||||
/// The integer identifier for media's recycle bin.
|
||||
/// </summary>
|
||||
public const int RecycleBinMedia = -21;
|
||||
|
||||
/// <summary>
|
||||
/// The string identifier for media's recycle bin.
|
||||
/// </summary>
|
||||
/// <remarks>Use this instead of re-creating the string everywhere.</remarks>
|
||||
public const string RecycleBinMediaString = "-21";
|
||||
|
||||
/// <summary>
|
||||
/// The string path prefix of the media's recycle bin.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>Everything that is in the media recycle bin, has a path that starts with the prefix.</para>
|
||||
/// <para>Use this instead of re-creating the string everywhere.</para>
|
||||
/// </remarks>
|
||||
public const string RecycleBinMediaPathPrefix = "-1,-21,";
|
||||
|
||||
public const int DefaultContentListViewDataTypeId = -95;
|
||||
public const int DefaultMediaListViewDataTypeId = -96;
|
||||
public const int DefaultMembersListViewDataTypeId = -97;
|
||||
@@ -29,6 +65,6 @@
|
||||
public const string UmbracoConnectionName = "umbracoDbDSN";
|
||||
public const string UmbracoMigrationName = "Umbraco";
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
@@ -21,9 +21,9 @@ namespace Umbraco.Core
|
||||
public static DateTime TruncateTo(this DateTime dt, DateTruncate truncateTo)
|
||||
{
|
||||
if (truncateTo == DateTruncate.Year)
|
||||
return new DateTime(dt.Year, 0, 0);
|
||||
return new DateTime(dt.Year, 1, 1);
|
||||
if (truncateTo == DateTruncate.Month)
|
||||
return new DateTime(dt.Year, dt.Month, 0);
|
||||
return new DateTime(dt.Year, dt.Month, 1);
|
||||
if (truncateTo == DateTruncate.Day)
|
||||
return new DateTime(dt.Year, dt.Month, dt.Day);
|
||||
if (truncateTo == DateTruncate.Hour)
|
||||
@@ -41,43 +41,43 @@ namespace Umbraco.Core
|
||||
Hour,
|
||||
Minute,
|
||||
Second
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Calculates the number of minutes from a date time, on a rolling daily basis (so if
|
||||
/// Calculates the number of minutes from a date time, on a rolling daily basis (so if
|
||||
/// date time is before the time, calculate onto next day)
|
||||
/// </summary>
|
||||
/// <param name="fromDateTime">Date to start from</param>
|
||||
/// <param name="scheduledTime">Time to compare against (in Hmm form, e.g. 330, 2200)</param>
|
||||
/// <returns></returns>
|
||||
public static int PeriodicMinutesFrom(this DateTime fromDateTime, string scheduledTime)
|
||||
{
|
||||
// Ensure time provided is 4 digits long
|
||||
if (scheduledTime.Length == 3)
|
||||
{
|
||||
scheduledTime = "0" + scheduledTime;
|
||||
}
|
||||
|
||||
var scheduledHour = int.Parse(scheduledTime.Substring(0, 2));
|
||||
var scheduledMinute = int.Parse(scheduledTime.Substring(2));
|
||||
|
||||
DateTime scheduledDateTime;
|
||||
if (IsScheduledInRemainingDay(fromDateTime, scheduledHour, scheduledMinute))
|
||||
{
|
||||
scheduledDateTime = new DateTime(fromDateTime.Year, fromDateTime.Month, fromDateTime.Day, scheduledHour, scheduledMinute, 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
var nextDay = fromDateTime.AddDays(1);
|
||||
scheduledDateTime = new DateTime(nextDay.Year, nextDay.Month, nextDay.Day, scheduledHour, scheduledMinute, 0);
|
||||
}
|
||||
|
||||
return (int)(scheduledDateTime - fromDateTime).TotalMinutes;
|
||||
}
|
||||
|
||||
private static bool IsScheduledInRemainingDay(DateTime fromDateTime, int scheduledHour, int scheduledMinute)
|
||||
{
|
||||
return scheduledHour > fromDateTime.Hour || (scheduledHour == fromDateTime.Hour && scheduledMinute >= fromDateTime.Minute);
|
||||
/// <returns></returns>
|
||||
public static int PeriodicMinutesFrom(this DateTime fromDateTime, string scheduledTime)
|
||||
{
|
||||
// Ensure time provided is 4 digits long
|
||||
if (scheduledTime.Length == 3)
|
||||
{
|
||||
scheduledTime = "0" + scheduledTime;
|
||||
}
|
||||
|
||||
var scheduledHour = int.Parse(scheduledTime.Substring(0, 2));
|
||||
var scheduledMinute = int.Parse(scheduledTime.Substring(2));
|
||||
|
||||
DateTime scheduledDateTime;
|
||||
if (IsScheduledInRemainingDay(fromDateTime, scheduledHour, scheduledMinute))
|
||||
{
|
||||
scheduledDateTime = new DateTime(fromDateTime.Year, fromDateTime.Month, fromDateTime.Day, scheduledHour, scheduledMinute, 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
var nextDay = fromDateTime.AddDays(1);
|
||||
scheduledDateTime = new DateTime(nextDay.Year, nextDay.Month, nextDay.Day, scheduledHour, scheduledMinute, 0);
|
||||
}
|
||||
|
||||
return (int)(scheduledDateTime - fromDateTime).TotalMinutes;
|
||||
}
|
||||
|
||||
private static bool IsScheduledInRemainingDay(DateTime fromDateTime, int scheduledHour, int scheduledMinute)
|
||||
{
|
||||
return scheduledHour > fromDateTime.Hour || (scheduledHour == fromDateTime.Hour && scheduledMinute >= fromDateTime.Minute);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,60 +2,60 @@ using System;
|
||||
using System.Threading;
|
||||
|
||||
namespace Umbraco.Core
|
||||
{
|
||||
/// <summary>
|
||||
/// Abstract implementation of IDisposable.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Can also be used as a pattern for when inheriting is not possible.
|
||||
///
|
||||
{
|
||||
/// <summary>
|
||||
/// Abstract implementation of IDisposable.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Can also be used as a pattern for when inheriting is not possible.
|
||||
///
|
||||
/// See also: https://msdn.microsoft.com/en-us/library/b1yfkh5e%28v=vs.110%29.aspx
|
||||
/// See also: https://lostechies.com/chrispatterson/2012/11/29/idisposable-done-right/
|
||||
///
|
||||
/// Note: if an object's ctor throws, it will never be disposed, and so if that ctor
|
||||
/// has allocated disposable objects, it should take care of disposing them.
|
||||
/// </remarks>
|
||||
public abstract class DisposableObject : IDisposable
|
||||
{
|
||||
private bool _disposed;
|
||||
private readonly object _locko = new object();
|
||||
|
||||
// gets a value indicating whether this instance is disposed.
|
||||
/// </remarks>
|
||||
public abstract class DisposableObject : IDisposable
|
||||
{
|
||||
private bool _disposed;
|
||||
private readonly object _locko = new object();
|
||||
|
||||
// gets a value indicating whether this instance is disposed.
|
||||
// for internal tests only (not thread safe)
|
||||
//TODO make this internal + rename "Disposed" when we can break compatibility
|
||||
public bool IsDisposed { get { return _disposed; } }
|
||||
|
||||
// implements IDisposable
|
||||
public void Dispose()
|
||||
{
|
||||
Dispose(true);
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
// finalizer
|
||||
~DisposableObject()
|
||||
{
|
||||
Dispose(false);
|
||||
}
|
||||
public bool IsDisposed { get { return _disposed; } }
|
||||
|
||||
// implements IDisposable
|
||||
public void Dispose()
|
||||
{
|
||||
Dispose(true);
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
// finalizer
|
||||
~DisposableObject()
|
||||
{
|
||||
Dispose(false);
|
||||
}
|
||||
|
||||
//TODO make this private, non-virtual when we can break compatibility
|
||||
protected virtual void Dispose(bool disposing)
|
||||
{
|
||||
lock (_locko)
|
||||
{
|
||||
if (_disposed) return;
|
||||
_disposed = true;
|
||||
}
|
||||
protected virtual void Dispose(bool disposing)
|
||||
{
|
||||
lock (_locko)
|
||||
{
|
||||
if (_disposed) return;
|
||||
_disposed = true;
|
||||
}
|
||||
|
||||
DisposeUnmanagedResources();
|
||||
|
||||
if (disposing)
|
||||
DisposeResources();
|
||||
}
|
||||
|
||||
protected abstract void DisposeResources();
|
||||
|
||||
protected virtual void DisposeUnmanagedResources()
|
||||
{ }
|
||||
}
|
||||
DisposeResources();
|
||||
}
|
||||
|
||||
protected abstract void DisposeResources();
|
||||
|
||||
protected virtual void DisposeUnmanagedResources()
|
||||
{ }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
using System;
|
||||
|
||||
namespace Umbraco.Core
|
||||
{
|
||||
/// <summary>
|
||||
/// This should not be use if there are ubmanaged resources to be disposed, use DisposableObject instead
|
||||
/// </summary>
|
||||
public abstract class DisposableObjectSlim : IDisposable
|
||||
{
|
||||
private bool _disposed;
|
||||
private readonly object _locko = new object();
|
||||
|
||||
// gets a value indicating whether this instance is disposed.
|
||||
// for internal tests only (not thread safe)
|
||||
internal bool Disposed { get { return _disposed; } }
|
||||
|
||||
// implements IDisposable
|
||||
public void Dispose()
|
||||
{
|
||||
Dispose(true);
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
private void Dispose(bool disposing)
|
||||
{
|
||||
lock (_locko)
|
||||
{
|
||||
if (_disposed) return;
|
||||
_disposed = true;
|
||||
}
|
||||
|
||||
if (disposing)
|
||||
DisposeResources();
|
||||
}
|
||||
|
||||
protected abstract void DisposeResources();
|
||||
}
|
||||
}
|
||||
@@ -11,8 +11,8 @@ namespace Umbraco.Core
|
||||
/// <summary>
|
||||
/// Starts the timer and invokes a callback upon disposal. Provides a simple way of timing an operation by wrapping it in a <code>using</code> (C#) statement.
|
||||
/// </summary>
|
||||
public class DisposableTimer : DisposableObject
|
||||
{
|
||||
public class DisposableTimer : DisposableObjectSlim
|
||||
{
|
||||
private readonly ILogger _logger;
|
||||
private readonly LogType? _logType;
|
||||
private readonly IProfiler _profiler;
|
||||
@@ -209,13 +209,13 @@ namespace Umbraco.Core
|
||||
loggerType,
|
||||
startMessage(),
|
||||
completeMessage());
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// Handles the disposal of resources. Derived from abstract class <see cref="DisposableObject"/> which handles common required locking logic.
|
||||
/// </summary>
|
||||
protected override void DisposeResources()
|
||||
|
||||
/// <summary>
|
||||
/// Handles the disposal of resources. Derived from abstract class <see cref="DisposableObjectSlim"/> which handles common required locking logic.
|
||||
/// </summary>
|
||||
protected override void DisposeResources()
|
||||
{
|
||||
if (_profiler != null)
|
||||
{
|
||||
|
||||
@@ -5,7 +5,7 @@ namespace Umbraco.Core.Events
|
||||
/// <summary>
|
||||
/// Event messages collection
|
||||
/// </summary>
|
||||
public sealed class EventMessages : DisposableObject
|
||||
public sealed class EventMessages : DisposableObjectSlim
|
||||
{
|
||||
private readonly List<EventMessage> _msgs = new List<EventMessage>();
|
||||
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
using System;
|
||||
using Umbraco.Core.Models;
|
||||
using Umbraco.Core.Models.Membership;
|
||||
|
||||
namespace Umbraco.Core.Events
|
||||
{
|
||||
internal class ExportedMemberEventArgs : EventArgs
|
||||
{
|
||||
public IMember Member { get; }
|
||||
public MemberExportModel Exported { get; }
|
||||
|
||||
public ExportedMemberEventArgs(IMember member, MemberExportModel exported)
|
||||
{
|
||||
Member = member;
|
||||
Exported = exported;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using Umbraco.Core.Packaging.Models;
|
||||
|
||||
namespace Umbraco.Core.Events
|
||||
@@ -7,18 +8,27 @@ namespace Umbraco.Core.Events
|
||||
public class ImportPackageEventArgs<TEntity> : CancellableEnumerableObjectEventArgs<TEntity>, IEquatable<ImportPackageEventArgs<TEntity>>
|
||||
{
|
||||
private readonly MetaData _packageMetaData;
|
||||
|
||||
|
||||
[EditorBrowsable(EditorBrowsableState.Never)]
|
||||
[Obsolete("Use the overload specifying packageMetaData instead")]
|
||||
public ImportPackageEventArgs(TEntity eventObject, bool canCancel)
|
||||
: base(new[] { eventObject }, canCancel)
|
||||
{
|
||||
}
|
||||
|
||||
public ImportPackageEventArgs(TEntity eventObject, MetaData packageMetaData)
|
||||
: base(new[] { eventObject })
|
||||
public ImportPackageEventArgs(TEntity eventObject, MetaData packageMetaData, bool canCancel)
|
||||
: base(new[] { eventObject }, canCancel)
|
||||
{
|
||||
if (packageMetaData == null) throw new ArgumentNullException("packageMetaData");
|
||||
_packageMetaData = packageMetaData;
|
||||
}
|
||||
|
||||
public ImportPackageEventArgs(TEntity eventObject, MetaData packageMetaData)
|
||||
: this(eventObject, packageMetaData, true)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public MetaData PackageMetaData
|
||||
{
|
||||
get { return _packageMetaData; }
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
using System;
|
||||
|
||||
namespace Umbraco.Core.Events
|
||||
{
|
||||
public class RolesEventArgs : EventArgs
|
||||
{
|
||||
public RolesEventArgs(int[] memberIds, string[] roles)
|
||||
{
|
||||
MemberIds = memberIds;
|
||||
Roles = roles;
|
||||
}
|
||||
|
||||
public int[] MemberIds { get; set; }
|
||||
public string[] Roles { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
using System;
|
||||
using Umbraco.Core.Models.Membership;
|
||||
|
||||
namespace Umbraco.Core.Events
|
||||
{
|
||||
internal class UserGroupWithUsers
|
||||
{
|
||||
public UserGroupWithUsers(IUserGroup userGroup, IUser[] addedUsers, IUser[] removedUsers)
|
||||
{
|
||||
UserGroup = userGroup;
|
||||
AddedUsers = addedUsers;
|
||||
RemovedUsers = removedUsers;
|
||||
}
|
||||
|
||||
public IUserGroup UserGroup { get; }
|
||||
public IUser[] AddedUsers { get; }
|
||||
public IUser[] RemovedUsers { get; }
|
||||
}
|
||||
}
|
||||
@@ -14,7 +14,7 @@ namespace Umbraco.Core
|
||||
/// This will use the crypto libs to generate the hash and will try to ensure that
|
||||
/// strings, etc... are not re-allocated so it's not consuming much memory.
|
||||
/// </remarks>
|
||||
internal class HashGenerator : DisposableObject
|
||||
internal class HashGenerator : DisposableObjectSlim
|
||||
{
|
||||
public HashGenerator()
|
||||
{
|
||||
|
||||
@@ -6,7 +6,7 @@ using Umbraco.Core.Logging;
|
||||
|
||||
namespace Umbraco.Core.Manifest
|
||||
{
|
||||
internal class ManifestWatcher : DisposableObject
|
||||
internal class ManifestWatcher : DisposableObjectSlim
|
||||
{
|
||||
private readonly ILogger _logger;
|
||||
private readonly List<FileSystemWatcher> _fws = new List<FileSystemWatcher>();
|
||||
|
||||
@@ -0,0 +1,94 @@
|
||||
using System;
|
||||
using System.Reflection;
|
||||
using System.Runtime.Serialization;
|
||||
using Umbraco.Core.Models.EntityBase;
|
||||
|
||||
namespace Umbraco.Core.Models
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents an audited event.
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
[DataContract(IsReference = true)]
|
||||
internal class AuditEntry : Entity, IAuditEntry
|
||||
{
|
||||
private static PropertySelectors _selectors;
|
||||
|
||||
private int _performingUserId;
|
||||
private string _performingDetails;
|
||||
private string _performingIp;
|
||||
private int _affectedUserId;
|
||||
private string _affectedDetails;
|
||||
private string _eventType;
|
||||
private string _eventDetails;
|
||||
|
||||
private static PropertySelectors Selectors => _selectors ?? (_selectors = new PropertySelectors());
|
||||
|
||||
private class PropertySelectors
|
||||
{
|
||||
public readonly PropertyInfo PerformingUserId = ExpressionHelper.GetPropertyInfo<AuditEntry, int>(x => x.PerformingUserId);
|
||||
public readonly PropertyInfo PerformingDetails = ExpressionHelper.GetPropertyInfo<AuditEntry, string>(x => x.PerformingDetails);
|
||||
public readonly PropertyInfo PerformingIp = ExpressionHelper.GetPropertyInfo<AuditEntry, string>(x => x.PerformingIp);
|
||||
public readonly PropertyInfo AffectedUserId = ExpressionHelper.GetPropertyInfo<AuditEntry, int>(x => x.AffectedUserId);
|
||||
public readonly PropertyInfo AffectedDetails = ExpressionHelper.GetPropertyInfo<AuditEntry, string>(x => x.AffectedDetails);
|
||||
public readonly PropertyInfo EventType = ExpressionHelper.GetPropertyInfo<AuditEntry, string>(x => x.EventType);
|
||||
public readonly PropertyInfo EventDetails = ExpressionHelper.GetPropertyInfo<AuditEntry, string>(x => x.EventDetails);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public int PerformingUserId
|
||||
{
|
||||
get => _performingUserId;
|
||||
set => SetPropertyValueAndDetectChanges(value, ref _performingUserId, Selectors.PerformingUserId);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public string PerformingDetails
|
||||
{
|
||||
get => _performingDetails;
|
||||
set => SetPropertyValueAndDetectChanges(value, ref _performingDetails, Selectors.PerformingDetails);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public string PerformingIp
|
||||
{
|
||||
get => _performingIp;
|
||||
set => SetPropertyValueAndDetectChanges(value, ref _performingIp, Selectors.PerformingIp);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public DateTime EventDateUtc
|
||||
{
|
||||
get => CreateDate;
|
||||
set => CreateDate = value;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public int AffectedUserId
|
||||
{
|
||||
get => _affectedUserId;
|
||||
set => SetPropertyValueAndDetectChanges(value, ref _affectedUserId, Selectors.AffectedUserId);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public string AffectedDetails
|
||||
{
|
||||
get => _affectedDetails;
|
||||
set => SetPropertyValueAndDetectChanges(value, ref _affectedDetails, Selectors.AffectedDetails);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public string EventType
|
||||
{
|
||||
get => _eventType;
|
||||
set => SetPropertyValueAndDetectChanges(value, ref _eventType, Selectors.EventType);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public string EventDetails
|
||||
{
|
||||
get => _eventDetails;
|
||||
set => SetPropertyValueAndDetectChanges(value, ref _eventDetails, Selectors.EventDetails);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,102 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Reflection;
|
||||
using System.Runtime.Serialization;
|
||||
using Umbraco.Core.Models.EntityBase;
|
||||
|
||||
namespace Umbraco.Core.Models
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a consent.
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
[DataContract(IsReference = true)]
|
||||
internal class Consent : Entity, IConsent
|
||||
{
|
||||
private static PropertySelectors _selector;
|
||||
|
||||
private bool _current;
|
||||
private string _source;
|
||||
private string _context;
|
||||
private string _action;
|
||||
private ConsentState _state;
|
||||
private string _comment;
|
||||
|
||||
// ReSharper disable once ClassNeverInstantiated.Local
|
||||
private class PropertySelectors
|
||||
{
|
||||
public readonly PropertyInfo Current = ExpressionHelper.GetPropertyInfo<Consent, bool>(x => x.Current);
|
||||
public readonly PropertyInfo Source = ExpressionHelper.GetPropertyInfo<Consent, string>(x => x.Source);
|
||||
public readonly PropertyInfo Context = ExpressionHelper.GetPropertyInfo<Consent, string>(x => x.Context);
|
||||
public readonly PropertyInfo Action = ExpressionHelper.GetPropertyInfo<Consent, string>(x => x.Action);
|
||||
public readonly PropertyInfo State = ExpressionHelper.GetPropertyInfo<Consent, ConsentState>(x => x.State);
|
||||
public readonly PropertyInfo Comment = ExpressionHelper.GetPropertyInfo<Consent, string>(x => x.Comment);
|
||||
}
|
||||
|
||||
private static PropertySelectors Selectors => _selector ?? (_selector = new PropertySelectors());
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool Current
|
||||
{
|
||||
get => _current;
|
||||
set => SetPropertyValueAndDetectChanges(value, ref _current, Selectors.Current);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public string Source
|
||||
{
|
||||
get => _source;
|
||||
set
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(value)) throw new ArgumentException(nameof(value));
|
||||
SetPropertyValueAndDetectChanges(value, ref _source, Selectors.Source);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public string Context
|
||||
{
|
||||
get => _context;
|
||||
set
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(value)) throw new ArgumentException(nameof(value));
|
||||
SetPropertyValueAndDetectChanges(value, ref _context, Selectors.Context);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public string Action
|
||||
{
|
||||
get => _action;
|
||||
set
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(value)) throw new ArgumentException(nameof(value));
|
||||
SetPropertyValueAndDetectChanges(value, ref _action, Selectors.Action);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public ConsentState State
|
||||
{
|
||||
get => _state;
|
||||
// note: we probably should validate the state here, but since the
|
||||
// enum is [Flags] with many combinations, this could be expensive
|
||||
set => SetPropertyValueAndDetectChanges(value, ref _state, Selectors.State);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public string Comment
|
||||
{
|
||||
get => _comment;
|
||||
set => SetPropertyValueAndDetectChanges(value, ref _comment, Selectors.Comment);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public IEnumerable<IConsent> History => HistoryInternal;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the previous states of this consent.
|
||||
/// </summary>
|
||||
internal List<IConsent> HistoryInternal { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
namespace Umbraco.Core.Models
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides extension methods for the <see cref="IConsent"/> interface.
|
||||
/// </summary>
|
||||
public static class ConsentExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Determines whether the consent is granted.
|
||||
/// </summary>
|
||||
public static bool IsGranted(this IConsent consent) => (consent.State & ConsentState.Granted) > 0;
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether the consent is revoked.
|
||||
/// </summary>
|
||||
public static bool IsRevoked(this IConsent consent) => (consent.State & ConsentState.Revoked) > 0;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
using System;
|
||||
|
||||
namespace Umbraco.Core.Models
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents the state of a consent.
|
||||
/// </summary>
|
||||
[Flags]
|
||||
public enum ConsentState // : int
|
||||
{
|
||||
// note - this is a [Flags] enumeration
|
||||
// on can create detailed flags such as:
|
||||
//GrantedOptIn = Granted | 0x0001
|
||||
//GrandedByForce = Granted | 0x0002
|
||||
//
|
||||
// 16 situations for each Pending/Granted/Revoked should be ok
|
||||
|
||||
/// <summary>
|
||||
/// There is no consent.
|
||||
/// </summary>
|
||||
None = 0,
|
||||
|
||||
/// <summary>
|
||||
/// Consent is pending and has not been granted yet.
|
||||
/// </summary>
|
||||
Pending = 0x10000,
|
||||
|
||||
/// <summary>
|
||||
/// Consent has been granted.
|
||||
/// </summary>
|
||||
Granted = 0x20000,
|
||||
|
||||
/// <summary>
|
||||
/// Consent has been revoked.
|
||||
/// </summary>
|
||||
Revoked = 0x40000
|
||||
}
|
||||
}
|
||||
@@ -544,6 +544,28 @@ namespace Umbraco.Core.Models
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns both instance dirty properties and property type properties
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public override IEnumerable<string> GetDirtyProperties()
|
||||
{
|
||||
var instanceProperties = base.GetDirtyProperties();
|
||||
var propertyTypes = Properties.Where(x => x.IsDirty()).Select(x => x.Alias);
|
||||
return instanceProperties.Concat(propertyTypes);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns both instance dirty properties and property type properties
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
internal override IEnumerable<string> GetPreviouslyDirtyProperties()
|
||||
{
|
||||
var instanceProperties = base.GetPreviouslyDirtyProperties();
|
||||
var propertyTypes = Properties.Where(x => x.WasDirty()).Select(x => x.Alias);
|
||||
return instanceProperties.Concat(propertyTypes);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -95,8 +95,10 @@ namespace Umbraco.Core.Models
|
||||
get { return _allowedTemplates; }
|
||||
set
|
||||
{
|
||||
SetPropertyValueAndDetectChanges(value, ref _allowedTemplates, Ps.Value.AllowedTemplatesSelector,
|
||||
Ps.Value.TemplateComparer);
|
||||
SetPropertyValueAndDetectChanges(value, ref _allowedTemplates, Ps.Value.AllowedTemplatesSelector, Ps.Value.TemplateComparer);
|
||||
|
||||
if (_allowedTemplates.Any(x => x.Id == _defaultTemplate) == false)
|
||||
DefaultTemplateId = 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -23,6 +23,13 @@ namespace Umbraco.Core.Models.EntityBase
|
||||
: _propertyChangedInfo.Where(x => x.Value).Select(x => x.Key);
|
||||
}
|
||||
|
||||
internal virtual IEnumerable<string> GetPreviouslyDirtyProperties()
|
||||
{
|
||||
return _lastPropertyChangedInfo == null
|
||||
? Enumerable.Empty<string>()
|
||||
: _lastPropertyChangedInfo.Where(x => x.Value).Select(x => x.Key);
|
||||
}
|
||||
|
||||
private bool _changeTrackingEnabled = true;
|
||||
|
||||
/// <summary>
|
||||
@@ -221,4 +228,4 @@ namespace Umbraco.Core.Models.EntityBase
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,60 @@
|
||||
using System;
|
||||
using Umbraco.Core.Models.EntityBase;
|
||||
|
||||
namespace Umbraco.Core.Models
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents an audited event.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>The free-form details properties can be used to capture relevant infos (for example,
|
||||
/// a user email and identifier) at the time of the audited event, even though they may change
|
||||
/// later on - but we want to keep a track of their value at that time.</para>
|
||||
/// <para>Depending on audit loggers, these properties can be purely free-form text, or
|
||||
/// contain json serialized objects.</para>
|
||||
/// </remarks>
|
||||
public interface IAuditEntry : IAggregateRoot, IRememberBeingDirty
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the identifier of the user triggering the audited event.
|
||||
/// </summary>
|
||||
int PerformingUserId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets free-form details about the user triggering the audited event.
|
||||
/// </summary>
|
||||
string PerformingDetails { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the IP address or the request triggering the audited event.
|
||||
/// </summary>
|
||||
string PerformingIp { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the date and time of the audited event.
|
||||
/// </summary>
|
||||
DateTime EventDateUtc { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the identifier of the user affected by the audited event.
|
||||
/// </summary>
|
||||
/// <remarks>Not used when no single user is affected by the event.</remarks>
|
||||
int AffectedUserId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets free-form details about the entity affected by the audited event.
|
||||
/// </summary>
|
||||
/// <remarks>The entity affected by the event can be another user, a member...</remarks>
|
||||
string AffectedDetails { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the type of the audited event.
|
||||
/// </summary>
|
||||
string EventType { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets free-form details about the audited event.
|
||||
/// </summary>
|
||||
string EventDetails { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
using System.Collections.Generic;
|
||||
using Umbraco.Core.Models.EntityBase;
|
||||
|
||||
namespace Umbraco.Core.Models
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a consent state.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>A consent is fully identified by a source (whoever is consenting), a context (for
|
||||
/// example, an application), and an action (whatever is consented).</para>
|
||||
/// <para>A consent state registers the state of the consent (granted, revoked...).</para>
|
||||
/// </remarks>
|
||||
public interface IConsent : IAggregateRoot, IRememberBeingDirty
|
||||
{
|
||||
/// <summary>
|
||||
/// Determines whether the consent entity represents the current state.
|
||||
/// </summary>
|
||||
bool Current { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the unique identifier of whoever is consenting.
|
||||
/// </summary>
|
||||
string Source { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the unique identifier of the context of the consent.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>Represents the domain, application, scope... of the action.</para>
|
||||
/// <para>When the action is a Udi, this should be the Udi type.</para>
|
||||
/// </remarks>
|
||||
string Context { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the unique identifier of the consented action.
|
||||
/// </summary>
|
||||
string Action { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the state of the consent.
|
||||
/// </summary>
|
||||
ConsentState State { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets some additional free text.
|
||||
/// </summary>
|
||||
string Comment { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the previous states of this consent.
|
||||
/// </summary>
|
||||
IEnumerable<IConsent> History { get; }
|
||||
}
|
||||
}
|
||||
@@ -19,6 +19,13 @@
|
||||
/// <returns></returns>
|
||||
bool MemberCanViewProperty(string propertyTypeAlias);
|
||||
|
||||
/// <summary>
|
||||
/// Gets a boolean indicating whether a Property is marked as storing sensitive values on the Members profile.
|
||||
/// </summary>
|
||||
/// <param name="propertyTypeAlias">PropertyType Alias of the Property to check</param>
|
||||
/// <returns></returns>
|
||||
bool IsSensitiveProperty(string propertyTypeAlias);
|
||||
|
||||
/// <summary>
|
||||
/// Sets a boolean indicating whether a Property is editable by the Member.
|
||||
/// </summary>
|
||||
@@ -32,5 +39,12 @@
|
||||
/// <param name="propertyTypeAlias">PropertyType Alias of the Property to set</param>
|
||||
/// <param name="value">Boolean value, true or false</param>
|
||||
void SetMemberCanViewProperty(string propertyTypeAlias, bool value);
|
||||
|
||||
/// <summary>
|
||||
/// Sets a boolean indicating whether a Property is a sensitive value on the Members profile.
|
||||
/// </summary>
|
||||
/// <param name="propertyTypeAlias">PropertyType Alias of the Property to set</param>
|
||||
/// <param name="value">Boolean value, true or false</param>
|
||||
void SetIsSensitiveProperty(string propertyTypeAlias, bool value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -34,6 +34,15 @@ namespace Umbraco.Core.Models.Identity
|
||||
.ForMember(user => user.CalculatedContentStartNodeIds, expression => expression.MapFrom(user => user.CalculateContentStartNodeIds(applicationContext.Services.EntityService)))
|
||||
.ForMember(user => user.CalculatedMediaStartNodeIds, expression => expression.MapFrom(user => user.CalculateMediaStartNodeIds(applicationContext.Services.EntityService)))
|
||||
.ForMember(user => user.AllowedSections, expression => expression.MapFrom(user => user.AllowedSections.ToArray()))
|
||||
|
||||
.ForMember(user => user.LockoutEnabled, expression => expression.Ignore())
|
||||
.ForMember(user => user.Logins, expression => expression.Ignore())
|
||||
.ForMember(user => user.Roles, expression => expression.Ignore())
|
||||
.ForMember(user => user.PhoneNumber, expression => expression.Ignore())
|
||||
.ForMember(user => user.PhoneNumberConfirmed, expression => expression.Ignore())
|
||||
.ForMember(user => user.TwoFactorEnabled, expression => expression.Ignore())
|
||||
.ForMember(user => user.Claims, expression => expression.Ignore())
|
||||
|
||||
.AfterMap((user, identityUser) =>
|
||||
{
|
||||
identityUser.ResetDirtyProperties(true);
|
||||
@@ -47,4 +56,4 @@ namespace Umbraco.Core.Models.Identity
|
||||
return storedPass.StartsWith(Constants.Security.EmptyPasswordPrefix) ? null : storedPass;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -170,7 +170,19 @@ namespace Umbraco.Core.Models
|
||||
public string RawPasswordValue
|
||||
{
|
||||
get { return _rawPasswordValue; }
|
||||
set { SetPropertyValueAndDetectChanges(value, ref _rawPasswordValue, Ps.Value.PasswordSelector); }
|
||||
set
|
||||
{
|
||||
if (value == null)
|
||||
{
|
||||
//special case, this is used to ensure that the password is not updated when persisting, in this case
|
||||
//we don't want to track changes either
|
||||
_rawPasswordValue = null;
|
||||
}
|
||||
else
|
||||
{
|
||||
SetPropertyValueAndDetectChanges(value, ref _rawPasswordValue, Ps.Value.PasswordSelector);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -628,4 +640,4 @@ namespace Umbraco.Core.Models
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -64,7 +64,7 @@ namespace Umbraco.Core.Models
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or Sets a Dictionary of Tuples (MemberCanEdit, VisibleOnProfile) by the PropertyTypes' alias.
|
||||
/// Gets or Sets a Dictionary of Tuples (MemberCanEdit, VisibleOnProfile, IsSensitive) by the PropertyTypes' alias.
|
||||
/// </summary>
|
||||
[DataMember]
|
||||
internal IDictionary<string, MemberTypePropertyProfileAccess> MemberTypePropertyTypes { get; private set; }
|
||||
@@ -76,11 +76,11 @@ namespace Umbraco.Core.Models
|
||||
/// <returns></returns>
|
||||
public bool MemberCanEditProperty(string propertyTypeAlias)
|
||||
{
|
||||
if (MemberTypePropertyTypes.ContainsKey(propertyTypeAlias))
|
||||
MemberTypePropertyProfileAccess propertyProfile;
|
||||
if (MemberTypePropertyTypes.TryGetValue(propertyTypeAlias, out propertyProfile))
|
||||
{
|
||||
return MemberTypePropertyTypes[propertyTypeAlias].IsEditable;
|
||||
return propertyProfile.IsEditable;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -91,11 +91,26 @@ namespace Umbraco.Core.Models
|
||||
/// <returns></returns>
|
||||
public bool MemberCanViewProperty(string propertyTypeAlias)
|
||||
{
|
||||
if (MemberTypePropertyTypes.ContainsKey(propertyTypeAlias))
|
||||
MemberTypePropertyProfileAccess propertyProfile;
|
||||
if (MemberTypePropertyTypes.TryGetValue(propertyTypeAlias, out propertyProfile))
|
||||
{
|
||||
return MemberTypePropertyTypes[propertyTypeAlias].IsVisible;
|
||||
return propertyProfile.IsVisible;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a boolean indicating whether a Property is marked as storing sensitive values on the Members profile.
|
||||
/// </summary>
|
||||
/// <param name="propertyTypeAlias">PropertyType Alias of the Property to check</param>
|
||||
/// <returns></returns>
|
||||
public bool IsSensitiveProperty(string propertyTypeAlias)
|
||||
{
|
||||
MemberTypePropertyProfileAccess propertyProfile;
|
||||
if (MemberTypePropertyTypes.TryGetValue(propertyTypeAlias, out propertyProfile))
|
||||
{
|
||||
return propertyProfile.IsSensitive;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -106,13 +121,14 @@ namespace Umbraco.Core.Models
|
||||
/// <param name="value">Boolean value, true or false</param>
|
||||
public void SetMemberCanEditProperty(string propertyTypeAlias, bool value)
|
||||
{
|
||||
if (MemberTypePropertyTypes.ContainsKey(propertyTypeAlias))
|
||||
MemberTypePropertyProfileAccess propertyProfile;
|
||||
if (MemberTypePropertyTypes.TryGetValue(propertyTypeAlias, out propertyProfile))
|
||||
{
|
||||
MemberTypePropertyTypes[propertyTypeAlias].IsEditable = value;
|
||||
propertyProfile.IsEditable = value;
|
||||
}
|
||||
else
|
||||
{
|
||||
var tuple = new MemberTypePropertyProfileAccess(false, value);
|
||||
var tuple = new MemberTypePropertyProfileAccess(false, value, false);
|
||||
MemberTypePropertyTypes.Add(propertyTypeAlias, tuple);
|
||||
}
|
||||
}
|
||||
@@ -124,15 +140,35 @@ namespace Umbraco.Core.Models
|
||||
/// <param name="value">Boolean value, true or false</param>
|
||||
public void SetMemberCanViewProperty(string propertyTypeAlias, bool value)
|
||||
{
|
||||
if (MemberTypePropertyTypes.ContainsKey(propertyTypeAlias))
|
||||
MemberTypePropertyProfileAccess propertyProfile;
|
||||
if (MemberTypePropertyTypes.TryGetValue(propertyTypeAlias, out propertyProfile))
|
||||
{
|
||||
MemberTypePropertyTypes[propertyTypeAlias].IsVisible = value;
|
||||
propertyProfile.IsVisible = value;
|
||||
}
|
||||
else
|
||||
{
|
||||
var tuple = new MemberTypePropertyProfileAccess(value, false);
|
||||
var tuple = new MemberTypePropertyProfileAccess(value, false, false);
|
||||
MemberTypePropertyTypes.Add(propertyTypeAlias, tuple);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets a boolean indicating whether a Property is a sensitive value on the Members profile.
|
||||
/// </summary>
|
||||
/// <param name="propertyTypeAlias">PropertyType Alias of the Property to set</param>
|
||||
/// <param name="value">Boolean value, true or false</param>
|
||||
public void SetIsSensitiveProperty(string propertyTypeAlias, bool value)
|
||||
{
|
||||
MemberTypePropertyProfileAccess propertyProfile;
|
||||
if (MemberTypePropertyTypes.TryGetValue(propertyTypeAlias, out propertyProfile))
|
||||
{
|
||||
propertyProfile.IsSensitive = value;
|
||||
}
|
||||
else
|
||||
{
|
||||
var tuple = new MemberTypePropertyProfileAccess(false, false, true);
|
||||
MemberTypePropertyTypes.Add(propertyTypeAlias, tuple);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,13 +5,15 @@ namespace Umbraco.Core.Models
|
||||
/// </summary>
|
||||
internal class MemberTypePropertyProfileAccess
|
||||
{
|
||||
public MemberTypePropertyProfileAccess(bool isVisible, bool isEditable)
|
||||
public MemberTypePropertyProfileAccess(bool isVisible, bool isEditable, bool isSenstive)
|
||||
{
|
||||
IsVisible = isVisible;
|
||||
IsEditable = isEditable;
|
||||
IsSensitive = isSenstive;
|
||||
}
|
||||
|
||||
public bool IsVisible { get; set; }
|
||||
public bool IsEditable { get; set; }
|
||||
public bool IsSensitive { get; set; }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -63,4 +63,4 @@ namespace Umbraco.Core.Models.Membership
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@ using Umbraco.Core.Models.EntityBase;
|
||||
|
||||
namespace Umbraco.Core.Models.Membership
|
||||
{
|
||||
public interface IUserGroup : IAggregateRoot
|
||||
public interface IUserGroup : IAggregateRoot, IRememberBeingDirty, ICanBeDirty
|
||||
{
|
||||
string Alias { get; set; }
|
||||
|
||||
@@ -41,4 +41,4 @@ namespace Umbraco.Core.Models.Membership
|
||||
/// </summary>
|
||||
int UserCount { get; }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Umbraco.Core.Models.Membership
|
||||
{
|
||||
internal class MemberExportModel
|
||||
{
|
||||
public int Id { get; set; }
|
||||
public Guid Key { get; set; }
|
||||
public string Name { get; set; }
|
||||
public string Username { get; set; }
|
||||
public string Email { get; set; }
|
||||
public List<string> Groups { get; set; }
|
||||
public string ContentTypeAlias { get; set; }
|
||||
public DateTime CreateDate { get; set; }
|
||||
public DateTime UpdateDate { get; set; }
|
||||
public List<MemberExportProperty> Properties { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
using System;
|
||||
|
||||
namespace Umbraco.Core.Models.Membership
|
||||
{
|
||||
internal class MemberExportProperty
|
||||
{
|
||||
public int Id { get; set; }
|
||||
public string Alias { get; set; }
|
||||
public string Name { get; set; }
|
||||
public object Value { get; set; }
|
||||
public DateTime? CreateDate { get; set; }
|
||||
public DateTime? UpdateDate { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -706,5 +706,6 @@ namespace Umbraco.Core.Models.Membership
|
||||
return _user.GetHashCode();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -152,4 +152,4 @@ namespace Umbraco.Core.Models.Membership
|
||||
|
||||
public int UserCount { get; private set; }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Runtime.Serialization;
|
||||
using System.Text.RegularExpressions;
|
||||
@@ -436,4 +435,4 @@ namespace Umbraco.Core.Models
|
||||
return clone;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -42,7 +42,7 @@ namespace Umbraco.Core.Models.PublishedContent
|
||||
var typeName = attribute == null ? type.Name : attribute.ContentTypeAlias;
|
||||
|
||||
if (constructors.ContainsKey(typeName))
|
||||
throw new InvalidOperationException(string.Format("More that one type want to be a model for content type {0}.", typeName));
|
||||
throw new InvalidOperationException(string.Format("More than one type wants to be a model for content type {0}.", typeName));
|
||||
|
||||
// should work everywhere, but slow
|
||||
//_constructors[typeName] = constructor;
|
||||
|
||||
@@ -0,0 +1,60 @@
|
||||
using System;
|
||||
using Umbraco.Core.Persistence;
|
||||
using Umbraco.Core.Persistence.DatabaseAnnotations;
|
||||
using Umbraco.Core.Persistence.DatabaseModelDefinitions;
|
||||
|
||||
namespace Umbraco.Core.Models.Rdbms
|
||||
{
|
||||
[TableName(TableName)]
|
||||
[PrimaryKey("id")]
|
||||
[ExplicitColumns]
|
||||
internal class AuditEntryDto
|
||||
{
|
||||
public const string TableName = "umbracoAudit";
|
||||
public const int IpLength = 64;
|
||||
public const int EventTypeLength = 256;
|
||||
public const int DetailsLength = 1024;
|
||||
|
||||
[Column("id")]
|
||||
[PrimaryKeyColumn]
|
||||
public int Id { get; set; }
|
||||
|
||||
// there is NO foreign key to the users table here, neither for performing user nor for
|
||||
// affected user, so we can delete users and NOT delete the associated audit trails, and
|
||||
// users can still be identified via the details free-form text fields.
|
||||
|
||||
[Column("performingUserId")]
|
||||
public int PerformingUserId { get; set; }
|
||||
|
||||
[Column("performingDetails")]
|
||||
[NullSetting(NullSetting = NullSettings.Null)]
|
||||
[Length(DetailsLength)]
|
||||
public string PerformingDetails { get; set; }
|
||||
|
||||
[Column("performingIp")]
|
||||
[NullSetting(NullSetting = NullSettings.Null)]
|
||||
[Length(IpLength)]
|
||||
public string PerformingIp { get; set; }
|
||||
|
||||
[Column("eventDateUtc")]
|
||||
[Constraint(Default = SystemMethods.CurrentDateTime)]
|
||||
public DateTime EventDateUtc { get; set; }
|
||||
|
||||
[Column("affectedUserId")]
|
||||
public int AffectedUserId { get; set; }
|
||||
|
||||
[Column("affectedDetails")]
|
||||
[NullSetting(NullSetting = NullSettings.Null)]
|
||||
[Length(DetailsLength)]
|
||||
public string AffectedDetails { get; set; }
|
||||
|
||||
[Column("eventType")]
|
||||
[Length(EventTypeLength)]
|
||||
public string EventType { get; set; }
|
||||
|
||||
[Column("eventDetails")]
|
||||
[NullSetting(NullSetting = NullSettings.Null)]
|
||||
[Length(DetailsLength)]
|
||||
public string EventDetails { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
using System;
|
||||
using Umbraco.Core.Persistence;
|
||||
using Umbraco.Core.Persistence.DatabaseAnnotations;
|
||||
using Umbraco.Core.Persistence.DatabaseModelDefinitions;
|
||||
|
||||
namespace Umbraco.Core.Models.Rdbms
|
||||
{
|
||||
[TableName(TableName)]
|
||||
[PrimaryKey("id")]
|
||||
[ExplicitColumns]
|
||||
public class ConsentDto
|
||||
{
|
||||
internal const string TableName = "umbracoConsent";
|
||||
|
||||
[Column("id")]
|
||||
[PrimaryKeyColumn]
|
||||
public int Id { get; set; }
|
||||
|
||||
[Column("current")]
|
||||
public bool Current { get; set; }
|
||||
|
||||
[Column("source")]
|
||||
[Length(512)]
|
||||
public string Source { get; set; }
|
||||
|
||||
[Column("context")]
|
||||
[Length(128)]
|
||||
public string Context { get; set; }
|
||||
|
||||
[Column("action")]
|
||||
[Length(512)]
|
||||
public string Action { get; set; }
|
||||
|
||||
[Column("createDate")]
|
||||
[Constraint(Default = SystemMethods.CurrentDateTime)]
|
||||
public DateTime CreateDate { get; set; }
|
||||
|
||||
[Column("state")]
|
||||
public int State { get; set; }
|
||||
|
||||
[Column("comment")]
|
||||
public string Comment { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -27,5 +27,9 @@ namespace Umbraco.Core.Models.Rdbms
|
||||
[Column("viewOnProfile")]
|
||||
[Constraint(Default = "0")]
|
||||
public bool ViewOnProfile { get; set; }
|
||||
|
||||
[Column("isSensitive")]
|
||||
[Constraint(Default = "0")]
|
||||
public bool IsSensitive { get; set; }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -45,6 +45,9 @@ namespace Umbraco.Core.Models.Rdbms
|
||||
[Column("viewOnProfile")]
|
||||
public bool ViewOnProfile { get; set; }
|
||||
|
||||
[Column("isSensitive")]
|
||||
public bool IsSensitive { get; set; }
|
||||
|
||||
/* cmsDataType */
|
||||
[Column("propertyEditorAlias")]
|
||||
public string PropertyEditorAlias { get; set; }
|
||||
@@ -52,7 +55,8 @@ namespace Umbraco.Core.Models.Rdbms
|
||||
[Column("dbType")]
|
||||
public string DbType { get; set; }
|
||||
|
||||
[Column("UniqueID")]
|
||||
[Column("UniqueID")]
|
||||
public Guid UniqueId { get; set; }
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,7 +17,7 @@ namespace Umbraco.Core.Models.Rdbms
|
||||
}
|
||||
|
||||
[Column("id")]
|
||||
[PrimaryKeyColumn(IdentitySeed = 5)]
|
||||
[PrimaryKeyColumn(IdentitySeed = 6)]
|
||||
public int Id { get; set; }
|
||||
|
||||
[Column("userGroupAlias")]
|
||||
@@ -68,4 +68,4 @@ namespace Umbraco.Core.Models.Rdbms
|
||||
[ResultColumn]
|
||||
public int UserCount { get; set; }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -261,6 +261,16 @@ namespace Umbraco.Core.Models
|
||||
return user.Groups != null && user.Groups.Any(x => x.Alias == Constants.Security.AdminGroupAlias);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether this user has access to view sensitive data
|
||||
/// </summary>
|
||||
/// <param name="user"></param>
|
||||
public static bool HasAccessToSensitiveData(this IUser user)
|
||||
{
|
||||
if (user == null) throw new ArgumentNullException("user");
|
||||
return user.Groups != null && user.Groups.Any(x => x.Alias == Constants.Security.SensitiveDataGroupAlias);
|
||||
}
|
||||
|
||||
// calc. start nodes, combining groups' and user's, and excluding what's in the bin
|
||||
public static int[] CalculateContentStartNodeIds(this IUser user, IEntityService entityService)
|
||||
{
|
||||
@@ -413,4 +423,4 @@ namespace Umbraco.Core.Models
|
||||
return lsn.ToArray();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -12,7 +12,7 @@ namespace Umbraco.Core.Persistence
|
||||
/// it will create one per context, otherwise it will be a global singleton object which is NOT thread safe
|
||||
/// since we need (at least) a new instance of the database object per thread.
|
||||
/// </remarks>
|
||||
internal class DefaultDatabaseFactory : DisposableObject, IDatabaseFactory2
|
||||
internal class DefaultDatabaseFactory : DisposableObjectSlim, IDatabaseFactory2
|
||||
{
|
||||
private readonly string _connectionStringName;
|
||||
private readonly ILogger _logger;
|
||||
|
||||
@@ -0,0 +1,52 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Umbraco.Core.Models;
|
||||
using Umbraco.Core.Models.Rdbms;
|
||||
|
||||
namespace Umbraco.Core.Persistence.Factories
|
||||
{
|
||||
internal static class AuditEntryFactory
|
||||
{
|
||||
public static IEnumerable<IAuditEntry> BuildEntities(IEnumerable<AuditEntryDto> dtos)
|
||||
{
|
||||
return dtos.Select(BuildEntity).ToList();
|
||||
}
|
||||
|
||||
public static IAuditEntry BuildEntity(AuditEntryDto dto)
|
||||
{
|
||||
var entity = new AuditEntry
|
||||
{
|
||||
Id = dto.Id,
|
||||
PerformingUserId = dto.PerformingUserId,
|
||||
PerformingDetails = dto.PerformingDetails,
|
||||
PerformingIp = dto.PerformingIp,
|
||||
EventDateUtc = dto.EventDateUtc,
|
||||
AffectedUserId = dto.AffectedUserId,
|
||||
AffectedDetails = dto.AffectedDetails,
|
||||
EventType = dto.EventType,
|
||||
EventDetails = dto.EventDetails
|
||||
};
|
||||
|
||||
//on initial construction we don't want to have dirty properties tracked
|
||||
// http://issues.umbraco.org/issue/U4-1946
|
||||
entity.ResetDirtyProperties(false);
|
||||
return entity;
|
||||
}
|
||||
|
||||
public static AuditEntryDto BuildDto(IAuditEntry entity)
|
||||
{
|
||||
return new AuditEntryDto
|
||||
{
|
||||
Id = entity.Id,
|
||||
PerformingUserId = entity.PerformingUserId,
|
||||
PerformingDetails = entity.PerformingDetails,
|
||||
PerformingIp = entity.PerformingIp,
|
||||
EventDateUtc = entity.EventDateUtc,
|
||||
AffectedUserId = entity.AffectedUserId,
|
||||
AffectedDetails = entity.AffectedDetails,
|
||||
EventType = entity.EventType,
|
||||
EventDetails = entity.EventDetails
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
using System.Collections.Generic;
|
||||
using Umbraco.Core.Models;
|
||||
using Umbraco.Core.Models.Rdbms;
|
||||
|
||||
namespace Umbraco.Core.Persistence.Factories
|
||||
{
|
||||
internal static class ConsentFactory
|
||||
{
|
||||
public static IEnumerable<IConsent> BuildEntities(IEnumerable<ConsentDto> dtos)
|
||||
{
|
||||
var ix = new Dictionary<string, Consent>();
|
||||
var output = new List<Consent>();
|
||||
|
||||
foreach (var dto in dtos)
|
||||
{
|
||||
var k = dto.Source + "::" + dto.Context + "::" + dto.Action;
|
||||
|
||||
var consent = new Consent
|
||||
{
|
||||
Id = dto.Id,
|
||||
Current = dto.Current,
|
||||
CreateDate = dto.CreateDate,
|
||||
Source = dto.Source,
|
||||
Context = dto.Context,
|
||||
Action = dto.Action,
|
||||
State = (ConsentState) dto.State, // assume value is valid
|
||||
Comment = dto.Comment
|
||||
};
|
||||
|
||||
//on initial construction we don't want to have dirty properties tracked
|
||||
// http://issues.umbraco.org/issue/U4-1946
|
||||
consent.ResetDirtyProperties(false);
|
||||
|
||||
if (ix.TryGetValue(k, out var current))
|
||||
{
|
||||
if (current.HistoryInternal == null)
|
||||
current.HistoryInternal = new List<IConsent>();
|
||||
current.HistoryInternal.Add(consent);
|
||||
}
|
||||
else
|
||||
{
|
||||
ix[k] = consent;
|
||||
output.Add(consent);
|
||||
}
|
||||
}
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
public static ConsentDto BuildDto(IConsent entity)
|
||||
{
|
||||
return new ConsentDto
|
||||
{
|
||||
Id = entity.Id,
|
||||
Current = entity.Current,
|
||||
CreateDate = entity.CreateDate,
|
||||
Source = entity.Source,
|
||||
Context = entity.Context,
|
||||
Action = entity.Action,
|
||||
State = (int) entity.State,
|
||||
Comment = entity.Comment
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -84,7 +84,8 @@ namespace Umbraco.Core.Persistence.Factories
|
||||
NodeId = entity.Id,
|
||||
PropertyTypeId = x.Id,
|
||||
CanEdit = memberType.MemberCanEditProperty(x.Alias),
|
||||
ViewOnProfile = memberType.MemberCanViewProperty(x.Alias)
|
||||
ViewOnProfile = memberType.MemberCanViewProperty(x.Alias),
|
||||
IsSensitive = memberType.IsSensitiveProperty(x.Alias)
|
||||
}).ToList();
|
||||
return dtos;
|
||||
}
|
||||
@@ -159,4 +160,4 @@ namespace Umbraco.Core.Persistence.Factories
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -56,10 +56,10 @@ namespace Umbraco.Core.Persistence.Factories
|
||||
|
||||
//Add the standard PropertyType to the current list
|
||||
propertyTypes.Add(standardPropertyType.Value);
|
||||
|
||||
//Internal dictionary for adding "MemberCanEdit" and "VisibleOnProfile" properties to each PropertyType
|
||||
|
||||
//Internal dictionary for adding "MemberCanEdit", "VisibleOnProfile", "IsSensitive" properties to each PropertyType
|
||||
memberType.MemberTypePropertyTypes.Add(standardPropertyType.Key,
|
||||
new MemberTypePropertyProfileAccess(false, false));
|
||||
new MemberTypePropertyProfileAccess(false, false, false));
|
||||
}
|
||||
memberType.NoGroupPropertyTypes = propertyTypes;
|
||||
|
||||
@@ -102,7 +102,7 @@ namespace Umbraco.Core.Persistence.Factories
|
||||
{
|
||||
//Internal dictionary for adding "MemberCanEdit" and "VisibleOnProfile" properties to each PropertyType
|
||||
memberType.MemberTypePropertyTypes.Add(typeDto.Alias,
|
||||
new MemberTypePropertyProfileAccess(typeDto.ViewOnProfile, typeDto.CanEdit));
|
||||
new MemberTypePropertyProfileAccess(typeDto.ViewOnProfile, typeDto.CanEdit, typeDto.IsSensitive));
|
||||
|
||||
var tempGroupDto = groupDto;
|
||||
|
||||
@@ -157,7 +157,7 @@ namespace Umbraco.Core.Persistence.Factories
|
||||
{
|
||||
//Internal dictionary for adding "MemberCanEdit" and "VisibleOnProfile" properties to each PropertyType
|
||||
memberType.MemberTypePropertyTypes.Add(typeDto.Alias,
|
||||
new MemberTypePropertyProfileAccess(typeDto.ViewOnProfile, typeDto.CanEdit));
|
||||
new MemberTypePropertyProfileAccess(typeDto.ViewOnProfile, typeDto.CanEdit, typeDto.IsSensitive));
|
||||
|
||||
//ensures that any built-in membership properties have their correct dbtype assigned no matter
|
||||
//what the underlying data type is
|
||||
@@ -198,4 +198,4 @@ namespace Umbraco.Core.Persistence.Factories
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
using System.Collections.Concurrent;
|
||||
using Umbraco.Core.Models;
|
||||
using Umbraco.Core.Models.Rdbms;
|
||||
|
||||
namespace Umbraco.Core.Persistence.Mappers
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a mapper for audit entry entities.
|
||||
/// </summary>
|
||||
[MapperFor(typeof(IAuditEntry))]
|
||||
[MapperFor(typeof(AuditEntry))]
|
||||
public sealed class AuditEntryMapper : BaseMapper
|
||||
{
|
||||
private static readonly ConcurrentDictionary<string, DtoMapModel> PropertyInfoCacheInstance
|
||||
= new ConcurrentDictionary<string, DtoMapModel>();
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="AuditEntryMapper"/> class.
|
||||
/// </summary>
|
||||
public AuditEntryMapper()
|
||||
{
|
||||
// note: why the base ctor does not invoke BuildMap is a mystery to me
|
||||
BuildMap();
|
||||
}
|
||||
|
||||
internal override ConcurrentDictionary<string, DtoMapModel> PropertyInfoCache => PropertyInfoCacheInstance;
|
||||
|
||||
internal override void BuildMap()
|
||||
{
|
||||
CacheMap<AuditEntry, AuditEntryDto>(entity => entity.Id, dto => dto.Id);
|
||||
CacheMap<AuditEntry, AuditEntryDto>(entity => entity.PerformingUserId, dto => dto.PerformingUserId);
|
||||
CacheMap<AuditEntry, AuditEntryDto>(entity => entity.PerformingDetails, dto => dto.PerformingDetails);
|
||||
CacheMap<AuditEntry, AuditEntryDto>(entity => entity.PerformingIp, dto => dto.PerformingIp);
|
||||
CacheMap<AuditEntry, AuditEntryDto>(entity => entity.EventDateUtc, dto => dto.EventDateUtc);
|
||||
CacheMap<AuditEntry, AuditEntryDto>(entity => entity.AffectedUserId, dto => dto.AffectedUserId);
|
||||
CacheMap<AuditEntry, AuditEntryDto>(entity => entity.AffectedDetails, dto => dto.AffectedDetails);
|
||||
CacheMap<AuditEntry, AuditEntryDto>(entity => entity.EventType, dto => dto.EventType);
|
||||
CacheMap<AuditEntry, AuditEntryDto>(entity => entity.EventDetails, dto => dto.EventDetails);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
using System.Collections.Concurrent;
|
||||
using Umbraco.Core.Models;
|
||||
using Umbraco.Core.Models.Rdbms;
|
||||
|
||||
namespace Umbraco.Core.Persistence.Mappers
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a mapper for consent entities.
|
||||
/// </summary>
|
||||
[MapperFor(typeof(IConsent))]
|
||||
[MapperFor(typeof(Consent))]
|
||||
public sealed class ConsentMapper : BaseMapper
|
||||
{
|
||||
private static readonly ConcurrentDictionary<string, DtoMapModel> PropertyInfoCacheInstance
|
||||
= new ConcurrentDictionary<string, DtoMapModel>();
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ConsentMapper"/> class.
|
||||
/// </summary>
|
||||
public ConsentMapper()
|
||||
{
|
||||
// note: why the base ctor does not invoke BuildMap is a mystery to me
|
||||
BuildMap();
|
||||
}
|
||||
|
||||
internal override ConcurrentDictionary<string, DtoMapModel> PropertyInfoCache => PropertyInfoCacheInstance;
|
||||
|
||||
internal override void BuildMap()
|
||||
{
|
||||
CacheMap<Consent, ConsentDto>(entity => entity.Id, dto => dto.Id);
|
||||
CacheMap<Consent, ConsentDto>(entity => entity.Current, dto => dto.Current);
|
||||
CacheMap<Consent, ConsentDto>(entity => entity.CreateDate, dto => dto.CreateDate);
|
||||
CacheMap<Consent, ConsentDto>(entity => entity.Source, dto => dto.Source);
|
||||
CacheMap<Consent, ConsentDto>(entity => entity.Context, dto => dto.Context);
|
||||
CacheMap<Consent, ConsentDto>(entity => entity.Action, dto => dto.Action);
|
||||
CacheMap<Consent, ConsentDto>(entity => entity.State, dto => dto.State);
|
||||
CacheMap<Consent, ConsentDto>(entity => entity.Comment, dto => dto.Comment);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -178,11 +178,13 @@ namespace Umbraco.Core.Persistence.Migrations.Initial
|
||||
_database.Insert("umbracoUserGroup", "id", false, new UserGroupDto { Id = 2, StartMediaId = -1, StartContentId = -1, Alias = "writer", Name = "Writers", DefaultPermissions = "CAH:F", CreateDate = DateTime.Now, UpdateDate = DateTime.Now, Icon = "icon-edit" });
|
||||
_database.Insert("umbracoUserGroup", "id", false, new UserGroupDto { Id = 3, StartMediaId = -1, StartContentId = -1, Alias = "editor", Name = "Editors", DefaultPermissions = "CADMOSKTPUZ:5Fï", CreateDate = DateTime.Now, UpdateDate = DateTime.Now, Icon = "icon-tools" });
|
||||
_database.Insert("umbracoUserGroup", "id", false, new UserGroupDto { Id = 4, StartMediaId = -1, StartContentId = -1, Alias = Constants.Security.TranslatorGroupAlias, Name = "Translators", DefaultPermissions = "AF", CreateDate = DateTime.Now, UpdateDate = DateTime.Now, Icon = "icon-globe" });
|
||||
_database.Insert("umbracoUserGroup", "id", false, new UserGroupDto { Id = 5, StartMediaId = -1, StartContentId = -1, Alias = Constants.Security.SensitiveDataGroupAlias, Name = "Sensitive data", DefaultPermissions = "", CreateDate = DateTime.Now, UpdateDate = DateTime.Now, Icon = "icon-lock" });
|
||||
}
|
||||
|
||||
private void CreateUmbracoUser2UserGroupData()
|
||||
{
|
||||
_database.Insert(new User2UserGroupDto { UserGroupId = 1, UserId = 0 });
|
||||
_database.Insert(new User2UserGroupDto { UserGroupId = 1, UserId = 0 }); //add admin to admins
|
||||
_database.Insert(new User2UserGroupDto { UserGroupId = 5, UserId = 0 }); //add admin to sensitive data
|
||||
}
|
||||
|
||||
private void CreateUmbracoUserGroup2AppData()
|
||||
@@ -337,4 +339,4 @@ namespace Umbraco.Core.Persistence.Migrations.Initial
|
||||
_database.Insert("umbracoMigration", "pk", false, dto);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -92,7 +92,9 @@ namespace Umbraco.Core.Persistence.Migrations.Initial
|
||||
{52, typeof (UserGroup2NodePermissionDto) },
|
||||
{53, typeof (UserGroup2AppDto) },
|
||||
{54, typeof (UserStartNodeDto) },
|
||||
{55, typeof (UserLoginDto)}
|
||||
{55, typeof (UserLoginDto)},
|
||||
{56, typeof (ConsentDto)},
|
||||
{57, typeof (AuditEntryDto)}
|
||||
};
|
||||
#endregion
|
||||
|
||||
@@ -378,4 +380,4 @@ namespace Umbraco.Core.Persistence.Migrations.Initial
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -154,6 +154,12 @@ namespace Umbraco.Core.Persistence.Migrations.Initial
|
||||
return new Version(7, 7, 0);
|
||||
}
|
||||
|
||||
//if the error is for isSensitive column it must be the previous version to 7.9 since that is when it is added
|
||||
if (Errors.Any(x => x.Item1.Equals("Column") && (x.Item2.InvariantEquals("cmsMemberType,isSensitive"))))
|
||||
{
|
||||
return new Version(7, 8, 0);
|
||||
}
|
||||
|
||||
return UmbracoVersion.Current;
|
||||
}
|
||||
|
||||
@@ -214,4 +220,4 @@ namespace Umbraco.Core.Persistence.Migrations.Initial
|
||||
return sb.ToString();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+35
@@ -0,0 +1,35 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Umbraco.Core.Logging;
|
||||
using Umbraco.Core.Persistence.DatabaseAnnotations;
|
||||
using Umbraco.Core.Persistence.SqlSyntax;
|
||||
|
||||
namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSevenEightZero
|
||||
{
|
||||
[Migration("7.9.0", 1, Constants.System.UmbracoMigrationName)]
|
||||
public class AddIsSensitiveMemberTypeColumn : MigrationBase
|
||||
{
|
||||
public AddIsSensitiveMemberTypeColumn(ISqlSyntaxProvider sqlSyntax, ILogger logger)
|
||||
: base(sqlSyntax, logger)
|
||||
{
|
||||
}
|
||||
|
||||
public override void Up()
|
||||
{
|
||||
//Don't exeucte if the column is already there
|
||||
var columns = SqlSyntax.GetColumnsInSchema(Context.Database).ToArray();
|
||||
|
||||
if (columns.Any(x => x.TableName.InvariantEquals("cmsMemberType") && x.ColumnName.InvariantEquals("isSensitive")) == false)
|
||||
{
|
||||
Create.Column("isSensitive").OnTable("cmsMemberType").AsBoolean().WithDefaultValue(0).NotNullable();
|
||||
}
|
||||
}
|
||||
|
||||
public override void Down()
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
+29
@@ -0,0 +1,29 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using Umbraco.Core.Logging;
|
||||
using Umbraco.Core.Models.Rdbms;
|
||||
using Umbraco.Core.Persistence.SqlSyntax;
|
||||
|
||||
namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSevenNineZero
|
||||
{
|
||||
[Migration("7.9.0", 1, Constants.System.UmbracoMigrationName)]
|
||||
public class AddUmbracoAuditTable : MigrationBase
|
||||
{
|
||||
public AddUmbracoAuditTable(ISqlSyntaxProvider sqlSyntax, ILogger logger)
|
||||
: base(sqlSyntax, logger)
|
||||
{ }
|
||||
|
||||
public override void Up()
|
||||
{
|
||||
var tables = SqlSyntax.GetTablesInSchema(Context.Database).ToArray();
|
||||
if (tables.InvariantContains(AuditEntryDto.TableName))
|
||||
return;
|
||||
Create.Table<AuditEntryDto>();
|
||||
}
|
||||
|
||||
public override void Down()
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
}
|
||||
}
|
||||
+29
@@ -0,0 +1,29 @@
|
||||
using System.Linq;
|
||||
using Umbraco.Core.Logging;
|
||||
using Umbraco.Core.Models.Rdbms;
|
||||
using Umbraco.Core.Persistence.SqlSyntax;
|
||||
|
||||
namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSevenNineZero
|
||||
{
|
||||
[Migration("7.9.0", 1, Constants.System.UmbracoMigrationName)]
|
||||
public class AddUmbracoConsentTable : MigrationBase
|
||||
{
|
||||
public AddUmbracoConsentTable(ISqlSyntaxProvider sqlSyntax, ILogger logger) : base(sqlSyntax, logger)
|
||||
{
|
||||
}
|
||||
|
||||
public override void Up()
|
||||
{
|
||||
var tables = SqlSyntax.GetTablesInSchema(Context.Database).ToArray();
|
||||
|
||||
if (tables.InvariantContains(ConsentDto.TableName))
|
||||
return;
|
||||
|
||||
Create.Table<ConsentDto>();
|
||||
}
|
||||
|
||||
public override void Down()
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
+37
@@ -0,0 +1,37 @@
|
||||
using System;
|
||||
using Umbraco.Core.Logging;
|
||||
using Umbraco.Core.Models.Rdbms;
|
||||
using Umbraco.Core.Persistence.SqlSyntax;
|
||||
|
||||
namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSevenNineZero
|
||||
{
|
||||
[Migration("7.9.0", 2, Constants.System.UmbracoMigrationName)]
|
||||
public class CreateSensitiveDataUserGroup : MigrationBase
|
||||
{
|
||||
public CreateSensitiveDataUserGroup(ISqlSyntaxProvider sqlSyntax, ILogger logger)
|
||||
: base(sqlSyntax, logger)
|
||||
{
|
||||
}
|
||||
|
||||
public override void Up()
|
||||
{
|
||||
Execute.Code(database =>
|
||||
{
|
||||
//Don't exeucte if the group is already there
|
||||
var exists = database.ExecuteScalar<int>("SELECT COUNT(*) FROM umbracoUserGroup WHERE userGroupAlias = @userGroupAlias",
|
||||
new {userGroupAlias = Constants.Security.SensitiveDataGroupAlias });
|
||||
if (exists == 0)
|
||||
{
|
||||
var resultId = database.Insert("umbracoUserGroup", "id", new UserGroupDto { StartMediaId = -1, StartContentId = -1, Alias = Constants.Security.SensitiveDataGroupAlias, Name = "Sensitive data", DefaultPermissions = "", CreateDate = DateTime.Now, UpdateDate = DateTime.Now, Icon = "icon-lock" });
|
||||
database.Insert(new User2UserGroupDto { UserGroupId = Convert.ToInt32(resultId), UserId = 0 }); //add admin to sensitive data
|
||||
}
|
||||
|
||||
return string.Empty;
|
||||
});
|
||||
}
|
||||
|
||||
public override void Down()
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,131 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Umbraco.Core.Logging;
|
||||
using Umbraco.Core.Models;
|
||||
using Umbraco.Core.Models.EntityBase;
|
||||
using Umbraco.Core.Models.Rdbms;
|
||||
using Umbraco.Core.Persistence.Factories;
|
||||
using Umbraco.Core.Persistence.Querying;
|
||||
using Umbraco.Core.Persistence.SqlSyntax;
|
||||
using Umbraco.Core.Persistence.UnitOfWork;
|
||||
|
||||
namespace Umbraco.Core.Persistence.Repositories
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents the PetaPoco implementatino of <see cref="IAuditEntryRepository"/>.
|
||||
/// </summary>
|
||||
internal class AuditEntryRepository : PetaPocoRepositoryBase<int, IAuditEntry>, IAuditEntryRepository
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="AuditEntryRepository"/> class.
|
||||
/// </summary>
|
||||
public AuditEntryRepository(IScopeUnitOfWork work, CacheHelper cache, ILogger logger, ISqlSyntaxProvider sqlSyntax)
|
||||
: base(work, cache, logger, sqlSyntax)
|
||||
{ }
|
||||
|
||||
protected override Guid NodeObjectTypeId => throw new NotSupportedException();
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override IAuditEntry PerformGet(int id)
|
||||
{
|
||||
var sql = new Sql()
|
||||
.Select("*")
|
||||
.From<AuditEntryDto>(SqlSyntax)
|
||||
.Where<AuditEntryDto>(x => x.Id == id, SqlSyntax);
|
||||
|
||||
var dto = Database.FirstOrDefault<AuditEntryDto>(sql);
|
||||
return dto == null ? null : AuditEntryFactory.BuildEntity(dto); }
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override IEnumerable<IAuditEntry> PerformGetAll(params int[] ids)
|
||||
{
|
||||
if (ids.Length == 0)
|
||||
{
|
||||
var sql = new Sql()
|
||||
.Select("*")
|
||||
.From<AuditEntryDto>(SqlSyntax);
|
||||
|
||||
return Database.Fetch<AuditEntryDto>(sql).Select(AuditEntryFactory.BuildEntity);
|
||||
}
|
||||
|
||||
var entries = new List<IAuditEntry>();
|
||||
|
||||
foreach (var group in ids.InGroupsOf(2000))
|
||||
{
|
||||
var sql = new Sql()
|
||||
.Select("*")
|
||||
.From<AuditEntryDto>(SqlSyntax)
|
||||
.WhereIn<AuditEntryDto>(x => x.Id, group, SqlSyntax);
|
||||
|
||||
entries.AddRange(Database.Fetch<AuditEntryDto>(sql).Select(AuditEntryFactory.BuildEntity));
|
||||
}
|
||||
|
||||
return entries;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override IEnumerable<IAuditEntry> PerformGetByQuery(IQuery<IAuditEntry> query)
|
||||
{
|
||||
var sqlClause = GetBaseQuery(false);
|
||||
var translator = new SqlTranslator<IAuditEntry>(sqlClause, query);
|
||||
var sql = translator.Translate();
|
||||
return Database.Fetch<AuditEntryDto>(sql).Select(AuditEntryFactory.BuildEntity);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override Sql GetBaseQuery(bool isCount)
|
||||
{
|
||||
return new Sql().Select(isCount ? "COUNT(*)" : "*").From<AuditEntryDto>(SqlSyntax);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override string GetBaseWhereClause()
|
||||
{
|
||||
return $"{AuditEntryDto.TableName}.id = @Id";
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override IEnumerable<string> GetDeleteClauses()
|
||||
{
|
||||
throw new NotSupportedException("Audit entries cannot be deleted.");
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void PersistNewItem(IAuditEntry entity)
|
||||
{
|
||||
((Entity) entity).AddingEntity();
|
||||
|
||||
var dto = AuditEntryFactory.BuildDto(entity);
|
||||
Database.Insert(dto);
|
||||
entity.Id = dto.Id;
|
||||
entity.ResetDirtyProperties();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void PersistUpdatedItem(IAuditEntry entity)
|
||||
{
|
||||
throw new NotSupportedException("Audit entries cannot be updated.");
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public IEnumerable<IAuditEntry> GetPage(long pageIndex, int pageCount, out long records)
|
||||
{
|
||||
var sql = new Sql()
|
||||
.Select("*")
|
||||
.From<AuditEntryDto>(SqlSyntax)
|
||||
.OrderByDescending<AuditEntryDto>(x => x.EventDateUtc, SqlSyntax);
|
||||
|
||||
var page = Database.Page<AuditEntryDto>(pageIndex + 1, pageCount, sql);
|
||||
records = page.TotalItems;
|
||||
return page.Items.Select(AuditEntryFactory.BuildEntity);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool IsAvailable()
|
||||
{
|
||||
var tables = SqlSyntax.GetTablesInSchema(Database).ToArray();
|
||||
return tables.InvariantContains(AuditEntryDto.TableName);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,103 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Umbraco.Core.Logging;
|
||||
using Umbraco.Core.Models;
|
||||
using Umbraco.Core.Models.EntityBase;
|
||||
using Umbraco.Core.Models.Rdbms;
|
||||
using Umbraco.Core.Persistence.Factories;
|
||||
using Umbraco.Core.Persistence.Querying;
|
||||
using Umbraco.Core.Persistence.SqlSyntax;
|
||||
using Umbraco.Core.Persistence.UnitOfWork;
|
||||
|
||||
namespace Umbraco.Core.Persistence.Repositories
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents the PetaPoco implementation of <see cref="IConsentRepository"/>.
|
||||
/// </summary>
|
||||
internal class ConsentRepository : PetaPocoRepositoryBase<int, IConsent>, IConsentRepository
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ConsentRepository"/> class.
|
||||
/// </summary>
|
||||
public ConsentRepository(IScopeUnitOfWork work, CacheHelper cache, ILogger logger, ISqlSyntaxProvider sqlSyntax)
|
||||
: base(work, cache, logger, sqlSyntax)
|
||||
{ }
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override Guid NodeObjectTypeId => throw new NotSupportedException();
|
||||
|
||||
/// <inheritdoc />
|
||||
public void ClearCurrent(string source, string context, string action)
|
||||
{
|
||||
var sql = new Sql($@"UPDATE {SqlSyntax.GetQuotedTableName(ConsentDto.TableName)}
|
||||
SET {SqlSyntax.GetQuotedColumnName("current")} = @0
|
||||
WHERE {SqlSyntax.GetQuotedColumnName("source")} = @1 AND {SqlSyntax.GetQuotedColumnName("context")} = @2 AND {SqlSyntax.GetQuotedColumnName("action")} = @3
|
||||
AND {SqlSyntax.GetQuotedColumnName("current")} <> @0 ", false, source, context, action);
|
||||
|
||||
Database.Execute(sql);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override IConsent PerformGet(int id)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override IEnumerable<IConsent> PerformGetAll(params int[] ids)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override IEnumerable<IConsent> PerformGetByQuery(IQuery<IConsent> query)
|
||||
{
|
||||
var sqlClause = new Sql().Select("*").From<ConsentDto>(SqlSyntax);
|
||||
var translator = new SqlTranslator<IConsent>(sqlClause, query);
|
||||
var sql = translator.Translate().OrderByDescending<ConsentDto>(x => x.CreateDate, SqlSyntax);
|
||||
return ConsentFactory.BuildEntities(Database.Fetch<ConsentDto>(sql));
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override Sql GetBaseQuery(bool isCount)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override string GetBaseWhereClause()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override IEnumerable<string> GetDeleteClauses()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void PersistNewItem(IConsent entity)
|
||||
{
|
||||
((Entity) entity).AddingEntity();
|
||||
|
||||
var dto = ConsentFactory.BuildDto(entity);
|
||||
Database.Insert(dto);
|
||||
entity.Id = dto.Id;
|
||||
entity.ResetDirtyProperties();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void PersistUpdatedItem(IConsent entity)
|
||||
{
|
||||
((Entity) entity).UpdatingEntity();
|
||||
|
||||
var dto = ConsentFactory.BuildDto(entity);
|
||||
Database.Update(dto);
|
||||
entity.ResetDirtyProperties();
|
||||
|
||||
IsolatedCache.ClearCacheItem(GetCacheIdKey<IConsent>(entity.Id));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -18,7 +18,7 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
/// <remarks>
|
||||
/// This is limited to objects that are based in the umbracoNode-table.
|
||||
/// </remarks>
|
||||
internal class EntityRepository : DisposableObject, IEntityRepository
|
||||
internal class EntityRepository : DisposableObjectSlim, IEntityRepository
|
||||
{
|
||||
private readonly IDatabaseUnitOfWork _work;
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@ using Umbraco.Core.Persistence.UnitOfWork;
|
||||
|
||||
namespace Umbraco.Core.Persistence.Repositories
|
||||
{
|
||||
internal abstract class FileRepository<TId, TEntity> : DisposableObject, IUnitOfWorkRepository, IRepository<TId, TEntity>
|
||||
internal abstract class FileRepository<TId, TEntity> : DisposableObjectSlim, IUnitOfWorkRepository, IRepository<TId, TEntity>
|
||||
where TEntity : IFile
|
||||
{
|
||||
private IUnitOfWork _work;
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
using System.Collections.Generic;
|
||||
using Umbraco.Core.Models;
|
||||
|
||||
namespace Umbraco.Core.Persistence.Repositories
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a repository for <see cref="IAuditEntry"/> entities.
|
||||
/// </summary>
|
||||
public interface IAuditEntryRepository : IRepositoryQueryable<int, IAuditEntry>
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets a page of entries.
|
||||
/// </summary>
|
||||
IEnumerable<IAuditEntry> GetPage(long pageIndex, int pageCount, out long records);
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether the repository is available.
|
||||
/// </summary>
|
||||
/// <remarks>During an upgrade, the repository may not be available, until the table has been created.</remarks>
|
||||
bool IsAvailable();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
using Umbraco.Core.Models;
|
||||
|
||||
namespace Umbraco.Core.Persistence.Repositories
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a repository for <see cref="IConsent"/> entities.
|
||||
/// </summary>
|
||||
public interface IConsentRepository : IRepositoryQueryable<int, IConsent>
|
||||
{
|
||||
/// <summary>
|
||||
/// Clears the current flag.
|
||||
/// </summary>
|
||||
void ClearCurrent(string source, string context, string action);
|
||||
}
|
||||
}
|
||||
@@ -39,5 +39,7 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
void AssignRoles(int[] memberIds, string[] roleNames);
|
||||
|
||||
void DissociateRoles(int[] memberIds, string[] roleNames);
|
||||
|
||||
int[] GetMemberIds(string[] names);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -183,7 +183,7 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
}
|
||||
|
||||
public IEnumerable<IMemberGroup> GetMemberGroupsForMember(string username)
|
||||
{
|
||||
{
|
||||
var sql = new Sql()
|
||||
.Select("un.*")
|
||||
.From("umbracoNode AS un")
|
||||
@@ -192,16 +192,15 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
.InnerJoin("cmsMember")
|
||||
.On("cmsMember.nodeId = cmsMember2MemberGroup.Member")
|
||||
.Where("un.nodeObjectType=@objectType", new { objectType = NodeObjectTypeId })
|
||||
.Where("cmsMember.LoginName=@loginName", new { loginName = username });
|
||||
|
||||
.Where("cmsMember.LoginName=@loginName", new { loginName = username });
|
||||
|
||||
return Database.Fetch<NodeDto>(sql)
|
||||
.DistinctBy(dto => dto.NodeId)
|
||||
.Select(x => _modelFactory.BuildEntity(x));
|
||||
}
|
||||
|
||||
public void AssignRoles(string[] usernames, string[] roleNames)
|
||||
public int[] GetMemberIds(string[] names)
|
||||
{
|
||||
//first get the member ids based on the usernames
|
||||
var memberSql = new Sql();
|
||||
var memberObjectType = new Guid(Constants.ObjectTypes.Member);
|
||||
memberSql.Select("umbracoNode.id")
|
||||
@@ -209,25 +208,19 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
.InnerJoin<MemberDto>()
|
||||
.On<NodeDto, MemberDto>(dto => dto.NodeId, dto => dto.NodeId)
|
||||
.Where<NodeDto>(x => x.NodeObjectType == memberObjectType)
|
||||
.Where("cmsMember.LoginName in (@usernames)", new { usernames = usernames });
|
||||
var memberIds = Database.Fetch<int>(memberSql).ToArray();
|
||||
.Where("cmsMember.LoginName in (@names)", new { names });
|
||||
return Database.Fetch<int>(memberSql).ToArray();
|
||||
}
|
||||
|
||||
public void AssignRoles(string[] usernames, string[] roleNames)
|
||||
{
|
||||
var memberIds = GetMemberIds(usernames);
|
||||
AssignRolesInternal(memberIds, roleNames);
|
||||
}
|
||||
|
||||
public void DissociateRoles(string[] usernames, string[] roleNames)
|
||||
{
|
||||
//first get the member ids based on the usernames
|
||||
var memberSql = new Sql();
|
||||
var memberObjectType = new Guid(Constants.ObjectTypes.Member);
|
||||
memberSql.Select("umbracoNode.id")
|
||||
.From<NodeDto>()
|
||||
.InnerJoin<MemberDto>()
|
||||
.On<NodeDto, MemberDto>(dto => dto.NodeId, dto => dto.NodeId)
|
||||
.Where<NodeDto>(x => x.NodeObjectType == memberObjectType)
|
||||
.Where("cmsMember.LoginName in (@usernames)", new { usernames = usernames });
|
||||
var memberIds = Database.Fetch<int>(memberSql).ToArray();
|
||||
|
||||
var memberIds = GetMemberIds(usernames);
|
||||
DissociateRolesInternal(memberIds, roleNames);
|
||||
}
|
||||
|
||||
@@ -337,4 +330,4 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
/// </summary>
|
||||
internal static event TypedEventHandler<IMemberGroupRepository, SaveEventArgs<IMemberGroup>> SavedMemberGroup;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -90,7 +90,8 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
sql.Select("umbracoNode.*", "cmsContentType.*", "cmsPropertyType.id AS PropertyTypeId", "cmsPropertyType.Alias",
|
||||
"cmsPropertyType.Name", "cmsPropertyType.Description", "cmsPropertyType.mandatory", "cmsPropertyType.UniqueID",
|
||||
"cmsPropertyType.validationRegExp", "cmsPropertyType.dataTypeId", "cmsPropertyType.sortOrder AS PropertyTypeSortOrder",
|
||||
"cmsPropertyType.propertyTypeGroupId AS PropertyTypesGroupId", "cmsMemberType.memberCanEdit", "cmsMemberType.viewOnProfile",
|
||||
"cmsPropertyType.propertyTypeGroupId AS PropertyTypesGroupId",
|
||||
"cmsMemberType.memberCanEdit", "cmsMemberType.viewOnProfile", "cmsMemberType.isSensitive",
|
||||
"cmsDataType.propertyEditorAlias", "cmsDataType.dbType", "cmsPropertyTypeGroup.id AS PropertyTypeGroupId",
|
||||
"cmsPropertyTypeGroup.text AS PropertyGroupName", "cmsPropertyTypeGroup.uniqueID AS PropertyGroupUniqueID",
|
||||
"cmsPropertyTypeGroup.sortorder AS PropertyGroupSortOrder", "cmsPropertyTypeGroup.contenttypeNodeId")
|
||||
@@ -362,4 +363,4 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
return Attempt<Guid>.Fail(propertyEditor);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,7 +10,7 @@ using Umbraco.Core.Scoping;
|
||||
|
||||
namespace Umbraco.Core.Persistence.Repositories
|
||||
{
|
||||
internal abstract class RepositoryBase : DisposableObject
|
||||
internal abstract class RepositoryBase : DisposableObjectSlim
|
||||
{
|
||||
private readonly IScopeUnitOfWork _work;
|
||||
private readonly CacheHelper _globalCache;
|
||||
@@ -308,6 +308,9 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
public virtual void PersistNewItem(IEntity entity)
|
||||
{
|
||||
CachePolicy.Create((TEntity) entity, PersistNewItem);
|
||||
|
||||
//TODO: In v8 we should automatically reset dirty properties so they don't have to be manually reset in all of the implemented repositories
|
||||
//if (entity is ICanBeDirty dirty) dirty.ResetDirtyProperties();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -317,6 +320,8 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
public virtual void PersistUpdatedItem(IEntity entity)
|
||||
{
|
||||
CachePolicy.Update((TEntity) entity, PersistUpdatedItem);
|
||||
//TODO: In v8 we should automatically reset dirty properties so they don't have to be manually reset in all of the implemented repositories
|
||||
//if (entity is ICanBeDirty dirty) dirty.ResetDirtyProperties();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -343,4 +348,4 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
UnitOfWork.DisposeIfDisposable();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,6 +20,12 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
|
||||
var name = Name;
|
||||
|
||||
// cater for instances where node has no name.
|
||||
if(string.IsNullOrWhiteSpace(name))
|
||||
{
|
||||
return _numPos;
|
||||
}
|
||||
|
||||
if (name[name.Length - 1] != ')')
|
||||
return _numPos = -1;
|
||||
|
||||
@@ -106,7 +112,7 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
}
|
||||
}
|
||||
|
||||
return uniqueing ? string.Concat(nodeName, " (", uniqueNumber.ToString(), ")") : nodeName;
|
||||
return uniqueing || string.IsNullOrWhiteSpace(nodeName) ? string.Concat(nodeName, " (", uniqueNumber.ToString(), ")") : nodeName;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -285,7 +285,7 @@ umbracoUserGroup.userGroupName, umbracoUserGroup2App.app, umbracoUserGroup2App.u
|
||||
|
||||
protected override void PersistNewItem(IUserGroup entity)
|
||||
{
|
||||
((UserGroup)entity).AddingEntity();
|
||||
((UserGroup)entity).AddingEntity();
|
||||
|
||||
var userGroupDto = UserGroupFactory.BuildDto(entity);
|
||||
|
||||
@@ -293,6 +293,8 @@ umbracoUserGroup.userGroupName, umbracoUserGroup2App.app, umbracoUserGroup2App.u
|
||||
entity.Id = id;
|
||||
|
||||
PersistAllowedSections(entity);
|
||||
|
||||
entity.ResetDirtyProperties();
|
||||
}
|
||||
|
||||
protected override void PersistUpdatedItem(IUserGroup entity)
|
||||
@@ -304,6 +306,8 @@ umbracoUserGroup.userGroupName, umbracoUserGroup2App.app, umbracoUserGroup2App.u
|
||||
Database.Update(userGroupDto);
|
||||
|
||||
PersistAllowedSections(entity);
|
||||
|
||||
entity.ResetDirtyProperties();
|
||||
}
|
||||
|
||||
private void PersistAllowedSections(IUserGroup entity)
|
||||
@@ -456,4 +460,4 @@ umbracoUserGroup.userGroupName, umbracoUserGroup2App.app, umbracoUserGroup2App.u
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -54,8 +54,8 @@ namespace Umbraco.Core.Persistence
|
||||
|
||||
//if the result is already a DeepCloneRuntimeCacheProvider then return it, otherwise
|
||||
//wrap the result with a DeepCloneRuntimeCacheProvider
|
||||
return cache is DeepCloneRuntimeCacheProvider
|
||||
? cache
|
||||
return cache is DeepCloneRuntimeCacheProvider
|
||||
? cache
|
||||
: new DeepCloneRuntimeCacheProvider(cache);
|
||||
};
|
||||
}
|
||||
@@ -158,11 +158,11 @@ namespace Umbraco.Core.Persistence
|
||||
CreateTemplateRepository(uow),
|
||||
CreateTagRepository(uow),
|
||||
_settings.Content)
|
||||
{
|
||||
{
|
||||
//duplicates are allowed
|
||||
EnsureUniqueNaming = false
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
public virtual IContentTypeRepository CreateContentTypeRepository(IScopeUnitOfWork uow)
|
||||
{
|
||||
@@ -319,13 +319,13 @@ namespace Umbraco.Core.Persistence
|
||||
var userMembershipProvider = MembershipProviderExtensions.GetUsersMembershipProvider();
|
||||
var passwordConfig = userMembershipProvider == null || userMembershipProvider.PasswordFormat != MembershipPasswordFormat.Hashed
|
||||
? null
|
||||
: new System.Collections.Generic.Dictionary<string, string> {{"hashAlgorithm", Membership.HashAlgorithmType}};
|
||||
: new System.Collections.Generic.Dictionary<string, string> {{"hashAlgorithm", Membership.HashAlgorithmType}};
|
||||
|
||||
return new UserRepository(
|
||||
uow,
|
||||
//Need to cache users - we look up user information more than anything in the back office!
|
||||
_cacheHelper,
|
||||
_logger,
|
||||
_logger,
|
||||
_sqlSyntax,
|
||||
passwordConfig);
|
||||
}
|
||||
@@ -397,5 +397,15 @@ namespace Umbraco.Core.Persistence
|
||||
_logger,
|
||||
_sqlSyntax);
|
||||
}
|
||||
|
||||
public IConsentRepository CreateConsentRepository(IScopeUnitOfWork uow)
|
||||
{
|
||||
return new ConsentRepository(uow, _cacheHelper, _logger, _sqlSyntax);
|
||||
}
|
||||
|
||||
public IAuditEntryRepository CreateAuditEntryRepository(IScopeUnitOfWork uow)
|
||||
{
|
||||
return new AuditEntryRepository(uow, _cacheHelper, _logger, _sqlSyntax);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,7 +10,7 @@ namespace Umbraco.Core.Persistence.UnitOfWork
|
||||
/// <summary>
|
||||
/// Represents a scoped unit of work.
|
||||
/// </summary>
|
||||
internal class ScopeUnitOfWork : DisposableObject, IScopeUnitOfWork
|
||||
internal class ScopeUnitOfWork : DisposableObjectSlim, IScopeUnitOfWork
|
||||
{
|
||||
private readonly Queue<Operation> _operations = new Queue<Operation>();
|
||||
private readonly IsolationLevel _isolationLevel;
|
||||
|
||||
@@ -21,7 +21,7 @@ namespace Umbraco.Core.PropertyEditors
|
||||
public PropertyEditorResolver(IServiceProvider serviceProvider, ILogger logger, Func<IEnumerable<Type>> typeListProducerList, IRuntimeCacheProvider runtimeCache)
|
||||
: base(serviceProvider, logger, typeListProducerList, ObjectLifetimeScope.Application)
|
||||
{
|
||||
var builder = new ManifestBuilder(
|
||||
var builder = new ManifestBuilder(
|
||||
runtimeCache,
|
||||
new ManifestParser(new DirectoryInfo(IOHelper.MapPath("~/App_Plugins")), runtimeCache));
|
||||
|
||||
@@ -49,7 +49,10 @@ namespace Umbraco.Core.PropertyEditors
|
||||
{
|
||||
var nestedContentEditorFromPackage = editors.FirstOrDefault(x => x.Alias == "Our.Umbraco.NestedContent");
|
||||
if (nestedContentEditorFromPackage != null)
|
||||
{
|
||||
nestedContentEditorFromPackage.Name = "(Obsolete) " + nestedContentEditorFromPackage.Name;
|
||||
nestedContentEditorFromPackage.IsDeprecated = true;
|
||||
}
|
||||
return editors;
|
||||
|
||||
}
|
||||
@@ -74,4 +77,4 @@ namespace Umbraco.Core.PropertyEditors
|
||||
return PropertyEditors.SingleOrDefault(x => x.Alias == alias);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Xml.Linq;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using Umbraco.Core.Logging;
|
||||
using Umbraco.Core.Manifest;
|
||||
using Umbraco.Core.Models;
|
||||
@@ -176,6 +176,12 @@ namespace Umbraco.Core.PropertyEditors
|
||||
/// <returns></returns>
|
||||
internal Attempt<object> TryConvertValueToCrlType(object value)
|
||||
{
|
||||
var jv = value as JValue;
|
||||
if (jv != null)
|
||||
{
|
||||
value = value.ToString();
|
||||
}
|
||||
|
||||
//this is a custom check to avoid any errors, if it's a string and it's empty just make it null
|
||||
var s = value as string;
|
||||
if (s != null)
|
||||
@@ -391,4 +397,4 @@ namespace Umbraco.Core.PropertyEditors
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+3
-1
@@ -18,6 +18,8 @@ namespace Umbraco.Core.PropertyEditors.ValueConverters
|
||||
return Constants.PropertyEditors.MultipleTextstringAlias.Equals(propertyType.PropertyEditorAlias);
|
||||
}
|
||||
|
||||
private static readonly string[] NewLineDelimiters = { "\r\n", "\r", "\n" };
|
||||
|
||||
public override object ConvertDataToSource(PublishedPropertyType propertyType, object source, bool preview)
|
||||
{
|
||||
// data is (both in database and xml):
|
||||
@@ -53,7 +55,7 @@ namespace Umbraco.Core.PropertyEditors.ValueConverters
|
||||
// Fall back on normal behaviour
|
||||
if (values.Any() == false)
|
||||
{
|
||||
return sourceString.Split(new string[] { Environment.NewLine }, StringSplitOptions.None);
|
||||
return sourceString.Split(NewLineDelimiters, StringSplitOptions.None);
|
||||
}
|
||||
|
||||
return values.ToArray();
|
||||
|
||||
@@ -401,9 +401,10 @@ namespace Umbraco.Core.Publishing
|
||||
content.Name, content.Id));
|
||||
}
|
||||
|
||||
// if newest is published, unpublish
|
||||
if (content.Published)
|
||||
content.ChangePublishedState(PublishedState.Unpublished);
|
||||
// make sure we dirty .Published and always unpublish
|
||||
// the version we have here could be the newest, !Published
|
||||
content.ChangePublishedState(PublishedState.Published);
|
||||
content.ChangePublishedState(PublishedState.Unpublished);
|
||||
|
||||
_logger.Info<PublishingStrategy>(
|
||||
string.Format("Content '{0}' with Id '{1}' has been unpublished.",
|
||||
|
||||
@@ -116,5 +116,19 @@ namespace Umbraco.Core.Scoping
|
||||
Enlisted[key] = enlistedOfT;
|
||||
return enlistedOfT.Item;
|
||||
}
|
||||
|
||||
public T GetEnlisted<T>(string key)
|
||||
{
|
||||
var enlistedObjects = _enlisted;
|
||||
if (enlistedObjects == null) return default (T);
|
||||
|
||||
IEnlistedObject enlisted;
|
||||
if (enlistedObjects.TryGetValue(key, out enlisted) == false)
|
||||
return default (T);
|
||||
|
||||
var enlistedAs = enlisted as EnlistedObject<T>;
|
||||
if (enlistedAs == null) throw new InvalidOperationException("An item with the key exists, but with a different type.");
|
||||
return enlistedAs.Item;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -420,6 +420,33 @@ namespace Umbraco.Core.Security
|
||||
return await base.CheckPasswordAsync(user, password);
|
||||
}
|
||||
|
||||
public override Task<IdentityResult> ResetPasswordAsync(int userId, string token, string newPassword)
|
||||
{
|
||||
var result = base.ResetPasswordAsync(userId, token, newPassword);
|
||||
if (result.Result.Succeeded)
|
||||
RaisePasswordResetEvent(userId);
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This is a special method that will reset the password but will raise the Password Changed event instead of the reset event
|
||||
/// </summary>
|
||||
/// <param name="userId"></param>
|
||||
/// <param name="token"></param>
|
||||
/// <param name="newPassword"></param>
|
||||
/// <returns></returns>
|
||||
/// <remarks>
|
||||
/// We use this because in the back office the only way an admin can change another user's password without first knowing their password
|
||||
/// is to generate a token and reset it, however, when we do this we want to track a password change, not a password reset
|
||||
/// </remarks>
|
||||
public Task<IdentityResult> ChangePasswordWithResetAsync(int userId, string token, string newPassword)
|
||||
{
|
||||
var result = base.ResetPasswordAsync(userId, token, newPassword);
|
||||
if (result.Result.Succeeded)
|
||||
RaisePasswordChangedEvent(userId);
|
||||
return result;
|
||||
}
|
||||
|
||||
public override Task<IdentityResult> ChangePasswordAsync(int userId, string currentPassword, string newPassword)
|
||||
{
|
||||
var result = base.ChangePasswordAsync(userId, currentPassword, newPassword);
|
||||
@@ -554,27 +581,27 @@ namespace Umbraco.Core.Security
|
||||
|
||||
internal void RaiseAccountLockedEvent(int userId)
|
||||
{
|
||||
OnAccountLocked(new IdentityAuditEventArgs(AuditEvent.AccountLocked, GetCurrentRequestIpAddress(), userId));
|
||||
OnAccountLocked(new IdentityAuditEventArgs(AuditEvent.AccountLocked, GetCurrentRequestIpAddress(), affectedUser: userId));
|
||||
}
|
||||
|
||||
internal void RaiseAccountUnlockedEvent(int userId)
|
||||
{
|
||||
OnAccountUnlocked(new IdentityAuditEventArgs(AuditEvent.AccountUnlocked, GetCurrentRequestIpAddress(), userId));
|
||||
OnAccountUnlocked(new IdentityAuditEventArgs(AuditEvent.AccountUnlocked, GetCurrentRequestIpAddress(), affectedUser: userId));
|
||||
}
|
||||
|
||||
internal void RaiseForgotPasswordRequestedEvent(int userId)
|
||||
{
|
||||
OnForgotPasswordRequested(new IdentityAuditEventArgs(AuditEvent.ForgotPasswordRequested, GetCurrentRequestIpAddress(), userId));
|
||||
OnForgotPasswordRequested(new IdentityAuditEventArgs(AuditEvent.ForgotPasswordRequested, GetCurrentRequestIpAddress(), affectedUser: userId));
|
||||
}
|
||||
|
||||
internal void RaiseForgotPasswordChangedSuccessEvent(int userId)
|
||||
{
|
||||
OnForgotPasswordChangedSuccess(new IdentityAuditEventArgs(AuditEvent.ForgotPasswordChangedSuccess, GetCurrentRequestIpAddress(), userId));
|
||||
OnForgotPasswordChangedSuccess(new IdentityAuditEventArgs(AuditEvent.ForgotPasswordChangedSuccess, GetCurrentRequestIpAddress(), affectedUser: userId));
|
||||
}
|
||||
|
||||
internal void RaiseLoginFailedEvent(int userId)
|
||||
{
|
||||
OnLoginFailed(new IdentityAuditEventArgs(AuditEvent.LoginFailed, GetCurrentRequestIpAddress(), userId));
|
||||
OnLoginFailed(new IdentityAuditEventArgs(AuditEvent.LoginFailed, GetCurrentRequestIpAddress(), affectedUser: userId));
|
||||
}
|
||||
|
||||
internal void RaiseInvalidLoginAttemptEvent(string username)
|
||||
@@ -584,31 +611,33 @@ namespace Umbraco.Core.Security
|
||||
|
||||
internal void RaiseLoginRequiresVerificationEvent(int userId)
|
||||
{
|
||||
OnLoginRequiresVerification(new IdentityAuditEventArgs(AuditEvent.LoginRequiresVerification, GetCurrentRequestIpAddress(), userId));
|
||||
OnLoginRequiresVerification(new IdentityAuditEventArgs(AuditEvent.LoginRequiresVerification, GetCurrentRequestIpAddress(), affectedUser: userId));
|
||||
}
|
||||
|
||||
internal void RaiseLoginSuccessEvent(int userId)
|
||||
{
|
||||
OnLoginSuccess(new IdentityAuditEventArgs(AuditEvent.LoginSucces, GetCurrentRequestIpAddress(), userId));
|
||||
OnLoginSuccess(new IdentityAuditEventArgs(AuditEvent.LoginSucces, GetCurrentRequestIpAddress(), affectedUser: userId));
|
||||
}
|
||||
|
||||
internal void RaiseLogoutSuccessEvent(int userId)
|
||||
{
|
||||
OnLogoutSuccess(new IdentityAuditEventArgs(AuditEvent.LogoutSuccess, GetCurrentRequestIpAddress(), userId));
|
||||
OnLogoutSuccess(new IdentityAuditEventArgs(AuditEvent.LogoutSuccess, GetCurrentRequestIpAddress(), affectedUser: userId));
|
||||
}
|
||||
|
||||
internal void RaisePasswordChangedEvent(int userId)
|
||||
{
|
||||
OnPasswordChanged(new IdentityAuditEventArgs(AuditEvent.PasswordChanged, GetCurrentRequestIpAddress(), userId));
|
||||
OnPasswordChanged(new IdentityAuditEventArgs(AuditEvent.PasswordChanged, GetCurrentRequestIpAddress(), affectedUser: userId));
|
||||
}
|
||||
|
||||
//TODO: I don't think this is required anymore since from 7.7 we no longer display the reset password checkbox since that didn't make sense.
|
||||
internal void RaisePasswordResetEvent(int userId)
|
||||
{
|
||||
OnPasswordReset(new IdentityAuditEventArgs(AuditEvent.PasswordReset, GetCurrentRequestIpAddress(), userId));
|
||||
OnPasswordReset(new IdentityAuditEventArgs(AuditEvent.PasswordReset, GetCurrentRequestIpAddress(), affectedUser: userId));
|
||||
}
|
||||
|
||||
internal void RaiseResetAccessFailedCountEvent(int userId)
|
||||
{
|
||||
OnResetAccessFailedCount(new IdentityAuditEventArgs(AuditEvent.ResetAccessFailedCount, GetCurrentRequestIpAddress(), userId));
|
||||
OnResetAccessFailedCount(new IdentityAuditEventArgs(AuditEvent.ResetAccessFailedCount, GetCurrentRequestIpAddress(), affectedUser: userId));
|
||||
}
|
||||
|
||||
public static event EventHandler AccountLocked;
|
||||
|
||||
@@ -18,7 +18,7 @@ using Task = System.Threading.Tasks.Task;
|
||||
|
||||
namespace Umbraco.Core.Security
|
||||
{
|
||||
public class BackOfficeUserStore : DisposableObject,
|
||||
public class BackOfficeUserStore : DisposableObjectSlim,
|
||||
IUserStore<BackOfficeIdentityUser, int>,
|
||||
IUserPasswordStore<BackOfficeIdentityUser, int>,
|
||||
IUserEmailStore<BackOfficeIdentityUser, int>,
|
||||
@@ -58,7 +58,7 @@ namespace Umbraco.Core.Security
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handles the disposal of resources. Derived from abstract class <see cref="DisposableObject"/> which handles common required locking logic.
|
||||
/// Handles the disposal of resources. Derived from abstract class <see cref="DisposableObjectSlim"/> which handles common required locking logic.
|
||||
/// </summary>
|
||||
protected override void DisposeResources()
|
||||
{
|
||||
|
||||
@@ -4,6 +4,7 @@ using System.Linq;
|
||||
using Umbraco.Core.Events;
|
||||
using Umbraco.Core.Logging;
|
||||
using Umbraco.Core.Models;
|
||||
using Umbraco.Core.Models.Rdbms;
|
||||
using Umbraco.Core.Persistence;
|
||||
using Umbraco.Core.Persistence.DatabaseModelDefinitions;
|
||||
using Umbraco.Core.Persistence.Querying;
|
||||
@@ -13,9 +14,13 @@ namespace Umbraco.Core.Services
|
||||
{
|
||||
public sealed class AuditService : ScopeRepositoryService, IAuditService
|
||||
{
|
||||
private readonly Lazy<bool> _isAvailable;
|
||||
|
||||
public AuditService(IDatabaseUnitOfWorkProvider provider, RepositoryFactory repositoryFactory, ILogger logger, IEventMessagesFactory eventMessagesFactory)
|
||||
: base(provider, repositoryFactory, logger, eventMessagesFactory)
|
||||
{ }
|
||||
{
|
||||
_isAvailable = new Lazy<bool>(DetermineIsAvailable);
|
||||
}
|
||||
|
||||
public void Add(AuditType type, string comment, int userId, int objectId)
|
||||
{
|
||||
@@ -107,5 +112,90 @@ namespace Umbraco.Core.Services
|
||||
return repository.GetPagedResultsByQuery(query, pageIndex, pageSize, out totalRecords, orderDirection, auditTypeFilter, customFilter);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public IAuditEntry Write(int performingUserId, string perfomingDetails, string performingIp, DateTime eventDateUtc, int affectedUserId, string affectedDetails, string eventType, string eventDetails)
|
||||
{
|
||||
if (performingUserId < 0) throw new ArgumentOutOfRangeException(nameof(performingUserId));
|
||||
if (string.IsNullOrWhiteSpace(perfomingDetails)) throw new ArgumentException("Value cannot be null or whitespace.", nameof(perfomingDetails));
|
||||
if (string.IsNullOrWhiteSpace(eventType)) throw new ArgumentException("Value cannot be null or whitespace.", nameof(eventType));
|
||||
if (string.IsNullOrWhiteSpace(eventDetails)) throw new ArgumentException("Value cannot be null or whitespace.", nameof(eventDetails));
|
||||
|
||||
//we need to truncate the data else we'll get SQL errors
|
||||
affectedDetails = affectedDetails?.Substring(0, Math.Min(affectedDetails.Length, AuditEntryDto.DetailsLength));
|
||||
eventDetails = eventDetails.Substring(0, Math.Min(eventDetails.Length, AuditEntryDto.DetailsLength));
|
||||
|
||||
//validate the eventType - must contain a forward slash, no spaces, no special chars
|
||||
var eventTypeParts = eventType.ToCharArray();
|
||||
if (eventTypeParts.Contains('/') == false || eventTypeParts.All(c => char.IsLetterOrDigit(c) || c == '/' || c == '-') == false)
|
||||
throw new ArgumentException(nameof(eventType) + " must contain only alphanumeric characters, hyphens and at least one '/' defining a category");
|
||||
if (eventType.Length > AuditEntryDto.EventTypeLength)
|
||||
throw new ArgumentException($"Must be max {AuditEntryDto.EventTypeLength} chars.", nameof(eventType));
|
||||
if (performingIp != null && performingIp.Length > AuditEntryDto.IpLength)
|
||||
throw new ArgumentException($"Must be max {AuditEntryDto.EventTypeLength} chars.", nameof(performingIp));
|
||||
|
||||
var entry = new AuditEntry
|
||||
{
|
||||
PerformingUserId = performingUserId,
|
||||
PerformingDetails = perfomingDetails,
|
||||
PerformingIp = performingIp,
|
||||
EventDateUtc = eventDateUtc,
|
||||
AffectedUserId = affectedUserId,
|
||||
AffectedDetails = affectedDetails,
|
||||
EventType = eventType,
|
||||
EventDetails = eventDetails
|
||||
};
|
||||
|
||||
if (_isAvailable.Value == false) return entry;
|
||||
|
||||
using (var uow = UowProvider.GetUnitOfWork())
|
||||
{
|
||||
var repository = RepositoryFactory.CreateAuditEntryRepository(uow);
|
||||
repository.AddOrUpdate(entry);
|
||||
uow.Commit();
|
||||
}
|
||||
|
||||
return entry;
|
||||
}
|
||||
|
||||
//TODO: Currently used in testing only, not part of the interface, need to add queryable methods to the interface instead
|
||||
internal IEnumerable<IAuditEntry> GetAll()
|
||||
{
|
||||
if (_isAvailable.Value == false) return Enumerable.Empty<IAuditEntry>();
|
||||
|
||||
using (var uow = UowProvider.GetUnitOfWork(readOnly: true))
|
||||
{
|
||||
var repository = RepositoryFactory.CreateAuditEntryRepository(uow);
|
||||
return repository.GetAll();
|
||||
}
|
||||
}
|
||||
|
||||
//TODO: Currently used in testing only, not part of the interface, need to add queryable methods to the interface instead
|
||||
internal IEnumerable<IAuditEntry> GetPage(long pageIndex, int pageCount, out long records)
|
||||
{
|
||||
if (_isAvailable.Value == false)
|
||||
{
|
||||
records = 0;
|
||||
return Enumerable.Empty<IAuditEntry>();
|
||||
}
|
||||
|
||||
using (var uow = UowProvider.GetUnitOfWork(readOnly: true))
|
||||
{
|
||||
var repository = RepositoryFactory.CreateAuditEntryRepository(uow);
|
||||
return repository.GetPage(pageIndex, pageCount, out records);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether the repository is available.
|
||||
/// </summary>
|
||||
private bool DetermineIsAvailable()
|
||||
{
|
||||
using (var uow = UowProvider.GetUnitOfWork(readOnly: true))
|
||||
{
|
||||
var repository = RepositoryFactory.CreateAuditEntryRepository(uow);
|
||||
return repository.IsAvailable();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,81 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Umbraco.Core.Events;
|
||||
using Umbraco.Core.Logging;
|
||||
using Umbraco.Core.Models;
|
||||
using Umbraco.Core.Persistence;
|
||||
using Umbraco.Core.Persistence.Querying;
|
||||
using Umbraco.Core.Persistence.UnitOfWork;
|
||||
|
||||
namespace Umbraco.Core.Services
|
||||
{
|
||||
/// <summary>
|
||||
/// Implements <see cref="IContentService"/>.
|
||||
/// </summary>
|
||||
internal class ConsentService : ScopeRepositoryService, IConsentService
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ContentService"/> class.
|
||||
/// </summary>
|
||||
public ConsentService(IScopeUnitOfWorkProvider provider, RepositoryFactory repositoryFactory, ILogger logger, IEventMessagesFactory eventMessagesFactory)
|
||||
: base(provider, repositoryFactory, logger, eventMessagesFactory)
|
||||
{ }
|
||||
|
||||
/// <inheritdoc />
|
||||
public IConsent RegisterConsent(string source, string context, string action, ConsentState state, string comment = null)
|
||||
{
|
||||
// prevent stupid states
|
||||
var v = 0;
|
||||
if ((state & ConsentState.Pending) > 0) v++;
|
||||
if ((state & ConsentState.Granted) > 0) v++;
|
||||
if ((state & ConsentState.Revoked) > 0) v++;
|
||||
if (v != 1)
|
||||
throw new ArgumentException("Invalid state.", nameof(state));
|
||||
|
||||
var consent = new Consent
|
||||
{
|
||||
Current = true,
|
||||
Source = source,
|
||||
Context = context,
|
||||
Action = action,
|
||||
CreateDate = DateTime.Now,
|
||||
State = state,
|
||||
Comment = comment
|
||||
};
|
||||
|
||||
using (var uow = UowProvider.GetUnitOfWork())
|
||||
{
|
||||
var repository = RepositoryFactory.CreateConsentRepository(uow);
|
||||
repository.ClearCurrent(source, context, action);
|
||||
repository.AddOrUpdate(consent);
|
||||
uow.Commit();
|
||||
}
|
||||
|
||||
return consent;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public IEnumerable<IConsent> LookupConsent(string source = null, string context = null, string action = null,
|
||||
bool sourceStartsWith = false, bool contextStartsWith = false, bool actionStartsWith = false,
|
||||
bool includeHistory = false)
|
||||
{
|
||||
using (var uow = UowProvider.GetUnitOfWork(readOnly: true))
|
||||
{
|
||||
var repository = RepositoryFactory.CreateConsentRepository(uow);
|
||||
|
||||
IQuery<IConsent> query = new Query<IConsent>();
|
||||
|
||||
if (string.IsNullOrWhiteSpace(source) == false)
|
||||
query = sourceStartsWith ? query.Where(x => x.Source.StartsWith(source)) : query.Where(x => x.Source == source);
|
||||
if (string.IsNullOrWhiteSpace(context) == false)
|
||||
query = contextStartsWith ? query.Where(x => x.Context.StartsWith(context)) : query.Where(x => x.Context == context);
|
||||
if (string.IsNullOrWhiteSpace(action) == false)
|
||||
query = actionStartsWith ? query.Where(x => x.Action.StartsWith(action)) : query.Where(x => x.Action == action);
|
||||
if (includeHistory == false)
|
||||
query = query.Where(x => x.Current);
|
||||
|
||||
return repository.GetByQuery(query);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -692,7 +692,7 @@ namespace Umbraco.Core.Services
|
||||
totalChildren = 0;
|
||||
return Enumerable.Empty<IContent>();
|
||||
}
|
||||
query.Where(x => x.Path.SqlStartsWith(string.Format("{0},", contentPath[0]), TextColumnType.NVarchar));
|
||||
query.Where(x => x.Path.SqlStartsWith(string.Format("{0},", contentPath[0].Path), TextColumnType.NVarchar));
|
||||
}
|
||||
|
||||
|
||||
@@ -737,7 +737,7 @@ namespace Umbraco.Core.Services
|
||||
totalChildren = 0;
|
||||
return Enumerable.Empty<IContent>();
|
||||
}
|
||||
query.Where(x => x.Path.SqlStartsWith(string.Format("{0},", contentPath[0]), TextColumnType.NVarchar));
|
||||
query.Where(x => x.Path.SqlStartsWith(string.Format("{0},", contentPath[0].Path), TextColumnType.NVarchar));
|
||||
}
|
||||
|
||||
return repository.GetPagedResultsByQuery(query, pageIndex, pageSize, out totalChildren, orderBy, orderDirection, orderBySystemField, filter);
|
||||
@@ -884,8 +884,8 @@ namespace Umbraco.Core.Services
|
||||
using (var uow = UowProvider.GetUnitOfWork(readOnly: true))
|
||||
{
|
||||
var repository = RepositoryFactory.CreateContentRepository(uow);
|
||||
var query = Query<IContent>.Builder.Where(x => x.Published && x.ExpireDate <= DateTime.Now);
|
||||
return repository.GetByQuery(query);
|
||||
var query = Query<IContent>.Builder.Where(x => x.ExpireDate <= DateTime.Now);
|
||||
return repository.GetByQuery(query).Where(x => x.HasPublishedVersion);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -912,13 +912,11 @@ namespace Umbraco.Core.Services
|
||||
using (var uow = UowProvider.GetUnitOfWork(readOnly: true))
|
||||
{
|
||||
var repository = RepositoryFactory.CreateContentRepository(uow);
|
||||
var query = Query<IContent>.Builder.Where(x => x.Path.Contains(Constants.System.RecycleBinContent.ToInvariantString()));
|
||||
var query = Query<IContent>.Builder.Where(x => x.Path.StartsWith(Constants.System.RecycleBinContentPathPrefix));
|
||||
return repository.GetByQuery(query);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Checks whether an <see cref="IContent"/> item has any children
|
||||
/// </summary>
|
||||
@@ -1344,6 +1342,40 @@ namespace Umbraco.Core.Services
|
||||
return content;
|
||||
}
|
||||
|
||||
public void DeleteBlueprintsOfTypes(IEnumerable<int> contentTypeIds, int userId = 0)
|
||||
{
|
||||
using (new WriteLock(Locker))
|
||||
using (var uow = UowProvider.GetUnitOfWork())
|
||||
{
|
||||
var repository = RepositoryFactory.CreateContentBlueprintRepository(uow);
|
||||
|
||||
var contentTypeIdsA = contentTypeIds.ToArray();
|
||||
var query = new Query<IContent>();
|
||||
if (contentTypeIdsA.Length > 0)
|
||||
{
|
||||
query.Where(x => contentTypeIdsA.Contains(x.ContentTypeId));
|
||||
}
|
||||
var blueprints = repository.GetByQuery(query).Select(x =>
|
||||
{
|
||||
((Content) x).IsBlueprint = true;
|
||||
return x;
|
||||
}).ToArray();
|
||||
|
||||
foreach (var blueprint in blueprints)
|
||||
{
|
||||
repository.Delete(blueprint);
|
||||
}
|
||||
|
||||
uow.Events.Dispatch(DeletedBlueprint, this, new DeleteEventArgs<IContent>(blueprints), "DeletedBlueprint");
|
||||
uow.Commit();
|
||||
}
|
||||
}
|
||||
|
||||
public void DeleteBlueprintsOfType(int contentTypeId, int userId = 0)
|
||||
{
|
||||
DeleteBlueprintsOfTypes(new[] {contentTypeId}, userId);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Saves a single <see cref="IContent"/> object
|
||||
/// </summary>
|
||||
|
||||
@@ -920,7 +920,9 @@ namespace Umbraco.Core.Services
|
||||
var deletedContentTypes = new List<IContentType> { contentType };
|
||||
deletedContentTypes.AddRange(GetDescendants(contentType));
|
||||
|
||||
_contentService.DeleteContentOfTypes(deletedContentTypes.Select(x => x.Id), userId);
|
||||
var ids = deletedContentTypes.Select(x => x.Id).ToArray();
|
||||
_contentService.DeleteContentOfTypes(ids, userId);
|
||||
_contentService.DeleteBlueprintsOfTypes(ids);
|
||||
|
||||
repository.Delete(contentType);
|
||||
deleteEventArgs.DeletedEntities = deletedContentTypes.DistinctBy(x => x.Id);
|
||||
|
||||
@@ -1,10 +1,15 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Umbraco.Core.Models;
|
||||
using Umbraco.Core.Persistence.DatabaseModelDefinitions;
|
||||
using Umbraco.Core.Persistence.Querying;
|
||||
|
||||
namespace Umbraco.Core.Services
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a service for handling audit.
|
||||
/// </summary>
|
||||
public interface IAuditService : IService
|
||||
{
|
||||
void Add(AuditType type, string comment, int userId, int objectId);
|
||||
@@ -54,5 +59,25 @@ namespace Umbraco.Core.Services
|
||||
Direction orderDirection = Direction.Descending,
|
||||
AuditType[] auditTypeFilter = null,
|
||||
IQuery<IAuditItem> customFilter = null);
|
||||
|
||||
/// <summary>
|
||||
/// Writes an audit entry for an audited event.
|
||||
/// </summary>
|
||||
/// <param name="performingUserId">The identifier of the user triggering the audited event.</param>
|
||||
/// <param name="perfomingDetails">Free-form details about the user triggering the audited event.</param>
|
||||
/// <param name="performingIp">The IP address or the request triggering the audited event.</param>
|
||||
/// <param name="eventDateUtc">The date and time of the audited event.</param>
|
||||
/// <param name="affectedUserId">The identifier of the user affected by the audited event.</param>
|
||||
/// <param name="affectedDetails">Free-form details about the entity affected by the audited event.</param>
|
||||
/// <param name="eventType">
|
||||
/// The type of the audited event - must contain only alphanumeric chars and hyphens with forward slashes separating categories.
|
||||
/// <example>
|
||||
/// The eventType will generally be formatted like: {application}/{entity-type}/{category}/{sub-category}
|
||||
/// Example: umbraco/user/sign-in/failed
|
||||
/// </example>
|
||||
/// </param>
|
||||
/// <param name="eventDetails">Free-form details about the audited event.</param>
|
||||
IAuditEntry Write(int performingUserId, string perfomingDetails, string performingIp, DateTime eventDateUtc, int affectedUserId, string affectedDetails, string eventType, string eventDetails);
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
using System.Collections.Generic;
|
||||
using Umbraco.Core.Models;
|
||||
|
||||
namespace Umbraco.Core.Services
|
||||
{
|
||||
/// <summary>
|
||||
/// A service for handling lawful data processing requirements
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>Consent can be given or revoked or changed via the <see cref="RegisterConsent"/> method, which
|
||||
/// creates a new <see cref="IConsent"/> entity to track the consent. Revoking a consent is performed by
|
||||
/// registering a revoked consent.</para>
|
||||
/// <para>A consent can be revoked, by registering a revoked consent, but cannot be deleted.</para>
|
||||
/// <para>Getter methods return the current state of a consent, i.e. the latest <see cref="IConsent"/>
|
||||
/// entity that was created.</para>
|
||||
/// </remarks>
|
||||
public interface IConsentService : IService
|
||||
{
|
||||
/// <summary>
|
||||
/// Registers consent.
|
||||
/// </summary>
|
||||
/// <param name="source">The source, i.e. whoever is consenting.</param>
|
||||
/// <param name="context"></param>
|
||||
/// <param name="action"></param>
|
||||
/// <param name="state">The state of the consent.</param>
|
||||
/// <param name="comment">Additional free text.</param>
|
||||
/// <returns>The corresponding consent entity.</returns>
|
||||
IConsent RegisterConsent(string source, string context, string action, ConsentState state, string comment = null);
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves consents.
|
||||
/// </summary>
|
||||
/// <param name="source">The optional source.</param>
|
||||
/// <param name="context">The optional context.</param>
|
||||
/// <param name="action">The optional action.</param>
|
||||
/// <param name="sourceStartsWith">Determines whether <paramref name="source"/> is a start pattern.</param>
|
||||
/// <param name="contextStartsWith">Determines whether <paramref name="context"/> is a start pattern.</param>
|
||||
/// <param name="actionStartsWith">Determines whether <paramref name="action"/> is a start pattern.</param>
|
||||
/// <param name="includeHistory">Determines whether to include the history of consents.</param>
|
||||
/// <returns>Consents matching the paramters.</returns>
|
||||
IEnumerable<IConsent> LookupConsent(string source = null, string context = null, string action = null,
|
||||
bool sourceStartsWith = false, bool contextStartsWith = false, bool actionStartsWith = false,
|
||||
bool includeHistory = false);
|
||||
}
|
||||
}
|
||||
@@ -101,7 +101,9 @@ namespace Umbraco.Core.Services
|
||||
IContent GetBlueprintById(Guid id);
|
||||
void SaveBlueprint(IContent content, int userId = 0);
|
||||
void DeleteBlueprint(IContent content, int userId = 0);
|
||||
IContent CreateContentFromBlueprint(IContent blueprint, string name, int userId = 0);
|
||||
IContent CreateContentFromBlueprint(IContent blueprint, string name, int userId = 0);
|
||||
void DeleteBlueprintsOfType(int contentTypeId, int userId = 0);
|
||||
void DeleteBlueprintsOfTypes(IEnumerable<int> contentTypeIds, int userId = 0);
|
||||
|
||||
/// <summary>
|
||||
/// Gets all XML entries found in the cmsContentXml table based on the given path
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Net.Http;
|
||||
using System.Xml.Linq;
|
||||
using Umbraco.Core.Models;
|
||||
using Umbraco.Core.Models.Membership;
|
||||
@@ -198,7 +199,7 @@ namespace Umbraco.Core.Services
|
||||
/// </summary>
|
||||
/// <param name="memberTypeId">Id of the MemberType</param>
|
||||
void DeleteMembersOfType(int memberTypeId);
|
||||
|
||||
|
||||
[Obsolete("Use the overload with 'long' parameter types instead")]
|
||||
[EditorBrowsable(EditorBrowsableState.Never)]
|
||||
IEnumerable<IMember> FindMembersByDisplayName(string displayNameToMatch, int pageIndex, int pageSize, out int totalRecords, StringPropertyMatchType matchType = StringPropertyMatchType.StartsWith);
|
||||
@@ -249,4 +250,4 @@ namespace Umbraco.Core.Services
|
||||
/// <returns><see cref="IEnumerable{IMember}"/></returns>
|
||||
IEnumerable<IMember> GetMembersByPropertyValue(string propertyTypeAlias, DateTime value, ValuePropertyMatchType matchType = ValuePropertyMatchType.Exact);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user