Compare commits
580 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| a91669abc0 | |||
| 3b9db8a92f | |||
| 1bfc1229bd | |||
| 3d6fd2d785 | |||
| e56881abc6 | |||
| d32991c30a | |||
| 8758f9649b | |||
| 6f0f1ab2c3 | |||
| cd28240172 | |||
| 7002291c41 | |||
| 51bbf7ceb5 | |||
| 82297a6ff1 | |||
| cc5ac1a84c | |||
| 6373add383 | |||
| 180099b718 | |||
| 65f15125e1 | |||
| 856c345905 | |||
| b2cd5dfb85 | |||
| 462f7a59b5 | |||
| 22689d24ec | |||
| 4055afdf05 | |||
| a1c70a219a | |||
| 3151515855 | |||
| 25df9da715 | |||
| 9c2f438476 | |||
| 0de3be42cf | |||
| 1545e0eed7 | |||
| 42a7ed6877 | |||
| 98b8aedc4d | |||
| c7a2b54d1c | |||
| 6583ff4439 | |||
| 7b08b3e936 | |||
| bd2fc71dc5 | |||
| 0af97f63e2 | |||
| 1db635f24c | |||
| 6e27b3d6d4 | |||
| 1dea0edcf1 | |||
| 1abab41955 | |||
| d1be38be64 | |||
| 8916c68a0c | |||
| 6faa7e2fc9 | |||
| a6093f41bd | |||
| 107e66e4da | |||
| 256849f8a6 | |||
| c9c451be65 | |||
| 1fcea083e7 | |||
| 85d6ee5713 | |||
| ec81583741 | |||
| 332da9bf2d | |||
| a0036d925e | |||
| 81a99d6f7d | |||
| e912238574 | |||
| 9a830e5de6 | |||
| 7d9ba0e36e | |||
| 6c5e09fd84 | |||
| 2f8c43c689 | |||
| 7a25cb52b2 | |||
| 19bc97a7fd | |||
| 72900ef3b3 | |||
| 74ddd09c5d | |||
| e461cb92da | |||
| e91b53e66d | |||
| b5e8cb29dd | |||
| 20dc4f5bc4 | |||
| 2c399860ca | |||
| 4dc4c36581 | |||
| fe774f557c | |||
| 3ecb9d0f86 | |||
| f78a425985 | |||
| 9f365727e0 | |||
| 74d73cfe94 | |||
| 7fb9d430a9 | |||
| 7cd8ba13c0 | |||
| 1888888650 | |||
| 39b1357a2a | |||
| e1ee8a1437 | |||
| 21ba88a34d | |||
| b5abbc7c30 | |||
| 7958b5fcb1 | |||
| 44b22503e1 | |||
| bfa12856db | |||
| d9542f4ca5 | |||
| f057976f78 | |||
| 151c28a20f | |||
| d2c8268fc4 | |||
| 13a9f40815 | |||
| 67f8141015 | |||
| 06a098215b | |||
| 051b320904 | |||
| 5aab50ba3b | |||
| 5b9cbae860 | |||
| 59759c4919 | |||
| 68ddea2776 | |||
| 8857b2db59 | |||
| fae5618865 | |||
| 40dbcbd7d9 | |||
| 074d894271 | |||
| eb6ae95039 | |||
| 5e461b0cec | |||
| d6a49f275f | |||
| 8e024fccff | |||
| 43227d4510 | |||
| 79cc4cd2fa | |||
| ff83d2e1f4 | |||
| cb7df58fdd | |||
| 5cc274a580 | |||
| 6c5b5ee637 | |||
| 9bfac0fdcd | |||
| bd5823f0de | |||
| 70035632b6 | |||
| 20f5560767 | |||
| ecd024c462 | |||
| ba91bf0b9c | |||
| 2b13297c36 | |||
| fb5faa3db7 | |||
| d7a142e724 | |||
| 63785ae70f | |||
| 68aa6ff093 | |||
| 3e985acef1 | |||
| 6d13d33cfd | |||
| ced0a852b1 | |||
| ba0da15f7f | |||
| 2875cd10f3 | |||
| b91661cf65 | |||
| 827f7a7bc8 | |||
| ff829d49a3 | |||
| d2483159bc | |||
| 48ab3f580c | |||
| c524d67a37 | |||
| 4f40fff5ee | |||
| b04d0fc886 | |||
| 4c3258812c | |||
| bc98f5e8fc | |||
| 6f2f6c088b | |||
| da91923577 | |||
| fbc46d8046 | |||
| e8a0fae557 | |||
| e0baf45247 | |||
| 43e983e27a | |||
| b882806847 | |||
| 58c30cd7a6 | |||
| ae1b6d95b8 | |||
| 403af78ba9 | |||
| 59fe690a88 | |||
| a15b005d95 | |||
| b94c396d4e | |||
| 8afc4f5ad1 | |||
| f68b2de9e3 | |||
| f4e29d90e0 | |||
| 5ad5e945c4 | |||
| 4ef7b7735e | |||
| db538d95ba | |||
| cef7cd1ca7 | |||
| 726416fe0c | |||
| 1957d8bb3b | |||
| fab30d353a | |||
| 75990b5961 | |||
| 18900d0e25 | |||
| d08f4230c8 | |||
| 15d06557e9 | |||
| 8d2e708d1c | |||
| 280573be28 | |||
| 415aaf3e2b | |||
| 5081027482 | |||
| 96b24d7ee0 | |||
| ac1785bc08 | |||
| 1da1a20734 | |||
| c765ab9112 | |||
| 664aa3842d | |||
| fbee58e445 | |||
| a5e87dcedf | |||
| 53f9e104ec | |||
| ffe41fe060 | |||
| e89cc24d51 | |||
| 61f3b2ae4a | |||
| a752c7ee4b | |||
| a14f0021bd | |||
| 5eb5549448 | |||
| f5e9151ffd | |||
| 5a9dd11a7c | |||
| 9e6123abd3 | |||
| 6c3ef2aaa9 | |||
| 95ae504c9b | |||
| 88fadcf74e | |||
| bb4994f9fc | |||
| c334f94d72 | |||
| ddf525db40 | |||
| 9c7ef46222 | |||
| eacc7f7fb3 | |||
| 80f597a7eb | |||
| d069da8a4f | |||
| 44ebd8d9ea | |||
| 2fd6dc3603 | |||
| 0629b49112 | |||
| 4958c5a59a | |||
| 523501db8c | |||
| 23cd2b09ca | |||
| a1ff1d4543 | |||
| 1bf2a3b1bf | |||
| 8598ab565c | |||
| a56936b6f9 | |||
| b3e21e0827 | |||
| 739ff33ce1 | |||
| f27a726a6e | |||
| aed906e682 | |||
| 220446afec | |||
| a101367292 | |||
| e90fedb835 | |||
| d5f9dfecc4 | |||
| cbb84f79d1 | |||
| a7377d0686 | |||
| 68a4749917 | |||
| 81763bf4d8 | |||
| 0e0bc9fbef | |||
| 964d9a9328 | |||
| e0ee786271 | |||
| 2664a75c84 | |||
| 5d9bde807e | |||
| 0370f9a1c8 | |||
| 64dac39bae | |||
| 6040d149ec | |||
| bfbc6595fb | |||
| 25d8fc7a13 | |||
| e11b5c3fdb | |||
| 87100feb3d | |||
| eae74b0e20 | |||
| 610efe552d | |||
| c4860a490f | |||
| 8e6bbc3df9 | |||
| c16d0b77c2 | |||
| 2c4403f892 | |||
| fd66f08520 | |||
| 8e50f4968f | |||
| 9acbe09449 | |||
| 4baaafd006 | |||
| 3f7293304d | |||
| f62581bd38 | |||
| 9782e3711e | |||
| 1f4ba9cfdc | |||
| ba4f0cc6a4 | |||
| fc84821e8b | |||
| 0fcf0b505a | |||
| d300146cf6 | |||
| c8482b9f59 | |||
| b7795e19ef | |||
| c97617f5b5 | |||
| b2d4eccf7b | |||
| 14265fc964 | |||
| 18c3345e47 | |||
| 924a016ffe | |||
| a02b41e9d2 | |||
| 068c4a0b16 | |||
| f7d28d769a | |||
| 9a07e206d8 | |||
| 0edb71f4aa | |||
| b376207ef6 | |||
| dbece874dd | |||
| 6d0aa824ad | |||
| 89afacf161 | |||
| 0fa6940bb6 | |||
| 1353b0a592 | |||
| 617fec171f | |||
| 2c63866cb3 | |||
| 18d3aa9ee2 | |||
| f83c966e55 | |||
| de41a1f1ca | |||
| c9f57c6ce3 | |||
| 54ab26e2ed | |||
| 31dc1b2cdb | |||
| 07c714fd11 | |||
| d4b9a711c6 | |||
| 9521742254 | |||
| 9fee3f5873 | |||
| cb10578e19 | |||
| cdc4435f2d | |||
| a885840b9f | |||
| 62173cef58 | |||
| e5b0a4d98c | |||
| 9479d63cba | |||
| 4c544acb42 | |||
| c7c5f481f1 | |||
| 5e8e62cbf7 | |||
| 555b520a0c | |||
| 8053db32b6 | |||
| 3e2d59e526 | |||
| 9d3e967c2a | |||
| 71ec09486d | |||
| 8dadc92aa8 | |||
| f472f5d950 | |||
| 775a6f80ea | |||
| 5bf8b8ecba | |||
| 397ef32665 | |||
| 2634e33f51 | |||
| 6e27caa547 | |||
| 9c027dcdbc | |||
| f3876b86dc | |||
| 58c3dfac1c | |||
| dcf59d2b17 | |||
| 71099514cb | |||
| 887111318f | |||
| bf9755b27c | |||
| aca181b32e | |||
| 886347e763 | |||
| 9fa606e065 | |||
| e40ac44afd | |||
| 48603fb840 | |||
| f824439180 | |||
| b3616c9f8c | |||
| 48383f2e7a | |||
| cb98385242 | |||
| 916bad82df | |||
| 29ec8cb2d3 | |||
| 04900337c7 | |||
| 1433685160 | |||
| c5994ed54a | |||
| 9fd80d791a | |||
| f5f9cb676c | |||
| 04e2d2370a | |||
| 8d00d931b0 | |||
| 2998ad8cea | |||
| 46c15ef28f | |||
| 2b369a57d8 | |||
| c37a6bb928 | |||
| 6e53291877 | |||
| bb78f756c7 | |||
| ffb0de4050 | |||
| 0fecad9867 | |||
| 3045068d68 | |||
| 37a6668313 | |||
| 36814d746f | |||
| a72f6edb6b | |||
| e01cbcd405 | |||
| 012ed6cf16 | |||
| eb3ff12916 | |||
| 1ebff69358 | |||
| 5435c44ea9 | |||
| 76fa477c35 | |||
| 1f6f36d8c8 | |||
| c46ac11125 | |||
| 73d5a3f993 | |||
| 5a99a6903d | |||
| 775639da41 | |||
| 2375ede161 | |||
| fcd00599da | |||
| d3f5090150 | |||
| 6a7faf457b | |||
| ec247d2b27 | |||
| f32479bb10 | |||
| 371269436f | |||
| ef13e4112f | |||
| ce5b6861bb | |||
| eaf64d1814 | |||
| 6bf1ecdb11 | |||
| fdd0ff2c23 | |||
| bade8e42ba | |||
| cfdf2768e6 | |||
| c3ca006f14 | |||
| 6c47c8f792 | |||
| 63160e2e2d | |||
| edc58fe8a5 | |||
| 23632dc0c3 | |||
| d739b9fb31 | |||
| 2ae4ede258 | |||
| cacd08b57c | |||
| 4eddfb4cf5 | |||
| f2b3d7c041 | |||
| 5d97a94dcb | |||
| de6e7dd28d | |||
| 420c029bf7 | |||
| 61441dd436 | |||
| 5c24867925 | |||
| 0409cbe613 | |||
| bbbea99ced | |||
| 3d213342bf | |||
| 48fdb75d96 | |||
| a97094458d | |||
| f81353af0b | |||
| f0e1460550 | |||
| d15c09ad2a | |||
| 5813202892 | |||
| 82f6b6a66d | |||
| eec6ee79aa | |||
| 5ed4f262cd | |||
| e5d7f6fd04 | |||
| a9ad0ae8eb | |||
| 9cd64a9061 | |||
| 839c0d011b | |||
| 15c9aadcdc | |||
| 36a1c0a9e0 | |||
| 30f0c96052 | |||
| cfab30a1ef | |||
| 1dd0086eb5 | |||
| a7f06ebc56 | |||
| 9498bc4bfd | |||
| 38b13bf6ce | |||
| c8cb5b26b0 | |||
| 5749f80a7f | |||
| 5d9a935621 | |||
| 1ff4fe26dd | |||
| bd69820028 | |||
| 3c9953979a | |||
| 2f6189ae24 | |||
| 5ff329778d | |||
| befb2b4d2f | |||
| f4ae45d8f2 | |||
| 7b73ed1796 | |||
| 0eacbcb7cb | |||
| f871de61d5 | |||
| e02693e46b | |||
| db62d14b1a | |||
| c096ab9e54 | |||
| ada5a031b4 | |||
| 9c8b9f8bb4 | |||
| a4bd3abfbe | |||
| a8a768a50f | |||
| 62b99ffc59 | |||
| 8e6bac534a | |||
| 6b0c94948e | |||
| 25f049e167 | |||
| f7979fa19d | |||
| 6fd1784337 | |||
| c5cf1fa6b2 | |||
| 1e4c9c2505 | |||
| a7bb8e0d00 | |||
| ed4a9f84f0 | |||
| a6b56db2f5 | |||
| bbe4a87784 | |||
| cbd5c295bd | |||
| 0d29e1438c | |||
| 4796827f02 | |||
| c6f7ece764 | |||
| e86aaefaaf | |||
| 97e9cd4be0 | |||
| 63af822bad | |||
| b65f653c91 | |||
| 3b404a2f76 | |||
| e9567e7adc | |||
| 697daab4d7 | |||
| 6c29ad854c | |||
| 93d89535aa | |||
| eb093ca4a6 | |||
| 1040f234dc | |||
| 5a1ace9362 | |||
| 3f835fe19c | |||
| bf43ce3281 | |||
| c37ba0dc8a | |||
| 7b5ad9a5c6 | |||
| f0afa701bc | |||
| 71dc1110d8 | |||
| 4de4eb9cc8 | |||
| 308d5ba344 | |||
| 74dac71e8a | |||
| 64518774c6 | |||
| f65b0df3e2 | |||
| 5cfe9e85f8 | |||
| 61044b6018 | |||
| 00fc5a5bb2 | |||
| da85e4eb28 | |||
| 3f23865e0a | |||
| fbc08d66f1 | |||
| 79f04bf939 | |||
| 9ab90595e2 | |||
| aac208c0e8 | |||
| f8e101ec13 | |||
| 23fcc1ac55 | |||
| 6356531207 | |||
| 51b618826c | |||
| 04c28e5006 | |||
| e9ef9b9d75 | |||
| c160ce98c1 | |||
| c3a60ea0e0 | |||
| 3408efdee4 | |||
| 1f0dc7e65d | |||
| f89083c772 | |||
| 752065e540 | |||
| 82e4e50584 | |||
| 775e63148d | |||
| 4259b02b8d | |||
| 0c402310d9 | |||
| 8d92330ad6 | |||
| 44e76f741f | |||
| ecbced5c62 | |||
| e6773dafc1 | |||
| 7b8b7ed170 | |||
| c0997e1b9f | |||
| d4f862a44f | |||
| 64c983eb07 | |||
| e25222162c | |||
| e022687457 | |||
| 84db2f4d85 | |||
| ef64409a9a | |||
| f0b68ac31e | |||
| dba7bcf60a | |||
| 6c80bd9eb9 | |||
| 43baba1733 | |||
| 95a9e4b5ff | |||
| 9b0aa49d4d | |||
| c7b7bf1510 | |||
| d1805b1448 | |||
| 780937dae9 | |||
| 9b054eaa47 | |||
| 1584530615 | |||
| 100ca5caa6 | |||
| badd9ae289 | |||
| db6755a5bb | |||
| 738815170c | |||
| ef091e8c3a | |||
| 71b3810e9c | |||
| a1e62dae5e | |||
| 96c087d00f | |||
| 0568bf51f0 | |||
| e3bade4d1a | |||
| 633b0b0003 | |||
| 82adf48699 | |||
| 63be12b9fe | |||
| 33f7ed5129 | |||
| 79583c571c | |||
| a7e2c0b4dc | |||
| da48e1b392 | |||
| f535ece685 | |||
| 04e46c7cd1 | |||
| 1089aef00a | |||
| 0288d6ec89 | |||
| 8950d94b25 | |||
| bb6299cc01 | |||
| 2176f2a1c9 | |||
| 6a351bede1 | |||
| efc1aae0ae | |||
| df39e027fd | |||
| 64f05e4c97 | |||
| 30bc89ba93 | |||
| 841c8d69fc | |||
| aa780d2b8a | |||
| 59f15f1618 | |||
| b2f0a9f23d | |||
| 40cd07f3f4 | |||
| 99e7f84f79 | |||
| 3a5a32c65f | |||
| c2627f3a2a | |||
| 22b8d41aee | |||
| ee47ea2026 | |||
| 57622cef9a | |||
| 30ad3f5aa6 | |||
| 2c11f0be0f | |||
| c514ce8da3 | |||
| 65d421b619 | |||
| 7eeb0c4966 | |||
| 62897f7064 | |||
| e0619edabb | |||
| 4142f47861 | |||
| 7135f9a268 | |||
| a75a58b719 | |||
| 7b969fc167 | |||
| 78475b0686 | |||
| 4925d55e8f | |||
| 52d2306e17 | |||
| 5e5207d4f3 | |||
| 96d46c82fe | |||
| 86bf50db12 | |||
| 14778422e6 | |||
| 6a931273a6 | |||
| 5fab1ea436 | |||
| 5310ce38fd | |||
| 9a99ed655a | |||
| e9d0aa87bd | |||
| 7abbfbcfcc | |||
| a22b97643a | |||
| 80be614731 | |||
| 803ca677c1 | |||
| e25886180a | |||
| d5a41d41e9 | |||
| 68ff77f5b6 | |||
| 47e7469d65 | |||
| 2e43a34812 | |||
| 1b9afc961b | |||
| 2276584c90 | |||
| 05a0846e89 | |||
| b2947264e1 | |||
| 066927742e | |||
| 4616afced4 |
@@ -131,3 +131,4 @@ src/*.boltdata/
|
||||
src/umbraco.sln.ide/*
|
||||
build/UmbracoCms.*/
|
||||
src/.vs/
|
||||
src/Umbraco.Web.UI/umbraco/js/install.loader.js
|
||||
|
||||
@@ -7,9 +7,9 @@ The easiest way to get started is to run `build/build.bat` which will build both
|
||||
|
||||
If you're interested in making changes to Belle make sure to read the [Belle ReadMe file](src/Umbraco.Web.UI.Client/README.md). Note that you can always [download a nightly build](http://nightly.umbraco.org/umbraco%207.0.0/) so you don't have to build the code yourself.
|
||||
|
||||
## Watch a five minute introduction video ##
|
||||
## Watch a introduction video ##
|
||||
|
||||
[](http://umbraco.org/help-and-support/video-tutorials/getting-started/what-is-umbraco)
|
||||
[](https://umbraco.tv/videos/umbraco-v7/content-editor/basics/introduction/cms-explanation/)
|
||||
|
||||
## Umbraco - the simple, flexible and friendly ASP.NET CMS ##
|
||||
|
||||
|
||||
+2
-2
@@ -175,8 +175,8 @@
|
||||
<!-- Copy SQL CE -->
|
||||
<ItemGroup>
|
||||
<SQLCE4Files
|
||||
Include="..\src\packages\SqlServerCE.4.0.0.0\**\*.*"
|
||||
Exclude="..\src\packages\SqlServerCE.4.0.0.0\lib\**\*;..\src\packages\SqlServerCE.4.0.0.0\**\*.nu*"
|
||||
Include="..\src\packages\SqlServerCE.4.0.0.1\**\*.*"
|
||||
Exclude="..\src\packages\SqlServerCE.4.0.0.1\lib\**\*;..\src\packages\SqlServerCE.4.0.0.1\**\*.nu*"
|
||||
/>
|
||||
</ItemGroup>
|
||||
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
@ECHO OFF
|
||||
SETLOCAL
|
||||
|
||||
SET release=%1
|
||||
ECHO Installing Npm NuGet Package
|
||||
|
||||
@@ -11,12 +13,9 @@ ECHO Current folder: %CD%
|
||||
for /f "delims=" %%A in ('dir %nuGetFolder%node.js.* /b') do set "nodePath=%nuGetFolder%%%A\"
|
||||
for /f "delims=" %%A in ('dir %nuGetFolder%npm.js.* /b') do set "npmPath=%nuGetFolder%%%A\tools\"
|
||||
|
||||
ECHO Temporarily adding Npm and Node to path
|
||||
SET oldPath=%PATH%
|
||||
|
||||
path=%npmPath%;%nodePath%;%PATH%
|
||||
|
||||
ECHO %path%
|
||||
ECHO Adding Npm and Node to path
|
||||
REM SETLOCAL is on, so changes to the path not persist to the actual user's path
|
||||
PATH=%npmPath%;%nodePath%;%PATH%
|
||||
|
||||
SET buildFolder=%CD%
|
||||
|
||||
@@ -29,8 +28,5 @@ call npm install -g grunt-cli --quiet
|
||||
call npm install -g bower --quiet
|
||||
call grunt build --buildversion=%release%
|
||||
|
||||
ECHO Reset path to what it was before
|
||||
path=%oldPath%
|
||||
|
||||
ECHO Move back to the build folder
|
||||
CD %buildFolder%
|
||||
@@ -1,19 +1,19 @@
|
||||
@ECHO OFF
|
||||
SET oldPath=%PATH%
|
||||
SETLOCAL
|
||||
REM SETLOCAL is on, so changes to the path not persist to the actual user's path
|
||||
|
||||
git.exe 2> NUL
|
||||
if %ERRORLEVEL%==9009 GOTO :trydefaultpath
|
||||
GOTO :EOF
|
||||
|
||||
:trydefaultpath
|
||||
path=C:\Program Files (x86)\Git\cmd;%PATH%
|
||||
path=C:\Program Files (x86)\Git\cmd;C:\Program Files\Git\cmd;%PATH%
|
||||
git.exe 2> NUL
|
||||
if %ERRORLEVEL%==9009 GOTO :showerror
|
||||
GOTO :EOF
|
||||
|
||||
:showerror
|
||||
path=%oldPath%
|
||||
ECHO Git is not in your path and could not be found in C:\Program Files (x86)\Git\bin
|
||||
ECHO Git is not in your path and could not be found in C:\Program Files (x86)\Git\cmd nor in C:\Program Files\Git\cmd
|
||||
set /p install=" Do you want to install Git through Chocolatey [y/n]? " %=%
|
||||
if %install%==y (
|
||||
GOTO :installgit
|
||||
@@ -29,5 +29,4 @@ GOTO :EOF
|
||||
ECHO Installing Chocolatey first
|
||||
@powershell -NoProfile -ExecutionPolicy unrestricted -Command "iex ((new-object net.webclient).DownloadString('https://chocolatey.org/install.ps1'))" && SET PATH=%PATH%;%ALLUSERSPROFILE%\chocolatey\bin
|
||||
ECHO Installing Git through Chocolatey
|
||||
choco install git
|
||||
path=C:\Program Files (x86)\Git\cmd;%path%
|
||||
choco install git
|
||||
@@ -26,18 +26,19 @@
|
||||
<dependency id="HtmlAgilityPack" version="[1.4.9, 2.0.0)" />
|
||||
<dependency id="Lucene.Net" version="[2.9.4.1, 3.0.0.0)" />
|
||||
<dependency id="SharpZipLib" version="[0.86.0, 1.0.0)" />
|
||||
<dependency id="MySql.Data" version="[6.9.7, 7.0.0)" />
|
||||
<dependency id="MySql.Data" version="[6.9.8, 7.0.0)" />
|
||||
<dependency id="xmlrpcnet" version="[2.5.0, 3.0.0)" />
|
||||
<dependency id="ClientDependency" version="[1.8.4, 2.0.0)" />
|
||||
<dependency id="ClientDependency-Mvc5" version="[1.8.0, 2.0.0)" />
|
||||
<dependency id="AutoMapper" version="[3.0.0, 3.1.0)" />
|
||||
<dependency id="Newtonsoft.Json" version="[6.0.8, 7.0.0)" />
|
||||
<dependency id="Examine" version="[0.1.68, 1.0.0)" />
|
||||
<dependency id="ImageProcessor" version="[2.3.0, 3.0.0)" />
|
||||
<dependency id="ImageProcessor.Web" version="[4.4.0, 5.0.0)" />
|
||||
<dependency id="ImageProcessor" version="[2.3.2, 3.0.0)" />
|
||||
<dependency id="ImageProcessor.Web" version="[4.5.1, 5.0.0)" />
|
||||
<dependency id="semver" version="[1.1.2, 2.0.0)" />
|
||||
<dependency id="Microsoft.AspNet.WebHelpers" version="[3.2.3, 4.0.0)" />
|
||||
<dependency id="Microsoft.AspNet.WebPages.Data" version="[3.2.3, 4.0.0)" />
|
||||
<dependency id="UrlRewritingNet" version="[2.0.7, 3.0.0)" />
|
||||
</dependencies>
|
||||
</metadata>
|
||||
<files>
|
||||
@@ -72,7 +73,6 @@
|
||||
<file src="..\_BuildOutput\WebApp\bin\Umbraco.Web.UI.xml" target="lib\Umbraco.Web.UI.xml" />
|
||||
<file src="..\_BuildOutput\WebApp\bin\UmbracoExamine.dll" target="lib\UmbracoExamine.dll" />
|
||||
<file src="..\_BuildOutput\WebApp\bin\UmbracoExamine.xml" target="lib\UmbracoExamine.xml" />
|
||||
<file src="..\_BuildOutput\WebApp\bin\UrlRewritingNet.UrlRewriter.dll" target="lib\UrlRewritingNet.UrlRewriter.dll" />
|
||||
<file src="tools\install.core.ps1" target="tools\install.ps1" />
|
||||
|
||||
<!-- Added to be able to produce a symbols package -->
|
||||
|
||||
@@ -1,6 +1,17 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<Target Name="CopyUmbracoFilesToWebRootForDeploy" AfterTargets="CopyAllFilesToSingleFolderForMsdeploy">
|
||||
<PropertyGroup>
|
||||
<UmbracoFilesFolder>$(MSBuildThisFileDirectory)..\UmbracoFiles\</UmbracoFilesFolder>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<UmbracoFiles Include="$(UmbracoFilesFolder)**\*" />
|
||||
</ItemGroup>
|
||||
<Copy SourceFiles="%(UmbracoFiles.FullPath)" DestinationFiles="$(_PackageTempDir)\%(RecursiveDir)%(Filename)%(Extension)" Condition="!Exists('%(RecursiveDir)%(Filename)%(Extension)')" />
|
||||
</Target>
|
||||
<Target Name="CopyUmbracoFilesToWebRoot" BeforeTargets="AfterBuild">
|
||||
<!-- This copies the files listed in `AddUmbracoFilesToOutput` to the webroot during NuGet install and should -->
|
||||
<!-- not be altered to support automated builds, use `CopyUmbracoFilesToWebRootForDeploy` for that instead -->
|
||||
<PropertyGroup>
|
||||
<UmbracoFilesFolder>$(MSBuildThisFileDirectory)..\UmbracoFiles\</UmbracoFilesFolder>
|
||||
</PropertyGroup>
|
||||
@@ -47,4 +58,4 @@
|
||||
</FilesForPackagingFromProject>
|
||||
</ItemGroup>
|
||||
</Target>
|
||||
</Project>
|
||||
</Project>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<?xml version="1.0"?>
|
||||
<dashBoard xmlns:xdt="http://schemas.microsoft.com/XML-Document-Transform">
|
||||
<section alias="StartupSettingsDashboardSection" xdt:Locator="Match(alias)">
|
||||
<section alias="StartupSettingsDashboardSection" xdt:Locator="Match(alias)" xdt:Transform="InsertIfMissing">
|
||||
<tab caption="Get Started" xdt:Locator="Match(caption)" xdt:Transform="Remove" />
|
||||
<tab caption="Welcome" xdt:Locator="Match(caption)" xdt:Transform="InsertIfMissing">
|
||||
<control showOnce="true" addPanel="true" panelCaption="" xdt:Transform="InsertIfMissing">
|
||||
@@ -20,7 +20,7 @@
|
||||
</tab>
|
||||
</section>
|
||||
|
||||
<section alias="StartupDeveloperDashboardSection" xdt:Locator="Match(alias)">
|
||||
<section alias="StartupDeveloperDashboardSection" xdt:Locator="Match(alias)" xdt:Transform="InsertIfMissing">
|
||||
<tab caption="Get Started" xdt:Locator="Match(caption)" xdt:Transform="Replace">
|
||||
<control showOnce="true" addPanel="true" panelCaption="">
|
||||
views/dashboard/developer/developerdashboardvideos.html
|
||||
@@ -29,7 +29,7 @@
|
||||
<tab caption="Examine Management" xdt:Locator="Match(caption)" xdt:Transform="Remove" />
|
||||
</section>
|
||||
|
||||
<section alias="StartupDeveloperDashboardSection" xdt:Locator="Match(alias)">
|
||||
<section alias="StartupDeveloperDashboardSection" xdt:Locator="Match(alias)" xdt:Transform="InsertIfMissing">
|
||||
<tab caption="Examine Management" xdt:Locator="Match(caption)" xdt:Transform="InsertIfMissing">
|
||||
<control>
|
||||
views/dashboard/developer/examinemanagement.html
|
||||
@@ -39,10 +39,10 @@
|
||||
<control>
|
||||
views/dashboard/developer/xmldataintegrityreport.html
|
||||
</control>
|
||||
</tab>
|
||||
</tab>
|
||||
</section>
|
||||
|
||||
<section alias="StartupMediaDashboardSection" xdt:Locator="Match(alias)">
|
||||
<section alias="StartupMediaDashboardSection" xdt:Locator="Match(alias)" xdt:Transform="InsertIfMissing">
|
||||
<tab caption="Get Started" xdt:Locator="Match(caption)" xdt:Transform="Remove" />
|
||||
<tab caption="Content" xdt:Locator="Match(caption)" xdt:Transform="InsertIfMissing" />
|
||||
<tab caption="Content" xdt:Locator="Match(caption)" xdt:Transform="Replace">
|
||||
@@ -52,7 +52,7 @@
|
||||
</tab>
|
||||
</section>
|
||||
|
||||
<section alias="StartupDashboardSection" xdt:Locator="Match(alias)">
|
||||
<section alias="StartupDashboardSection" xdt:Locator="Match(alias)" xdt:Transform="InsertIfMissing">
|
||||
<tab caption="Get Started" xdt:Locator="Match(caption)" xdt:Transform="Remove" />
|
||||
<tab caption="Get Started" xdt:Transform="Insert">
|
||||
<access>
|
||||
@@ -62,17 +62,10 @@
|
||||
views/dashboard/default/startupdashboardintro.html
|
||||
</control>
|
||||
</tab>
|
||||
<tab caption="Change password" xdt:Locator="Match(caption)" xdt:Transform="Remove" />
|
||||
<tab caption="Change Password" xdt:Locator="Match(caption)" xdt:Transform="Remove" />
|
||||
<tab caption="Change Password" xdt:Transform="Insert">
|
||||
<control showOnce="false" addPanel="false" panelCaption="">
|
||||
views/dashboard/ChangePassword.html
|
||||
</control>
|
||||
</tab>
|
||||
<tab caption="Last Edits" xdt:Locator="Match(caption)" xdt:Transform="Remove" />
|
||||
</section>
|
||||
|
||||
<section alias="StartupMemberDashboardSection" xdt:Locator="Match(alias)">
|
||||
<section alias="StartupMemberDashboardSection" xdt:Locator="Match(alias)" xdt:Transform="InsertIfMissing">
|
||||
<tab caption="Get Started" xdt:Locator="Match(caption)" xdt:Transform="Remove" />
|
||||
<tab caption="Get Started" xdt:Transform="Insert">
|
||||
<control showOnce="true" addPanel="true" panelCaption="">
|
||||
|
||||
@@ -10,9 +10,6 @@
|
||||
|
||||
Don't forget to build!
|
||||
|
||||
When upgrading your website using NuGet you should answer "No" to the questions to overwrite the Web.config
|
||||
file (and config files in the config folder).
|
||||
|
||||
We've done our best to transform your configuration files but in case something is not quite right: remember we
|
||||
backed up your files in App_Data\NuGetBackup so you can find the original files before they were transformed.
|
||||
|
||||
|
||||
@@ -48,8 +48,13 @@
|
||||
<system.web xdt:Transform="InsertIfMissing" />
|
||||
<system.web>
|
||||
<siteMap xdt:Transform="Remove" />
|
||||
<siteMap xdt:Transform="InsertIfMissing">
|
||||
<providers xdt:Transform="InsertIfMissing">
|
||||
<remove name="MySqlSiteMapProvider" xdt:Transform="InsertIfMissing" />
|
||||
</providers>
|
||||
</siteMap>
|
||||
<httpRuntime xdt:Transform="InsertIfMissing" />
|
||||
<httpRuntime targetFramework="4.5" xdt:Transform="SetAttributes(targetFramework)" />
|
||||
<httpRuntime targetFramework="4.5" maxRequestLength="51200" fcnMode="Single" xdt:Transform="SetAttributes(targetFramework,fcnMode,maxRequestLength)" />
|
||||
<membership defaultProvider="DefaultMembershipProvider" xdt:Locator="Match(defaultProvider)" xdt:Transform="Remove" />
|
||||
<roleManager defaultProvider="DefaultRoleProvider" xdt:Locator="Match(defaultProvider)" xdt:Transform="Remove"/>
|
||||
<profile defaultProvider="DefaultProfileProvider" xdt:Locator="Match(defaultProvider)" xdt:Transform="Remove"/>>
|
||||
@@ -76,6 +81,10 @@
|
||||
</compilation>
|
||||
|
||||
<compilation>
|
||||
<assemblies>
|
||||
<remove assembly="System.Web.Http" xdt:Locator="Match(assembly)" xdt:Transform="Remove" />
|
||||
<remove assembly="System.Net.Http" xdt:Locator="Match(assembly)" xdt:Transform="Remove" />
|
||||
</assemblies>
|
||||
<assemblies>
|
||||
<remove assembly="System.Web.Http" xdt:Locator="Match(assembly)" xdt:Transform="InsertBefore(/configuration/system.web/compilation/assemblies/add)" />
|
||||
<remove assembly="System.Net.Http" xdt:Locator="Match(assembly)" xdt:Transform="InsertBefore(/configuration/system.web/compilation/assemblies/add)" />
|
||||
@@ -131,6 +140,13 @@
|
||||
<remove name="SpellChecker" xdt:Locator="Match(name)" xdt:Transform="Remove" />
|
||||
<add name="SpellChecker" xdt:Locator="Match(name)" xdt:Transform="Remove" />
|
||||
</handlers>
|
||||
|
||||
<security xdt:Transform="InsertIfMissing">
|
||||
<requestFiltering xdt:Transform="InsertIfMissing">
|
||||
<requestLimits maxAllowedContentLength="52428800" xdt:Transform="InsertIfMissing" />
|
||||
</requestFiltering>
|
||||
</security>
|
||||
|
||||
</system.webServer>
|
||||
|
||||
<system.codedom xdt:Transform="Remove" />
|
||||
@@ -142,20 +158,26 @@
|
||||
|
||||
<runtime>
|
||||
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
|
||||
<dependentAssembly xdt:Locator="Condition(./_defaultNamespace:assemblyIdentity/@name='HtmlAgilityPack')" xdt:Transform="Remove" />
|
||||
<dependentAssembly xdt:Locator="Condition(./_defaultNamespace:assemblyIdentity/@name='System.Net.Http')" xdt:Transform="Remove" />
|
||||
<dependentAssembly xdt:Locator="Condition(./_defaultNamespace:assemblyIdentity/@name='Newtonsoft.Json')" xdt:Transform="Remove" />
|
||||
<dependentAssembly xdt:Locator="Condition(./_defaultNamespace:assemblyIdentity/@name='System.Web.Mvc')" xdt:Transform="Remove" />
|
||||
<dependentAssembly xdt:Locator="Condition(./_defaultNamespace:assemblyIdentity/@name='System.Web.WebPages.Razor')" xdt:Transform="Remove" />
|
||||
<dependentAssembly xdt:Locator="Condition(./_defaultNamespace:assemblyIdentity/@name='System.Web.Http')" xdt:Transform="Remove" />
|
||||
<dependentAssembly xdt:Locator="Condition(./_defaultNamespace:assemblyIdentity/@name='Microsoft.Owin')" xdt:Transform="Remove" />
|
||||
<dependentAssembly xdt:Locator="Condition(./_defaultNamespace:assemblyIdentity/@name='Microsoft.Owin.Security.OAuth')" xdt:Transform="Remove" />
|
||||
<dependentAssembly xdt:Locator="Condition(./_defaultNamespace:assemblyIdentity/@name='Microsoft.Owin.Security')" xdt:Transform="Remove" />
|
||||
<dependentAssembly xdt:Locator="Condition(./_defaultNamespace:assemblyIdentity/@name='Microsoft.Owin.Security.Cookies')" xdt:Transform="Remove" />
|
||||
<dependentAssembly xdt:Locator="Condition(./_defaultNamespace:assemblyIdentity/@name='System.Net.Http.Formatting')" xdt:Transform="Remove" />
|
||||
<dependentAssembly xdt:Locator="Condition(./_defaultNamespace:assemblyIdentity/@name='System.Net.Http.Formatting')" xdt:Transform="Remove" />
|
||||
</assemblyBinding>
|
||||
</runtime>
|
||||
|
||||
<runtime>
|
||||
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
|
||||
<dependentAssembly>
|
||||
<assemblyIdentity name="HtmlAgilityPack" publicKeyToken="bd319b19eaf3b43a" culture="neutral" />
|
||||
<bindingRedirect oldVersion="0.0.0.0-1.4.9.0" newVersion="1.4.9.0" />
|
||||
</dependentAssembly>
|
||||
<dependentAssembly xdt:Transform="Insert">
|
||||
<assemblyIdentity name="System.Net.Http" publicKeyToken="b03f5f7f11d50a3a" culture="neutral"/>
|
||||
<bindingRedirect oldVersion="0.0.0.0-4.0.0.0" newVersion="4.0.0.0"/>
|
||||
@@ -168,27 +190,31 @@
|
||||
<assemblyIdentity name="System.Web.Mvc" publicKeyToken="31bf3856ad364e35" culture="neutral" />
|
||||
<bindingRedirect oldVersion="0.0.0.0-4.0.0.1" newVersion="4.0.0.1" />
|
||||
</dependentAssembly>
|
||||
<dependentAssembly xdt:Transform="Insert">
|
||||
<assemblyIdentity name="System.Web.WebPages.Razor" publicKeyToken="31bf3856ad364e35" />
|
||||
<bindingRedirect oldVersion="0.0.0.0-3.0.0.0" newVersion="3.0.0.0" />
|
||||
</dependentAssembly>
|
||||
<dependentAssembly xdt:Transform="Insert">
|
||||
<assemblyIdentity name="System.Web.Http" publicKeyToken="31bf3856ad364e35" culture="neutral" />
|
||||
<bindingRedirect oldVersion="0.0.0.0-5.2.3.0" newVersion="5.2.3.0" />
|
||||
</dependentAssembly>
|
||||
<dependentAssembly>
|
||||
<dependentAssembly xdt:Transform="Insert">
|
||||
<assemblyIdentity name="Microsoft.Owin" publicKeyToken="31bf3856ad364e35" culture="neutral" />
|
||||
<bindingRedirect oldVersion="0.0.0.0-3.0.1.0" newVersion="3.0.1.0" />
|
||||
</dependentAssembly>
|
||||
<dependentAssembly>
|
||||
<dependentAssembly xdt:Transform="Insert">
|
||||
<assemblyIdentity name="Microsoft.Owin.Security.OAuth" publicKeyToken="31bf3856ad364e35" culture="neutral" />
|
||||
<bindingRedirect oldVersion="0.0.0.0-3.0.1.0" newVersion="3.0.1.0" />
|
||||
</dependentAssembly>
|
||||
<dependentAssembly>
|
||||
<dependentAssembly xdt:Transform="Insert">
|
||||
<assemblyIdentity name="Microsoft.Owin.Security" publicKeyToken="31bf3856ad364e35" culture="neutral" />
|
||||
<bindingRedirect oldVersion="0.0.0.0-3.0.1.0" newVersion="3.0.1.0" />
|
||||
</dependentAssembly>
|
||||
<dependentAssembly>
|
||||
<dependentAssembly xdt:Transform="Insert">
|
||||
<assemblyIdentity name="Microsoft.Owin.Security.Cookies" publicKeyToken="31bf3856ad364e35" culture="neutral" />
|
||||
<bindingRedirect oldVersion="0.0.0.0-3.0.1.0" newVersion="3.0.1.0" />
|
||||
</dependentAssembly>
|
||||
<dependentAssembly>
|
||||
<dependentAssembly xdt:Transform="Insert">
|
||||
<assemblyIdentity name="System.Net.Http.Formatting" publicKeyToken="31bf3856ad364e35" culture="neutral" />
|
||||
<bindingRedirect oldVersion="0.0.0.0-5.2.3.0" newVersion="5.2.3.0" />
|
||||
</dependentAssembly>
|
||||
|
||||
@@ -57,13 +57,25 @@ if ($project) {
|
||||
if(Test-Path $umbracoBinFolder\ImageProcessor.dll) { Remove-Item $umbracoBinFolder\ImageProcessor.dll -Force -Confirm:$false }
|
||||
if(Test-Path $umbracoBinFolder\ImageProcessor.Web.dll) { Remove-Item $umbracoBinFolder\ImageProcessor.Web.dll -Force -Confirm:$false }
|
||||
if(Test-Path $umbracoBinFolder\Lucene.Net.dll) { Remove-Item $umbracoBinFolder\Lucene.Net.dll -Force -Confirm:$false }
|
||||
if(Test-Path $umbracoBinFolder\Microsoft.AspNet.Identity.Core.dll) { Remove-Item $umbracoBinFolder\Microsoft.AspNet.Identity.Core.dll -Force -Confirm:$false }
|
||||
if(Test-Path $umbracoBinFolder\Microsoft.AspNet.Identity.Owin.dll) { Remove-Item $umbracoBinFolder\Microsoft.AspNet.Identity.Owin.dll -Force -Confirm:$false }
|
||||
if(Test-Path $umbracoBinFolder\Microsoft.Owin.dll) { Remove-Item $umbracoBinFolder\Microsoft.Owin.dll -Force -Confirm:$false }
|
||||
if(Test-Path $umbracoBinFolder\Microsoft.Owin.Host.SystemWeb.dll) { Remove-Item $umbracoBinFolder\Microsoft.Owin.Host.SystemWeb.dll -Force -Confirm:$false }
|
||||
if(Test-Path $umbracoBinFolder\Microsoft.Owin.Security.dll) { Remove-Item $umbracoBinFolder\Microsoft.Owin.Security.dll -Force -Confirm:$false }
|
||||
if(Test-Path $umbracoBinFolder\Microsoft.Owin.Security.Cookies.dll) { Remove-Item $umbracoBinFolder\Microsoft.Owin.Security.Cookies.dll -Force -Confirm:$false }
|
||||
if(Test-Path $umbracoBinFolder\Microsoft.Owin.Security.OAuth.dll) { Remove-Item $umbracoBinFolder\Microsoft.Owin.Security.OAuth.dll -Force -Confirm:$false }
|
||||
if(Test-Path $umbracoBinFolder\Microsoft.Web.Infrastructure.dll) { Remove-Item $umbracoBinFolder\Microsoft.Web.Infrastructure.dll -Force -Confirm:$false }
|
||||
if(Test-Path $umbracoBinFolder\Microsoft.Web.Helpers.dll) { Remove-Item $umbracoBinFolder\Microsoft.Web.Helpers.dll -Force -Confirm:$false }
|
||||
if(Test-Path $umbracoBinFolder\Microsoft.Web.Mvc.FixedDisplayModes.dll) { Remove-Item $umbracoBinFolder\Microsoft.Web.Mvc.FixedDisplayModes.dll -Force -Confirm:$false }
|
||||
if(Test-Path $umbracoBinFolder\MiniProfiler.dll) { Remove-Item $umbracoBinFolder\MiniProfiler.dll -Force -Confirm:$false }
|
||||
if(Test-Path $umbracoBinFolder\MySql.Data.dll) { Remove-Item $umbracoBinFolder\MySql.Data.dll -Force -Confirm:$false }
|
||||
if(Test-Path $umbracoBinFolder\Newtonsoft.Json.dll) { Remove-Item $umbracoBinFolder\Newtonsoft.Json.dll -Force -Confirm:$false }
|
||||
if(Test-Path $umbracoBinFolder\Owin.dll) { Remove-Item $umbracoBinFolder\Owin.dll -Force -Confirm:$false }
|
||||
if(Test-Path $umbracoBinFolder\Semver.dll) { Remove-Item $umbracoBinFolder\Semver.dll -Force -Confirm:$false }
|
||||
if(Test-Path $umbracoBinFolder\System.Net.Http.Extensions.dll) { Remove-Item $umbracoBinFolder\System.Net.Http.Extensions.dll -Force -Confirm:$false }
|
||||
if(Test-Path $umbracoBinFolder\System.Net.Http.Formatting.dll) { Remove-Item $umbracoBinFolder\System.Net.Http.Formatting.dll -Force -Confirm:$false }
|
||||
if(Test-Path $umbracoBinFolder\System.Net.Http.Primitives.dll) { Remove-Item $umbracoBinFolder\System.Net.Http.Primitives.dll -Force -Confirm:$false }
|
||||
if(Test-Path $umbracoBinFolder\System.Web.Helpers.dll) { Remove-Item $umbracoBinFolder\System.Web.Helpers.dll -Force -Confirm:$false }
|
||||
if(Test-Path $umbracoBinFolder\System.Web.Http.dll) { Remove-Item $umbracoBinFolder\System.Web.Http.dll -Force -Confirm:$false }
|
||||
if(Test-Path $umbracoBinFolder\System.Web.Http.WebHost.dll) { Remove-Item $umbracoBinFolder\System.Web.Http.WebHost.dll -Force -Confirm:$false }
|
||||
if(Test-Path $umbracoBinFolder\System.Web.Mvc.dll) { Remove-Item $umbracoBinFolder\System.Web.Mvc.dll -Force -Confirm:$false }
|
||||
|
||||
@@ -45,9 +45,6 @@ del ..\src\Umbraco.Web.UI\UserControls\*.*
|
||||
echo Removing masterpage files
|
||||
del ..\src\Umbraco.Web.UI\masterpages\*.*
|
||||
|
||||
echo Removing view files
|
||||
del ..\src\Umbraco.Web.UI\Views\*.*
|
||||
|
||||
echo Removing razor files
|
||||
del ..\src\Umbraco.Web.UI\macroScripts\*.*
|
||||
|
||||
@@ -104,7 +101,9 @@ echo Removing user control files
|
||||
FOR %%A IN (..\src\Umbraco.Web.UI\usercontrols\*.*) DO DEL %%A
|
||||
|
||||
echo Removing view files
|
||||
FOR %%A IN (..\src\Umbraco.Web.UI\Views\*.*) DO DEL %%A
|
||||
ATTRIB +H ..\src\Umbraco.Web.UI\Views\Partials\Grid\*.cshtml /S
|
||||
FOR %%A IN (..\src\Umbraco.Web.UI\Views\) DO DEL /Q /S *.cshtml -H
|
||||
ATTRIB -H ..\src\Umbraco.Web.UI\Views\Partials\Grid\*.cshtml /S
|
||||
|
||||
echo Removing razor files
|
||||
FOR %%A IN (..\src\Umbraco.Web.UI\macroScripts\*.*) DO DEL %%A
|
||||
|
||||
@@ -54,9 +54,6 @@ del ..\src\Umbraco.Web.UI\UserControls\*.*
|
||||
echo Removing masterpage files
|
||||
del ..\src\Umbraco.Web.UI\masterpages\*.*
|
||||
|
||||
echo Removing view files
|
||||
del ..\src\Umbraco.Web.UI\Views\*.*
|
||||
|
||||
echo Removing razor files
|
||||
del ..\src\Umbraco.Web.UI\macroScripts\*.*
|
||||
|
||||
@@ -122,7 +119,9 @@ echo Removing user control files
|
||||
FOR %%A IN (..\src\Umbraco.Web.UI\usercontrols\*.*) DO DEL %%A
|
||||
|
||||
echo Removing view files
|
||||
FOR %%A IN (..\src\Umbraco.Web.UI\Views\*.*) DO DEL %%A
|
||||
ATTRIB +H ..\src\Umbraco.Web.UI\Views\Partials\Grid\*.cshtml /S
|
||||
FOR %%A IN (..\src\Umbraco.Web.UI\Views\) DO DEL /Q /S *.cshtml -H
|
||||
ATTRIB -H ..\src\Umbraco.Web.UI\Views\Partials\Grid\*.cshtml /S
|
||||
|
||||
echo Removing razor files
|
||||
FOR %%A IN (..\src\Umbraco.Web.UI\macroScripts\*.*) DO DEL %%A
|
||||
@@ -151,6 +150,9 @@ rmdir "..\src\Umbraco.Web.UI\usercontrols\umbracoContour\" /S /Q
|
||||
echo Start with a clean web.config
|
||||
copy ..\src\Umbraco.Web.UI\web.Template.config ..\src\Umbraco.Web.UI\web.config /Y
|
||||
|
||||
echo Start with a clean web.config
|
||||
copy ..\src\Umbraco.Web.UI\web.Template.config ..\src\Umbraco.Web.UI\web.config /Y
|
||||
|
||||
echo "Umbraco install reverted to clean install"
|
||||
pause
|
||||
exit
|
||||
|
||||
@@ -1,2 +1,2 @@
|
||||
# Usage: on line 2 put the release version, on line 3 put the version comment (example: beta)
|
||||
7.3.0
|
||||
7.3.8
|
||||
@@ -47,13 +47,15 @@
|
||||
<ItemGroup>
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.Core" />
|
||||
<Reference Include="System.Data.SqlServerCe, Version=4.0.0.0, Culture=neutral, PublicKeyToken=89845dcd8080cc91, processorArchitecture=MSIL">
|
||||
<Private>True</Private>
|
||||
<HintPath>..\packages\SqlServerCE.4.0.0.0\lib\System.Data.SqlServerCe.dll</HintPath>
|
||||
<Reference Include="System.Data.SqlServerCe, Version=4.0.0.1, Culture=neutral, PublicKeyToken=89845dcd8080cc91, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\SqlServerCE.4.0.0.1\lib\System.Data.SqlServerCe.dll</HintPath>
|
||||
<SpecificVersion>False</SpecificVersion>
|
||||
<Private>False</Private>
|
||||
</Reference>
|
||||
<Reference Include="System.Data.SqlServerCe.Entity, Version=4.0.0.0, Culture=neutral, PublicKeyToken=89845dcd8080cc91, processorArchitecture=MSIL">
|
||||
<Private>True</Private>
|
||||
<HintPath>..\packages\SqlServerCE.4.0.0.0\lib\System.Data.SqlServerCe.Entity.dll</HintPath>
|
||||
<Reference Include="System.Data.SqlServerCe.Entity, Version=4.0.0.1, Culture=neutral, PublicKeyToken=89845dcd8080cc91, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\SqlServerCE.4.0.0.1\lib\System.Data.SqlServerCe.Entity.dll</HintPath>
|
||||
<SpecificVersion>False</SpecificVersion>
|
||||
<Private>False</Private>
|
||||
</Reference>
|
||||
<Reference Include="System.Xml.Linq" />
|
||||
<Reference Include="System.Data.DataSetExtensions" />
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<packages>
|
||||
<package id="SqlServerCE" version="4.0.0.0" targetFramework="net40" />
|
||||
<package id="SqlServerCE" version="4.0.0.1" targetFramework="net45" />
|
||||
</packages>
|
||||
+3
-3
@@ -2,7 +2,7 @@
|
||||
using System.Resources;
|
||||
|
||||
[assembly: AssemblyCompany("Umbraco")]
|
||||
[assembly: AssemblyCopyright("Copyright © Umbraco 2015")]
|
||||
[assembly: AssemblyCopyright("Copyright © Umbraco 2016")]
|
||||
[assembly: AssemblyTrademark("")]
|
||||
[assembly: AssemblyCulture("")]
|
||||
|
||||
@@ -11,5 +11,5 @@ using System.Resources;
|
||||
|
||||
[assembly: AssemblyVersion("1.0.*")]
|
||||
|
||||
[assembly: AssemblyFileVersion("7.3.0")]
|
||||
[assembly: AssemblyInformationalVersion("7.3.0")]
|
||||
[assembly: AssemblyFileVersion("7.3.8")]
|
||||
[assembly: AssemblyInformationalVersion("7.3.8")]
|
||||
@@ -11,7 +11,7 @@ namespace Umbraco.Core
|
||||
/// <summary>
|
||||
/// A resolver to return all IAction objects
|
||||
/// </summary>
|
||||
internal sealed class ActionsResolver : LazyManyObjectsResolverBase<ActionsResolver, IAction>
|
||||
public sealed class ActionsResolver : LazyManyObjectsResolverBase<ActionsResolver, IAction>
|
||||
{
|
||||
/// <summary>
|
||||
/// Constructor
|
||||
|
||||
@@ -283,7 +283,12 @@ namespace Umbraco.Core
|
||||
{
|
||||
var configStatus = ConfigurationStatus;
|
||||
var currentVersion = UmbracoVersion.GetSemanticVersion();
|
||||
var ok = configStatus == currentVersion;
|
||||
|
||||
var ok =
|
||||
//we are not configured if this is null
|
||||
string.IsNullOrWhiteSpace(configStatus) == false
|
||||
//they must match
|
||||
&& configStatus == currentVersion;
|
||||
|
||||
if (ok)
|
||||
{
|
||||
@@ -308,8 +313,9 @@ namespace Umbraco.Core
|
||||
|
||||
return ok;
|
||||
}
|
||||
catch
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogHelper.Error<ApplicationContext>("Error determining if application is configured, returning false", ex);
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -400,7 +406,8 @@ namespace Umbraco.Core
|
||||
//clear the cache
|
||||
if (ApplicationCache != null)
|
||||
{
|
||||
ApplicationCache.ClearAllCache();
|
||||
ApplicationCache.RuntimeCache.ClearAllCache();
|
||||
ApplicationCache.IsolatedRuntimeCache.ClearAllCaches();
|
||||
}
|
||||
//reset all resolvers
|
||||
ResolverCollection.ResetAll();
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using System.Runtime.ConstrainedExecution;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
@@ -117,38 +118,55 @@ namespace Umbraco.Core
|
||||
private class NamedSemaphoreReleaser : CriticalFinalizerObject, IDisposable
|
||||
{
|
||||
private readonly Semaphore _semaphore;
|
||||
private GCHandle _handle;
|
||||
|
||||
internal NamedSemaphoreReleaser(Semaphore semaphore)
|
||||
{
|
||||
_semaphore = semaphore;
|
||||
_handle = GCHandle.Alloc(_semaphore);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Dispose(true);
|
||||
GC.SuppressFinalize(this);
|
||||
GC.SuppressFinalize(this); // finalize will not run
|
||||
}
|
||||
|
||||
private void Dispose(bool disposing)
|
||||
{
|
||||
// critical
|
||||
_handle.Free();
|
||||
_semaphore.Release();
|
||||
_semaphore.Dispose();
|
||||
}
|
||||
|
||||
// we WANT to release the semaphore because it's a system object
|
||||
// ie a critical non-managed resource - so we inherit from CriticalFinalizerObject
|
||||
// which means that the finalizer "should" run in all situations
|
||||
// we WANT to release the semaphore because it's a system object, ie a critical
|
||||
// non-managed resource - and if it is not released then noone else can acquire
|
||||
// the lock - so we inherit from CriticalFinalizerObject which means that the
|
||||
// finalizer "should" run in all situations - there is always a chance that it
|
||||
// does not run and the semaphore remains "acquired" but then chances are the
|
||||
// whole process (w3wp.exe...) is going down, at which point the semaphore will
|
||||
// be destroyed by Windows.
|
||||
|
||||
// however... that can fail with System.ObjectDisposedException because the
|
||||
// underlying handle was closed... because we cannot guarantee that the semaphore
|
||||
// is not gone already... unless we get a GCHandle = GCHandle.Alloc(_semaphore);
|
||||
// which should keep it around and then we free the handle?
|
||||
// however, the semaphore is a managed object, and so when the finalizer runs it
|
||||
// might have been finalized already, and then we get a, ObjectDisposedException
|
||||
// in the finalizer - which is bad.
|
||||
|
||||
// so... I'm not sure this is safe really...
|
||||
// in order to prevent this we do two things
|
||||
// - use a GCHandler to ensure the semaphore is still there when the finalizer
|
||||
// runs, so we can actually release it
|
||||
// - wrap the finalizer code in a try...catch to make sure it never throws
|
||||
|
||||
~NamedSemaphoreReleaser()
|
||||
{
|
||||
Dispose(false);
|
||||
try
|
||||
{
|
||||
Dispose(false);
|
||||
}
|
||||
catch
|
||||
{
|
||||
// we do NOT want the finalizer to throw - never ever
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
using System;
|
||||
using System.ComponentModel;
|
||||
using Umbraco.Core.CodeAnnotations;
|
||||
|
||||
namespace Umbraco.Core.Cache
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// Constants storing cache keys used in caching
|
||||
/// </summary>
|
||||
@@ -12,52 +13,78 @@ namespace Umbraco.Core.Cache
|
||||
public const string ApplicationsCacheKey = "ApplicationCache";
|
||||
|
||||
[Obsolete("This is no longer used and will be removed from the codebase in the future")]
|
||||
[EditorBrowsable(EditorBrowsableState.Never)]
|
||||
public const string UserTypeCacheKey = "UserTypeCache";
|
||||
|
||||
[Obsolete("This is no longer used and will be removed from the codebase in the future - it is referenced but no cache is stored against this key")]
|
||||
[EditorBrowsable(EditorBrowsableState.Never)]
|
||||
public const string ContentItemCacheKey = "contentItem";
|
||||
|
||||
[UmbracoWillObsolete("This cache key is only used for the legacy 'library' caching, remove in v8")]
|
||||
public const string MediaCacheKey = "UL_GetMedia";
|
||||
|
||||
public const string MacroXsltCacheKey = "macroXslt_";
|
||||
|
||||
[UmbracoWillObsolete("This cache key is only used for legacy business logic caching, remove in v8")]
|
||||
public const string MacroCacheKey = "UmbracoMacroCache";
|
||||
|
||||
public const string MacroHtmlCacheKey = "macroHtml_";
|
||||
public const string MacroControlCacheKey = "macroControl_";
|
||||
public const string MacroHtmlDateAddedCacheKey = "macroHtml_DateAdded_";
|
||||
public const string MacroControlDateAddedCacheKey = "macroControl_DateAdded_";
|
||||
|
||||
[UmbracoWillObsolete("This cache key is only used for legacy 'library' member caching, remove in v8")]
|
||||
public const string MemberLibraryCacheKey = "UL_GetMember";
|
||||
|
||||
[UmbracoWillObsolete("This cache key is only used for legacy business logic caching, remove in v8")]
|
||||
public const string MemberBusinessLogicCacheKey = "MemberCacheItem_";
|
||||
|
||||
|
||||
[UmbracoWillObsolete("This cache key is only used for legacy template business logic caching, remove in v8")]
|
||||
public const string TemplateFrontEndCacheKey = "template";
|
||||
|
||||
[Obsolete("This is no longer used and will be removed from the codebase in the future")]
|
||||
[EditorBrowsable(EditorBrowsableState.Never)]
|
||||
public const string TemplateBusinessLogicCacheKey = "UmbracoTemplateCache";
|
||||
|
||||
[Obsolete("This is no longer used and will be removed from the codebase in the future")]
|
||||
[EditorBrowsable(EditorBrowsableState.Never)]
|
||||
public const string UserContextCacheKey = "UmbracoUserContext";
|
||||
|
||||
public const string UserContextTimeoutCacheKey = "UmbracoUserContextTimeout";
|
||||
|
||||
[Obsolete("This is no longer used and will be removed from the codebase in the future")]
|
||||
[EditorBrowsable(EditorBrowsableState.Never)]
|
||||
public const string UserCacheKey = "UmbracoUser";
|
||||
|
||||
public const string UserPermissionsCacheKey = "UmbracoUserPermissions";
|
||||
|
||||
[UmbracoWillObsolete("This cache key is only used for legacy business logic caching, remove in v8")]
|
||||
public const string ContentTypeCacheKey = "UmbracoContentType";
|
||||
|
||||
[UmbracoWillObsolete("This cache key is only used for legacy business logic caching, remove in v8")]
|
||||
public const string ContentTypePropertiesCacheKey = "ContentType_PropertyTypes_Content:";
|
||||
|
||||
[UmbracoWillObsolete("This cache key is only used for legacy business logic caching, remove in v8")]
|
||||
public const string PropertyTypeCacheKey = "UmbracoPropertyTypeCache";
|
||||
|
||||
[Obsolete("This is no longer used and will be removed from the codebase in the future")]
|
||||
[EditorBrowsable(EditorBrowsableState.Never)]
|
||||
public const string LanguageCacheKey = "UmbracoLanguageCache";
|
||||
|
||||
[Obsolete("This is no longer used and will be removed from the codebase in the future")]
|
||||
[EditorBrowsable(EditorBrowsableState.Never)]
|
||||
public const string DomainCacheKey = "UmbracoDomainList";
|
||||
|
||||
[Obsolete("This is no longer used and will be removed from the codebase in the future")]
|
||||
[EditorBrowsable(EditorBrowsableState.Never)]
|
||||
public const string StylesheetCacheKey = "UmbracoStylesheet";
|
||||
|
||||
[Obsolete("This is no longer used and will be removed from the codebase in the future")]
|
||||
[EditorBrowsable(EditorBrowsableState.Never)]
|
||||
public const string StylesheetPropertyCacheKey = "UmbracoStylesheetProperty";
|
||||
|
||||
[Obsolete("This is no longer used and will be removed from the codebase in the future")]
|
||||
[EditorBrowsable(EditorBrowsableState.Never)]
|
||||
public const string DataTypeCacheKey = "UmbracoDataTypeDefinition";
|
||||
public const string DataTypePreValuesCacheKey = "UmbracoPreVal";
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
using Umbraco.Core.Events;
|
||||
using Umbraco.Core.Sync;
|
||||
using umbraco.interfaces;
|
||||
using Umbraco.Core.Models.EntityBase;
|
||||
|
||||
namespace Umbraco.Core.Cache
|
||||
{
|
||||
@@ -63,5 +64,15 @@ namespace Umbraco.Core.Cache
|
||||
{
|
||||
OnCacheUpdated(Instance, new CacheRefresherEventArgs(id, MessageType.RefreshById));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clears the cache for all repository entities of this type
|
||||
/// </summary>
|
||||
/// <typeparam name="TEntity"></typeparam>
|
||||
internal void ClearAllIsolatedCacheByEntityType<TEntity>()
|
||||
where TEntity : class, IAggregateRoot
|
||||
{
|
||||
ApplicationContext.Current.ApplicationCache.IsolatedRuntimeCache.ClearCache<TEntity>();
|
||||
}
|
||||
}
|
||||
}
|
||||
+11
-4
@@ -2,20 +2,27 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Web.Caching;
|
||||
using Umbraco.Core.Cache;
|
||||
using Umbraco.Core.Models;
|
||||
using Umbraco.Core.Models.EntityBase;
|
||||
|
||||
namespace Umbraco.Core.Persistence.Repositories
|
||||
namespace Umbraco.Core.Cache
|
||||
{
|
||||
/// <summary>
|
||||
/// Interface describing this cache provider as a wrapper for another
|
||||
/// </summary>
|
||||
internal interface IRuntimeCacheProviderWrapper
|
||||
{
|
||||
IRuntimeCacheProvider InnerProvider { get; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A wrapper for any IRuntimeCacheProvider that ensures that all inserts and returns
|
||||
/// are a deep cloned copy of the item when the item is IDeepCloneable and that tracks changes are
|
||||
/// reset if the object is TracksChangesEntityBase
|
||||
/// </summary>
|
||||
internal class DeepCloneRuntimeCacheProvider : IRuntimeCacheProvider
|
||||
internal class DeepCloneRuntimeCacheProvider : IRuntimeCacheProvider, IRuntimeCacheProviderWrapper
|
||||
{
|
||||
internal IRuntimeCacheProvider InnerProvider { get; private set; }
|
||||
public IRuntimeCacheProvider InnerProvider { get; private set; }
|
||||
|
||||
public DeepCloneRuntimeCacheProvider(IRuntimeCacheProvider innerProvider)
|
||||
{
|
||||
@@ -0,0 +1,268 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Umbraco.Core.Logging;
|
||||
using Umbraco.Core.Models.EntityBase;
|
||||
|
||||
namespace Umbraco.Core.Cache
|
||||
{
|
||||
/// <summary>
|
||||
/// The default cache policy for retrieving a single entity
|
||||
/// </summary>
|
||||
/// <typeparam name="TEntity"></typeparam>
|
||||
/// <typeparam name="TId"></typeparam>
|
||||
/// <remarks>
|
||||
/// This cache policy uses sliding expiration and caches instances for 5 minutes. However if allow zero count is true, then we use the
|
||||
/// default policy with no expiry.
|
||||
/// </remarks>
|
||||
internal class DefaultRepositoryCachePolicy<TEntity, TId> : RepositoryCachePolicyBase<TEntity, TId>
|
||||
where TEntity : class, IAggregateRoot
|
||||
{
|
||||
private readonly RepositoryCachePolicyOptions _options;
|
||||
|
||||
public DefaultRepositoryCachePolicy(IRuntimeCacheProvider cache, RepositoryCachePolicyOptions options)
|
||||
: base(cache)
|
||||
{
|
||||
if (options == null) throw new ArgumentNullException("options");
|
||||
_options = options;
|
||||
}
|
||||
|
||||
protected string GetCacheIdKey(object id)
|
||||
{
|
||||
if (id == null) throw new ArgumentNullException("id");
|
||||
|
||||
return string.Format("{0}{1}", GetCacheTypeKey(), id);
|
||||
}
|
||||
|
||||
protected string GetCacheTypeKey()
|
||||
{
|
||||
return string.Format("uRepo_{0}_", typeof(TEntity).Name);
|
||||
}
|
||||
|
||||
public override void CreateOrUpdate(TEntity entity, Action<TEntity> persistMethod)
|
||||
{
|
||||
if (entity == null) throw new ArgumentNullException("entity");
|
||||
if (persistMethod == null) throw new ArgumentNullException("persistMethod");
|
||||
|
||||
try
|
||||
{
|
||||
persistMethod(entity);
|
||||
|
||||
//set the disposal action
|
||||
SetCacheAction(() =>
|
||||
{
|
||||
//just to be safe, we cannot cache an item without an identity
|
||||
if (entity.HasIdentity)
|
||||
{
|
||||
Cache.InsertCacheItem(GetCacheIdKey(entity.Id), () => entity,
|
||||
timeout: TimeSpan.FromMinutes(5),
|
||||
isSliding: true);
|
||||
}
|
||||
|
||||
//If there's a GetAllCacheAllowZeroCount cache, ensure it is cleared
|
||||
Cache.ClearCacheItem(GetCacheTypeKey());
|
||||
});
|
||||
|
||||
}
|
||||
catch
|
||||
{
|
||||
//set the disposal action
|
||||
SetCacheAction(() =>
|
||||
{
|
||||
//if an exception is thrown we need to remove the entry from cache, this is ONLY a work around because of the way
|
||||
// that we cache entities: http://issues.umbraco.org/issue/U4-4259
|
||||
Cache.ClearCacheItem(GetCacheIdKey(entity.Id));
|
||||
|
||||
//If there's a GetAllCacheAllowZeroCount cache, ensure it is cleared
|
||||
Cache.ClearCacheItem(GetCacheTypeKey());
|
||||
});
|
||||
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
public override void Remove(TEntity entity, Action<TEntity> persistMethod)
|
||||
{
|
||||
if (entity == null) throw new ArgumentNullException("entity");
|
||||
if (persistMethod == null) throw new ArgumentNullException("persistMethod");
|
||||
|
||||
try
|
||||
{
|
||||
persistMethod(entity);
|
||||
}
|
||||
finally
|
||||
{
|
||||
//set the disposal action
|
||||
var cacheKey = GetCacheIdKey(entity.Id);
|
||||
SetCacheAction(() =>
|
||||
{
|
||||
Cache.ClearCacheItem(cacheKey);
|
||||
//If there's a GetAllCacheAllowZeroCount cache, ensure it is cleared
|
||||
Cache.ClearCacheItem(GetCacheTypeKey());
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public override TEntity Get(TId id, Func<TId, TEntity> getFromRepo)
|
||||
{
|
||||
if (getFromRepo == null) throw new ArgumentNullException("getFromRepo");
|
||||
|
||||
var cacheKey = GetCacheIdKey(id);
|
||||
var fromCache = Cache.GetCacheItem<TEntity>(cacheKey);
|
||||
if (fromCache != null)
|
||||
return fromCache;
|
||||
|
||||
var entity = getFromRepo(id);
|
||||
|
||||
//set the disposal action
|
||||
SetCacheAction(cacheKey, entity);
|
||||
|
||||
return entity;
|
||||
}
|
||||
|
||||
public override TEntity Get(TId id)
|
||||
{
|
||||
var cacheKey = GetCacheIdKey(id);
|
||||
return Cache.GetCacheItem<TEntity>(cacheKey);
|
||||
}
|
||||
|
||||
public override bool Exists(TId id, Func<TId, bool> getFromRepo)
|
||||
{
|
||||
if (getFromRepo == null) throw new ArgumentNullException("getFromRepo");
|
||||
|
||||
var cacheKey = GetCacheIdKey(id);
|
||||
var fromCache = Cache.GetCacheItem<TEntity>(cacheKey);
|
||||
return fromCache != null || getFromRepo(id);
|
||||
}
|
||||
|
||||
public override TEntity[] GetAll(TId[] ids, Func<TId[], IEnumerable<TEntity>> getFromRepo)
|
||||
{
|
||||
if (getFromRepo == null) throw new ArgumentNullException("getFromRepo");
|
||||
|
||||
if (ids.Any())
|
||||
{
|
||||
var entities = ids.Select(Get).ToArray();
|
||||
if (ids.Length.Equals(entities.Length) && entities.Any(x => x == null) == false)
|
||||
return entities;
|
||||
}
|
||||
else
|
||||
{
|
||||
var allEntities = GetAllFromCache();
|
||||
if (allEntities.Any())
|
||||
{
|
||||
if (_options.GetAllCacheValidateCount)
|
||||
{
|
||||
//Get count of all entities of current type (TEntity) to ensure cached result is correct
|
||||
var totalCount = _options.PerformCount();
|
||||
if (allEntities.Length == totalCount)
|
||||
return allEntities;
|
||||
}
|
||||
else
|
||||
{
|
||||
return allEntities;
|
||||
}
|
||||
}
|
||||
else if (_options.GetAllCacheAllowZeroCount)
|
||||
{
|
||||
//if the repository allows caching a zero count, then check the zero count cache
|
||||
if (HasZeroCountCache())
|
||||
{
|
||||
//there is a zero count cache so return an empty list
|
||||
return new TEntity[] {};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//we need to do the lookup from the repo
|
||||
var entityCollection = getFromRepo(ids)
|
||||
//ensure we don't include any null refs in the returned collection!
|
||||
.WhereNotNull()
|
||||
.ToArray();
|
||||
|
||||
//set the disposal action
|
||||
SetCacheAction(ids, entityCollection);
|
||||
|
||||
return entityCollection;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up the zero count cache, must return null if it doesn't exist
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
protected bool HasZeroCountCache()
|
||||
{
|
||||
var zeroCount = Cache.GetCacheItem<TEntity[]>(GetCacheTypeKey());
|
||||
return (zeroCount != null && zeroCount.Any() == false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Performs the lookup for all entities of this type from the cache
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
protected TEntity[] GetAllFromCache()
|
||||
{
|
||||
var allEntities = Cache.GetCacheItemsByKeySearch<TEntity>(GetCacheTypeKey())
|
||||
.WhereNotNull()
|
||||
.ToArray();
|
||||
return allEntities.Any() ? allEntities : new TEntity[] {};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the action to execute on disposal for a single entity
|
||||
/// </summary>
|
||||
/// <param name="cacheKey"></param>
|
||||
/// <param name="entity"></param>
|
||||
protected virtual void SetCacheAction(string cacheKey, TEntity entity)
|
||||
{
|
||||
if (entity == null) return;
|
||||
|
||||
SetCacheAction(() =>
|
||||
{
|
||||
//just to be safe, we cannot cache an item without an identity
|
||||
if (entity.HasIdentity)
|
||||
{
|
||||
Cache.InsertCacheItem(cacheKey, () => entity,
|
||||
timeout: TimeSpan.FromMinutes(5),
|
||||
isSliding: true);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the action to execute on disposal for an entity collection
|
||||
/// </summary>
|
||||
/// <param name="ids"></param>
|
||||
/// <param name="entityCollection"></param>
|
||||
protected virtual void SetCacheAction(TId[] ids, TEntity[] entityCollection)
|
||||
{
|
||||
SetCacheAction(() =>
|
||||
{
|
||||
//This option cannot execute if we are looking up specific Ids
|
||||
if (ids.Any() == false && entityCollection.Length == 0 && _options.GetAllCacheAllowZeroCount)
|
||||
{
|
||||
//there was nothing returned but we want to cache a zero count result so add an TEntity[] to the cache
|
||||
// to signify that there is a zero count cache
|
||||
//NOTE: Don't set expiry/sliding for a zero count
|
||||
Cache.InsertCacheItem(GetCacheTypeKey(), () => new TEntity[] {});
|
||||
}
|
||||
else
|
||||
{
|
||||
//This is the default behavior, we'll individually cache each item so that if/when these items are resolved
|
||||
// by id, they are returned from the already existing cache.
|
||||
foreach (var entity in entityCollection.WhereNotNull())
|
||||
{
|
||||
var localCopy = entity;
|
||||
//just to be safe, we cannot cache an item without an identity
|
||||
if (localCopy.HasIdentity)
|
||||
{
|
||||
Cache.InsertCacheItem(GetCacheIdKey(entity.Id), () => localCopy,
|
||||
timeout: TimeSpan.FromMinutes(5),
|
||||
isSliding: true);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
using Umbraco.Core.Models.EntityBase;
|
||||
|
||||
namespace Umbraco.Core.Cache
|
||||
{
|
||||
/// <summary>
|
||||
/// Creates cache policies
|
||||
/// </summary>
|
||||
/// <typeparam name="TEntity"></typeparam>
|
||||
/// <typeparam name="TId"></typeparam>
|
||||
internal class DefaultRepositoryCachePolicyFactory<TEntity, TId> : IRepositoryCachePolicyFactory<TEntity, TId>
|
||||
where TEntity : class, IAggregateRoot
|
||||
{
|
||||
private readonly IRuntimeCacheProvider _runtimeCache;
|
||||
private readonly RepositoryCachePolicyOptions _options;
|
||||
|
||||
public DefaultRepositoryCachePolicyFactory(IRuntimeCacheProvider runtimeCache, RepositoryCachePolicyOptions options)
|
||||
{
|
||||
_runtimeCache = runtimeCache;
|
||||
_options = options;
|
||||
}
|
||||
|
||||
public virtual IRepositoryCachePolicy<TEntity, TId> CreatePolicy()
|
||||
{
|
||||
return new DefaultRepositoryCachePolicy<TEntity, TId>(_runtimeCache, _options);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,229 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Umbraco.Core.Collections;
|
||||
using Umbraco.Core.Models.EntityBase;
|
||||
|
||||
namespace Umbraco.Core.Cache
|
||||
{
|
||||
/// <summary>
|
||||
/// A caching policy that caches an entire dataset as a single collection
|
||||
/// </summary>
|
||||
/// <typeparam name="TEntity"></typeparam>
|
||||
/// <typeparam name="TId"></typeparam>
|
||||
internal class FullDataSetRepositoryCachePolicy<TEntity, TId> : RepositoryCachePolicyBase<TEntity, TId>
|
||||
where TEntity : class, IAggregateRoot
|
||||
{
|
||||
private readonly Func<TEntity, TId> _getEntityId;
|
||||
private readonly Func<IEnumerable<TEntity>> _getAllFromRepo;
|
||||
private readonly bool _expires;
|
||||
|
||||
public FullDataSetRepositoryCachePolicy(IRuntimeCacheProvider cache, Func<TEntity, TId> getEntityId, Func<IEnumerable<TEntity>> getAllFromRepo, bool expires)
|
||||
: base(cache)
|
||||
{
|
||||
_getEntityId = getEntityId;
|
||||
_getAllFromRepo = getAllFromRepo;
|
||||
_expires = expires;
|
||||
}
|
||||
|
||||
private bool? _hasZeroCountCache;
|
||||
|
||||
|
||||
protected string GetCacheTypeKey()
|
||||
{
|
||||
return string.Format("uRepo_{0}_", typeof(TEntity).Name);
|
||||
}
|
||||
|
||||
public override void CreateOrUpdate(TEntity entity, Action<TEntity> persistMethod)
|
||||
{
|
||||
if (entity == null) throw new ArgumentNullException("entity");
|
||||
if (persistMethod == null) throw new ArgumentNullException("persistMethod");
|
||||
|
||||
try
|
||||
{
|
||||
persistMethod(entity);
|
||||
|
||||
//set the disposal action
|
||||
SetCacheAction(() =>
|
||||
{
|
||||
//Clear all
|
||||
Cache.ClearCacheItem(GetCacheTypeKey());
|
||||
});
|
||||
}
|
||||
catch
|
||||
{
|
||||
//set the disposal action
|
||||
SetCacheAction(() =>
|
||||
{
|
||||
//Clear all
|
||||
Cache.ClearCacheItem(GetCacheTypeKey());
|
||||
});
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
public override void Remove(TEntity entity, Action<TEntity> persistMethod)
|
||||
{
|
||||
if (entity == null) throw new ArgumentNullException("entity");
|
||||
if (persistMethod == null) throw new ArgumentNullException("persistMethod");
|
||||
|
||||
try
|
||||
{
|
||||
persistMethod(entity);
|
||||
}
|
||||
finally
|
||||
{
|
||||
//set the disposal action
|
||||
SetCacheAction(() =>
|
||||
{
|
||||
//Clear all
|
||||
Cache.ClearCacheItem(GetCacheTypeKey());
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public override TEntity Get(TId id, Func<TId, TEntity> getFromRepo)
|
||||
{
|
||||
//Force get all with cache
|
||||
var found = GetAll(new TId[] { }, ids => _getAllFromRepo().WhereNotNull());
|
||||
|
||||
//we don't have anything in cache (this should never happen), just return from the repo
|
||||
if (found == null) return getFromRepo(id);
|
||||
var entity = found.FirstOrDefault(x => _getEntityId(x).Equals(id));
|
||||
if (entity == null) return null;
|
||||
|
||||
//We must ensure to deep clone each one out manually since the deep clone list only clones one way
|
||||
return (TEntity)entity.DeepClone();
|
||||
}
|
||||
|
||||
public override TEntity Get(TId id)
|
||||
{
|
||||
//Force get all with cache
|
||||
var found = GetAll(new TId[] { }, ids => _getAllFromRepo().WhereNotNull());
|
||||
|
||||
//we don't have anything in cache (this should never happen), just return null
|
||||
if (found == null) return null;
|
||||
var entity = found.FirstOrDefault(x => _getEntityId(x).Equals(id));
|
||||
if (entity == null) return null;
|
||||
|
||||
//We must ensure to deep clone each one out manually since the deep clone list only clones one way
|
||||
return (TEntity)entity.DeepClone();
|
||||
}
|
||||
|
||||
public override bool Exists(TId id, Func<TId, bool> getFromRepo)
|
||||
{
|
||||
//Force get all with cache
|
||||
var found = GetAll(new TId[] {}, ids => _getAllFromRepo().WhereNotNull());
|
||||
|
||||
//we don't have anything in cache (this should never happen), just return from the repo
|
||||
return found == null
|
||||
? getFromRepo(id)
|
||||
: found.Any(x => _getEntityId(x).Equals(id));
|
||||
}
|
||||
|
||||
public override TEntity[] GetAll(TId[] ids, Func<TId[], IEnumerable<TEntity>> getFromRepo)
|
||||
{
|
||||
//process getting all including setting the cache callback
|
||||
var result = PerformGetAll(getFromRepo);
|
||||
|
||||
//now that the base result has been calculated, they will all be cached.
|
||||
// Now we can just filter by ids if they have been supplied
|
||||
|
||||
return (ids.Any()
|
||||
? result.Where(x => ids.Contains(_getEntityId(x))).ToArray()
|
||||
: result)
|
||||
//We must ensure to deep clone each one out manually since the deep clone list only clones one way
|
||||
.Select(x => (TEntity)x.DeepClone())
|
||||
.ToArray();
|
||||
}
|
||||
|
||||
private TEntity[] PerformGetAll(Func<TId[], IEnumerable<TEntity>> getFromRepo)
|
||||
{
|
||||
var allEntities = GetAllFromCache();
|
||||
if (allEntities.Any())
|
||||
{
|
||||
return allEntities;
|
||||
}
|
||||
|
||||
//check the zero count cache
|
||||
if (HasZeroCountCache())
|
||||
{
|
||||
//there is a zero count cache so return an empty list
|
||||
return new TEntity[] { };
|
||||
}
|
||||
|
||||
//we need to do the lookup from the repo
|
||||
var entityCollection = getFromRepo(new TId[] {})
|
||||
//ensure we don't include any null refs in the returned collection!
|
||||
.WhereNotNull()
|
||||
.ToArray();
|
||||
|
||||
//set the disposal action
|
||||
SetCacheAction(entityCollection);
|
||||
|
||||
return entityCollection;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// For this type of caching policy, we don't cache individual items
|
||||
/// </summary>
|
||||
/// <param name="cacheKey"></param>
|
||||
/// <param name="entity"></param>
|
||||
protected void SetCacheAction(string cacheKey, TEntity entity)
|
||||
{
|
||||
//No-op
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the action to execute on disposal for an entity collection
|
||||
/// </summary>
|
||||
/// <param name="entityCollection"></param>
|
||||
protected void SetCacheAction(TEntity[] entityCollection)
|
||||
{
|
||||
//set the disposal action
|
||||
SetCacheAction(() =>
|
||||
{
|
||||
//We want to cache the result as a single collection
|
||||
|
||||
if (_expires)
|
||||
{
|
||||
Cache.InsertCacheItem(GetCacheTypeKey(), () => new DeepCloneableList<TEntity>(entityCollection),
|
||||
timeout: TimeSpan.FromMinutes(5),
|
||||
isSliding: true);
|
||||
}
|
||||
else
|
||||
{
|
||||
Cache.InsertCacheItem(GetCacheTypeKey(), () => new DeepCloneableList<TEntity>(entityCollection));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up the zero count cache, must return null if it doesn't exist
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
protected bool HasZeroCountCache()
|
||||
{
|
||||
if (_hasZeroCountCache.HasValue)
|
||||
return _hasZeroCountCache.Value;
|
||||
|
||||
_hasZeroCountCache = Cache.GetCacheItem<DeepCloneableList<TEntity>>(GetCacheTypeKey()) != null;
|
||||
return _hasZeroCountCache.Value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This policy will cache the full data set as a single collection
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
protected TEntity[] GetAllFromCache()
|
||||
{
|
||||
var found = Cache.GetCacheItem<DeepCloneableList<TEntity>>(GetCacheTypeKey());
|
||||
|
||||
//This method will get called before checking for zero count cache, so we'll just set the flag here
|
||||
_hasZeroCountCache = found != null;
|
||||
|
||||
return found == null ? new TEntity[] { } : found.WhereNotNull().ToArray();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Umbraco.Core.Models.EntityBase;
|
||||
|
||||
namespace Umbraco.Core.Cache
|
||||
{
|
||||
/// <summary>
|
||||
/// Creates cache policies
|
||||
/// </summary>
|
||||
/// <typeparam name="TEntity"></typeparam>
|
||||
/// <typeparam name="TId"></typeparam>
|
||||
internal class FullDataSetRepositoryCachePolicyFactory<TEntity, TId> : IRepositoryCachePolicyFactory<TEntity, TId>
|
||||
where TEntity : class, IAggregateRoot
|
||||
{
|
||||
private readonly IRuntimeCacheProvider _runtimeCache;
|
||||
private readonly Func<TEntity, TId> _getEntityId;
|
||||
private readonly Func<IEnumerable<TEntity>> _getAllFromRepo;
|
||||
private readonly bool _expires;
|
||||
|
||||
public FullDataSetRepositoryCachePolicyFactory(IRuntimeCacheProvider runtimeCache, Func<TEntity, TId> getEntityId, Func<IEnumerable<TEntity>> getAllFromRepo, bool expires)
|
||||
{
|
||||
_runtimeCache = runtimeCache;
|
||||
_getEntityId = getEntityId;
|
||||
_getAllFromRepo = getAllFromRepo;
|
||||
_expires = expires;
|
||||
}
|
||||
|
||||
public virtual IRepositoryCachePolicy<TEntity, TId> CreatePolicy()
|
||||
{
|
||||
return new FullDataSetRepositoryCachePolicy<TEntity, TId>(_runtimeCache, _getEntityId, _getAllFromRepo, _expires);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Umbraco.Core.Models.EntityBase;
|
||||
|
||||
namespace Umbraco.Core.Cache
|
||||
{
|
||||
internal interface IRepositoryCachePolicy<TEntity, TId> : IDisposable
|
||||
where TEntity : class, IAggregateRoot
|
||||
{
|
||||
TEntity Get(TId id, Func<TId, TEntity> getFromRepo);
|
||||
TEntity Get(TId id);
|
||||
bool Exists(TId id, Func<TId, bool> getFromRepo);
|
||||
|
||||
void CreateOrUpdate(TEntity entity, Action<TEntity> persistMethod);
|
||||
void Remove(TEntity entity, Action<TEntity> persistMethod);
|
||||
TEntity[] GetAll(TId[] ids, Func<TId[], IEnumerable<TEntity>> getFromRepo);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
using Umbraco.Core.Models.EntityBase;
|
||||
|
||||
namespace Umbraco.Core.Cache
|
||||
{
|
||||
internal interface IRepositoryCachePolicyFactory<TEntity, TId> where TEntity : class, IAggregateRoot
|
||||
{
|
||||
IRepositoryCachePolicy<TEntity, TId> CreatePolicy();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
|
||||
namespace Umbraco.Core.Cache
|
||||
{
|
||||
/// <summary>
|
||||
/// Used to get/create/manipulate isolated runtime cache
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This is useful for repository level caches to ensure that cache lookups by key are fast so
|
||||
/// that the repository doesn't need to search through all keys on a global scale.
|
||||
/// </remarks>
|
||||
public class IsolatedRuntimeCache
|
||||
{
|
||||
internal Func<Type, IRuntimeCacheProvider> CacheFactory { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Constructor that allows specifying a factory for the type of runtime isolated cache to create
|
||||
/// </summary>
|
||||
/// <param name="cacheFactory"></param>
|
||||
public IsolatedRuntimeCache(Func<Type, IRuntimeCacheProvider> cacheFactory)
|
||||
{
|
||||
CacheFactory = cacheFactory;
|
||||
}
|
||||
|
||||
private readonly ConcurrentDictionary<Type, IRuntimeCacheProvider> _isolatedCache = new ConcurrentDictionary<Type, IRuntimeCacheProvider>();
|
||||
|
||||
/// <summary>
|
||||
/// Returns an isolated runtime cache for a given type
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <returns></returns>
|
||||
public IRuntimeCacheProvider GetOrCreateCache<T>()
|
||||
{
|
||||
return _isolatedCache.GetOrAdd(typeof(T), type => CacheFactory(type));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns an isolated runtime cache for a given type
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public IRuntimeCacheProvider GetOrCreateCache(Type type)
|
||||
{
|
||||
return _isolatedCache.GetOrAdd(type, t => CacheFactory(t));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tries to get a cache by the type specified
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <returns></returns>
|
||||
public Attempt<IRuntimeCacheProvider> GetCache<T>()
|
||||
{
|
||||
IRuntimeCacheProvider cache;
|
||||
if (_isolatedCache.TryGetValue(typeof(T), out cache))
|
||||
{
|
||||
return Attempt.Succeed(cache);
|
||||
}
|
||||
return Attempt<IRuntimeCacheProvider>.Fail();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clears all values inside this isolated runtime cache
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <returns></returns>
|
||||
public void ClearCache<T>()
|
||||
{
|
||||
IRuntimeCacheProvider cache;
|
||||
if (_isolatedCache.TryGetValue(typeof(T), out cache))
|
||||
{
|
||||
cache.ClearAllCache();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clears all of the isolated caches
|
||||
/// </summary>
|
||||
public void ClearAllCaches()
|
||||
{
|
||||
foreach (var key in _isolatedCache.Keys)
|
||||
{
|
||||
IRuntimeCacheProvider cache;
|
||||
if (_isolatedCache.TryRemove(key, out cache))
|
||||
{
|
||||
cache.ClearAllCache();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
using Umbraco.Core.Models.EntityBase;
|
||||
|
||||
namespace Umbraco.Core.Cache
|
||||
{
|
||||
/// <summary>
|
||||
/// Creates cache policies
|
||||
/// </summary>
|
||||
/// <typeparam name="TEntity"></typeparam>
|
||||
/// <typeparam name="TId"></typeparam>
|
||||
internal class OnlySingleItemsRepositoryCachePolicyFactory<TEntity, TId> : IRepositoryCachePolicyFactory<TEntity, TId>
|
||||
where TEntity : class, IAggregateRoot
|
||||
{
|
||||
private readonly IRuntimeCacheProvider _runtimeCache;
|
||||
private readonly RepositoryCachePolicyOptions _options;
|
||||
|
||||
public OnlySingleItemsRepositoryCachePolicyFactory(IRuntimeCacheProvider runtimeCache, RepositoryCachePolicyOptions options)
|
||||
{
|
||||
_runtimeCache = runtimeCache;
|
||||
_options = options;
|
||||
}
|
||||
|
||||
public virtual IRepositoryCachePolicy<TEntity, TId> CreatePolicy()
|
||||
{
|
||||
return new SingleItemsOnlyRepositoryCachePolicy<TEntity, TId>(_runtimeCache, _options);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Umbraco.Core.Models.EntityBase;
|
||||
|
||||
namespace Umbraco.Core.Cache
|
||||
{
|
||||
internal abstract class RepositoryCachePolicyBase<TEntity, TId> : DisposableObject, IRepositoryCachePolicy<TEntity, TId>
|
||||
where TEntity : class, IAggregateRoot
|
||||
{
|
||||
private Action _action;
|
||||
|
||||
protected RepositoryCachePolicyBase(IRuntimeCacheProvider cache)
|
||||
{
|
||||
if (cache == null) throw new ArgumentNullException("cache");
|
||||
|
||||
Cache = cache;
|
||||
}
|
||||
|
||||
protected IRuntimeCacheProvider Cache { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// The disposal performs the caching
|
||||
/// </summary>
|
||||
protected override void DisposeResources()
|
||||
{
|
||||
if (_action != null)
|
||||
{
|
||||
_action();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the action to execute on disposal
|
||||
/// </summary>
|
||||
/// <param name="action"></param>
|
||||
protected void SetCacheAction(Action action)
|
||||
{
|
||||
_action = action;
|
||||
}
|
||||
|
||||
public abstract TEntity Get(TId id, Func<TId, TEntity> getFromRepo);
|
||||
public abstract TEntity Get(TId id);
|
||||
public abstract bool Exists(TId id, Func<TId, bool> getFromRepo);
|
||||
public abstract void CreateOrUpdate(TEntity entity, Action<TEntity> persistMethod);
|
||||
public abstract void Remove(TEntity entity, Action<TEntity> persistMethod);
|
||||
public abstract TEntity[] GetAll(TId[] ids, Func<TId[], IEnumerable<TEntity>> getFromRepo);
|
||||
}
|
||||
}
|
||||
+23
-11
@@ -1,17 +1,34 @@
|
||||
namespace Umbraco.Core.Persistence.Repositories
|
||||
using System;
|
||||
|
||||
namespace Umbraco.Core.Cache
|
||||
{
|
||||
internal class RepositoryCacheOptions
|
||||
internal class RepositoryCachePolicyOptions
|
||||
{
|
||||
/// <summary>
|
||||
/// Constructor sets defaults
|
||||
/// Ctor - sets GetAllCacheValidateCount = true
|
||||
/// </summary>
|
||||
public RepositoryCacheOptions()
|
||||
public RepositoryCachePolicyOptions(Func<int> performCount)
|
||||
{
|
||||
PerformCount = performCount;
|
||||
GetAllCacheValidateCount = true;
|
||||
GetAllCacheAllowZeroCount = false;
|
||||
GetAllCacheThresholdLimit = 100;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Ctor - sets GetAllCacheValidateCount = false
|
||||
/// </summary>
|
||||
public RepositoryCachePolicyOptions()
|
||||
{
|
||||
PerformCount = null;
|
||||
GetAllCacheValidateCount = false;
|
||||
GetAllCacheAllowZeroCount = false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Callback required to get count for GetAllCacheValidateCount
|
||||
/// </summary>
|
||||
public Func<int> PerformCount { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// True/false as to validate the total item count when all items are returned from cache, the default is true but this
|
||||
/// means that a db lookup will occur - though that lookup will probably be significantly less expensive than the normal
|
||||
@@ -21,16 +38,11 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
/// setting this to return false will improve performance of GetAll cache with no params but should only be used
|
||||
/// for specific circumstances
|
||||
/// </remarks>
|
||||
public bool GetAllCacheValidateCount { get; set; }
|
||||
public bool GetAllCacheValidateCount { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// True if the GetAll method will cache that there are zero results so that the db is not hit when there are no results found
|
||||
/// </summary>
|
||||
public bool GetAllCacheAllowZeroCount { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The threshold entity count for which the GetAll method will cache entities
|
||||
/// </summary>
|
||||
public int GetAllCacheThresholdLimit { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
using System.Linq;
|
||||
using Umbraco.Core.Collections;
|
||||
using Umbraco.Core.Models.EntityBase;
|
||||
|
||||
namespace Umbraco.Core.Cache
|
||||
{
|
||||
/// <summary>
|
||||
/// A caching policy that ignores all caches for GetAll - it will only cache calls for individual items
|
||||
/// </summary>
|
||||
/// <typeparam name="TEntity"></typeparam>
|
||||
/// <typeparam name="TId"></typeparam>
|
||||
internal class SingleItemsOnlyRepositoryCachePolicy<TEntity, TId> : DefaultRepositoryCachePolicy<TEntity, TId>
|
||||
where TEntity : class, IAggregateRoot
|
||||
{
|
||||
public SingleItemsOnlyRepositoryCachePolicy(IRuntimeCacheProvider cache, RepositoryCachePolicyOptions options) : base(cache, options)
|
||||
{
|
||||
}
|
||||
|
||||
protected override void SetCacheAction(TId[] ids, TEntity[] entityCollection)
|
||||
{
|
||||
//no-op
|
||||
}
|
||||
}
|
||||
}
|
||||
+117
-223
@@ -1,6 +1,8 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
@@ -11,20 +13,15 @@ using Umbraco.Core.Logging;
|
||||
|
||||
namespace Umbraco.Core
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// Class that is exposed by the ApplicationContext for application wide caching purposes
|
||||
/// </summary>
|
||||
public class CacheHelper
|
||||
{
|
||||
private readonly bool _enableCache;
|
||||
private readonly ICacheProvider _requestCache;
|
||||
private readonly ICacheProvider _nullRequestCache = new NullCacheProvider();
|
||||
private readonly ICacheProvider _staticCache;
|
||||
private readonly ICacheProvider _nullStaticCache = new NullCacheProvider();
|
||||
private readonly IRuntimeCacheProvider _httpCache;
|
||||
private readonly IRuntimeCacheProvider _nullHttpCache = new NullCacheProvider();
|
||||
|
||||
private static readonly ICacheProvider NullRequestCache = new NullCacheProvider();
|
||||
private static readonly ICacheProvider NullStaticCache = new NullCacheProvider();
|
||||
private static readonly IRuntimeCacheProvider NullRuntimeCache = new NullCacheProvider();
|
||||
|
||||
/// <summary>
|
||||
/// Creates a cache helper with disabled caches
|
||||
/// </summary>
|
||||
@@ -34,7 +31,7 @@ namespace Umbraco.Core
|
||||
/// </remarks>
|
||||
public static CacheHelper CreateDisabledCacheHelper()
|
||||
{
|
||||
return new CacheHelper(null, null, null, false);
|
||||
return new CacheHelper(NullRuntimeCache, NullStaticCache, NullRequestCache, new IsolatedRuntimeCache(t => NullRuntimeCache));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -44,7 +41,8 @@ namespace Umbraco.Core
|
||||
: this(
|
||||
new HttpRuntimeCacheProvider(HttpRuntime.Cache),
|
||||
new StaticCacheProvider(),
|
||||
new HttpRequestCacheProvider())
|
||||
new HttpRequestCacheProvider(),
|
||||
new IsolatedRuntimeCache(t => new ObjectCacheRuntimeCacheProvider()))
|
||||
{
|
||||
}
|
||||
|
||||
@@ -56,93 +54,75 @@ namespace Umbraco.Core
|
||||
: this(
|
||||
new HttpRuntimeCacheProvider(cache),
|
||||
new StaticCacheProvider(),
|
||||
new HttpRequestCacheProvider())
|
||||
new HttpRequestCacheProvider(),
|
||||
new IsolatedRuntimeCache(t => new ObjectCacheRuntimeCacheProvider()))
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance based on the provided providers
|
||||
/// </summary>
|
||||
/// <param name="httpCacheProvider"></param>
|
||||
/// <param name="staticCacheProvider"></param>
|
||||
/// <param name="requestCacheProvider"></param>
|
||||
[Obsolete("Use the constructor the specifies all dependencies")]
|
||||
[EditorBrowsable(EditorBrowsableState.Never)]
|
||||
public CacheHelper(
|
||||
IRuntimeCacheProvider httpCacheProvider,
|
||||
ICacheProvider staticCacheProvider,
|
||||
ICacheProvider requestCacheProvider)
|
||||
: this(httpCacheProvider, staticCacheProvider, requestCacheProvider, true)
|
||||
: this(httpCacheProvider, staticCacheProvider, requestCacheProvider, new IsolatedRuntimeCache(t => new ObjectCacheRuntimeCacheProvider()))
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Private ctor used for creating a disabled cache helper
|
||||
/// </summary>
|
||||
/// <param name="httpCacheProvider"></param>
|
||||
/// <param name="staticCacheProvider"></param>
|
||||
/// <param name="requestCacheProvider"></param>
|
||||
/// <param name="enableCache"></param>
|
||||
private CacheHelper(
|
||||
/// <summary>
|
||||
/// Initializes a new instance based on the provided providers
|
||||
/// </summary>
|
||||
/// <param name="httpCacheProvider"></param>
|
||||
/// <param name="staticCacheProvider"></param>
|
||||
/// <param name="requestCacheProvider"></param>
|
||||
/// <param name="isolatedCacheManager"></param>
|
||||
public CacheHelper(
|
||||
IRuntimeCacheProvider httpCacheProvider,
|
||||
ICacheProvider staticCacheProvider,
|
||||
ICacheProvider requestCacheProvider,
|
||||
bool enableCache)
|
||||
ICacheProvider requestCacheProvider,
|
||||
IsolatedRuntimeCache isolatedCacheManager)
|
||||
{
|
||||
if (enableCache)
|
||||
{
|
||||
_httpCache = httpCacheProvider;
|
||||
_staticCache = staticCacheProvider;
|
||||
_requestCache = requestCacheProvider;
|
||||
}
|
||||
else
|
||||
{
|
||||
_httpCache = null;
|
||||
_staticCache = null;
|
||||
_requestCache = null;
|
||||
}
|
||||
|
||||
_enableCache = enableCache;
|
||||
if (httpCacheProvider == null) throw new ArgumentNullException("httpCacheProvider");
|
||||
if (staticCacheProvider == null) throw new ArgumentNullException("staticCacheProvider");
|
||||
if (requestCacheProvider == null) throw new ArgumentNullException("requestCacheProvider");
|
||||
if (isolatedCacheManager == null) throw new ArgumentNullException("isolatedCacheManager");
|
||||
RuntimeCache = httpCacheProvider;
|
||||
StaticCache = staticCacheProvider;
|
||||
RequestCache = requestCacheProvider;
|
||||
IsolatedRuntimeCache = isolatedCacheManager;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the current Request cache
|
||||
/// </summary>
|
||||
public ICacheProvider RequestCache
|
||||
{
|
||||
get { return _enableCache ? _requestCache : _nullRequestCache; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the current Runtime cache
|
||||
/// </summary>
|
||||
public ICacheProvider StaticCache
|
||||
{
|
||||
get { return _enableCache ? _staticCache : _nullStaticCache; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the current Runtime cache
|
||||
/// </summary>
|
||||
public IRuntimeCacheProvider RuntimeCache
|
||||
{
|
||||
get { return _enableCache ? _httpCache : _nullHttpCache; }
|
||||
}
|
||||
public ICacheProvider RequestCache { get; internal set; }
|
||||
|
||||
#region Legacy Runtime/Http Cache accessors
|
||||
/// <summary>
|
||||
/// Returns the current Runtime cache
|
||||
/// </summary>
|
||||
public ICacheProvider StaticCache { get; internal set; }
|
||||
|
||||
/// <summary>
|
||||
/// Returns the current Runtime cache
|
||||
/// </summary>
|
||||
public IRuntimeCacheProvider RuntimeCache { get; internal set; }
|
||||
|
||||
/// <summary>
|
||||
/// Returns the current Isolated Runtime cache manager
|
||||
/// </summary>
|
||||
public IsolatedRuntimeCache IsolatedRuntimeCache { get; internal set; }
|
||||
|
||||
#region Legacy Runtime/Http Cache accessors
|
||||
|
||||
/// <summary>
|
||||
/// Clears the item in umbraco's runtime cache
|
||||
/// </summary>
|
||||
[Obsolete("Do not use this method, access the runtime cache from the RuntimeCache property")]
|
||||
[EditorBrowsable(EditorBrowsableState.Never)]
|
||||
public void ClearAllCache()
|
||||
{
|
||||
if (_enableCache == false)
|
||||
{
|
||||
_nullHttpCache.ClearAllCache();
|
||||
}
|
||||
else
|
||||
{
|
||||
_httpCache.ClearAllCache();
|
||||
}
|
||||
RuntimeCache.ClearAllCache();
|
||||
IsolatedRuntimeCache.ClearAllCaches();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -150,16 +130,10 @@ namespace Umbraco.Core
|
||||
/// </summary>
|
||||
/// <param name="key">Key</param>
|
||||
[Obsolete("Do not use this method, access the runtime cache from the RuntimeCache property")]
|
||||
[EditorBrowsable(EditorBrowsableState.Never)]
|
||||
public void ClearCacheItem(string key)
|
||||
{
|
||||
if (_enableCache == false)
|
||||
{
|
||||
_nullHttpCache.ClearCacheItem(key);
|
||||
}
|
||||
else
|
||||
{
|
||||
_httpCache.ClearCacheItem(key);
|
||||
}
|
||||
RuntimeCache.ClearCacheItem(key);
|
||||
}
|
||||
|
||||
|
||||
@@ -171,30 +145,17 @@ namespace Umbraco.Core
|
||||
[Obsolete("Do not use this method, access the runtime cache from the RuntimeCache property")]
|
||||
public void ClearCacheObjectTypes(string typeName)
|
||||
{
|
||||
if (_enableCache == false)
|
||||
{
|
||||
_nullHttpCache.ClearCacheObjectTypes(typeName);
|
||||
}
|
||||
else
|
||||
{
|
||||
_httpCache.ClearCacheObjectTypes(typeName);
|
||||
}
|
||||
RuntimeCache.ClearCacheObjectTypes(typeName);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clears all objects in the System.Web.Cache with the System.Type specified
|
||||
/// </summary>
|
||||
[Obsolete("Do not use this method, access the runtime cache from the RuntimeCache property")]
|
||||
[EditorBrowsable(EditorBrowsableState.Never)]
|
||||
public void ClearCacheObjectTypes<T>()
|
||||
{
|
||||
if (_enableCache == false)
|
||||
{
|
||||
_nullHttpCache.ClearCacheObjectTypes<T>();
|
||||
}
|
||||
else
|
||||
{
|
||||
_httpCache.ClearCacheObjectTypes<T>();
|
||||
}
|
||||
RuntimeCache.ClearCacheObjectTypes<T>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -202,16 +163,10 @@ namespace Umbraco.Core
|
||||
/// </summary>
|
||||
/// <param name="keyStartsWith">The start of the key</param>
|
||||
[Obsolete("Do not use this method, access the runtime cache from the RuntimeCache property")]
|
||||
[EditorBrowsable(EditorBrowsableState.Never)]
|
||||
public void ClearCacheByKeySearch(string keyStartsWith)
|
||||
{
|
||||
if (_enableCache == false)
|
||||
{
|
||||
_nullHttpCache.ClearCacheByKeySearch(keyStartsWith);
|
||||
}
|
||||
else
|
||||
{
|
||||
_httpCache.ClearCacheByKeySearch(keyStartsWith);
|
||||
}
|
||||
RuntimeCache.ClearCacheByKeySearch(keyStartsWith);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -219,29 +174,17 @@ namespace Umbraco.Core
|
||||
/// </summary>
|
||||
/// <param name="regexString"></param>
|
||||
[Obsolete("Do not use this method, access the runtime cache from the RuntimeCache property")]
|
||||
[EditorBrowsable(EditorBrowsableState.Never)]
|
||||
public void ClearCacheByKeyExpression(string regexString)
|
||||
{
|
||||
if (_enableCache == false)
|
||||
{
|
||||
_nullHttpCache.ClearCacheByKeyExpression(regexString);
|
||||
}
|
||||
else
|
||||
{
|
||||
_httpCache.ClearCacheByKeyExpression(regexString);
|
||||
}
|
||||
RuntimeCache.ClearCacheByKeyExpression(regexString);
|
||||
}
|
||||
|
||||
[Obsolete("Do not use this method, access the runtime cache from the RuntimeCache property")]
|
||||
[EditorBrowsable(EditorBrowsableState.Never)]
|
||||
public IEnumerable<T> GetCacheItemsByKeySearch<T>(string keyStartsWith)
|
||||
{
|
||||
if (_enableCache == false)
|
||||
{
|
||||
return _nullHttpCache.GetCacheItemsByKeySearch<T>(keyStartsWith);
|
||||
}
|
||||
else
|
||||
{
|
||||
return _httpCache.GetCacheItemsByKeySearch<T>(keyStartsWith);
|
||||
}
|
||||
return RuntimeCache.GetCacheItemsByKeySearch<T>(keyStartsWith);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -251,16 +194,10 @@ namespace Umbraco.Core
|
||||
/// <param name="cacheKey"></param>
|
||||
/// <returns></returns>
|
||||
[Obsolete("Do not use this method, access the runtime cache from the RuntimeCache property")]
|
||||
[EditorBrowsable(EditorBrowsableState.Never)]
|
||||
public TT GetCacheItem<TT>(string cacheKey)
|
||||
{
|
||||
if (_enableCache == false)
|
||||
{
|
||||
return _nullHttpCache.GetCacheItem<TT>(cacheKey);
|
||||
}
|
||||
else
|
||||
{
|
||||
return _httpCache.GetCacheItem<TT>(cacheKey);
|
||||
}
|
||||
return RuntimeCache.GetCacheItem<TT>(cacheKey);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -271,16 +208,11 @@ namespace Umbraco.Core
|
||||
/// <param name="getCacheItem"></param>
|
||||
/// <returns></returns>
|
||||
[Obsolete("Do not use this method, access the runtime cache from the RuntimeCache property")]
|
||||
[EditorBrowsable(EditorBrowsableState.Never)]
|
||||
public TT GetCacheItem<TT>(string cacheKey, Func<TT> getCacheItem)
|
||||
{
|
||||
if (_enableCache == false)
|
||||
{
|
||||
return _nullHttpCache.GetCacheItem<TT>(cacheKey, getCacheItem);
|
||||
}
|
||||
else
|
||||
{
|
||||
return _httpCache.GetCacheItem<TT>(cacheKey, getCacheItem);
|
||||
}
|
||||
return RuntimeCache.GetCacheItem<TT>(cacheKey, getCacheItem);
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -292,17 +224,12 @@ namespace Umbraco.Core
|
||||
/// <param name="getCacheItem"></param>
|
||||
/// <returns></returns>
|
||||
[Obsolete("Do not use this method, access the runtime cache from the RuntimeCache property")]
|
||||
[EditorBrowsable(EditorBrowsableState.Never)]
|
||||
public TT GetCacheItem<TT>(string cacheKey,
|
||||
TimeSpan timeout, Func<TT> getCacheItem)
|
||||
{
|
||||
if (_enableCache == false)
|
||||
{
|
||||
return _nullHttpCache.GetCacheItem<TT>(cacheKey, getCacheItem, timeout);
|
||||
}
|
||||
else
|
||||
{
|
||||
return _httpCache.GetCacheItem<TT>(cacheKey, getCacheItem, timeout);
|
||||
}
|
||||
return RuntimeCache.GetCacheItem<TT>(cacheKey, getCacheItem, timeout);
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -315,18 +242,13 @@ namespace Umbraco.Core
|
||||
/// <param name="getCacheItem"></param>
|
||||
/// <returns></returns>
|
||||
[Obsolete("Do not use this method, access the runtime cache from the RuntimeCache property")]
|
||||
[EditorBrowsable(EditorBrowsableState.Never)]
|
||||
public TT GetCacheItem<TT>(string cacheKey,
|
||||
CacheItemRemovedCallback refreshAction, TimeSpan timeout,
|
||||
Func<TT> getCacheItem)
|
||||
{
|
||||
if (!_enableCache)
|
||||
{
|
||||
return _nullHttpCache.GetCacheItem<TT>(cacheKey, getCacheItem, timeout, removedCallback: refreshAction);
|
||||
}
|
||||
else
|
||||
{
|
||||
return _httpCache.GetCacheItem<TT>(cacheKey, getCacheItem, timeout, removedCallback: refreshAction);
|
||||
}
|
||||
return RuntimeCache.GetCacheItem<TT>(cacheKey, getCacheItem, timeout, removedCallback: refreshAction);
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -340,18 +262,13 @@ namespace Umbraco.Core
|
||||
/// <param name="getCacheItem"></param>
|
||||
/// <returns></returns>
|
||||
[Obsolete("Do not use this method, access the runtime cache from the RuntimeCache property")]
|
||||
[EditorBrowsable(EditorBrowsableState.Never)]
|
||||
public TT GetCacheItem<TT>(string cacheKey,
|
||||
CacheItemPriority priority, CacheItemRemovedCallback refreshAction, TimeSpan timeout,
|
||||
Func<TT> getCacheItem)
|
||||
{
|
||||
if (_enableCache == false)
|
||||
{
|
||||
return _nullHttpCache.GetCacheItem<TT>(cacheKey, getCacheItem, timeout, false, priority, refreshAction);
|
||||
}
|
||||
else
|
||||
{
|
||||
return _httpCache.GetCacheItem<TT>(cacheKey, getCacheItem, timeout, false, priority, refreshAction);
|
||||
}
|
||||
return RuntimeCache.GetCacheItem<TT>(cacheKey, getCacheItem, timeout, false, priority, refreshAction);
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -373,20 +290,13 @@ namespace Umbraco.Core
|
||||
TimeSpan timeout,
|
||||
Func<TT> getCacheItem)
|
||||
{
|
||||
if (_enableCache == false)
|
||||
var cache = GetHttpRuntimeCacheProvider(RuntimeCache);
|
||||
if (cache != null)
|
||||
{
|
||||
return _nullHttpCache.GetCacheItem<TT>(cacheKey, getCacheItem, timeout, false, priority, refreshAction, null);
|
||||
}
|
||||
else
|
||||
{
|
||||
var cache = _httpCache as HttpRuntimeCacheProvider;
|
||||
if (cache != null)
|
||||
{
|
||||
var result = cache.GetCacheItem(cacheKey, () => getCacheItem(), timeout, false, priority, refreshAction, cacheDependency);
|
||||
return result == null ? default(TT) : result.TryConvertTo<TT>().Result;
|
||||
}
|
||||
throw new InvalidOperationException("Cannot use this obsoleted overload when the current provider is not of type " + typeof(HttpRuntimeCacheProvider));
|
||||
var result = cache.GetCacheItem(cacheKey, () => getCacheItem(), timeout, false, priority, refreshAction, cacheDependency);
|
||||
return result == null ? default(TT) : result.TryConvertTo<TT>().Result;
|
||||
}
|
||||
throw new InvalidOperationException("Cannot use this obsoleted overload when the current provider is not of type " + typeof(HttpRuntimeCacheProvider));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -404,20 +314,13 @@ namespace Umbraco.Core
|
||||
CacheDependency cacheDependency,
|
||||
Func<TT> getCacheItem)
|
||||
{
|
||||
if (!_enableCache)
|
||||
var cache = GetHttpRuntimeCacheProvider(RuntimeCache);
|
||||
if (cache != null)
|
||||
{
|
||||
return _nullHttpCache.GetCacheItem<TT>(cacheKey, getCacheItem, null, false, priority, null, null);
|
||||
}
|
||||
else
|
||||
{
|
||||
var cache = _httpCache as HttpRuntimeCacheProvider;
|
||||
if (cache != null)
|
||||
{
|
||||
var result = cache.GetCacheItem(cacheKey, () => getCacheItem(), null, false, priority, null, cacheDependency);
|
||||
return result == null ? default(TT) : result.TryConvertTo<TT>().Result;
|
||||
}
|
||||
throw new InvalidOperationException("Cannot use this obsoleted overload when the current provider is not of type " + typeof(HttpRuntimeCacheProvider));
|
||||
var result = cache.GetCacheItem(cacheKey, () => getCacheItem(), null, false, priority, null, cacheDependency);
|
||||
return result == null ? default(TT) : result.TryConvertTo<TT>().Result;
|
||||
}
|
||||
throw new InvalidOperationException("Cannot use this obsoleted overload when the current provider is not of type " + typeof(HttpRuntimeCacheProvider));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -427,18 +330,14 @@ namespace Umbraco.Core
|
||||
/// <param name="cacheKey"></param>
|
||||
/// <param name="priority"></param>
|
||||
/// <param name="getCacheItem"></param>
|
||||
[Obsolete("Do not use this method, access the runtime cache from the RuntimeCache property")]
|
||||
[EditorBrowsable(EditorBrowsableState.Never)]
|
||||
public void InsertCacheItem<T>(string cacheKey,
|
||||
CacheItemPriority priority,
|
||||
Func<T> getCacheItem)
|
||||
{
|
||||
if (_enableCache == false)
|
||||
{
|
||||
_nullHttpCache.InsertCacheItem<T>(cacheKey, getCacheItem, priority: priority);
|
||||
}
|
||||
else
|
||||
{
|
||||
_httpCache.InsertCacheItem<T>(cacheKey, getCacheItem, priority: priority);
|
||||
}
|
||||
RuntimeCache.InsertCacheItem<T>(cacheKey, getCacheItem, priority: priority);
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -449,19 +348,14 @@ namespace Umbraco.Core
|
||||
/// <param name="priority"></param>
|
||||
/// <param name="timeout">This will set an absolute expiration from now until the timeout</param>
|
||||
/// <param name="getCacheItem"></param>
|
||||
[Obsolete("Do not use this method, access the runtime cache from the RuntimeCache property")]
|
||||
[EditorBrowsable(EditorBrowsableState.Never)]
|
||||
public void InsertCacheItem<T>(string cacheKey,
|
||||
CacheItemPriority priority,
|
||||
TimeSpan timeout,
|
||||
Func<T> getCacheItem)
|
||||
{
|
||||
if (_enableCache == false)
|
||||
{
|
||||
_nullHttpCache.InsertCacheItem<T>(cacheKey, getCacheItem, timeout, priority: priority);
|
||||
}
|
||||
else
|
||||
{
|
||||
_httpCache.InsertCacheItem<T>(cacheKey, getCacheItem, timeout, priority: priority);
|
||||
}
|
||||
RuntimeCache.InsertCacheItem<T>(cacheKey, getCacheItem, timeout, priority: priority);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -480,19 +374,12 @@ namespace Umbraco.Core
|
||||
TimeSpan timeout,
|
||||
Func<T> getCacheItem)
|
||||
{
|
||||
if (_enableCache == false)
|
||||
var cache = GetHttpRuntimeCacheProvider(RuntimeCache);
|
||||
if (cache != null)
|
||||
{
|
||||
_nullHttpCache.InsertCacheItem<T>(cacheKey, getCacheItem, timeout, priority: priority, dependentFiles:null);
|
||||
}
|
||||
else
|
||||
{
|
||||
var cache = _httpCache as HttpRuntimeCacheProvider;
|
||||
if (cache != null)
|
||||
{
|
||||
cache.InsertCacheItem(cacheKey, () => getCacheItem(), timeout, false, priority, null, cacheDependency);
|
||||
}
|
||||
throw new InvalidOperationException("Cannot use this obsoleted overload when the current provider is not of type " + typeof(HttpRuntimeCacheProvider));
|
||||
cache.InsertCacheItem(cacheKey, () => getCacheItem(), timeout, false, priority, null, cacheDependency);
|
||||
}
|
||||
throw new InvalidOperationException("Cannot use this obsoleted overload when the current provider is not of type " + typeof(HttpRuntimeCacheProvider));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -513,22 +400,29 @@ namespace Umbraco.Core
|
||||
TimeSpan? timeout,
|
||||
Func<T> getCacheItem)
|
||||
{
|
||||
if (_enableCache == false)
|
||||
var cache = GetHttpRuntimeCacheProvider(RuntimeCache);
|
||||
if (cache != null)
|
||||
{
|
||||
_nullHttpCache.InsertCacheItem<T>(cacheKey, getCacheItem, timeout, false, priority, refreshAction, null);
|
||||
}
|
||||
else
|
||||
{
|
||||
var cache = _httpCache as HttpRuntimeCacheProvider;
|
||||
if (cache != null)
|
||||
{
|
||||
cache.InsertCacheItem(cacheKey, () => getCacheItem(), timeout, false, priority, refreshAction, cacheDependency);
|
||||
}
|
||||
throw new InvalidOperationException("Cannot use this obsoleted overload when the current provider is not of type " + typeof(HttpRuntimeCacheProvider));
|
||||
cache.InsertCacheItem(cacheKey, () => getCacheItem(), timeout, false, priority, refreshAction, cacheDependency);
|
||||
}
|
||||
throw new InvalidOperationException("Cannot use this obsoleted overload when the current provider is not of type " + typeof(HttpRuntimeCacheProvider));
|
||||
}
|
||||
#endregion
|
||||
|
||||
}
|
||||
private HttpRuntimeCacheProvider GetHttpRuntimeCacheProvider(IRuntimeCacheProvider runtimeCache)
|
||||
{
|
||||
HttpRuntimeCacheProvider cache;
|
||||
var wrapper = RuntimeCache as IRuntimeCacheProviderWrapper;
|
||||
if (wrapper != null)
|
||||
{
|
||||
cache = wrapper.InnerProvider as HttpRuntimeCacheProvider;
|
||||
}
|
||||
else
|
||||
{
|
||||
cache = RuntimeCache as HttpRuntimeCacheProvider;
|
||||
}
|
||||
return cache;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,141 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Umbraco.Core.Models;
|
||||
using Umbraco.Core.Models.EntityBase;
|
||||
|
||||
namespace Umbraco.Core.Collections
|
||||
{
|
||||
/// <summary>
|
||||
/// A List that can be deep cloned with deep cloned elements and can reset the collection's items dirty flags
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
internal class DeepCloneableList<T> : List<T>, IDeepCloneable, IRememberBeingDirty
|
||||
{
|
||||
private readonly ListCloneBehavior _listCloneBehavior;
|
||||
|
||||
public DeepCloneableList(ListCloneBehavior listCloneBehavior)
|
||||
{
|
||||
_listCloneBehavior = listCloneBehavior;
|
||||
}
|
||||
|
||||
public DeepCloneableList(IEnumerable<T> collection, ListCloneBehavior listCloneBehavior) : base(collection)
|
||||
{
|
||||
_listCloneBehavior = listCloneBehavior;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Default behavior is CloneOnce
|
||||
/// </summary>
|
||||
/// <param name="collection"></param>
|
||||
public DeepCloneableList(IEnumerable<T> collection)
|
||||
: this(collection, ListCloneBehavior.CloneOnce)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new list and adds each element as a deep cloned element if it is of type IDeepCloneable
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public object DeepClone()
|
||||
{
|
||||
switch (_listCloneBehavior)
|
||||
{
|
||||
case ListCloneBehavior.CloneOnce:
|
||||
//we are cloning once, so create a new list in none mode
|
||||
// and deep clone all items into it
|
||||
var newList = new DeepCloneableList<T>(ListCloneBehavior.None);
|
||||
foreach (var item in this)
|
||||
{
|
||||
var dc = item as IDeepCloneable;
|
||||
if (dc != null)
|
||||
{
|
||||
newList.Add((T)dc.DeepClone());
|
||||
}
|
||||
else
|
||||
{
|
||||
newList.Add(item);
|
||||
}
|
||||
}
|
||||
return newList;
|
||||
case ListCloneBehavior.None:
|
||||
//we are in none mode, so just return a new list with the same items
|
||||
return new DeepCloneableList<T>(this, ListCloneBehavior.None);
|
||||
case ListCloneBehavior.Always:
|
||||
//always clone to new list
|
||||
var newList2 = new DeepCloneableList<T>(ListCloneBehavior.Always);
|
||||
foreach (var item in this)
|
||||
{
|
||||
var dc = item as IDeepCloneable;
|
||||
if (dc != null)
|
||||
{
|
||||
newList2.Add((T)dc.DeepClone());
|
||||
}
|
||||
else
|
||||
{
|
||||
newList2.Add(item);
|
||||
}
|
||||
}
|
||||
return newList2;
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException();
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsDirty()
|
||||
{
|
||||
return this.OfType<IRememberBeingDirty>().Any(x => x.IsDirty());
|
||||
}
|
||||
|
||||
public bool WasDirty()
|
||||
{
|
||||
return this.OfType<IRememberBeingDirty>().Any(x => x.WasDirty());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Always returns false, the list has no properties we need to report
|
||||
/// </summary>
|
||||
/// <param name="propName"></param>
|
||||
/// <returns></returns>
|
||||
public bool IsPropertyDirty(string propName)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Always returns false, the list has no properties we need to report
|
||||
/// </summary>
|
||||
/// <param name="propertyName"></param>
|
||||
/// <returns></returns>
|
||||
public bool WasPropertyDirty(string propertyName)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
public void ResetDirtyProperties()
|
||||
{
|
||||
foreach (var dc in this.OfType<IRememberBeingDirty>())
|
||||
{
|
||||
dc.ResetDirtyProperties();
|
||||
}
|
||||
}
|
||||
|
||||
public void ForgetPreviouslyDirtyProperties()
|
||||
{
|
||||
foreach (var dc in this.OfType<IRememberBeingDirty>())
|
||||
{
|
||||
dc.ForgetPreviouslyDirtyProperties();
|
||||
}
|
||||
}
|
||||
|
||||
public void ResetDirtyProperties(bool rememberPreviouslyChangedProperties)
|
||||
{
|
||||
foreach (var dc in this.OfType<IRememberBeingDirty>())
|
||||
{
|
||||
dc.ResetDirtyProperties(rememberPreviouslyChangedProperties);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
namespace Umbraco.Core.Collections
|
||||
{
|
||||
internal enum ListCloneBehavior
|
||||
{
|
||||
/// <summary>
|
||||
/// When set, DeepClone will clone the items one time and the result list behavior will be None
|
||||
/// </summary>
|
||||
CloneOnce,
|
||||
|
||||
/// <summary>
|
||||
/// When set, DeepClone will not clone any items
|
||||
/// </summary>
|
||||
None,
|
||||
|
||||
/// <summary>
|
||||
/// When set, DeepClone will always clone all items
|
||||
/// </summary>
|
||||
Always
|
||||
}
|
||||
}
|
||||
@@ -36,7 +36,7 @@ namespace Umbraco.Core.Configuration
|
||||
//make this volatile so that we can ensure thread safety with a double check lock
|
||||
private static volatile string _reservedUrlsCache;
|
||||
private static string _reservedPathsCache;
|
||||
private static StartsWithContainer _reservedList = new StartsWithContainer();
|
||||
private static HashSet<string> _reservedList = new HashSet<string>();
|
||||
private static string _reservedPaths;
|
||||
private static string _reservedUrls;
|
||||
//ensure the built on (non-changeable) reserved paths are there at all times
|
||||
@@ -767,38 +767,31 @@ namespace Umbraco.Core.Configuration
|
||||
// store references to strings to determine changes
|
||||
_reservedPathsCache = GlobalSettings.ReservedPaths;
|
||||
_reservedUrlsCache = GlobalSettings.ReservedUrls;
|
||||
|
||||
string _root = SystemDirectories.Root.Trim().ToLower();
|
||||
|
||||
|
||||
// add URLs and paths to a new list
|
||||
StartsWithContainer _newReservedList = new StartsWithContainer();
|
||||
foreach (string reservedUrl in _reservedUrlsCache.Split(new[] { "," }, StringSplitOptions.RemoveEmptyEntries))
|
||||
var newReservedList = new HashSet<string>();
|
||||
foreach (var reservedUrlTrimmed in _reservedUrlsCache
|
||||
.Split(new[] {","}, StringSplitOptions.RemoveEmptyEntries)
|
||||
.Select(x => x.Trim().ToLowerInvariant())
|
||||
.Where(x => x.IsNullOrWhiteSpace() == false)
|
||||
.Select(reservedUrl => IOHelper.ResolveUrl(reservedUrl).Trim().EnsureStartsWith("/"))
|
||||
.Where(reservedUrlTrimmed => reservedUrlTrimmed.IsNullOrWhiteSpace() == false))
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(reservedUrl))
|
||||
continue;
|
||||
|
||||
|
||||
//resolves the url to support tilde chars
|
||||
string reservedUrlTrimmed = IOHelper.ResolveUrl(reservedUrl.Trim()).Trim().ToLower();
|
||||
if (reservedUrlTrimmed.Length > 0)
|
||||
_newReservedList.Add(reservedUrlTrimmed);
|
||||
newReservedList.Add(reservedUrlTrimmed);
|
||||
}
|
||||
|
||||
foreach (string reservedPath in _reservedPathsCache.Split(new[] { "," }, StringSplitOptions.RemoveEmptyEntries))
|
||||
foreach (var reservedPathTrimmed in _reservedPathsCache
|
||||
.Split(new[] {","}, StringSplitOptions.RemoveEmptyEntries)
|
||||
.Select(x => x.Trim().ToLowerInvariant())
|
||||
.Where(x => x.IsNullOrWhiteSpace() == false)
|
||||
.Select(reservedPath => IOHelper.ResolveUrl(reservedPath).Trim().EnsureStartsWith("/").EnsureEndsWith("/"))
|
||||
.Where(reservedPathTrimmed => reservedPathTrimmed.IsNullOrWhiteSpace() == false))
|
||||
{
|
||||
bool trimEnd = !reservedPath.EndsWith("/");
|
||||
if (string.IsNullOrWhiteSpace(reservedPath))
|
||||
continue;
|
||||
|
||||
//resolves the url to support tilde chars
|
||||
string reservedPathTrimmed = IOHelper.ResolveUrl(reservedPath.Trim()).Trim().ToLower();
|
||||
|
||||
if (reservedPathTrimmed.Length > 0)
|
||||
_newReservedList.Add(reservedPathTrimmed + (reservedPathTrimmed.EndsWith("/") ? "" : "/"));
|
||||
newReservedList.Add(reservedPathTrimmed);
|
||||
}
|
||||
|
||||
// use the new list from now on
|
||||
_reservedList = _newReservedList;
|
||||
_reservedList = newReservedList;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -806,107 +799,17 @@ namespace Umbraco.Core.Configuration
|
||||
//The url should be cleaned up before checking:
|
||||
// * If it doesn't contain an '.' in the path then we assume it is a path based URL, if that is the case we should add an trailing '/' because all of our reservedPaths use a trailing '/'
|
||||
// * We shouldn't be comparing the query at all
|
||||
var pathPart = url.Split('?')[0];
|
||||
if (!pathPart.Contains(".") && !pathPart.EndsWith("/"))
|
||||
var pathPart = url.Split(new[] {'?'}, StringSplitOptions.RemoveEmptyEntries)[0].ToLowerInvariant();
|
||||
if (pathPart.Contains(".") == false)
|
||||
{
|
||||
pathPart += "/";
|
||||
pathPart = pathPart.EnsureEndsWith('/');
|
||||
}
|
||||
|
||||
// return true if url starts with an element of the reserved list
|
||||
return _reservedList.StartsWith(pathPart.ToLowerInvariant());
|
||||
return _reservedList.Any(x => pathPart.InvariantStartsWith(x));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Structure that checks in logarithmic time
|
||||
/// if a given string starts with one of the added keys.
|
||||
/// </summary>
|
||||
private class StartsWithContainer
|
||||
{
|
||||
/// <summary>Internal sorted list of keys.</summary>
|
||||
public SortedList<string, string> _list
|
||||
= new SortedList<string, string>(StartsWithComparator.Instance);
|
||||
|
||||
/// <summary>
|
||||
/// Adds the specified new key.
|
||||
/// </summary>
|
||||
/// <param name="newKey">The new key.</param>
|
||||
public void Add(string newKey)
|
||||
{
|
||||
// if the list already contains an element that begins with newKey, return
|
||||
if (String.IsNullOrEmpty(newKey) || StartsWith(newKey))
|
||||
return;
|
||||
|
||||
// create a new collection, so the old one can still be accessed
|
||||
SortedList<string, string> newList
|
||||
= new SortedList<string, string>(_list.Count + 1, StartsWithComparator.Instance);
|
||||
|
||||
// add only keys that don't already start with newKey, others are unnecessary
|
||||
foreach (string key in _list.Keys)
|
||||
if (!key.StartsWith(newKey))
|
||||
newList.Add(key, null);
|
||||
// add the new key
|
||||
newList.Add(newKey, null);
|
||||
|
||||
// update the list (thread safe, _list was never in incomplete state)
|
||||
_list = newList;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if the given string starts with any of the added keys.
|
||||
/// </summary>
|
||||
/// <param name="target">The target.</param>
|
||||
/// <returns>true if a key is found that matches the start of target</returns>
|
||||
/// <remarks>
|
||||
/// Runs in O(s*log(n)), with n the number of keys and s the length of target.
|
||||
/// </remarks>
|
||||
public bool StartsWith(string target)
|
||||
{
|
||||
return _list.ContainsKey(target);
|
||||
}
|
||||
|
||||
/// <summary>Comparator that tests if a string starts with another.</summary>
|
||||
/// <remarks>Not a real comparator, since it is not reflexive. (x==y does not imply y==x)</remarks>
|
||||
private sealed class StartsWithComparator : IComparer<string>
|
||||
{
|
||||
/// <summary>Default string comparer.</summary>
|
||||
private readonly static Comparer<string> _stringComparer = Comparer<string>.Default;
|
||||
|
||||
/// <summary>Gets an instance of the StartsWithComparator.</summary>
|
||||
public static readonly StartsWithComparator Instance = new StartsWithComparator();
|
||||
|
||||
/// <summary>
|
||||
/// Tests if whole begins with all characters of part.
|
||||
/// </summary>
|
||||
/// <param name="part">The part.</param>
|
||||
/// <param name="whole">The whole.</param>
|
||||
/// <returns>
|
||||
/// Returns 0 if whole starts with part, otherwise performs standard string comparison.
|
||||
/// </returns>
|
||||
public int Compare(string part, string whole)
|
||||
{
|
||||
// let the default string comparer deal with null or when part is not smaller then whole
|
||||
if (part == null || whole == null || part.Length >= whole.Length)
|
||||
return _stringComparer.Compare(part, whole);
|
||||
|
||||
////ensure both have a / on the end
|
||||
//part = part.EndsWith("/") ? part : part + "/";
|
||||
//whole = whole.EndsWith("/") ? whole : whole + "/";
|
||||
//if (part.Length >= whole.Length)
|
||||
// return _stringComparer.Compare(part, whole);
|
||||
|
||||
// loop through all characters that part and whole have in common
|
||||
int pos = 0;
|
||||
bool match;
|
||||
do
|
||||
{
|
||||
match = (part[pos] == whole[pos]);
|
||||
} while (match && ++pos < part.Length);
|
||||
|
||||
// return result of last comparison
|
||||
return match ? 0 : (part[pos] < whole[pos] ? -1 : 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ namespace Umbraco.Core.Configuration
|
||||
{
|
||||
public class UmbracoVersion
|
||||
{
|
||||
private static readonly Version Version = new Version("7.3.0");
|
||||
private static readonly Version Version = new Version("7.3.8");
|
||||
|
||||
/// <summary>
|
||||
/// Gets the current version of Umbraco.
|
||||
|
||||
@@ -43,7 +43,7 @@
|
||||
public const string Users = "users";
|
||||
|
||||
/// <summary>
|
||||
/// Application alias for the users section.
|
||||
/// Application alias for the forms section.
|
||||
/// </summary>
|
||||
public const string Forms = "forms";
|
||||
}
|
||||
|
||||
@@ -206,7 +206,7 @@ namespace Umbraco.Core
|
||||
/// </summary>
|
||||
internal const string StandardPropertiesGroupName = "Membership";
|
||||
|
||||
internal static Dictionary<string, PropertyType> GetStandardPropertyTypeStubs()
|
||||
public static Dictionary<string, PropertyType> GetStandardPropertyTypeStubs()
|
||||
{
|
||||
return new Dictionary<string, PropertyType>
|
||||
{
|
||||
|
||||
@@ -310,13 +310,13 @@ namespace Umbraco.Core
|
||||
public const string TextboxAlias = "Umbraco.Textbox";
|
||||
|
||||
/// <summary>
|
||||
/// Guid for the Textbox multiple datatype.
|
||||
/// Guid for the Textarea datatype.
|
||||
/// </summary>
|
||||
[Obsolete("GUIDs are no longer used to reference Property Editors, use the Alias constant instead. This will be removed in future versions")]
|
||||
public const string TextboxMultiple = "67DB8357-EF57-493E-91AC-936D305E0F2A";
|
||||
|
||||
/// <summary>
|
||||
/// Alias for the Textbox multiple datatype.
|
||||
/// Alias for the Textarea datatype.
|
||||
/// </summary>
|
||||
public const string TextboxMultipleAlias = "Umbraco.TextboxMultiple";
|
||||
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
namespace Umbraco.Core
|
||||
using System;
|
||||
using System.ComponentModel;
|
||||
|
||||
namespace Umbraco.Core
|
||||
{
|
||||
public static partial class Constants
|
||||
{
|
||||
@@ -7,6 +10,12 @@
|
||||
/// </summary>
|
||||
public static class Web
|
||||
{
|
||||
public const string UmbracoContextDataToken = "umbraco-context";
|
||||
public const string UmbracoDataToken = "umbraco";
|
||||
public const string PublishedDocumentRequestDataToken = "umbraco-doc-request";
|
||||
public const string CustomRouteDataToken = "umbraco-custom-route";
|
||||
public const string UmbracoRouteDefinitionDataToken = "umbraco-route-def";
|
||||
|
||||
/// <summary>
|
||||
/// The preview cookie name
|
||||
/// </summary>
|
||||
@@ -15,6 +24,8 @@
|
||||
/// <summary>
|
||||
/// The auth cookie name
|
||||
/// </summary>
|
||||
[Obsolete("DO NOT USE THIS, USE ISecuritySection.AuthCookieName, this will be removed in future versions")]
|
||||
[EditorBrowsable(EditorBrowsableState.Never)]
|
||||
public const string AuthCookieName = "UMB_UCONTEXT";
|
||||
|
||||
}
|
||||
|
||||
@@ -136,7 +136,10 @@ namespace Umbraco.Core
|
||||
{
|
||||
try
|
||||
{
|
||||
x.OnApplicationInitialized(UmbracoApplication, ApplicationContext);
|
||||
using (ProfilingLogger.DebugDuration<CoreBootManager>(string.Format("Executing {0} in ApplicationInitialized", x.GetType())))
|
||||
{
|
||||
x.OnApplicationInitialized(UmbracoApplication, ApplicationContext);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
@@ -188,10 +191,16 @@ namespace Umbraco.Core
|
||||
protected virtual CacheHelper CreateApplicationCache()
|
||||
{
|
||||
var cacheHelper = new CacheHelper(
|
||||
new ObjectCacheRuntimeCacheProvider(),
|
||||
//we need to have the dep clone runtime cache provider to ensure
|
||||
//all entities are cached properly (cloned in and cloned out)
|
||||
new DeepCloneRuntimeCacheProvider(new ObjectCacheRuntimeCacheProvider()),
|
||||
new StaticCacheProvider(),
|
||||
//we have no request based cache when not running in web-based context
|
||||
new NullCacheProvider());
|
||||
new NullCacheProvider(),
|
||||
new IsolatedRuntimeCache(type =>
|
||||
//we need to have the dep clone runtime cache provider to ensure
|
||||
//all entities are cached properly (cloned in and cloned out)
|
||||
new DeepCloneRuntimeCacheProvider(new ObjectCacheRuntimeCacheProvider())));
|
||||
|
||||
return cacheHelper;
|
||||
}
|
||||
@@ -293,7 +302,10 @@ namespace Umbraco.Core
|
||||
{
|
||||
try
|
||||
{
|
||||
x.OnApplicationStarting(UmbracoApplication, ApplicationContext);
|
||||
using (ProfilingLogger.DebugDuration<CoreBootManager>(string.Format("Executing {0} in ApplicationStarting", x.GetType())))
|
||||
{
|
||||
x.OnApplicationStarting(UmbracoApplication, ApplicationContext);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
@@ -344,7 +356,10 @@ namespace Umbraco.Core
|
||||
{
|
||||
try
|
||||
{
|
||||
x.OnApplicationStarted(UmbracoApplication, ApplicationContext);
|
||||
using (ProfilingLogger.DebugDuration<CoreBootManager>(string.Format("Executing {0} in ApplicationStarted", x.GetType())))
|
||||
{
|
||||
x.OnApplicationStarted(UmbracoApplication, ApplicationContext);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Diagnostics;
|
||||
using System.Web;
|
||||
@@ -12,6 +13,12 @@ namespace Umbraco.Core
|
||||
/// </summary>
|
||||
public class DisposableTimer : DisposableObject
|
||||
{
|
||||
private readonly ILogger _logger;
|
||||
private readonly LogType? _logType;
|
||||
private readonly IProfiler _profiler;
|
||||
private readonly Type _loggerType;
|
||||
private readonly string _endMessage;
|
||||
private readonly IDisposable _profilerStep;
|
||||
private readonly Stopwatch _stopwatch = Stopwatch.StartNew();
|
||||
private readonly Action<long> _callback;
|
||||
|
||||
@@ -25,25 +32,12 @@ namespace Umbraco.Core
|
||||
if (logger == null) throw new ArgumentNullException("logger");
|
||||
if (loggerType == null) throw new ArgumentNullException("loggerType");
|
||||
|
||||
_callback = x =>
|
||||
{
|
||||
if (profiler != null)
|
||||
{
|
||||
profiler.DisposeIfDisposable();
|
||||
}
|
||||
switch (logType)
|
||||
{
|
||||
case LogType.Debug:
|
||||
logger.Debug(loggerType, () => endMessage + " (took " + x + "ms)");
|
||||
break;
|
||||
case LogType.Info:
|
||||
logger.Info(loggerType, () => endMessage + " (took " + x + "ms)");
|
||||
break;
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException("logType");
|
||||
}
|
||||
|
||||
};
|
||||
_logger = logger;
|
||||
_logType = logType;
|
||||
_profiler = profiler;
|
||||
_loggerType = loggerType;
|
||||
_endMessage = endMessage;
|
||||
|
||||
switch (logType)
|
||||
{
|
||||
case LogType.Debug:
|
||||
@@ -58,7 +52,7 @@ namespace Umbraco.Core
|
||||
|
||||
if (profiler != null)
|
||||
{
|
||||
profiler.Step(loggerType, startMessage);
|
||||
_profilerStep = profiler.Step(loggerType, startMessage);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -223,7 +217,36 @@ namespace Umbraco.Core
|
||||
/// </summary>
|
||||
protected override void DisposeResources()
|
||||
{
|
||||
_callback.Invoke(Stopwatch.ElapsedMilliseconds);
|
||||
if (_profiler != null)
|
||||
{
|
||||
_profiler.DisposeIfDisposable();
|
||||
}
|
||||
|
||||
if (_profilerStep != null)
|
||||
{
|
||||
_profilerStep.Dispose();
|
||||
}
|
||||
|
||||
if (_logType.HasValue && _endMessage.IsNullOrWhiteSpace() == false && _loggerType != null && _logger != null)
|
||||
{
|
||||
switch (_logType)
|
||||
{
|
||||
case LogType.Debug:
|
||||
_logger.Debug(_loggerType, () => _endMessage + " (took " + Stopwatch.ElapsedMilliseconds + "ms)");
|
||||
break;
|
||||
case LogType.Info:
|
||||
_logger.Info(_loggerType, () => _endMessage + " (took " + Stopwatch.ElapsedMilliseconds + "ms)");
|
||||
break;
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException("logType");
|
||||
}
|
||||
}
|
||||
|
||||
if (_callback != null)
|
||||
{
|
||||
_callback.Invoke(Stopwatch.ElapsedMilliseconds);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -154,7 +154,7 @@ namespace Umbraco.Core.IO
|
||||
{
|
||||
get
|
||||
{
|
||||
return IOHelper.ReturnPath("umbracoWebservicesPath", "~/umbraco/webservices");
|
||||
return IOHelper.ReturnPath("umbracoWebservicesPath", Umbraco.EnsureEndsWith("/") + "webservices");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -199,8 +199,8 @@ namespace Umbraco.Core.IO
|
||||
using (var image = Image.FromStream(fs))
|
||||
{
|
||||
var fileNameThumb = string.IsNullOrWhiteSpace(fileNameAddition)
|
||||
? string.Format("{0}_UMBRACOSYSTHUMBNAIL.jpg", Path.Substring(0, Path.LastIndexOf(".", StringComparison.Ordinal)))
|
||||
: string.Format("{0}_{1}.jpg", Path.Substring(0, Path.LastIndexOf(".", StringComparison.Ordinal)), fileNameAddition);
|
||||
? string.Format("{0}_UMBRACOSYSTHUMBNAIL." + Extension, Path.Substring(0, Path.LastIndexOf(".", StringComparison.Ordinal)))
|
||||
: string.Format("{0}_{1}." + Extension, Path.Substring(0, Path.LastIndexOf(".", StringComparison.Ordinal)), fileNameAddition);
|
||||
|
||||
var thumbnail = maxWidthHeight == -1
|
||||
? ImageHelper.GenerateThumbnail(image, width, height, fileNameThumb, Extension, _fs)
|
||||
|
||||
@@ -11,8 +11,12 @@ namespace Umbraco.Core.Macros
|
||||
/// </summary>
|
||||
internal class MacroTagParser
|
||||
{
|
||||
private static readonly Regex MacroRteContent = new Regex(@"(<!--\s*?)(<\?UMBRACO_MACRO.*?/>)(\s*?-->)", RegexOptions.Compiled | RegexOptions.IgnoreCase | RegexOptions.Singleline);
|
||||
private static readonly Regex MacroPersistedFormat = new Regex(@"(<\?UMBRACO_MACRO (?:.+?)?macroAlias=[""']([^""\'\n\r]+?)[""'].+?)(?:/>|>.*?</\?UMBRACO_MACRO>)", RegexOptions.Compiled | RegexOptions.IgnoreCase);
|
||||
private static readonly Regex MacroRteContent = new Regex(@"(<!--\s*?)(<\?UMBRACO_MACRO.*?/>)(\s*?-->)",
|
||||
RegexOptions.Compiled | RegexOptions.IgnoreCase | RegexOptions.CultureInvariant | RegexOptions.Singleline);
|
||||
|
||||
private static readonly Regex MacroPersistedFormat =
|
||||
new Regex(@"(<\?UMBRACO_MACRO (?:.+?)??macroAlias=[""']([^""\'\n\r]+?)[""'].+?)(?:/>|>.*?</\?UMBRACO_MACRO>)",
|
||||
RegexOptions.Compiled | RegexOptions.IgnoreCase | RegexOptions.CultureInvariant | RegexOptions.Singleline);
|
||||
|
||||
/// <summary>
|
||||
/// This formats the persisted string to something useful for the rte so that the macro renders properly since we
|
||||
|
||||
@@ -75,7 +75,7 @@ namespace Umbraco.Core.Media.Exif
|
||||
{
|
||||
if (items.ContainsKey (key))
|
||||
items.Remove (key);
|
||||
if (key == ExifTag.WindowsTitle || key == ExifTag.WindowsTitle || key == ExifTag.WindowsComment || key == ExifTag.WindowsAuthor || key == ExifTag.WindowsKeywords || key == ExifTag.WindowsSubject) {
|
||||
if (key == ExifTag.WindowsTitle || key == ExifTag.WindowsComment || key == ExifTag.WindowsAuthor || key == ExifTag.WindowsKeywords || key == ExifTag.WindowsSubject) {
|
||||
items.Add (key, new WindowsByteString (key, value));
|
||||
} else {
|
||||
items.Add (key, new ExifAscii (key, value, parent.Encoding));
|
||||
|
||||
@@ -59,7 +59,7 @@ namespace Umbraco.Core.Media
|
||||
var fileHeight = image.Height;
|
||||
return new Size(fileWidth, fileHeight);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
public static string GetMimeType(this Image image)
|
||||
@@ -79,22 +79,22 @@ namespace Umbraco.Core.Media
|
||||
/// <param name="additionalThumbSizes"></param>
|
||||
/// <returns></returns>
|
||||
internal static IEnumerable<ResizedImage> GenerateMediaThumbnails(
|
||||
IFileSystem fs,
|
||||
string fileName,
|
||||
string extension,
|
||||
IFileSystem fs,
|
||||
string fileName,
|
||||
string extension,
|
||||
Image originalImage,
|
||||
IEnumerable<int> additionalThumbSizes)
|
||||
{
|
||||
|
||||
var result = new List<ResizedImage>();
|
||||
|
||||
var allSizesDictionary = new Dictionary<int,string> {{100,"thumb"}, {500,"big-thumb"}};
|
||||
|
||||
var allSizesDictionary = new Dictionary<int, string> { { 100, "thumb" }, { 500, "big-thumb" } };
|
||||
|
||||
//combine the static dictionary with the additional sizes with only unique values
|
||||
var allSizes = allSizesDictionary.Select(kv => kv.Key)
|
||||
.Union(additionalThumbSizes.Where(x => x > 0).Distinct());
|
||||
|
||||
var sizesDictionary = allSizes.ToDictionary(s => s, s => allSizesDictionary.ContainsKey(s) ? allSizesDictionary[s]: "");
|
||||
var sizesDictionary = allSizes.ToDictionary(s => s, s => allSizesDictionary.ContainsKey(s) ? allSizesDictionary[s] : "");
|
||||
|
||||
foreach (var s in sizesDictionary)
|
||||
{
|
||||
@@ -121,9 +121,9 @@ namespace Umbraco.Core.Media
|
||||
/// <returns></returns>
|
||||
private static ResizedImage Resize(IFileSystem fileSystem, string path, string extension, int maxWidthHeight, string fileNameAddition, Image originalImage)
|
||||
{
|
||||
var fileNameThumb = String.IsNullOrEmpty(fileNameAddition)
|
||||
? string.Format("{0}_UMBRACOSYSTHUMBNAIL.jpg", path.Substring(0, path.LastIndexOf(".")))
|
||||
: string.Format("{0}_{1}.jpg", path.Substring(0, path.LastIndexOf(".")), fileNameAddition);
|
||||
var fileNameThumb = string.IsNullOrWhiteSpace(fileNameAddition)
|
||||
? string.Format("{0}_UMBRACOSYSTHUMBNAIL." + extension, path.Substring(0, path.LastIndexOf(".", StringComparison.Ordinal)))
|
||||
: string.Format("{0}_{1}." + extension, path.Substring(0, path.LastIndexOf(".", StringComparison.Ordinal)), fileNameAddition);
|
||||
|
||||
var thumb = GenerateThumbnail(
|
||||
originalImage,
|
||||
@@ -190,9 +190,9 @@ namespace Umbraco.Core.Media
|
||||
//use best quality
|
||||
g.InterpolationMode = InterpolationMode.HighQualityBicubic;
|
||||
}
|
||||
|
||||
|
||||
g.SmoothingMode = SmoothingMode.HighQuality;
|
||||
|
||||
g.SmoothingMode = SmoothingMode.HighQuality;
|
||||
g.PixelOffsetMode = PixelOffsetMode.HighQuality;
|
||||
g.CompositingQuality = CompositingQuality.HighQuality;
|
||||
|
||||
@@ -202,10 +202,29 @@ namespace Umbraco.Core.Media
|
||||
|
||||
// Copy metadata
|
||||
var imageEncoders = ImageCodecInfo.GetImageEncoders();
|
||||
|
||||
var codec = extension.ToLower() == "png" || extension.ToLower() == "gif"
|
||||
? imageEncoders.Single(t => t.MimeType.Equals("image/png"))
|
||||
: imageEncoders.Single(t => t.MimeType.Equals("image/jpeg"));
|
||||
ImageCodecInfo codec;
|
||||
switch (extension.ToLower())
|
||||
{
|
||||
case "png":
|
||||
codec = imageEncoders.Single(t => t.MimeType.Equals("image/png"));
|
||||
break;
|
||||
case "gif":
|
||||
codec = imageEncoders.Single(t => t.MimeType.Equals("image/gif"));
|
||||
break;
|
||||
case "tif":
|
||||
case "tiff":
|
||||
codec = imageEncoders.Single(t => t.MimeType.Equals("image/tiff"));
|
||||
break;
|
||||
case "bmp":
|
||||
codec = imageEncoders.Single(t => t.MimeType.Equals("image/bmp"));
|
||||
break;
|
||||
// TODO: this is dirty, defaulting to jpg but the return value of this thing is used all over the
|
||||
// place so left it here, but it needs to not set a codec if it doesn't know which one to pick
|
||||
// Note: when fixing this: both .jpg and .jpeg should be handled as extensions
|
||||
default:
|
||||
codec = imageEncoders.Single(t => t.MimeType.Equals("image/jpeg"));
|
||||
break;
|
||||
}
|
||||
|
||||
// Set compresion ratio to 90%
|
||||
var ep = new EncoderParameters();
|
||||
@@ -213,12 +232,14 @@ namespace Umbraco.Core.Media
|
||||
|
||||
// Save the new image using the dimensions of the image
|
||||
var predictableThumbnailName = thumbnailFileName.Replace("UMBRACOSYSTHUMBNAIL", maxWidthHeight.ToString(CultureInfo.InvariantCulture));
|
||||
var predictableThumbnailNameJpg = predictableThumbnailName.Substring(0, predictableThumbnailName.LastIndexOf(".", StringComparison.Ordinal)) + ".jpg";
|
||||
using (var ms = new MemoryStream())
|
||||
{
|
||||
bp.Save(ms, codec, ep);
|
||||
ms.Seek(0, 0);
|
||||
|
||||
fs.AddFile(predictableThumbnailName, ms);
|
||||
fs.AddFile(predictableThumbnailNameJpg, ms);
|
||||
}
|
||||
|
||||
// TODO: Remove this, this is ONLY here for backwards compatibility but it is essentially completely unusable see U4-5385
|
||||
|
||||
@@ -560,8 +560,7 @@ namespace Umbraco.Core.Models
|
||||
//Additional thumbnails configured as prevalues on the DataType
|
||||
if (thumbnailSizes != null)
|
||||
{
|
||||
var sep = (thumbnailSizes.Contains("") == false && thumbnailSizes.Contains(",")) ? ',' : ';';
|
||||
foreach (var thumb in thumbnailSizes.Split(sep))
|
||||
foreach (var thumb in thumbnailSizes.Split(new[] { ";", "," }, StringSplitOptions.RemoveEmptyEntries))
|
||||
{
|
||||
int thumbSize;
|
||||
if (thumb != "" && int.TryParse(thumb, out thumbSize))
|
||||
|
||||
@@ -53,6 +53,9 @@ namespace Umbraco.Core.Models
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the alias of the default Template.
|
||||
/// TODO: This should be ignored from cloning!!!!!!!!!!!!!!
|
||||
/// - but to do that we have to implement callback hacks, this needs to be fixed in v8,
|
||||
/// we should not store direct entity
|
||||
/// </summary>
|
||||
[IgnoreDataMember]
|
||||
public ITemplate DefaultTemplate
|
||||
@@ -79,6 +82,9 @@ namespace Umbraco.Core.Models
|
||||
|
||||
/// <summary>
|
||||
/// Gets or Sets a list of Templates which are allowed for the ContentType
|
||||
/// TODO: This should be ignored from cloning!!!!!!!!!!!!!!
|
||||
/// - but to do that we have to implement callback hacks, this needs to be fixed in v8,
|
||||
/// we should not store direct entity
|
||||
/// </summary>
|
||||
[DataMember]
|
||||
public IEnumerable<ITemplate> AllowedTemplates
|
||||
|
||||
@@ -533,6 +533,18 @@ namespace Umbraco.Core.Models
|
||||
/// <param name="propertyGroupName">Name of the <see cref="PropertyGroup"/> to remove</param>
|
||||
public void RemovePropertyGroup(string propertyGroupName)
|
||||
{
|
||||
// if no group exists with that name, do nothing
|
||||
var group = PropertyGroups[propertyGroupName];
|
||||
if (group == null) return;
|
||||
|
||||
// re-assign the group's properties to no group
|
||||
foreach (var property in group.PropertyTypes)
|
||||
{
|
||||
property.PropertyGroupId = new Lazy<int>(() => 0);
|
||||
_propertyTypes.Add(property);
|
||||
}
|
||||
|
||||
// actually remove the group
|
||||
PropertyGroups.RemoveItem(propertyGroupName);
|
||||
OnPropertyChanged(PropertyGroupCollectionSelector);
|
||||
}
|
||||
|
||||
@@ -9,10 +9,30 @@ namespace Umbraco.Core.Models
|
||||
{
|
||||
public static class DeepCloneHelper
|
||||
{
|
||||
/// <summary>
|
||||
/// Stores the metadata for the properties for a given type so we know how to create them
|
||||
/// </summary>
|
||||
private struct ClonePropertyInfo
|
||||
{
|
||||
public ClonePropertyInfo(PropertyInfo propertyInfo) : this()
|
||||
{
|
||||
if (propertyInfo == null) throw new ArgumentNullException("propertyInfo");
|
||||
PropertyInfo = propertyInfo;
|
||||
}
|
||||
|
||||
public PropertyInfo PropertyInfo { get; private set; }
|
||||
public bool IsDeepCloneable { get; set; }
|
||||
public Type GenericListType { get; set; }
|
||||
public bool IsList
|
||||
{
|
||||
get { return GenericListType != null; }
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Used to avoid constant reflection (perf)
|
||||
/// </summary>
|
||||
private static readonly ConcurrentDictionary<Type, PropertyInfo[]> PropCache = new ConcurrentDictionary<Type, PropertyInfo[]>();
|
||||
private static readonly ConcurrentDictionary<Type, ClonePropertyInfo[]> PropCache = new ConcurrentDictionary<Type, ClonePropertyInfo[]>();
|
||||
|
||||
/// <summary>
|
||||
/// Used to deep clone any reference properties on the object (should be done after a MemberwiseClone for which the outcome is 'output')
|
||||
@@ -30,75 +50,99 @@ namespace Umbraco.Core.Models
|
||||
throw new InvalidOperationException("Both the input and output types must be the same");
|
||||
}
|
||||
|
||||
//get the property metadata from cache so we only have to figure this out once per type
|
||||
var refProperties = PropCache.GetOrAdd(inputType, type =>
|
||||
inputType.GetProperties()
|
||||
.Where(x =>
|
||||
//is not attributed with the ignore clone attribute
|
||||
x.GetCustomAttribute<DoNotCloneAttribute>() == null
|
||||
.Select<PropertyInfo, ClonePropertyInfo?>(propertyInfo =>
|
||||
{
|
||||
if (
|
||||
//is not attributed with the ignore clone attribute
|
||||
propertyInfo.GetCustomAttribute<DoNotCloneAttribute>() != null
|
||||
//reference type but not string
|
||||
&& x.PropertyType.IsValueType == false && x.PropertyType != typeof (string)
|
||||
|| propertyInfo.PropertyType.IsValueType || propertyInfo.PropertyType == typeof (string)
|
||||
//settable
|
||||
&& x.CanWrite
|
||||
|| propertyInfo.CanWrite == false
|
||||
//non-indexed
|
||||
&& x.GetIndexParameters().Any() == false)
|
||||
|| propertyInfo.GetIndexParameters().Any())
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
if (TypeHelper.IsTypeAssignableFrom<IDeepCloneable>(propertyInfo.PropertyType))
|
||||
{
|
||||
return new ClonePropertyInfo(propertyInfo) { IsDeepCloneable = true };
|
||||
}
|
||||
|
||||
if (TypeHelper.IsTypeAssignableFrom<IEnumerable>(propertyInfo.PropertyType)
|
||||
&& TypeHelper.IsTypeAssignableFrom<string>(propertyInfo.PropertyType) == false)
|
||||
{
|
||||
if (propertyInfo.PropertyType.IsGenericType
|
||||
&& (propertyInfo.PropertyType.GetGenericTypeDefinition() == typeof(IEnumerable<>)
|
||||
|| propertyInfo.PropertyType.GetGenericTypeDefinition() == typeof(ICollection<>)
|
||||
|| propertyInfo.PropertyType.GetGenericTypeDefinition() == typeof(IList<>)))
|
||||
{
|
||||
//if it is a IEnumerable<>, IList<T> or ICollection<> we'll use a List<>
|
||||
var genericType = typeof(List<>).MakeGenericType(propertyInfo.PropertyType.GetGenericArguments());
|
||||
return new ClonePropertyInfo(propertyInfo) { GenericListType = genericType };
|
||||
}
|
||||
if (propertyInfo.PropertyType.IsArray
|
||||
|| (propertyInfo.PropertyType.IsInterface && propertyInfo.PropertyType.IsGenericType == false))
|
||||
{
|
||||
//if its an array, we'll create a list to work with first and then convert to array later
|
||||
//otherwise if its just a regular derivitave of IEnumerable, we can use a list too
|
||||
return new ClonePropertyInfo(propertyInfo) { GenericListType = typeof(List<object>) };
|
||||
}
|
||||
//skip instead of trying to create instance of abstract or interface
|
||||
if (propertyInfo.PropertyType.IsAbstract || propertyInfo.PropertyType.IsInterface)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
//its a custom IEnumerable, we'll try to create it
|
||||
try
|
||||
{
|
||||
var custom = Activator.CreateInstance(propertyInfo.PropertyType);
|
||||
//if it's an IList we can work with it, otherwise we cannot
|
||||
var newList = custom as IList;
|
||||
if (newList == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
return new ClonePropertyInfo(propertyInfo) {GenericListType = propertyInfo.PropertyType};
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
//could not create this type so we'll skip it
|
||||
return null;
|
||||
}
|
||||
}
|
||||
return new ClonePropertyInfo(propertyInfo);
|
||||
})
|
||||
.Where(x => x.HasValue)
|
||||
.Select(x => x.Value)
|
||||
.ToArray());
|
||||
|
||||
foreach (var propertyInfo in refProperties)
|
||||
foreach (var clonePropertyInfo in refProperties)
|
||||
{
|
||||
if (TypeHelper.IsTypeAssignableFrom<IDeepCloneable>(propertyInfo.PropertyType))
|
||||
if (clonePropertyInfo.IsDeepCloneable)
|
||||
{
|
||||
//this ref property is also deep cloneable so clone it
|
||||
var result = (IDeepCloneable)propertyInfo.GetValue(input, null);
|
||||
var result = (IDeepCloneable)clonePropertyInfo.PropertyInfo.GetValue(input, null);
|
||||
|
||||
if (result != null)
|
||||
{
|
||||
//set the cloned value to the property
|
||||
propertyInfo.SetValue(output, result.DeepClone(), null);
|
||||
clonePropertyInfo.PropertyInfo.SetValue(output, result.DeepClone(), null);
|
||||
}
|
||||
}
|
||||
else if (TypeHelper.IsTypeAssignableFrom<IEnumerable>(propertyInfo.PropertyType)
|
||||
&& TypeHelper.IsTypeAssignableFrom<string>(propertyInfo.PropertyType) == false)
|
||||
else if (clonePropertyInfo.IsList)
|
||||
{
|
||||
IList newList;
|
||||
if (propertyInfo.PropertyType.IsGenericType
|
||||
&& (propertyInfo.PropertyType.GetGenericTypeDefinition() == typeof(IEnumerable<>)
|
||||
|| propertyInfo.PropertyType.GetGenericTypeDefinition() == typeof(ICollection<>)
|
||||
|| propertyInfo.PropertyType.GetGenericTypeDefinition() == typeof(IList<>)))
|
||||
{
|
||||
//if it is a IEnumerable<>, IList<T> or ICollection<> we'll use a List<>
|
||||
var genericType = typeof(List<>).MakeGenericType(propertyInfo.PropertyType.GetGenericArguments());
|
||||
newList = (IList)Activator.CreateInstance(genericType);
|
||||
}
|
||||
else if (propertyInfo.PropertyType.IsArray
|
||||
|| (propertyInfo.PropertyType.IsInterface && propertyInfo.PropertyType.IsGenericType == false))
|
||||
{
|
||||
//if its an array, we'll create a list to work with first and then convert to array later
|
||||
//otherwise if its just a regular derivitave of IEnumerable, we can use a list too
|
||||
newList = new List<object>();
|
||||
}
|
||||
else
|
||||
{
|
||||
//its a custom IEnumerable, we'll try to create it
|
||||
try
|
||||
{
|
||||
var custom = Activator.CreateInstance(propertyInfo.PropertyType);
|
||||
//if it's an IList we can work with it, otherwise we cannot
|
||||
newList = custom as IList;
|
||||
if (newList == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
//could not create this type so we'll skip it
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
var enumerable = (IEnumerable)propertyInfo.GetValue(input, null);
|
||||
var enumerable = (IEnumerable)clonePropertyInfo.PropertyInfo.GetValue(input, null);
|
||||
if (enumerable == null) continue;
|
||||
|
||||
var newList = (IList)Activator.CreateInstance(clonePropertyInfo.GenericListType);
|
||||
|
||||
var isUsableType = true;
|
||||
|
||||
//now clone each item
|
||||
@@ -130,21 +174,21 @@ namespace Umbraco.Core.Models
|
||||
continue;
|
||||
}
|
||||
|
||||
if (propertyInfo.PropertyType.IsArray)
|
||||
if (clonePropertyInfo.PropertyInfo.PropertyType.IsArray)
|
||||
{
|
||||
//need to convert to array
|
||||
var arr = (object[])Activator.CreateInstance(propertyInfo.PropertyType, newList.Count);
|
||||
var arr = (object[])Activator.CreateInstance(clonePropertyInfo.PropertyInfo.PropertyType, newList.Count);
|
||||
for (int i = 0; i < newList.Count; i++)
|
||||
{
|
||||
arr[i] = newList[i];
|
||||
}
|
||||
//set the cloned collection
|
||||
propertyInfo.SetValue(output, arr, null);
|
||||
clonePropertyInfo.PropertyInfo.SetValue(output, arr, null);
|
||||
}
|
||||
else
|
||||
{
|
||||
//set the cloned collection
|
||||
propertyInfo.SetValue(output, newList, null);
|
||||
clonePropertyInfo.PropertyInfo.SetValue(output, newList, null);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -14,6 +14,7 @@ namespace Umbraco.Core.Models
|
||||
[DataContract(IsReference = true)]
|
||||
public class DictionaryItem : Entity, IDictionaryItem
|
||||
{
|
||||
public Func<int, ILanguage> GetLanguage { get; set; }
|
||||
private Guid? _parentId;
|
||||
private string _itemKey;
|
||||
private IEnumerable<IDictionaryTranslation> _translations;
|
||||
@@ -78,7 +79,17 @@ namespace Umbraco.Core.Models
|
||||
{
|
||||
SetPropertyValueAndDetectChanges(o =>
|
||||
{
|
||||
_translations = value;
|
||||
var asArray = value.ToArray();
|
||||
//ensure the language callback is set on each translation
|
||||
if (GetLanguage != null)
|
||||
{
|
||||
foreach (var translation in asArray.OfType<DictionaryTranslation>())
|
||||
{
|
||||
translation.GetLanguage = GetLanguage;
|
||||
}
|
||||
}
|
||||
|
||||
_translations = asArray;
|
||||
return _translations;
|
||||
}, _translations, TranslationsSelector,
|
||||
//Custom comparer for enumerable
|
||||
|
||||
@@ -13,13 +13,18 @@ namespace Umbraco.Core.Models
|
||||
[DataContract(IsReference = true)]
|
||||
public class DictionaryTranslation : Entity, IDictionaryTranslation
|
||||
{
|
||||
internal Func<int, ILanguage> GetLanguage { get; set; }
|
||||
|
||||
private ILanguage _language;
|
||||
private string _value;
|
||||
//note: this will be memberwise cloned
|
||||
private int _languageId;
|
||||
|
||||
public DictionaryTranslation(ILanguage language, string value)
|
||||
{
|
||||
if (language == null) throw new ArgumentNullException("language");
|
||||
_language = language;
|
||||
_languageId = _language.Id;
|
||||
_value = value;
|
||||
}
|
||||
|
||||
@@ -27,6 +32,20 @@ namespace Umbraco.Core.Models
|
||||
{
|
||||
if (language == null) throw new ArgumentNullException("language");
|
||||
_language = language;
|
||||
_languageId = _language.Id;
|
||||
_value = value;
|
||||
Key = uniqueId;
|
||||
}
|
||||
|
||||
internal DictionaryTranslation(int languageId, string value)
|
||||
{
|
||||
_languageId = languageId;
|
||||
_value = value;
|
||||
}
|
||||
|
||||
internal DictionaryTranslation(int languageId, string value, Guid uniqueId)
|
||||
{
|
||||
_languageId = languageId;
|
||||
_value = value;
|
||||
Key = uniqueId;
|
||||
}
|
||||
@@ -37,20 +56,43 @@ namespace Umbraco.Core.Models
|
||||
/// <summary>
|
||||
/// Gets or sets the <see cref="Language"/> for the translation
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Marked as DoNotClone - TODO: this member shouldn't really exist here in the first place, the DictionaryItem
|
||||
/// class will have a deep hierarchy of objects which all get deep cloned which we don't want. This should have simply
|
||||
/// just referenced a language ID not the actual language object. In v8 we need to fix this.
|
||||
/// We're going to have to do the same hacky stuff we had to do with the Template/File contents so that this is returned
|
||||
/// on a callback.
|
||||
/// </remarks>
|
||||
[DataMember]
|
||||
[DoNotClone]
|
||||
public ILanguage Language
|
||||
{
|
||||
get { return _language; }
|
||||
get
|
||||
{
|
||||
if (_language != null)
|
||||
return _language;
|
||||
|
||||
// else, must lazy-load
|
||||
if (GetLanguage != null && _languageId > 0)
|
||||
_language = GetLanguage(_languageId);
|
||||
return _language;
|
||||
}
|
||||
set
|
||||
{
|
||||
SetPropertyValueAndDetectChanges(o =>
|
||||
{
|
||||
_language = value;
|
||||
_languageId = _language == null ? -1 : _language.Id;
|
||||
return _language;
|
||||
}, _language, LanguageSelector);
|
||||
}
|
||||
}
|
||||
|
||||
public int LanguageId
|
||||
{
|
||||
get { return _languageId; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the translated text
|
||||
/// </summary>
|
||||
@@ -68,5 +110,23 @@ namespace Umbraco.Core.Models
|
||||
}
|
||||
}
|
||||
|
||||
public override object DeepClone()
|
||||
{
|
||||
var clone = (DictionaryTranslation)base.DeepClone();
|
||||
|
||||
// clear fields that were memberwise-cloned and that we don't want to clone
|
||||
clone._language = null;
|
||||
|
||||
// turn off change tracking
|
||||
clone.DisableChangeTracking();
|
||||
|
||||
// this shouldn't really be needed since we're not tracking
|
||||
clone.ResetDirtyProperties(false);
|
||||
|
||||
// re-enable tracking
|
||||
clone.EnableChangeTracking();
|
||||
|
||||
return clone;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -12,6 +12,8 @@ namespace Umbraco.Core.Models
|
||||
[DataMember]
|
||||
ILanguage Language { get; set; }
|
||||
|
||||
int LanguageId { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the translated text
|
||||
/// </summary>
|
||||
|
||||
@@ -4,6 +4,7 @@ using AutoMapper;
|
||||
|
||||
using Umbraco.Core.Models.Mapping;
|
||||
using Umbraco.Core.Models.Membership;
|
||||
using Umbraco.Core.Security;
|
||||
|
||||
namespace Umbraco.Core.Models.Identity
|
||||
{
|
||||
@@ -24,6 +25,18 @@ namespace Umbraco.Core.Models.Identity
|
||||
.ForMember(user => user.UserTypeAlias, expression => expression.MapFrom(user => user.UserType.Alias))
|
||||
.ForMember(user => user.AccessFailedCount, expression => expression.MapFrom(user => user.FailedPasswordAttempts))
|
||||
.ForMember(user => user.AllowedSections, expression => expression.MapFrom(user => user.AllowedSections.ToArray()));
|
||||
|
||||
config.CreateMap<BackOfficeIdentityUser, UserData>()
|
||||
.ConstructUsing((BackOfficeIdentityUser user) => new UserData(Guid.NewGuid().ToString("N"))) //this is the 'session id'
|
||||
.ForMember(detail => detail.Id, opt => opt.MapFrom(user => user.Id))
|
||||
.ForMember(detail => detail.AllowedApplications, opt => opt.MapFrom(user => user.AllowedSections))
|
||||
.ForMember(detail => detail.RealName, opt => opt.MapFrom(user => user.Name))
|
||||
.ForMember(detail => detail.Roles, opt => opt.MapFrom(user => new[] { user.UserTypeAlias }))
|
||||
.ForMember(detail => detail.StartContentNode, opt => opt.MapFrom(user => user.StartContentId))
|
||||
.ForMember(detail => detail.StartMediaNode, opt => opt.MapFrom(user => user.StartMediaId))
|
||||
.ForMember(detail => detail.Username, opt => opt.MapFrom(user => user.UserName))
|
||||
.ForMember(detail => detail.Culture, opt => opt.MapFrom(user => user.Culture))
|
||||
.ForMember(detail => detail.SessionId, opt => opt.MapFrom(user => user.SecurityStamp.IsNullOrWhiteSpace() ? Guid.NewGuid().ToString("N") : user.SecurityStamp));
|
||||
}
|
||||
|
||||
private string GetPasswordHash(string storedPass)
|
||||
|
||||
@@ -64,7 +64,7 @@ namespace Umbraco.Core.Models
|
||||
[IgnoreDataMember]
|
||||
public CultureInfo CultureInfo
|
||||
{
|
||||
get { return CultureInfo.CreateSpecificCulture(IsoCode); }
|
||||
get { return CultureInfo.GetCultureInfo(IsoCode); }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -280,7 +280,7 @@ namespace Umbraco.Core.Models
|
||||
//This is the default value if the prop is not found
|
||||
true);
|
||||
if (a.Success == false) return a.Result;
|
||||
|
||||
if (Properties[Constants.Conventions.Member.IsApproved].Value == null) return true;
|
||||
var tryConvert = Properties[Constants.Conventions.Member.IsApproved].Value.TryConvertTo<bool>();
|
||||
if (tryConvert.Success)
|
||||
{
|
||||
@@ -313,7 +313,7 @@ namespace Umbraco.Core.Models
|
||||
{
|
||||
var a = WarnIfPropertyTypeNotFoundOnGet(Constants.Conventions.Member.IsLockedOut, "IsLockedOut", false);
|
||||
if (a.Success == false) return a.Result;
|
||||
|
||||
if (Properties[Constants.Conventions.Member.IsLockedOut].Value == null) return false;
|
||||
var tryConvert = Properties[Constants.Conventions.Member.IsLockedOut].Value.TryConvertTo<bool>();
|
||||
if (tryConvert.Success)
|
||||
{
|
||||
@@ -346,7 +346,7 @@ namespace Umbraco.Core.Models
|
||||
{
|
||||
var a = WarnIfPropertyTypeNotFoundOnGet(Constants.Conventions.Member.LastLoginDate, "LastLoginDate", default(DateTime));
|
||||
if (a.Success == false) return a.Result;
|
||||
|
||||
if (Properties[Constants.Conventions.Member.LastLoginDate].Value == null) return default(DateTime);
|
||||
var tryConvert = Properties[Constants.Conventions.Member.LastLoginDate].Value.TryConvertTo<DateTime>();
|
||||
if (tryConvert.Success)
|
||||
{
|
||||
@@ -379,7 +379,7 @@ namespace Umbraco.Core.Models
|
||||
{
|
||||
var a = WarnIfPropertyTypeNotFoundOnGet(Constants.Conventions.Member.LastPasswordChangeDate, "LastPasswordChangeDate", default(DateTime));
|
||||
if (a.Success == false) return a.Result;
|
||||
|
||||
if (Properties[Constants.Conventions.Member.LastPasswordChangeDate].Value == null) return default(DateTime);
|
||||
var tryConvert = Properties[Constants.Conventions.Member.LastPasswordChangeDate].Value.TryConvertTo<DateTime>();
|
||||
if (tryConvert.Success)
|
||||
{
|
||||
@@ -412,7 +412,7 @@ namespace Umbraco.Core.Models
|
||||
{
|
||||
var a = WarnIfPropertyTypeNotFoundOnGet(Constants.Conventions.Member.LastLockoutDate, "LastLockoutDate", default(DateTime));
|
||||
if (a.Success == false) return a.Result;
|
||||
|
||||
if (Properties[Constants.Conventions.Member.LastLockoutDate].Value == null) return default(DateTime);
|
||||
var tryConvert = Properties[Constants.Conventions.Member.LastLockoutDate].Value.TryConvertTo<DateTime>();
|
||||
if (tryConvert.Success)
|
||||
{
|
||||
@@ -446,7 +446,7 @@ namespace Umbraco.Core.Models
|
||||
{
|
||||
var a = WarnIfPropertyTypeNotFoundOnGet(Constants.Conventions.Member.FailedPasswordAttempts, "FailedPasswordAttempts", 0);
|
||||
if (a.Success == false) return a.Result;
|
||||
|
||||
if (Properties[Constants.Conventions.Member.FailedPasswordAttempts].Value == null) return default(int);
|
||||
var tryConvert = Properties[Constants.Conventions.Member.FailedPasswordAttempts].Value.TryConvertTo<int>();
|
||||
if (tryConvert.Success)
|
||||
{
|
||||
|
||||
@@ -230,13 +230,13 @@ namespace Umbraco.Core.Models.PublishedContent
|
||||
{
|
||||
_sourceCacheLevel = converterMeta.GetPropertyCacheLevel(this, PropertyCacheValue.Source);
|
||||
_objectCacheLevel = converterMeta.GetPropertyCacheLevel(this, PropertyCacheValue.Object);
|
||||
_objectCacheLevel = converterMeta.GetPropertyCacheLevel(this, PropertyCacheValue.XPath);
|
||||
_xpathCacheLevel = converterMeta.GetPropertyCacheLevel(this, PropertyCacheValue.XPath);
|
||||
}
|
||||
else
|
||||
{
|
||||
_sourceCacheLevel = GetCacheLevel(_converter, PropertyCacheValue.Source);
|
||||
_objectCacheLevel = GetCacheLevel(_converter, PropertyCacheValue.Object);
|
||||
_objectCacheLevel = GetCacheLevel(_converter, PropertyCacheValue.XPath);
|
||||
_xpathCacheLevel = GetCacheLevel(_converter, PropertyCacheValue.XPath);
|
||||
}
|
||||
if (_objectCacheLevel < _sourceCacheLevel) _objectCacheLevel = _sourceCacheLevel;
|
||||
if (_xpathCacheLevel < _sourceCacheLevel) _xpathCacheLevel = _sourceCacheLevel;
|
||||
|
||||
@@ -11,7 +11,7 @@ namespace Umbraco.Core.Models.Rdbms
|
||||
internal class MigrationDto
|
||||
{
|
||||
[Column("id")]
|
||||
[PrimaryKeyColumn(AutoIncrement = true)]
|
||||
[PrimaryKeyColumn(AutoIncrement = true, IdentitySeed = 100)]
|
||||
public int Id { get; set; }
|
||||
|
||||
[Column("name")]
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using Umbraco.Core.Models.Identity;
|
||||
using Umbraco.Core.Models.Membership;
|
||||
using Umbraco.Core.Services;
|
||||
|
||||
|
||||
@@ -1,15 +1,10 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Linq.Expressions;
|
||||
using System.Reflection;
|
||||
using System.Runtime.Serialization;
|
||||
using System.Runtime.Serialization.Formatters.Binary;
|
||||
using System.Security;
|
||||
using System.Xml;
|
||||
|
||||
namespace Umbraco.Core
|
||||
@@ -51,7 +46,7 @@ namespace Umbraco.Core
|
||||
public static Attempt<T> TryConvertTo<T>(this object input)
|
||||
{
|
||||
var result = TryConvertTo(input, typeof(T));
|
||||
if (!result.Success)
|
||||
if (result.Success == false)
|
||||
{
|
||||
//just try a straight up conversion
|
||||
try
|
||||
@@ -64,7 +59,7 @@ namespace Umbraco.Core
|
||||
return Attempt<T>.Fail(e);
|
||||
}
|
||||
}
|
||||
return !result.Success ? Attempt<T>.Fail() : Attempt<T>.Succeed((T)result.Result);
|
||||
return result.Success == false ? Attempt<T>.Fail() : Attempt<T>.Succeed((T)result.Result);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -117,7 +112,7 @@ namespace Umbraco.Core
|
||||
}
|
||||
|
||||
// we've already dealed with nullables, so any other generic types need to fall through
|
||||
if (!destinationType.IsGenericType)
|
||||
if (destinationType.IsGenericType == false)
|
||||
{
|
||||
if (input is string)
|
||||
{
|
||||
@@ -335,11 +330,11 @@ namespace Umbraco.Core
|
||||
return null; // we can't decide...
|
||||
}
|
||||
|
||||
private readonly static char[] NumberDecimalSeparatorsToNormalize = new[] {'.', ','};
|
||||
private static readonly char[] NumberDecimalSeparatorsToNormalize = new[] {'.', ','};
|
||||
|
||||
private static string NormalizeNumberDecimalSeparator(string s)
|
||||
{
|
||||
var normalized = System.Threading.Thread.CurrentThread.CurrentUICulture.NumberFormat.NumberDecimalSeparator[0];
|
||||
var normalized = System.Threading.Thread.CurrentThread.CurrentCulture.NumberFormat.NumberDecimalSeparator[0];
|
||||
return s.ReplaceMany(NumberDecimalSeparatorsToNormalize, normalized);
|
||||
}
|
||||
|
||||
@@ -442,7 +437,7 @@ namespace Umbraco.Core
|
||||
{
|
||||
var props = TypeDescriptor.GetProperties(o);
|
||||
var d = new Dictionary<string, TVal>();
|
||||
foreach (var prop in props.Cast<PropertyDescriptor>().Where(x => !ignoreProperties.Contains(x.Name)))
|
||||
foreach (var prop in props.Cast<PropertyDescriptor>().Where(x => ignoreProperties.Contains(x.Name) == false))
|
||||
{
|
||||
var val = prop.GetValue(o);
|
||||
if (val != null)
|
||||
@@ -478,13 +473,13 @@ namespace Umbraco.Core
|
||||
|
||||
var items = (from object enumItem in enumerable let value = GetEnumPropertyDebugString(enumItem, levels) where value != null select value).Take(10).ToList();
|
||||
|
||||
return items.Count() > 0
|
||||
return items.Any()
|
||||
? "{{ {0} }}".InvariantFormat(String.Join(", ", items))
|
||||
: null;
|
||||
}
|
||||
|
||||
var props = obj.GetType().GetProperties();
|
||||
if ((props.Count() == 2) && props[0].Name == "Key" && props[1].Name == "Value" && levels > -2)
|
||||
if ((props.Length == 2) && props[0].Name == "Key" && props[1].Name == "Value" && levels > -2)
|
||||
{
|
||||
try
|
||||
{
|
||||
@@ -500,12 +495,12 @@ namespace Umbraco.Core
|
||||
if (levels > -1)
|
||||
{
|
||||
var items =
|
||||
from propertyInfo in props
|
||||
(from propertyInfo in props
|
||||
let value = GetPropertyDebugString(propertyInfo, obj, levels)
|
||||
where value != null
|
||||
select "{0}={1}".InvariantFormat(propertyInfo.Name, value);
|
||||
select "{0}={1}".InvariantFormat(propertyInfo.Name, value)).ToArray();
|
||||
|
||||
return items.Count() > 0
|
||||
return items.Any()
|
||||
? "[{0}]:{{ {1} }}".InvariantFormat(obj.GetType().Name, String.Join(", ", items))
|
||||
: null;
|
||||
}
|
||||
|
||||
@@ -42,7 +42,7 @@ namespace Umbraco.Core.Persistence.Factories
|
||||
{
|
||||
var text = new LanguageTextDto
|
||||
{
|
||||
LanguageId = translation.Language.Id,
|
||||
LanguageId = translation.LanguageId,
|
||||
UniqueId = translation.Key,
|
||||
Value = translation.Value
|
||||
};
|
||||
|
||||
@@ -7,20 +7,19 @@ namespace Umbraco.Core.Persistence.Factories
|
||||
internal class DictionaryTranslationFactory
|
||||
{
|
||||
private readonly Guid _uniqueId;
|
||||
private ILanguage _language;
|
||||
|
||||
public DictionaryTranslationFactory(Guid uniqueId, ILanguage language)
|
||||
public DictionaryTranslationFactory(Guid uniqueId)
|
||||
{
|
||||
_uniqueId = uniqueId;
|
||||
_language = language;
|
||||
}
|
||||
|
||||
#region Implementation of IEntityFactory<DictionaryTranslation,LanguageTextDto>
|
||||
|
||||
public IDictionaryTranslation BuildEntity(LanguageTextDto dto)
|
||||
{
|
||||
var item = new DictionaryTranslation(_language, dto.Value, _uniqueId)
|
||||
var item = new DictionaryTranslation(dto.LanguageId, dto.Value, _uniqueId)
|
||||
{Id = dto.PrimaryKey};
|
||||
|
||||
//on initial construction we don't want to have dirty properties tracked
|
||||
// http://issues.umbraco.org/issue/U4-1946
|
||||
item.ResetDirtyProperties(false);
|
||||
@@ -31,7 +30,7 @@ namespace Umbraco.Core.Persistence.Factories
|
||||
{
|
||||
var text = new LanguageTextDto
|
||||
{
|
||||
LanguageId = entity.Language.Id,
|
||||
LanguageId = entity.LanguageId,
|
||||
UniqueId = _uniqueId,
|
||||
Value = entity.Value
|
||||
};
|
||||
|
||||
@@ -129,11 +129,7 @@ namespace Umbraco.Core.Persistence.Factories
|
||||
Name = propertyType.Name,
|
||||
SortOrder = propertyType.SortOrder,
|
||||
ValidationRegExp = propertyType.ValidationRegExp,
|
||||
UniqueId = propertyType.HasIdentity
|
||||
? propertyType.Key == Guid.Empty
|
||||
? Guid.NewGuid()
|
||||
: propertyType.Key
|
||||
: Guid.NewGuid()
|
||||
UniqueId = propertyType.Key == Guid.Empty ? Guid.NewGuid() : propertyType.Key
|
||||
};
|
||||
|
||||
if (tabId != default(int))
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
using System.Collections.Concurrent;
|
||||
using Umbraco.Core.Models;
|
||||
using Umbraco.Core.Models.Rdbms;
|
||||
|
||||
namespace Umbraco.Core.Persistence.Mappers
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a <see cref="TaskType"/> to DTO mapper used to translate the properties of the public api
|
||||
/// implementation to that of the database's DTO as sql: [tableName].[columnName].
|
||||
/// </summary>
|
||||
[MapperFor(typeof(TaskType))]
|
||||
public sealed class TaskTypeMapper : BaseMapper
|
||||
{
|
||||
private static readonly ConcurrentDictionary<string, DtoMapModel> PropertyInfoCacheInstance = new ConcurrentDictionary<string, DtoMapModel>();
|
||||
|
||||
//NOTE: its an internal class but the ctor must be public since we're using Activator.CreateInstance to create it
|
||||
// otherwise that would fail because there is no public constructor.
|
||||
public TaskTypeMapper()
|
||||
{
|
||||
BuildMap();
|
||||
}
|
||||
|
||||
#region Overrides of BaseMapper
|
||||
|
||||
internal override ConcurrentDictionary<string, DtoMapModel> PropertyInfoCache
|
||||
{
|
||||
get { return PropertyInfoCacheInstance; }
|
||||
}
|
||||
|
||||
internal override void BuildMap()
|
||||
{
|
||||
CacheMap<TaskType, TaskTypeDto>(src => src.Id, dto => dto.Id);
|
||||
CacheMap<TaskType, TaskTypeDto>(src => src.Alias, dto => dto.Alias);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -108,7 +108,7 @@ namespace Umbraco.Core.Persistence.Migrations.Initial
|
||||
_database.Insert("umbracoNode", "id", false, new NodeDto { NodeId = -21, Trashed = false, ParentId = -1, UserId = 0, Level = 0, Path = "-1,-21", SortOrder = 0, UniqueId = new Guid("BF7C7CBC-952F-4518-97A2-69E9C7B33842"), Text = "Recycle Bin", NodeObjectType = new Guid(Constants.ObjectTypes.MediaRecycleBin), CreateDate = DateTime.Now });
|
||||
_database.Insert("umbracoNode", "id", false, new NodeDto { NodeId = -92, Trashed = false, ParentId = -1, UserId = 0, Level = 1, Path = "-1,-92", SortOrder = 35, UniqueId = new Guid("f0bc4bfb-b499-40d6-ba86-058885a5178c"), Text = "Label", NodeObjectType = new Guid(Constants.ObjectTypes.DataType), CreateDate = DateTime.Now });
|
||||
_database.Insert("umbracoNode", "id", false, new NodeDto { NodeId = -90, Trashed = false, ParentId = -1, UserId = 0, Level = 1, Path = "-1,-90", SortOrder = 34, UniqueId = new Guid("84c6b441-31df-4ffe-b67e-67d5bc3ae65a"), Text = "Upload", NodeObjectType = new Guid(Constants.ObjectTypes.DataType), CreateDate = DateTime.Now });
|
||||
_database.Insert("umbracoNode", "id", false, new NodeDto { NodeId = -89, Trashed = false, ParentId = -1, UserId = 0, Level = 1, Path = "-1,-89", SortOrder = 33, UniqueId = new Guid("c6bac0dd-4ab9-45b1-8e30-e4b619ee5da3"), Text = "Textbox multiple", NodeObjectType = new Guid(Constants.ObjectTypes.DataType), CreateDate = DateTime.Now });
|
||||
_database.Insert("umbracoNode", "id", false, new NodeDto { NodeId = -89, Trashed = false, ParentId = -1, UserId = 0, Level = 1, Path = "-1,-89", SortOrder = 33, UniqueId = new Guid("c6bac0dd-4ab9-45b1-8e30-e4b619ee5da3"), Text = "Textarea", NodeObjectType = new Guid(Constants.ObjectTypes.DataType), CreateDate = DateTime.Now });
|
||||
_database.Insert("umbracoNode", "id", false, new NodeDto { NodeId = -88, Trashed = false, ParentId = -1, UserId = 0, Level = 1, Path = "-1,-88", SortOrder = 32, UniqueId = new Guid("0cc0eba1-9960-42c9-bf9b-60e150b429ae"), Text = "Textstring", NodeObjectType = new Guid(Constants.ObjectTypes.DataType), CreateDate = DateTime.Now });
|
||||
_database.Insert("umbracoNode", "id", false, new NodeDto { NodeId = -87, Trashed = false, ParentId = -1, UserId = 0, Level = 1, Path = "-1,-87", SortOrder = 4, UniqueId = new Guid("ca90c950-0aff-4e72-b976-a30b1ac57dad"), Text = "Richtext editor", NodeObjectType = new Guid(Constants.ObjectTypes.DataType), CreateDate = DateTime.Now });
|
||||
_database.Insert("umbracoNode", "id", false, new NodeDto { NodeId = -51, Trashed = false, ParentId = -1, UserId = 0, Level = 1, Path = "-1,-51", SortOrder = 2, UniqueId = new Guid("2e6d3631-066e-44b8-aec4-96f09099b2b5"), Text = "Numeric", NodeObjectType = new Guid(Constants.ObjectTypes.DataType), CreateDate = DateTime.Now });
|
||||
|
||||
@@ -289,7 +289,7 @@ namespace Umbraco.Core.Persistence.Migrations
|
||||
//NOTE: We CANNOT do this as part of the transaction!!! This is because when upgrading to 7.3, we cannot
|
||||
// create the migrations table and then add data to it in the same transaction without issuing things like GO
|
||||
// commands and since we need to support all Dbs, we need to just do this after the fact.
|
||||
var exists = _migrationEntryService.FindEntry(GlobalSettings.UmbracoMigrationName, _targetVersion);
|
||||
var exists = _migrationEntryService.FindEntry(_productName, _targetVersion);
|
||||
if (exists == null)
|
||||
{
|
||||
_migrationEntryService.CreateEntry(_productName, _targetVersion);
|
||||
|
||||
+38
@@ -0,0 +1,38 @@
|
||||
using System.Linq;
|
||||
using Umbraco.Core.Configuration;
|
||||
using Umbraco.Core.Logging;
|
||||
using Umbraco.Core.Models.Rdbms;
|
||||
using Umbraco.Core.Persistence.SqlSyntax;
|
||||
|
||||
namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSevenThreeOne
|
||||
{
|
||||
/// <summary>
|
||||
/// This fixes the storage of user languages from the old format like en_us to en-US
|
||||
/// </summary>
|
||||
[Migration("7.3.1", 0, GlobalSettings.UmbracoMigrationName)]
|
||||
public class UpdateUserLanguagesToIsoCode : MigrationBase
|
||||
{
|
||||
public UpdateUserLanguagesToIsoCode(ISqlSyntaxProvider sqlSyntax, ILogger logger) : base(sqlSyntax, logger)
|
||||
{
|
||||
}
|
||||
|
||||
public override void Up()
|
||||
{
|
||||
var userData = Context.Database.Fetch<UserDto>(new Sql().Select("*").From<UserDto>(SqlSyntax));
|
||||
foreach (var user in userData.Where(x => x.UserLanguage.Contains("_")))
|
||||
{
|
||||
var languageParts = user.UserLanguage.Split('_');
|
||||
if (languageParts.Length == 2)
|
||||
{
|
||||
Update.Table("umbracoUser")
|
||||
.Set(new {userLanguage = languageParts[0] + "-" + languageParts[1].ToUpperInvariant()})
|
||||
.Where(new {id = user.Id});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override void Down()
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
+50
@@ -0,0 +1,50 @@
|
||||
using System.Collections.Generic;
|
||||
using Umbraco.Core.Configuration;
|
||||
using Umbraco.Core.Logging;
|
||||
using Umbraco.Core.Models.Rdbms;
|
||||
using Umbraco.Core.Persistence.SqlSyntax;
|
||||
|
||||
namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSevenThreeTwo
|
||||
{
|
||||
/// <summary>
|
||||
/// This reinserts all migrations in the migrations table to account for initial rows inserted
|
||||
/// on creation without identity enabled.
|
||||
/// </summary>
|
||||
[Migration("7.3.2", 0, GlobalSettings.UmbracoMigrationName)]
|
||||
public class EnsureMigrationsTableIdentityIsCorrect : MigrationBase
|
||||
{
|
||||
public EnsureMigrationsTableIdentityIsCorrect(ISqlSyntaxProvider sqlSyntax, ILogger logger) : base(sqlSyntax, logger)
|
||||
{
|
||||
}
|
||||
|
||||
public override void Up()
|
||||
{
|
||||
// Due to the delayed execution of migrations, we have to wrap this code in Execute.Code to ensure the previous
|
||||
// migration steps (esp. creating the migrations table) have completed before trying to fetch migrations from
|
||||
// this table.
|
||||
List<MigrationDto> migrations = null;
|
||||
Execute.Code(db =>
|
||||
{
|
||||
migrations = Context.Database.Fetch<MigrationDto>(new Sql().Select("*").From<MigrationDto>(SqlSyntax));
|
||||
return string.Empty;
|
||||
});
|
||||
Delete.FromTable("umbracoMigration").AllRows();
|
||||
Execute.Code(database =>
|
||||
{
|
||||
if (migrations != null)
|
||||
{
|
||||
foreach (var migration in migrations)
|
||||
{
|
||||
database.Insert("umbracoMigration", "id", true,
|
||||
new {name = migration.Name, createDate = migration.CreateDate, version = migration.Version});
|
||||
}
|
||||
}
|
||||
return string.Empty;
|
||||
});
|
||||
}
|
||||
|
||||
public override void Down()
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
+13
-13
@@ -28,37 +28,38 @@ namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSixZeroOne
|
||||
{
|
||||
if (database != null)
|
||||
{
|
||||
//Fetch all PropertyTypes that belongs to a PropertyTypeGroup
|
||||
//Fetch all PropertyTypes that belongs to a PropertyTypeGroup
|
||||
//NOTE: We are writing the full query because we've added a column to the PropertyTypeDto in later versions so one of the columns
|
||||
// won't exist yet
|
||||
var propertyTypes = database.Fetch<PropertyTypeDto>("SELECT * FROM cmsPropertyType WHERE propertyTypeGroupId > 0");
|
||||
//NOTE: We're using dynamic to avoid having this migration fail due to the UniqueId column added in 7.3 (this column is not added
|
||||
// in the table yet and will make the mapping of done by Fetch fail when the actual type is used here).
|
||||
var propertyTypes = database.Fetch<dynamic>("SELECT * FROM cmsPropertyType WHERE propertyTypeGroupId > 0");
|
||||
|
||||
var propertyGroups = database.Fetch<PropertyTypeGroupDto>("WHERE id > 0");
|
||||
|
||||
foreach (var propertyType in propertyTypes)
|
||||
{
|
||||
//Get the PropertyTypeGroup that the current PropertyType references
|
||||
var parentPropertyTypeGroup = propertyGroups.FirstOrDefault(x => x.Id == propertyType.PropertyTypeGroupId);
|
||||
var parentPropertyTypeGroup = propertyGroups.FirstOrDefault(x => x.Id == propertyType.propertyTypeGroupId);
|
||||
if (parentPropertyTypeGroup != null)
|
||||
{
|
||||
//If the ContentType is the same on the PropertyType and the PropertyTypeGroup the group is valid and we skip to the next
|
||||
if (parentPropertyTypeGroup.ContentTypeNodeId == propertyType.ContentTypeId) continue;
|
||||
if (parentPropertyTypeGroup.ContentTypeNodeId == propertyType.contentTypeId) continue;
|
||||
|
||||
//Check if the 'new' PropertyTypeGroup has already been created
|
||||
var existingPropertyTypeGroup =
|
||||
propertyGroups.FirstOrDefault(
|
||||
x =>
|
||||
x.ParentGroupId == parentPropertyTypeGroup.Id && x.Text == parentPropertyTypeGroup.Text &&
|
||||
x.ContentTypeNodeId == propertyType.ContentTypeId);
|
||||
x.ContentTypeNodeId == propertyType.contentTypeId);
|
||||
|
||||
//This should ensure that we don't create duplicate groups for a single ContentType
|
||||
if (existingPropertyTypeGroup == null)
|
||||
{
|
||||
|
||||
//Create a new PropertyTypeGroup that references the parent group that the PropertyType was referencing pre-6.0.1
|
||||
var propertyGroup = new PropertyTypeGroupDto
|
||||
{
|
||||
ContentTypeNodeId = propertyType.ContentTypeId,
|
||||
ContentTypeNodeId = propertyType.contentTypeId,
|
||||
ParentGroupId = parentPropertyTypeGroup.Id,
|
||||
Text = parentPropertyTypeGroup.Text,
|
||||
SortOrder = parentPropertyTypeGroup.SortOrder
|
||||
@@ -69,19 +70,18 @@ namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSixZeroOne
|
||||
propertyGroup.Id = id;
|
||||
propertyGroups.Add(propertyGroup);
|
||||
//Update the reference to the new PropertyTypeGroup on the current PropertyType
|
||||
propertyType.PropertyTypeGroupId = id;
|
||||
database.Update(propertyType);
|
||||
propertyType.propertyTypeGroupId = id;
|
||||
database.Update("cmsPropertyType", "id", propertyType);
|
||||
}
|
||||
else
|
||||
{
|
||||
//Update the reference to the existing PropertyTypeGroup on the current PropertyType
|
||||
propertyType.PropertyTypeGroupId = existingPropertyTypeGroup.Id;
|
||||
database.Update(propertyType);
|
||||
//Update the reference to the existing PropertyTypeGroup on the current PropertyType
|
||||
propertyType.propertyTypeGroupId = existingPropertyTypeGroup.Id;
|
||||
database.Update("cmsPropertyType", "id", propertyType);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return string.Empty;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -834,7 +834,7 @@ namespace Umbraco.Core.Persistence
|
||||
var pd = PocoData.ForType(typeof(T));
|
||||
try
|
||||
{
|
||||
r = cmd.ExecuteReader();
|
||||
r = cmd.ExecuteReaderWithRetry();
|
||||
OnExecutedCommand(cmd);
|
||||
}
|
||||
catch (Exception x)
|
||||
|
||||
@@ -18,8 +18,11 @@ namespace Umbraco.Core.Persistence.Relators
|
||||
// Is this the same DictionaryItem as the current one we're processing
|
||||
if (Current != null && Current.Id == a.Id)
|
||||
{
|
||||
// Yes, just add this User2AppDto to the current item's collection
|
||||
Current.User2AppDtos.Add(p);
|
||||
if (p.AppAlias.IsNullOrWhiteSpace() == false)
|
||||
{
|
||||
// Yes, just add this User2AppDto to the current item's collection
|
||||
Current.User2AppDtos.Add(p);
|
||||
}
|
||||
|
||||
// Return null to indicate we're not done with this User yet
|
||||
return null;
|
||||
@@ -35,7 +38,7 @@ namespace Umbraco.Core.Persistence.Relators
|
||||
Current = a;
|
||||
Current.User2AppDtos = new List<User2AppDto>();
|
||||
//this can be null since we are doing a left join
|
||||
if (p.AppAlias != null)
|
||||
if (p.AppAlias.IsNullOrWhiteSpace() == false)
|
||||
{
|
||||
Current.User2AppDtos.Add(p);
|
||||
}
|
||||
|
||||
@@ -155,8 +155,8 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
"DELETE FROM cmsContentVersion WHERE ContentId = @Id",
|
||||
"DELETE FROM cmsContentXml WHERE nodeId = @Id",
|
||||
"DELETE FROM cmsContent WHERE nodeId = @Id",
|
||||
"DELETE FROM umbracoNode WHERE id = @Id",
|
||||
"DELETE FROM umbracoAccess WHERE nodeId = @Id"
|
||||
"DELETE FROM umbracoAccess WHERE nodeId = @Id",
|
||||
"DELETE FROM umbracoNode WHERE id = @Id"
|
||||
};
|
||||
return list;
|
||||
}
|
||||
@@ -234,8 +234,15 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
var processed = 0;
|
||||
do
|
||||
{
|
||||
var descendants = GetPagedResultsByQuery(query, pageIndex, pageSize, out total, "Path", Direction.Ascending);
|
||||
|
||||
//NOTE: This is an important call, we cannot simply make a call to:
|
||||
// GetPagedResultsByQuery(query, pageIndex, pageSize, out total, "Path", Direction.Ascending);
|
||||
// because that method is used to query 'latest' content items where in this case we don't necessarily
|
||||
// want latest content items because a pulished content item might not actually be the latest.
|
||||
// see: http://issues.umbraco.org/issue/U4-6322 & http://issues.umbraco.org/issue/U4-5982
|
||||
var descendants = GetPagedResultsByQuery<DocumentDto, Content>(query, pageIndex, pageSize, out total,
|
||||
new Tuple<string, string>("cmsDocument", "nodeId"),
|
||||
ProcessQuery, "Path", Direction.Ascending);
|
||||
|
||||
var xmlItems = (from descendant in descendants
|
||||
let xml = serializer(descendant)
|
||||
select new ContentXmlDto { NodeId = descendant.Id, Xml = xml.ToDataString() }).ToArray();
|
||||
@@ -728,7 +735,7 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
/// <param name="content"></param>
|
||||
/// <param name="xml"></param>
|
||||
public void AddOrUpdateContentXml(IContent content, Func<IContent, XElement> xml)
|
||||
{
|
||||
{
|
||||
_contentXmlRepository.AddOrUpdate(new ContentXmlEntity<IContent>(content, xml));
|
||||
}
|
||||
|
||||
@@ -826,11 +833,11 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
var contentTypes = _contentTypeRepository.GetAll(dtos.Select(x => x.ContentVersionDto.ContentDto.ContentTypeId).ToArray())
|
||||
.ToArray();
|
||||
|
||||
|
||||
|
||||
var ids = dtos
|
||||
.Where(dto => dto.TemplateId.HasValue && dto.TemplateId.Value > 0)
|
||||
.Select(x => x.TemplateId.Value).ToArray();
|
||||
|
||||
|
||||
//NOTE: This should be ok for an SQL 'IN' statement, there shouldn't be an insane amount of content types
|
||||
var templates = ids.Length == 0 ? Enumerable.Empty<ITemplate>() : _templateRepository.GetAll(ids).ToArray();
|
||||
|
||||
@@ -972,4 +979,4 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
_contentXmlRepository.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -30,12 +30,9 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
|
||||
protected ContentTypeBaseRepository(IDatabaseUnitOfWork work, CacheHelper cache, ILogger logger, ISqlSyntaxProvider sqlSyntax)
|
||||
: base(work, cache, logger, sqlSyntax)
|
||||
{
|
||||
_guidRepo = new GuidReadOnlyContentTypeBaseRepository(this, work, cache, logger, sqlSyntax);
|
||||
{
|
||||
}
|
||||
|
||||
private readonly GuidReadOnlyContentTypeBaseRepository _guidRepo;
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Returns the content type ids that match the query
|
||||
/// </summary>
|
||||
@@ -454,6 +451,7 @@ AND umbracoNode.id <> @id",
|
||||
propType.DataTypeDefinitionId = dto.DataTypeId;
|
||||
propType.Description = dto.Description;
|
||||
propType.Id = dto.Id;
|
||||
propType.Key = dto.UniqueId;
|
||||
propType.Name = dto.Name;
|
||||
propType.Mandatory = dto.Mandatory;
|
||||
propType.SortOrder = dto.SortOrder;
|
||||
@@ -564,14 +562,13 @@ AND umbracoNode.id <> @id",
|
||||
}
|
||||
}
|
||||
|
||||
public static IEnumerable<IMediaType> GetMediaTypes<TRepo, TId>(
|
||||
TId[] mediaTypeIds, Database db, ISqlSyntaxProvider sqlSyntax,
|
||||
public static IEnumerable<IMediaType> GetMediaTypes<TRepo>(
|
||||
Database db, ISqlSyntaxProvider sqlSyntax,
|
||||
TRepo contentTypeRepository)
|
||||
where TRepo : IReadRepository<TId, TEntity>
|
||||
where TId: struct
|
||||
where TRepo : IReadRepository<int, TEntity>
|
||||
{
|
||||
IDictionary<TId, IEnumerable<TId>> allParentMediaTypeIds;
|
||||
var mediaTypes = MapMediaTypes(mediaTypeIds, db, sqlSyntax, out allParentMediaTypeIds)
|
||||
IDictionary<int, List<int>> allParentMediaTypeIds;
|
||||
var mediaTypes = MapMediaTypes(db, sqlSyntax, out allParentMediaTypeIds)
|
||||
.ToArray();
|
||||
|
||||
MapContentTypeChildren(mediaTypes, db, sqlSyntax, contentTypeRepository, allParentMediaTypeIds);
|
||||
@@ -579,16 +576,15 @@ AND umbracoNode.id <> @id",
|
||||
return mediaTypes;
|
||||
}
|
||||
|
||||
public static IEnumerable<IContentType> GetContentTypes<TRepo, TId>(
|
||||
TId[] contentTypeIds, Database db, ISqlSyntaxProvider sqlSyntax,
|
||||
public static IEnumerable<IContentType> GetContentTypes<TRepo>(
|
||||
Database db, ISqlSyntaxProvider sqlSyntax,
|
||||
TRepo contentTypeRepository,
|
||||
ITemplateRepository templateRepository)
|
||||
where TRepo : IReadRepository<TId, TEntity>
|
||||
where TId : struct
|
||||
where TRepo : IReadRepository<int, TEntity>
|
||||
{
|
||||
IDictionary<TId, IEnumerable<AssociatedTemplate>> allAssociatedTemplates;
|
||||
IDictionary<TId, IEnumerable<TId>> allParentContentTypeIds;
|
||||
var contentTypes = MapContentTypes(contentTypeIds, db, sqlSyntax, out allAssociatedTemplates, out allParentContentTypeIds)
|
||||
IDictionary<int, List<AssociatedTemplate>> allAssociatedTemplates;
|
||||
IDictionary<int, List<int>> allParentContentTypeIds;
|
||||
var contentTypes = MapContentTypes(db, sqlSyntax, out allAssociatedTemplates, out allParentContentTypeIds)
|
||||
.ToArray();
|
||||
|
||||
if (contentTypes.Any())
|
||||
@@ -603,12 +599,11 @@ AND umbracoNode.id <> @id",
|
||||
return contentTypes;
|
||||
}
|
||||
|
||||
internal static void MapContentTypeChildren<TRepo, TId>(IContentTypeComposition[] contentTypes,
|
||||
internal static void MapContentTypeChildren<TRepo>(IContentTypeComposition[] contentTypes,
|
||||
Database db, ISqlSyntaxProvider sqlSyntax,
|
||||
TRepo contentTypeRepository,
|
||||
IDictionary<TId, IEnumerable<TId>> allParentContentTypeIds)
|
||||
where TRepo : IReadRepository<TId, TEntity>
|
||||
where TId : struct
|
||||
IDictionary<int, List<int>> allParentContentTypeIds)
|
||||
where TRepo : IReadRepository<int, TEntity>
|
||||
{
|
||||
//NOTE: SQL call #2
|
||||
|
||||
@@ -630,20 +625,17 @@ AND umbracoNode.id <> @id",
|
||||
var allParentIdsAsArray = allParentContentTypeIds.SelectMany(x => x.Value).Distinct().ToArray();
|
||||
if (allParentIdsAsArray.Any())
|
||||
{
|
||||
var allParentContentTypes = contentTypeRepository.GetAll(allParentIdsAsArray).ToArray();
|
||||
var allParentContentTypes = contentTypes.Where(x => allParentIdsAsArray.Contains(x.Id)).ToArray();
|
||||
|
||||
foreach (var contentType in contentTypes)
|
||||
{
|
||||
//TODO: this is pretty hacky right now but i don't have time to refactor/fix running queries based on ints and Guids
|
||||
// (i.e. for v8) but we need queries by GUIDs now so this is how it's gonna have to be
|
||||
var entityId = typeof(TId) == typeof(int) ? contentType.Id : (object)contentType.Key;
|
||||
{
|
||||
var entityId = contentType.Id;
|
||||
|
||||
var parentContentTypes = allParentContentTypes.Where(x =>
|
||||
{
|
||||
//TODO: this is pretty hacky right now but i don't have time to refactor/fix running queries based on ints and Guids
|
||||
// (i.e. for v8) but we need queries by GUIDs now so this is how it's gonna have to be
|
||||
var parentEntityId = typeof(TId) == typeof(int) ? x.Id : (object)x.Key;
|
||||
var parentEntityId = x.Id;
|
||||
|
||||
return allParentContentTypeIds[(TId)entityId].Contains((TId)parentEntityId);
|
||||
return allParentContentTypeIds[entityId].Contains(parentEntityId);
|
||||
});
|
||||
foreach (var parentContentType in parentContentTypes)
|
||||
{
|
||||
@@ -661,13 +653,12 @@ AND umbracoNode.id <> @id",
|
||||
|
||||
}
|
||||
|
||||
internal static void MapContentTypeTemplates<TRepo, TId>(IContentType[] contentTypes,
|
||||
internal static void MapContentTypeTemplates<TRepo>(IContentType[] contentTypes,
|
||||
Database db,
|
||||
TRepo contentTypeRepository,
|
||||
ITemplateRepository templateRepository,
|
||||
IDictionary<TId, IEnumerable<AssociatedTemplate>> associatedTemplates)
|
||||
where TRepo : IReadRepository<TId, TEntity>
|
||||
where TId: struct
|
||||
IDictionary<int, List<AssociatedTemplate>> associatedTemplates)
|
||||
where TRepo : IReadRepository<int, TEntity>
|
||||
{
|
||||
if (associatedTemplates == null || associatedTemplates.Any() == false) return;
|
||||
|
||||
@@ -684,11 +675,9 @@ AND umbracoNode.id <> @id",
|
||||
|
||||
foreach (var contentType in contentTypes)
|
||||
{
|
||||
//TODO: this is pretty hacky right now but i don't have time to refactor/fix running queries based on ints and Guids
|
||||
// (i.e. for v8) but we need queries by GUIDs now so this is how it's gonna have to be
|
||||
var entityId = typeof(TId) == typeof(int) ? contentType.Id : (object)contentType.Key;
|
||||
|
||||
var associatedTemplateIds = associatedTemplates[(TId)entityId].Select(x => x.TemplateId)
|
||||
var entityId = contentType.Id;
|
||||
|
||||
var associatedTemplateIds = associatedTemplates[entityId].Select(x => x.TemplateId)
|
||||
.Distinct()
|
||||
.ToArray();
|
||||
|
||||
@@ -700,19 +689,14 @@ AND umbracoNode.id <> @id",
|
||||
|
||||
}
|
||||
|
||||
internal static IEnumerable<IMediaType> MapMediaTypes<TId>(TId[] mediaTypeIds, Database db, ISqlSyntaxProvider sqlSyntax,
|
||||
out IDictionary<TId, IEnumerable<TId>> parentMediaTypeIds)
|
||||
where TId : struct
|
||||
internal static IEnumerable<IMediaType> MapMediaTypes(Database db, ISqlSyntaxProvider sqlSyntax,
|
||||
out IDictionary<int, List<int>> parentMediaTypeIds)
|
||||
{
|
||||
Mandate.That(mediaTypeIds.Any(), () => new InvalidOperationException("must be at least one content type id specified"));
|
||||
Mandate.ParameterNotNull(db, "db");
|
||||
|
||||
//ensure they are unique
|
||||
mediaTypeIds = mediaTypeIds.Distinct().ToArray();
|
||||
|
||||
|
||||
var sql = @"SELECT cmsContentType.pk as ctPk, cmsContentType.alias as ctAlias, cmsContentType.allowAtRoot as ctAllowAtRoot, cmsContentType.description as ctDesc,
|
||||
cmsContentType.icon as ctIcon, cmsContentType.isContainer as ctIsContainer, cmsContentType.nodeId as ctId, cmsContentType.thumbnail as ctThumb,
|
||||
AllowedTypes.allowedId as ctaAllowedId, AllowedTypes.SortOrder as ctaSortOrder, AllowedTypes.alias as ctaAlias,
|
||||
AllowedTypes.AllowedId as ctaAllowedId, AllowedTypes.SortOrder as ctaSortOrder, AllowedTypes.alias as ctaAlias,
|
||||
ParentTypes.parentContentTypeId as chtParentId, ParentTypes.parentContentTypeKey as chtParentKey,
|
||||
umbracoNode.createDate as nCreateDate, umbracoNode." + sqlSyntax.GetQuotedColumnName("level") + @" as nLevel, umbracoNode.nodeObjectType as nObjectType, umbracoNode.nodeUser as nUser,
|
||||
umbracoNode.parentID as nParentId, umbracoNode." + sqlSyntax.GetQuotedColumnName("path") + @" as nPath, umbracoNode.sortOrder as nSortOrder, umbracoNode." + sqlSyntax.GetQuotedColumnName("text") + @" as nName, umbracoNode.trashed as nTrashed,
|
||||
@@ -734,27 +718,10 @@ AND umbracoNode.id <> @id",
|
||||
ON cmsContentType2ContentType.parentContentTypeId = umbracoNode." + sqlSyntax.GetQuotedColumnName("id") + @"
|
||||
) ParentTypes
|
||||
ON ParentTypes.childContentTypeId = cmsContentType.nodeId
|
||||
WHERE (umbracoNode.nodeObjectType = @nodeObjectType)";
|
||||
|
||||
if (mediaTypeIds.Any())
|
||||
{
|
||||
//TODO: This is all sorts of hacky but i don't have time to refactor a lot to get both ints and guids working nicely... this will
|
||||
// work for the time being.
|
||||
if (typeof(TId) == typeof(int))
|
||||
{
|
||||
sql = sql + " AND (umbracoNode.id IN (@contentTypeIds))";
|
||||
}
|
||||
else if (typeof(TId) == typeof(Guid))
|
||||
{
|
||||
sql = sql + " AND (umbracoNode.uniqueID IN (@contentTypeIds))";
|
||||
}
|
||||
}
|
||||
|
||||
//NOTE: we are going to assume there's not going to be more than 2100 content type ids since that is the max SQL param count!
|
||||
if ((mediaTypeIds.Length - 1) > 2000)
|
||||
throw new InvalidOperationException("Cannot perform this lookup, too many sql parameters");
|
||||
|
||||
var result = db.Fetch<dynamic>(sql, new { nodeObjectType = new Guid(Constants.ObjectTypes.MediaType), contentTypeIds = mediaTypeIds });
|
||||
WHERE (umbracoNode.nodeObjectType = @nodeObjectType)
|
||||
ORDER BY ctId";
|
||||
|
||||
var result = db.Fetch<dynamic>(sql, new { nodeObjectType = new Guid(Constants.ObjectTypes.MediaType) });
|
||||
|
||||
if (result.Any() == false)
|
||||
{
|
||||
@@ -762,91 +729,114 @@ AND umbracoNode.id <> @id",
|
||||
return Enumerable.Empty<IMediaType>();
|
||||
}
|
||||
|
||||
parentMediaTypeIds = new Dictionary<TId, IEnumerable<TId>>();
|
||||
parentMediaTypeIds = new Dictionary<int, List<int>>();
|
||||
var mappedMediaTypes = new List<IMediaType>();
|
||||
|
||||
foreach (var contentTypeId in mediaTypeIds)
|
||||
//loop through each result and fill in our required values, each row will contain different requried data than the rest.
|
||||
// it is much quicker to iterate each result and populate instead of looking up the values over and over in the result like
|
||||
// we used to do.
|
||||
var queue = new Queue<dynamic>(result);
|
||||
var currAllowedContentTypes = new List<ContentTypeSort>();
|
||||
|
||||
while (queue.Count > 0)
|
||||
{
|
||||
//the current content type id that we're working with
|
||||
var ct = queue.Dequeue();
|
||||
|
||||
var currentCtId = contentTypeId;
|
||||
|
||||
//first we want to get the main content type data this is 1 : 1 with umbraco node data
|
||||
|
||||
var ct = result
|
||||
.Where(x =>
|
||||
{
|
||||
//TODO: This is a bit hacky right now but don't have time to do a nice refactor to support both GUID and Int queries, so this is
|
||||
// how it is for now.
|
||||
return (typeof (TId) == typeof (int))
|
||||
? x.ctId == currentCtId
|
||||
: x.nUniqueId == currentCtId;
|
||||
})
|
||||
.Select(x => new { x.ctPk, x.ctId, x.ctAlias, x.ctAllowAtRoot, x.ctDesc, x.ctIcon, x.ctIsContainer, x.ctThumb, x.nName, x.nCreateDate, x.nLevel, x.nObjectType, x.nUser, x.nParentId, x.nPath, x.nSortOrder, x.nTrashed, x.nUniqueId })
|
||||
.DistinctBy(x => (int)x.ctId)
|
||||
.FirstOrDefault();
|
||||
|
||||
if (ct == null)
|
||||
//check for allowed content types
|
||||
int? allowedCtId = ct.ctaAllowedId;
|
||||
int? allowedCtSort = ct.ctaSortOrder;
|
||||
string allowedCtAlias = ct.ctaAlias;
|
||||
if (allowedCtId.HasValue && allowedCtSort.HasValue && allowedCtAlias != null)
|
||||
{
|
||||
continue;
|
||||
var ctSort = new ContentTypeSort(new Lazy<int>(() => allowedCtId.Value), allowedCtSort.Value, allowedCtAlias);
|
||||
if (currAllowedContentTypes.Contains(ctSort) == false)
|
||||
{
|
||||
currAllowedContentTypes.Add(ctSort);
|
||||
}
|
||||
}
|
||||
|
||||
var contentTypeDto = new ContentTypeDto
|
||||
//always ensure there's a list for this content type
|
||||
if (parentMediaTypeIds.ContainsKey(ct.ctId) == false)
|
||||
parentMediaTypeIds[ct.ctId] = new List<int>();
|
||||
|
||||
//check for parent ids and assign to the outgoing collection
|
||||
int? parentId = ct.chtParentId;
|
||||
if (parentId.HasValue)
|
||||
{
|
||||
Alias = ct.ctAlias,
|
||||
AllowAtRoot = ct.ctAllowAtRoot,
|
||||
Description = ct.ctDesc,
|
||||
Icon = ct.ctIcon,
|
||||
IsContainer = ct.ctIsContainer,
|
||||
NodeId = ct.ctId,
|
||||
PrimaryKey = ct.ctPk,
|
||||
Thumbnail = ct.ctThumb,
|
||||
//map the underlying node dto
|
||||
NodeDto = new NodeDto
|
||||
{
|
||||
CreateDate = ct.nCreateDate,
|
||||
Level = (short)ct.nLevel,
|
||||
NodeId = ct.ctId,
|
||||
NodeObjectType = ct.nObjectType,
|
||||
ParentId = ct.nParentId,
|
||||
Path = ct.nPath,
|
||||
SortOrder = ct.nSortOrder,
|
||||
Text = ct.nName,
|
||||
Trashed = ct.nTrashed,
|
||||
UniqueId = ct.nUniqueId,
|
||||
UserId = ct.nUser
|
||||
}
|
||||
};
|
||||
var associatedParentIds = parentMediaTypeIds[ct.ctId];
|
||||
if (associatedParentIds.Contains(parentId.Value) == false)
|
||||
associatedParentIds.Add(parentId.Value);
|
||||
}
|
||||
|
||||
//now create the media type object
|
||||
if (queue.Count == 0 || queue.Peek().ctId != ct.ctId)
|
||||
{
|
||||
//it's the last in the queue or the content type is changing (moving to the next one)
|
||||
var mediaType = CreateForMapping(ct, currAllowedContentTypes);
|
||||
mappedMediaTypes.Add(mediaType);
|
||||
|
||||
var factory = new MediaTypeFactory(new Guid(Constants.ObjectTypes.MediaType));
|
||||
var mediaType = factory.BuildEntity(contentTypeDto);
|
||||
|
||||
//map the allowed content types
|
||||
//map the child content type ids
|
||||
MapCommonContentTypeObjects(mediaType, currentCtId, result, parentMediaTypeIds);
|
||||
|
||||
mappedMediaTypes.Add(mediaType);
|
||||
//Here we need to reset the current variables, we're now collecting data for a different content type
|
||||
currAllowedContentTypes = new List<ContentTypeSort>();
|
||||
}
|
||||
}
|
||||
|
||||
return mappedMediaTypes;
|
||||
}
|
||||
|
||||
internal static IEnumerable<IContentType> MapContentTypes<TId>(TId[] contentTypeIds, Database db, ISqlSyntaxProvider sqlSyntax,
|
||||
out IDictionary<TId, IEnumerable<AssociatedTemplate>> associatedTemplates,
|
||||
out IDictionary<TId, IEnumerable<TId>> parentContentTypeIds)
|
||||
where TId : struct
|
||||
private static IMediaType CreateForMapping(dynamic currCt, List<ContentTypeSort> currAllowedContentTypes)
|
||||
{
|
||||
// * create the DTO object
|
||||
// * create the content type object
|
||||
// * map the allowed content types
|
||||
// * add to the outgoing list
|
||||
|
||||
var contentTypeDto = new ContentTypeDto
|
||||
{
|
||||
Alias = currCt.ctAlias,
|
||||
AllowAtRoot = currCt.ctAllowAtRoot,
|
||||
Description = currCt.ctDesc,
|
||||
Icon = currCt.ctIcon,
|
||||
IsContainer = currCt.ctIsContainer,
|
||||
NodeId = currCt.ctId,
|
||||
PrimaryKey = currCt.ctPk,
|
||||
Thumbnail = currCt.ctThumb,
|
||||
//map the underlying node dto
|
||||
NodeDto = new NodeDto
|
||||
{
|
||||
CreateDate = currCt.nCreateDate,
|
||||
Level = (short)currCt.nLevel,
|
||||
NodeId = currCt.ctId,
|
||||
NodeObjectType = currCt.nObjectType,
|
||||
ParentId = currCt.nParentId,
|
||||
Path = currCt.nPath,
|
||||
SortOrder = currCt.nSortOrder,
|
||||
Text = currCt.nName,
|
||||
Trashed = currCt.nTrashed,
|
||||
UniqueId = currCt.nUniqueId,
|
||||
UserId = currCt.nUser
|
||||
}
|
||||
};
|
||||
|
||||
//now create the content type object
|
||||
|
||||
var factory = new MediaTypeFactory(new Guid(Constants.ObjectTypes.MediaType));
|
||||
var mediaType = factory.BuildEntity(contentTypeDto);
|
||||
|
||||
//map the allowed content types
|
||||
mediaType.AllowedContentTypes = currAllowedContentTypes;
|
||||
|
||||
return mediaType;
|
||||
}
|
||||
|
||||
internal static IEnumerable<IContentType> MapContentTypes(Database db, ISqlSyntaxProvider sqlSyntax,
|
||||
out IDictionary<int, List<AssociatedTemplate>> associatedTemplates,
|
||||
out IDictionary<int, List<int>> parentContentTypeIds)
|
||||
{
|
||||
Mandate.ParameterNotNull(db, "db");
|
||||
|
||||
//ensure they are unique
|
||||
contentTypeIds = contentTypeIds.Distinct().ToArray();
|
||||
|
||||
|
||||
var sql = @"SELECT cmsDocumentType.IsDefault as dtIsDefault, cmsDocumentType.templateNodeId as dtTemplateId,
|
||||
cmsContentType.pk as ctPk, cmsContentType.alias as ctAlias, cmsContentType.allowAtRoot as ctAllowAtRoot, cmsContentType.description as ctDesc,
|
||||
cmsContentType.icon as ctIcon, cmsContentType.isContainer as ctIsContainer, cmsContentType.nodeId as ctId, cmsContentType.thumbnail as ctThumb,
|
||||
AllowedTypes.allowedId as ctaAllowedId, AllowedTypes.SortOrder as ctaSortOrder, AllowedTypes.alias as ctaAlias,
|
||||
AllowedTypes.AllowedId as ctaAllowedId, AllowedTypes.SortOrder as ctaSortOrder, AllowedTypes.alias as ctaAlias,
|
||||
ParentTypes.parentContentTypeId as chtParentId,ParentTypes.parentContentTypeKey as chtParentKey,
|
||||
umbracoNode.createDate as nCreateDate, umbracoNode." + sqlSyntax.GetQuotedColumnName("level") + @" as nLevel, umbracoNode.nodeObjectType as nObjectType, umbracoNode.nodeUser as nUser,
|
||||
umbracoNode.parentID as nParentId, umbracoNode." + sqlSyntax.GetQuotedColumnName("path") + @" as nPath, umbracoNode.sortOrder as nSortOrder, umbracoNode." + sqlSyntax.GetQuotedColumnName("text") + @" as nName, umbracoNode.trashed as nTrashed,
|
||||
@@ -877,28 +867,10 @@ AND umbracoNode.id <> @id",
|
||||
ON cmsContentType2ContentType.parentContentTypeId = umbracoNode." + sqlSyntax.GetQuotedColumnName("id") + @"
|
||||
) ParentTypes
|
||||
ON ParentTypes.childContentTypeId = cmsContentType.nodeId
|
||||
WHERE (umbracoNode.nodeObjectType = @nodeObjectType)";
|
||||
|
||||
if (contentTypeIds.Any())
|
||||
{
|
||||
//TODO: This is all sorts of hacky but i don't have time to refactor a lot to get both ints and guids working nicely... this will
|
||||
// work for the time being.
|
||||
if (typeof(TId) == typeof(int))
|
||||
{
|
||||
sql = sql + " AND (umbracoNode.id IN (@contentTypeIds))";
|
||||
}
|
||||
else if (typeof(TId) == typeof(Guid))
|
||||
{
|
||||
sql = sql + " AND (umbracoNode.uniqueID IN (@contentTypeIds))";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//NOTE: we are going to assume there's not going to be more than 2100 content type ids since that is the max SQL param count!
|
||||
if ((contentTypeIds.Length - 1) > 2000)
|
||||
throw new InvalidOperationException("Cannot perform this lookup, too many sql parameters");
|
||||
|
||||
var result = db.Fetch<dynamic>(sql, new { nodeObjectType = new Guid(Constants.ObjectTypes.DocumentType), contentTypeIds = contentTypeIds });
|
||||
WHERE (umbracoNode.nodeObjectType = @nodeObjectType)
|
||||
ORDER BY ctId";
|
||||
|
||||
var result = db.Fetch<dynamic>(sql, new { nodeObjectType = new Guid(Constants.ObjectTypes.DocumentType)});
|
||||
|
||||
if (result.Any() == false)
|
||||
{
|
||||
@@ -907,121 +879,141 @@ AND umbracoNode.id <> @id",
|
||||
return Enumerable.Empty<IContentType>();
|
||||
}
|
||||
|
||||
parentContentTypeIds = new Dictionary<TId, IEnumerable<TId>>();
|
||||
associatedTemplates = new Dictionary<TId, IEnumerable<AssociatedTemplate>>();
|
||||
parentContentTypeIds = new Dictionary<int, List<int>>();
|
||||
associatedTemplates = new Dictionary<int, List<AssociatedTemplate>>();
|
||||
var mappedContentTypes = new List<IContentType>();
|
||||
|
||||
foreach (var contentTypeId in contentTypeIds)
|
||||
var queue = new Queue<dynamic>(result);
|
||||
var currDefaultTemplate = -1;
|
||||
var currAllowedContentTypes = new List<ContentTypeSort>();
|
||||
while (queue.Count > 0)
|
||||
{
|
||||
//the current content type id that we're working with
|
||||
var ct = queue.Dequeue();
|
||||
|
||||
var currentCtId = contentTypeId;
|
||||
|
||||
//first we want to get the main content type data this is 1 : 1 with umbraco node data
|
||||
|
||||
var ct = result
|
||||
.Where(x =>
|
||||
{
|
||||
//TODO: This is a bit hacky right now but don't have time to do a nice refactor to support both GUID and Int queries, so this is
|
||||
// how it is for now.
|
||||
return (typeof(TId) == typeof(int))
|
||||
? x.ctId == currentCtId
|
||||
: x.nUniqueId == currentCtId;
|
||||
})
|
||||
.Select(x => new { x.ctPk, x.ctId, x.ctAlias, x.ctAllowAtRoot, x.ctDesc, x.ctIcon, x.ctIsContainer, x.ctThumb, x.nName, x.nCreateDate, x.nLevel, x.nObjectType, x.nUser, x.nParentId, x.nPath, x.nSortOrder, x.nTrashed, x.nUniqueId })
|
||||
.DistinctBy(x => (int)x.ctId)
|
||||
.FirstOrDefault();
|
||||
|
||||
if (ct == null)
|
||||
//check for default templates
|
||||
bool? isDefaultTemplate = Convert.ToBoolean(ct.dtIsDefault);
|
||||
int? templateId = ct.dtTemplateId;
|
||||
if (currDefaultTemplate == -1 && isDefaultTemplate.HasValue && templateId.HasValue)
|
||||
{
|
||||
continue;
|
||||
currDefaultTemplate = templateId.Value;
|
||||
}
|
||||
|
||||
//get the unique list of associated templates
|
||||
var defaultTemplates = result
|
||||
.Where(x =>
|
||||
{
|
||||
//TODO: This is a bit hacky right now but don't have time to do a nice refactor to support both GUID and Int queries, so this is
|
||||
// how it is for now.
|
||||
return (typeof(TId) == typeof(int))
|
||||
? x.ctId == currentCtId
|
||||
: x.nUniqueId == currentCtId;
|
||||
})
|
||||
//use a tuple so that distinct checks both values (in some rare cases the dtIsDefault will not compute as bool?, so we force it with Convert.ToBoolean)
|
||||
.Select(x => new Tuple<bool?, int?>(Convert.ToBoolean(x.dtIsDefault), x.dtTemplateId))
|
||||
.Where(x => x.Item1.HasValue && x.Item2.HasValue)
|
||||
.Distinct()
|
||||
.OrderByDescending(x => x.Item1.Value)
|
||||
.ToArray();
|
||||
//if there isn't one set to default explicitly, we'll pick the first one
|
||||
var defaultTemplate = defaultTemplates.FirstOrDefault(x => x.Item1.Value)
|
||||
?? defaultTemplates.FirstOrDefault();
|
||||
//always ensure there's a list for this content type
|
||||
if (associatedTemplates.ContainsKey(ct.ctId) == false)
|
||||
associatedTemplates[ct.ctId] = new List<AssociatedTemplate>();
|
||||
|
||||
var dtDto = new DocumentTypeDto
|
||||
//check for associated templates and assign to the outgoing collection
|
||||
if (ct.tId != null)
|
||||
{
|
||||
//create the content type dto
|
||||
ContentTypeDto = new ContentTypeDto
|
||||
var associatedTemplate = new AssociatedTemplate(ct.tId, ct.tAlias, ct.tText);
|
||||
var associatedList = associatedTemplates[ct.ctId];
|
||||
|
||||
if (associatedList.Contains(associatedTemplate) == false)
|
||||
associatedList.Add(associatedTemplate);
|
||||
}
|
||||
|
||||
//check for allowed content types
|
||||
int? allowedCtId = ct.ctaAllowedId;
|
||||
int? allowedCtSort = ct.ctaSortOrder;
|
||||
string allowedCtAlias = ct.ctaAlias;
|
||||
if (allowedCtId.HasValue && allowedCtSort.HasValue && allowedCtAlias != null)
|
||||
{
|
||||
var ctSort = new ContentTypeSort(new Lazy<int>(() => allowedCtId.Value), allowedCtSort.Value, allowedCtAlias);
|
||||
if (currAllowedContentTypes.Contains(ctSort) == false)
|
||||
{
|
||||
Alias = ct.ctAlias,
|
||||
AllowAtRoot = ct.ctAllowAtRoot,
|
||||
Description = ct.ctDesc,
|
||||
Icon = ct.ctIcon,
|
||||
IsContainer = ct.ctIsContainer,
|
||||
NodeId = ct.ctId,
|
||||
PrimaryKey = ct.ctPk,
|
||||
Thumbnail = ct.ctThumb,
|
||||
//map the underlying node dto
|
||||
NodeDto = new NodeDto
|
||||
{
|
||||
CreateDate = ct.nCreateDate,
|
||||
Level = (short)ct.nLevel,
|
||||
NodeId = ct.ctId,
|
||||
NodeObjectType = ct.nObjectType,
|
||||
ParentId = ct.nParentId,
|
||||
Path = ct.nPath,
|
||||
SortOrder = ct.nSortOrder,
|
||||
Text = ct.nName,
|
||||
Trashed = ct.nTrashed,
|
||||
UniqueId = ct.nUniqueId,
|
||||
UserId = ct.nUser
|
||||
}
|
||||
},
|
||||
ContentTypeNodeId = ct.ctId,
|
||||
IsDefault = defaultTemplate != null,
|
||||
TemplateNodeId = defaultTemplate != null ? defaultTemplate.Item2.Value : 0,
|
||||
};
|
||||
currAllowedContentTypes.Add(ctSort);
|
||||
}
|
||||
}
|
||||
|
||||
// We will map a subset of the associated template - alias, id, name
|
||||
//always ensure there's a list for this content type
|
||||
if (parentContentTypeIds.ContainsKey(ct.ctId) == false)
|
||||
parentContentTypeIds[ct.ctId] = new List<int>();
|
||||
|
||||
associatedTemplates.Add(currentCtId, result
|
||||
.Where(x =>
|
||||
{
|
||||
//TODO: This is a bit hacky right now but don't have time to do a nice refactor to support both GUID and Int queries, so this is
|
||||
// how it is for now.
|
||||
return (typeof(TId) == typeof(int))
|
||||
? x.ctId == currentCtId
|
||||
: x.nUniqueId == currentCtId;
|
||||
})
|
||||
.Where(x => x.tId != null)
|
||||
.Select(x => new AssociatedTemplate(x.tId, x.tAlias, x.tText))
|
||||
.Distinct()
|
||||
.ToArray());
|
||||
//check for parent ids and assign to the outgoing collection
|
||||
int? parentId = ct.chtParentId;
|
||||
if (parentId.HasValue)
|
||||
{
|
||||
var associatedParentIds = parentContentTypeIds[ct.ctId];
|
||||
|
||||
//now create the content type object
|
||||
if (associatedParentIds.Contains(parentId.Value) == false)
|
||||
associatedParentIds.Add(parentId.Value);
|
||||
}
|
||||
|
||||
var factory = new ContentTypeFactory(new Guid(Constants.ObjectTypes.DocumentType));
|
||||
var contentType = factory.BuildEntity(dtDto);
|
||||
if (queue.Count == 0 || queue.Peek().ctId != ct.ctId)
|
||||
{
|
||||
//it's the last in the queue or the content type is changing (moving to the next one)
|
||||
var contentType = CreateForMapping(ct, currAllowedContentTypes, currDefaultTemplate);
|
||||
mappedContentTypes.Add(contentType);
|
||||
|
||||
//map the allowed content types
|
||||
//map the child content type ids
|
||||
MapCommonContentTypeObjects(contentType, currentCtId, result, parentContentTypeIds);
|
||||
|
||||
mappedContentTypes.Add(contentType);
|
||||
//Here we need to reset the current variables, we're now collecting data for a different content type
|
||||
currDefaultTemplate = -1;
|
||||
currAllowedContentTypes = new List<ContentTypeSort>();
|
||||
}
|
||||
}
|
||||
|
||||
return mappedContentTypes;
|
||||
}
|
||||
|
||||
private static IContentType CreateForMapping(dynamic currCt, List<ContentTypeSort> currAllowedContentTypes, int currDefaultTemplate)
|
||||
{
|
||||
// * set the default template to the first one if a default isn't found
|
||||
// * create the DTO object
|
||||
// * create the content type object
|
||||
// * map the allowed content types
|
||||
// * add to the outgoing list
|
||||
|
||||
var dtDto = new DocumentTypeDto
|
||||
{
|
||||
//create the content type dto
|
||||
ContentTypeDto = new ContentTypeDto
|
||||
{
|
||||
Alias = currCt.ctAlias,
|
||||
AllowAtRoot = currCt.ctAllowAtRoot,
|
||||
Description = currCt.ctDesc,
|
||||
Icon = currCt.ctIcon,
|
||||
IsContainer = currCt.ctIsContainer,
|
||||
NodeId = currCt.ctId,
|
||||
PrimaryKey = currCt.ctPk,
|
||||
Thumbnail = currCt.ctThumb,
|
||||
//map the underlying node dto
|
||||
NodeDto = new NodeDto
|
||||
{
|
||||
CreateDate = currCt.nCreateDate,
|
||||
Level = (short)currCt.nLevel,
|
||||
NodeId = currCt.ctId,
|
||||
NodeObjectType = currCt.nObjectType,
|
||||
ParentId = currCt.nParentId,
|
||||
Path = currCt.nPath,
|
||||
SortOrder = currCt.nSortOrder,
|
||||
Text = currCt.nName,
|
||||
Trashed = currCt.nTrashed,
|
||||
UniqueId = currCt.nUniqueId,
|
||||
UserId = currCt.nUser
|
||||
}
|
||||
},
|
||||
ContentTypeNodeId = currCt.ctId,
|
||||
IsDefault = currDefaultTemplate != -1,
|
||||
TemplateNodeId = currDefaultTemplate != -1 ? currDefaultTemplate : 0,
|
||||
};
|
||||
|
||||
//now create the content type object
|
||||
|
||||
var factory = new ContentTypeFactory(new Guid(Constants.ObjectTypes.DocumentType));
|
||||
var contentType = factory.BuildEntity(dtDto);
|
||||
|
||||
// NOTE
|
||||
// that was done by the factory but makes little sense, moved here, so
|
||||
// now we have to reset dirty props again (as the factory does it) and yet,
|
||||
// we are not managing allowed templates... the whole thing is weird.
|
||||
((ContentType)contentType).DefaultTemplateId = dtDto.TemplateNodeId;
|
||||
contentType.ResetDirtyProperties(false);
|
||||
|
||||
//map the allowed content types
|
||||
contentType.AllowedContentTypes = currAllowedContentTypes;
|
||||
|
||||
return contentType;
|
||||
}
|
||||
|
||||
private static void MapCommonContentTypeObjects<T, TId>(T contentType, TId currentCtId, List<dynamic> result, IDictionary<TId, IEnumerable<TId>> parentContentTypeIds)
|
||||
where T : IContentTypeBase
|
||||
where TId : struct
|
||||
@@ -1196,71 +1188,20 @@ AND umbracoNode.id <> @id",
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Inner repository to support the GUID lookups and keep the caching consistent
|
||||
/// </summary>
|
||||
internal class GuidReadOnlyContentTypeBaseRepository : PetaPocoRepositoryBase<Guid, TEntity>
|
||||
{
|
||||
private readonly ContentTypeBaseRepository<TEntity> _parentRepo;
|
||||
|
||||
public GuidReadOnlyContentTypeBaseRepository(
|
||||
ContentTypeBaseRepository<TEntity> parentRepo,
|
||||
IDatabaseUnitOfWork work, CacheHelper cache, ILogger logger, ISqlSyntaxProvider sqlSyntax)
|
||||
: base(work, cache, logger, sqlSyntax)
|
||||
{
|
||||
_parentRepo = parentRepo;
|
||||
}
|
||||
|
||||
protected override TEntity PerformGet(Guid id)
|
||||
{
|
||||
return _parentRepo.PerformGet(id);
|
||||
}
|
||||
|
||||
protected override IEnumerable<TEntity> PerformGetAll(params Guid[] ids)
|
||||
{
|
||||
return _parentRepo.PerformGetAll(ids);
|
||||
}
|
||||
|
||||
protected override Sql GetBaseQuery(bool isCount)
|
||||
{
|
||||
return _parentRepo.GetBaseQuery(isCount);
|
||||
}
|
||||
|
||||
protected override string GetBaseWhereClause()
|
||||
{
|
||||
return "umbracoNode.uniqueID = @Id";
|
||||
}
|
||||
|
||||
#region No implementation required
|
||||
protected override IEnumerable<TEntity> PerformGetByQuery(IQuery<TEntity> query)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
protected override IEnumerable<string> GetDeleteClauses()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
protected override Guid NodeObjectTypeId
|
||||
{
|
||||
get { throw new NotImplementedException(); }
|
||||
}
|
||||
|
||||
protected override void PersistNewItem(TEntity entity)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
protected override void PersistUpdatedItem(TEntity entity)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
|
||||
protected abstract TEntity PerformGet(Guid id);
|
||||
protected abstract TEntity PerformGet(string alias);
|
||||
protected abstract IEnumerable<TEntity> PerformGetAll(params Guid[] ids);
|
||||
protected abstract bool PerformExists(Guid id);
|
||||
|
||||
/// <summary>
|
||||
/// Gets an Entity by alias
|
||||
/// </summary>
|
||||
/// <param name="alias"></param>
|
||||
/// <returns></returns>
|
||||
public TEntity Get(string alias)
|
||||
{
|
||||
return PerformGet(alias);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets an Entity by Id
|
||||
@@ -1269,7 +1210,7 @@ AND umbracoNode.id <> @id",
|
||||
/// <returns></returns>
|
||||
public TEntity Get(Guid id)
|
||||
{
|
||||
return _guidRepo.Get(id);
|
||||
return PerformGet(id);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -1277,9 +1218,12 @@ AND umbracoNode.id <> @id",
|
||||
/// </summary>
|
||||
/// <param name="ids"></param>
|
||||
/// <returns></returns>
|
||||
public IEnumerable<TEntity> GetAll(params Guid[] ids)
|
||||
/// <remarks>
|
||||
/// Ensure explicit implementation, we don't want to have any accidental calls to this since it is essentially the same signature as the main GetAll when there are no parameters
|
||||
/// </remarks>
|
||||
IEnumerable<TEntity> IReadRepository<Guid, TEntity>.GetAll(params Guid[] ids)
|
||||
{
|
||||
return _guidRepo.GetAll(ids);
|
||||
return PerformGetAll(ids);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -1289,7 +1233,7 @@ AND umbracoNode.id <> @id",
|
||||
/// <returns></returns>
|
||||
public bool Exists(Guid id)
|
||||
{
|
||||
return _guidRepo.Exists(id);
|
||||
return PerformExists(id);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Umbraco.Core.Cache;
|
||||
using Umbraco.Core.Logging;
|
||||
using Umbraco.Core.Models;
|
||||
using Umbraco.Core.Models.EntityBase;
|
||||
@@ -27,46 +28,54 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
_templateRepository = templateRepository;
|
||||
}
|
||||
|
||||
#region Overrides of RepositoryBase<int,IContentType>
|
||||
|
||||
private FullDataSetRepositoryCachePolicyFactory<IContentType, int> _cachePolicyFactory;
|
||||
protected override IRepositoryCachePolicyFactory<IContentType, int> CachePolicyFactory
|
||||
{
|
||||
get
|
||||
{
|
||||
//Use a FullDataSet cache policy - this will cache the entire GetAll result in a single collection
|
||||
return _cachePolicyFactory ?? (_cachePolicyFactory = new FullDataSetRepositoryCachePolicyFactory<IContentType, int>(
|
||||
RuntimeCache, GetEntityId, () => PerformGetAll(),
|
||||
//allow this cache to expire
|
||||
expires:true));
|
||||
}
|
||||
}
|
||||
|
||||
protected override IContentType PerformGet(int id)
|
||||
{
|
||||
var contentTypes = ContentTypeQueryMapper.GetContentTypes(
|
||||
new[] {id}, Database, SqlSyntax, this, _templateRepository);
|
||||
|
||||
var contentType = contentTypes.SingleOrDefault();
|
||||
return contentType;
|
||||
//use the underlying GetAll which will force cache all content types
|
||||
return GetAll().FirstOrDefault(x => x.Id == id);
|
||||
}
|
||||
|
||||
protected override IEnumerable<IContentType> PerformGetAll(params int[] ids)
|
||||
{
|
||||
if (ids.Any())
|
||||
{
|
||||
return ContentTypeQueryMapper.GetContentTypes(ids, Database, SqlSyntax, this, _templateRepository);
|
||||
}
|
||||
else
|
||||
{
|
||||
var sql = new Sql().Select("id").From<NodeDto>().Where<NodeDto>(dto => dto.NodeObjectType == NodeObjectTypeId);
|
||||
var allIds = Database.Fetch<int>(sql).ToArray();
|
||||
return ContentTypeQueryMapper.GetContentTypes(allIds, Database, SqlSyntax, this, _templateRepository);
|
||||
//NOTE: This logic should never be executed according to our cache policy
|
||||
return ContentTypeQueryMapper.GetContentTypes(Database, SqlSyntax, this, _templateRepository)
|
||||
.Where(x => ids.Contains(x.Id));
|
||||
}
|
||||
|
||||
return ContentTypeQueryMapper.GetContentTypes(Database, SqlSyntax, this, _templateRepository);
|
||||
}
|
||||
|
||||
protected override IEnumerable<IContentType> PerformGetByQuery(IQuery<IContentType> query)
|
||||
{
|
||||
var sqlClause = GetBaseQuery(false);
|
||||
var translator = new SqlTranslator<IContentType>(sqlClause, query);
|
||||
var sql = translator.Translate()
|
||||
.OrderBy<NodeDto>(x => x.Text);
|
||||
var sql = translator.Translate();
|
||||
|
||||
var dtos = Database.Fetch<DocumentTypeDto, ContentTypeDto, NodeDto>(sql);
|
||||
return dtos.Any()
|
||||
? GetAll(dtos.DistinctBy(x => x.ContentTypeDto.NodeId).Select(x => x.ContentTypeDto.NodeId).ToArray())
|
||||
: Enumerable.Empty<IContentType>();
|
||||
|
||||
return
|
||||
//This returns a lookup from the GetAll cached looup
|
||||
(dtos.Any()
|
||||
? GetAll(dtos.DistinctBy(x => x.ContentTypeDto.NodeId).Select(x => x.ContentTypeDto.NodeId).ToArray())
|
||||
: Enumerable.Empty<IContentType>())
|
||||
//order the result by name
|
||||
.OrderBy(x => x.Name);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Gets all entities of the specified <see cref="PropertyType"/> query
|
||||
/// </summary>
|
||||
@@ -244,25 +253,33 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
|
||||
protected override IContentType PerformGet(Guid id)
|
||||
{
|
||||
var contentTypes = ContentTypeQueryMapper.GetContentTypes(
|
||||
new[] { id }, Database, SqlSyntax, this, _templateRepository);
|
||||
//use the underlying GetAll which will force cache all content types
|
||||
return GetAll().FirstOrDefault(x => x.Key == id);
|
||||
}
|
||||
|
||||
var contentType = contentTypes.SingleOrDefault();
|
||||
return contentType;
|
||||
protected override IContentType PerformGet(string alias)
|
||||
{
|
||||
//use the underlying GetAll which will force cache all content types
|
||||
return GetAll().FirstOrDefault(x => x.Alias.InvariantEquals(alias));
|
||||
}
|
||||
|
||||
protected override IEnumerable<IContentType> PerformGetAll(params Guid[] ids)
|
||||
{
|
||||
//use the underlying GetAll which will force cache all content types
|
||||
|
||||
if (ids.Any())
|
||||
{
|
||||
return ContentTypeQueryMapper.GetContentTypes(ids, Database, SqlSyntax, this, _templateRepository);
|
||||
return GetAll().Where(x => ids.Contains(x.Key));
|
||||
}
|
||||
else
|
||||
{
|
||||
var sql = new Sql().Select("id").From<NodeDto>(SqlSyntax).Where<NodeDto>(dto => dto.NodeObjectType == NodeObjectTypeId);
|
||||
var allIds = Database.Fetch<int>(sql).ToArray();
|
||||
return ContentTypeQueryMapper.GetContentTypes(allIds, Database, SqlSyntax, this, _templateRepository);
|
||||
return GetAll();
|
||||
}
|
||||
}
|
||||
|
||||
protected override bool PerformExists(Guid id)
|
||||
{
|
||||
return GetAll().FirstOrDefault(x => x.Key == id) != null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -24,17 +24,15 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
/// </summary>
|
||||
internal class DataTypeDefinitionRepository : PetaPocoRepositoryBase<int, IDataTypeDefinition>, IDataTypeDefinitionRepository
|
||||
{
|
||||
private readonly CacheHelper _cacheHelper;
|
||||
private readonly IContentTypeRepository _contentTypeRepository;
|
||||
private readonly DataTypePreValueRepository _preValRepository;
|
||||
|
||||
public DataTypeDefinitionRepository(IDatabaseUnitOfWork work, CacheHelper cache, CacheHelper cacheHelper, ILogger logger, ISqlSyntaxProvider sqlSyntax,
|
||||
public DataTypeDefinitionRepository(IDatabaseUnitOfWork work, CacheHelper cache, ILogger logger, ISqlSyntaxProvider sqlSyntax,
|
||||
IContentTypeRepository contentTypeRepository)
|
||||
: base(work, cache, logger, sqlSyntax)
|
||||
{
|
||||
_cacheHelper = cacheHelper;
|
||||
_contentTypeRepository = contentTypeRepository;
|
||||
_preValRepository = new DataTypePreValueRepository(work, CacheHelper.CreateDisabledCacheHelper(), logger, sqlSyntax);
|
||||
_preValRepository = new DataTypePreValueRepository(work, CacheHelper.CreateDisabledCacheHelper(), logger, sqlSyntax);
|
||||
}
|
||||
|
||||
#region Overrides of RepositoryBase<int,DataTypeDefinition>
|
||||
@@ -231,7 +229,7 @@ AND umbracoNode.id <> @id",
|
||||
|
||||
//NOTE: This is a special case, we need to clear the custom cache for pre-values here so they are not stale if devs
|
||||
// are querying for them in the Saved event (before the distributed call cache is clearing it)
|
||||
_cacheHelper.RuntimeCache.ClearCacheItem(GetPrefixedCacheKey(entity.Id));
|
||||
RuntimeCache.ClearCacheItem(GetPrefixedCacheKey(entity.Id));
|
||||
|
||||
entity.ResetDirtyProperties();
|
||||
}
|
||||
@@ -270,7 +268,7 @@ AND umbracoNode.id <> @id",
|
||||
|
||||
public PreValueCollection GetPreValuesCollectionByDataTypeId(int dataTypeId)
|
||||
{
|
||||
var cached = _cacheHelper.RuntimeCache.GetCacheItemsByKeySearch<PreValueCollection>(GetPrefixedCacheKey(dataTypeId));
|
||||
var cached = RuntimeCache.GetCacheItemsByKeySearch<PreValueCollection>(GetPrefixedCacheKey(dataTypeId));
|
||||
if (cached != null && cached.Any())
|
||||
{
|
||||
//return from the cache, ensure it's a cloned result
|
||||
@@ -289,7 +287,7 @@ AND umbracoNode.id <> @id",
|
||||
{
|
||||
//We need to see if we can find the cached PreValueCollection based on the cache key above
|
||||
|
||||
var cached = _cacheHelper.RuntimeCache.GetCacheItemsByKeyExpression<PreValueCollection>(GetCacheKeyRegex(preValueId));
|
||||
var cached = RuntimeCache.GetCacheItemsByKeyExpression<PreValueCollection>(GetCacheKeyRegex(preValueId));
|
||||
if (cached != null && cached.Any())
|
||||
{
|
||||
//return from the cache
|
||||
@@ -408,7 +406,7 @@ AND umbracoNode.id <> @id",
|
||||
+ string.Join(",", collection.FormatAsDictionary().Select(x => x.Value.Id).ToArray());
|
||||
|
||||
//store into cache
|
||||
_cacheHelper.RuntimeCache.InsertCacheItem(key, () => collection,
|
||||
RuntimeCache.InsertCacheItem(key, () => collection,
|
||||
//30 mins
|
||||
new TimeSpan(0, 0, 30),
|
||||
//sliding is true
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Umbraco.Core.Cache;
|
||||
using Umbraco.Core.Logging;
|
||||
using Umbraco.Core.Models;
|
||||
using Umbraco.Core.Models.EntityBase;
|
||||
@@ -19,30 +20,40 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
/// </summary>
|
||||
internal class DictionaryRepository : PetaPocoRepositoryBase<int, IDictionaryItem>, IDictionaryRepository
|
||||
{
|
||||
private readonly ILanguageRepository _languageRepository;
|
||||
|
||||
public DictionaryRepository(IDatabaseUnitOfWork work, CacheHelper cache, ILogger logger, ISqlSyntaxProvider syntax, ILanguageRepository languageRepository)
|
||||
public DictionaryRepository(IDatabaseUnitOfWork work, CacheHelper cache, ILogger logger, ISqlSyntaxProvider syntax)
|
||||
: base(work, cache, logger, syntax)
|
||||
{
|
||||
_languageRepository = languageRepository;
|
||||
}
|
||||
{
|
||||
}
|
||||
|
||||
private IRepositoryCachePolicyFactory<IDictionaryItem, int> _cachePolicyFactory;
|
||||
protected override IRepositoryCachePolicyFactory<IDictionaryItem, int> CachePolicyFactory
|
||||
{
|
||||
get
|
||||
{
|
||||
//custom cache policy which will not cache any results for GetAll
|
||||
return _cachePolicyFactory ?? (_cachePolicyFactory = new OnlySingleItemsRepositoryCachePolicyFactory<IDictionaryItem, int>(
|
||||
RuntimeCache,
|
||||
new RepositoryCachePolicyOptions
|
||||
{
|
||||
//allow zero to be cached
|
||||
GetAllCacheAllowZeroCount = true
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
#region Overrides of RepositoryBase<int,DictionaryItem>
|
||||
|
||||
protected override IDictionaryItem PerformGet(int id)
|
||||
{
|
||||
var sql = GetBaseQuery(false)
|
||||
.Where(GetBaseWhereClause(), new {Id = id})
|
||||
.Where(GetBaseWhereClause(), new { Id = id })
|
||||
.OrderBy<DictionaryDto>(x => x.UniqueId, SqlSyntax);
|
||||
|
||||
var dto = Database.Fetch<DictionaryDto, LanguageTextDto, DictionaryDto>(new DictionaryLanguageTextRelator().Map, sql).FirstOrDefault();
|
||||
if (dto == null)
|
||||
return null;
|
||||
|
||||
//This will be cached
|
||||
var allLanguages = _languageRepository.GetAll().ToArray();
|
||||
|
||||
var entity = ConvertFromDto(dto, allLanguages);
|
||||
|
||||
var entity = ConvertFromDto(dto);
|
||||
|
||||
//on initial construction we don't want to have dirty properties tracked
|
||||
// http://issues.umbraco.org/issue/U4-1946
|
||||
@@ -56,14 +67,11 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
var sql = GetBaseQuery(false).Where("cmsDictionary.pk > 0");
|
||||
if (ids.Any())
|
||||
{
|
||||
sql.Where("cmsDictionary.pk in (@ids)", new { ids = ids });
|
||||
sql.Where("cmsDictionary.pk in (@ids)", new { ids = ids });
|
||||
}
|
||||
|
||||
//This will be cached
|
||||
var allLanguages = _languageRepository.GetAll().ToArray();
|
||||
|
||||
return Database.Fetch<DictionaryDto, LanguageTextDto, DictionaryDto>(new DictionaryLanguageTextRelator().Map, sql)
|
||||
.Select(dto => ConvertFromDto(dto, allLanguages));
|
||||
.Select(dto => ConvertFromDto(dto));
|
||||
}
|
||||
|
||||
protected override IEnumerable<IDictionaryItem> PerformGetByQuery(IQuery<IDictionaryItem> query)
|
||||
@@ -72,12 +80,9 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
var translator = new SqlTranslator<IDictionaryItem>(sqlClause, query);
|
||||
var sql = translator.Translate();
|
||||
sql.OrderBy<DictionaryDto>(x => x.UniqueId, SqlSyntax);
|
||||
|
||||
//This will be cached
|
||||
var allLanguages = _languageRepository.GetAll().ToArray();
|
||||
|
||||
|
||||
return Database.Fetch<DictionaryDto, LanguageTextDto, DictionaryDto>(new DictionaryLanguageTextRelator().Map, sql)
|
||||
.Select(x => ConvertFromDto(x, allLanguages));
|
||||
.Select(x => ConvertFromDto(x));
|
||||
}
|
||||
|
||||
#endregion
|
||||
@@ -87,7 +92,7 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
protected override Sql GetBaseQuery(bool isCount)
|
||||
{
|
||||
var sql = new Sql();
|
||||
if(isCount)
|
||||
if (isCount)
|
||||
{
|
||||
sql.Select("COUNT(*)")
|
||||
.From<DictionaryDto>(SqlSyntax);
|
||||
@@ -123,26 +128,28 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
|
||||
protected override void PersistNewItem(IDictionaryItem entity)
|
||||
{
|
||||
((DictionaryItem)entity).AddingEntity();
|
||||
var dictionaryItem = ((DictionaryItem) entity);
|
||||
|
||||
foreach (var translation in entity.Translations)
|
||||
dictionaryItem.AddingEntity();
|
||||
|
||||
foreach (var translation in dictionaryItem.Translations)
|
||||
translation.Value = translation.Value.ToValidXmlString();
|
||||
|
||||
var factory = new DictionaryItemFactory();
|
||||
var dto = factory.BuildDto(entity);
|
||||
var dto = factory.BuildDto(dictionaryItem);
|
||||
|
||||
var id = Convert.ToInt32(Database.Insert(dto));
|
||||
entity.Id = id;
|
||||
dictionaryItem.Id = id;
|
||||
|
||||
var translationFactory = new DictionaryTranslationFactory(entity.Key, null);
|
||||
foreach (var translation in entity.Translations)
|
||||
var translationFactory = new DictionaryTranslationFactory(dictionaryItem.Key);
|
||||
foreach (var translation in dictionaryItem.Translations)
|
||||
{
|
||||
var textDto = translationFactory.BuildDto(translation);
|
||||
translation.Id = Convert.ToInt32(Database.Insert(textDto));
|
||||
translation.Key = entity.Key;
|
||||
translation.Key = dictionaryItem.Key;
|
||||
}
|
||||
|
||||
entity.ResetDirtyProperties();
|
||||
dictionaryItem.ResetDirtyProperties();
|
||||
}
|
||||
|
||||
protected override void PersistUpdatedItem(IDictionaryItem entity)
|
||||
@@ -157,11 +164,11 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
|
||||
Database.Update(dto);
|
||||
|
||||
var translationFactory = new DictionaryTranslationFactory(entity.Key, null);
|
||||
var translationFactory = new DictionaryTranslationFactory(entity.Key);
|
||||
foreach (var translation in entity.Translations)
|
||||
{
|
||||
var textDto = translationFactory.BuildDto(translation);
|
||||
if(translation.HasIdentity)
|
||||
if (translation.HasIdentity)
|
||||
{
|
||||
Database.Update(textDto);
|
||||
}
|
||||
@@ -183,7 +190,7 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
{
|
||||
RecursiveDelete(entity.Key);
|
||||
|
||||
Database.Delete<LanguageTextDto>("WHERE UniqueId = @Id", new { Id = entity.Key});
|
||||
Database.Delete<LanguageTextDto>("WHERE UniqueId = @Id", new { Id = entity.Key });
|
||||
Database.Delete<DictionaryDto>("WHERE id = @Id", new { Id = entity.Key });
|
||||
|
||||
//Clear the cache entries that exist by uniqueid/item key
|
||||
@@ -193,7 +200,7 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
|
||||
private void RecursiveDelete(Guid parentId)
|
||||
{
|
||||
var list = Database.Fetch<DictionaryDto>("WHERE parent = @ParentId", new {ParentId = parentId});
|
||||
var list = Database.Fetch<DictionaryDto>("WHERE parent = @ParentId", new { ParentId = parentId });
|
||||
foreach (var dto in list)
|
||||
{
|
||||
RecursiveDelete(dto.UniqueId);
|
||||
@@ -209,20 +216,18 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
|
||||
#endregion
|
||||
|
||||
protected IDictionaryItem ConvertFromDto(DictionaryDto dto, ILanguage[] allLanguages)
|
||||
protected IDictionaryItem ConvertFromDto(DictionaryDto dto)
|
||||
{
|
||||
var factory = new DictionaryItemFactory();
|
||||
var entity = factory.BuildEntity(dto);
|
||||
|
||||
var list = new List<IDictionaryTranslation>();
|
||||
foreach (var textDto in dto.LanguageTextDtos)
|
||||
{
|
||||
//Assuming this is cached!
|
||||
var language = allLanguages.FirstOrDefault(x => x.Id == textDto.LanguageId);
|
||||
if (language == null)
|
||||
{
|
||||
if (textDto.LanguageId <= 0)
|
||||
continue;
|
||||
|
||||
var translationFactory = new DictionaryTranslationFactory(dto.UniqueId, language);
|
||||
var translationFactory = new DictionaryTranslationFactory(dto.UniqueId);
|
||||
list.Add(translationFactory.BuildEntity(textDto));
|
||||
}
|
||||
entity.Translations = list;
|
||||
@@ -234,7 +239,7 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
{
|
||||
using (var uniqueIdRepo = new DictionaryByUniqueIdRepository(this, UnitOfWork, RepositoryCache, Logger, SqlSyntax))
|
||||
{
|
||||
return uniqueIdRepo.Get(uniqueId);
|
||||
return uniqueIdRepo.Get(uniqueId);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -242,10 +247,10 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
{
|
||||
using (var keyRepo = new DictionaryByKeyRepository(this, UnitOfWork, RepositoryCache, Logger, SqlSyntax))
|
||||
{
|
||||
return keyRepo.Get(key);
|
||||
return keyRepo.Get(key);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private IEnumerable<IDictionaryItem> GetRootDictionaryItems()
|
||||
{
|
||||
var query = Query<IDictionaryItem>.Builder.Where(x => x.ParentId == null);
|
||||
@@ -254,9 +259,6 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
|
||||
public IEnumerable<IDictionaryItem> GetDictionaryItemDescendants(Guid? parentId)
|
||||
{
|
||||
//This will be cached
|
||||
var allLanguages = _languageRepository.GetAll().ToArray();
|
||||
|
||||
//This methods will look up children at each level, since we do not store a path for dictionary (ATM), we need to do a recursive
|
||||
// lookup to get descendants. Currently this is the most efficient way to do it
|
||||
|
||||
@@ -275,7 +277,7 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
sql.OrderBy<DictionaryDto>(x => x.UniqueId, SqlSyntax);
|
||||
|
||||
return Database.Fetch<DictionaryDto, LanguageTextDto, DictionaryDto>(new DictionaryLanguageTextRelator().Map, sql)
|
||||
.Select(x => ConvertFromDto(x, allLanguages));
|
||||
.Select(x => ConvertFromDto(x));
|
||||
});
|
||||
};
|
||||
|
||||
@@ -284,7 +286,7 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
: getItemsFromParents(new[] { parentId.Value });
|
||||
|
||||
return childItems.SelectRecursive(items => getItemsFromParents(items.Select(x => x.Key).ToArray())).SelectMany(items => items);
|
||||
|
||||
|
||||
}
|
||||
|
||||
private class DictionaryByUniqueIdRepository : SimpleGetRepository<Guid, IDictionaryItem, DictionaryDto>
|
||||
@@ -314,20 +316,34 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
|
||||
protected override IDictionaryItem ConvertToEntity(DictionaryDto dto)
|
||||
{
|
||||
//This will be cached
|
||||
var allLanguages = _dictionaryRepository._languageRepository.GetAll().ToArray();
|
||||
return _dictionaryRepository.ConvertFromDto(dto, allLanguages);
|
||||
return _dictionaryRepository.ConvertFromDto(dto);
|
||||
}
|
||||
|
||||
protected override object GetBaseWhereClauseArguments(Guid id)
|
||||
{
|
||||
return new {Id = id};
|
||||
return new { Id = id };
|
||||
}
|
||||
|
||||
protected override string GetWhereInClauseForGetAll()
|
||||
{
|
||||
return "cmsDictionary." + SqlSyntax.GetQuotedColumnName("id") + " in (@ids)";
|
||||
}
|
||||
|
||||
private IRepositoryCachePolicyFactory<IDictionaryItem, Guid> _cachePolicyFactory;
|
||||
protected override IRepositoryCachePolicyFactory<IDictionaryItem, Guid> CachePolicyFactory
|
||||
{
|
||||
get
|
||||
{
|
||||
//custom cache policy which will not cache any results for GetAll
|
||||
return _cachePolicyFactory ?? (_cachePolicyFactory = new OnlySingleItemsRepositoryCachePolicyFactory<IDictionaryItem, Guid>(
|
||||
RuntimeCache,
|
||||
new RepositoryCachePolicyOptions
|
||||
{
|
||||
//allow zero to be cached
|
||||
GetAllCacheAllowZeroCount = true
|
||||
}));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class DictionaryByKeyRepository : SimpleGetRepository<string, IDictionaryItem, DictionaryDto>
|
||||
@@ -357,9 +373,7 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
|
||||
protected override IDictionaryItem ConvertToEntity(DictionaryDto dto)
|
||||
{
|
||||
//This will be cached
|
||||
var allLanguages = _dictionaryRepository._languageRepository.GetAll().ToArray();
|
||||
return _dictionaryRepository.ConvertFromDto(dto, allLanguages);
|
||||
return _dictionaryRepository.ConvertFromDto(dto);
|
||||
}
|
||||
|
||||
protected override object GetBaseWhereClauseArguments(string id)
|
||||
@@ -371,17 +385,24 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
{
|
||||
return "cmsDictionary." + SqlSyntax.GetQuotedColumnName("key") + " in (@ids)";
|
||||
}
|
||||
|
||||
private IRepositoryCachePolicyFactory<IDictionaryItem, string> _cachePolicyFactory;
|
||||
protected override IRepositoryCachePolicyFactory<IDictionaryItem, string> CachePolicyFactory
|
||||
{
|
||||
get
|
||||
{
|
||||
//custom cache policy which will not cache any results for GetAll
|
||||
return _cachePolicyFactory ?? (_cachePolicyFactory = new OnlySingleItemsRepositoryCachePolicyFactory<IDictionaryItem, string>(
|
||||
RuntimeCache,
|
||||
new RepositoryCachePolicyOptions
|
||||
{
|
||||
//allow zero to be cached
|
||||
GetAllCacheAllowZeroCount = true
|
||||
}));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Dispose disposable properties
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Ensure the unit of work is disposed
|
||||
/// </remarks>
|
||||
protected override void DisposeResources()
|
||||
{
|
||||
_languageRepository.Dispose();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -18,25 +18,20 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
|
||||
internal class DomainRepository : PetaPocoRepositoryBase<int, IDomain>, IDomainRepository
|
||||
{
|
||||
private readonly RepositoryCacheOptions _cacheOptions;
|
||||
|
||||
public DomainRepository(IDatabaseUnitOfWork work, CacheHelper cache, ILogger logger, ISqlSyntaxProvider sqlSyntax)
|
||||
: base(work, cache, logger, sqlSyntax)
|
||||
{
|
||||
//Custom cache options for better performance
|
||||
_cacheOptions = new RepositoryCacheOptions
|
||||
{
|
||||
GetAllCacheAllowZeroCount = true,
|
||||
GetAllCacheValidateCount = false
|
||||
};
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the repository cache options
|
||||
/// </summary>
|
||||
protected override RepositoryCacheOptions RepositoryCacheOptions
|
||||
private FullDataSetRepositoryCachePolicyFactory<IDomain, int> _cachePolicyFactory;
|
||||
protected override IRepositoryCachePolicyFactory<IDomain, int> CachePolicyFactory
|
||||
{
|
||||
get { return _cacheOptions; }
|
||||
get
|
||||
{
|
||||
//Use a FullDataSet cache policy - this will cache the entire GetAll result in a single collection
|
||||
return _cachePolicyFactory ?? (_cachePolicyFactory = new FullDataSetRepositoryCachePolicyFactory<IDomain, int>(
|
||||
RuntimeCache, GetEntityId, () => PerformGetAll(), false));
|
||||
}
|
||||
}
|
||||
|
||||
protected override IDomain PerformGet(int id)
|
||||
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
using System;
|
||||
using Umbraco.Core.Models;
|
||||
|
||||
namespace Umbraco.Core.Persistence.Repositories
|
||||
{
|
||||
public interface IContentTypeCompositionRepository<TEntity> : IRepositoryQueryable<int, TEntity>, IReadRepository<Guid, TEntity>
|
||||
where TEntity : IContentTypeComposition
|
||||
{
|
||||
TEntity Get(string alias);
|
||||
}
|
||||
}
|
||||
@@ -5,7 +5,7 @@ using Umbraco.Core.Persistence.Querying;
|
||||
|
||||
namespace Umbraco.Core.Persistence.Repositories
|
||||
{
|
||||
public interface IContentTypeRepository : IRepositoryQueryable<int, IContentType>, IReadRepository<Guid, IContentType>
|
||||
public interface IContentTypeRepository : IContentTypeCompositionRepository<IContentType>
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets all entities of the specified <see cref="PropertyType"/> query
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Generic;
|
||||
using Umbraco.Core.Models;
|
||||
using Umbraco.Core.Persistence.Querying;
|
||||
|
||||
namespace Umbraco.Core.Persistence.Repositories
|
||||
{
|
||||
public interface IMediaTypeRepository : IRepositoryQueryable<int, IMediaType>, IReadRepository<Guid, IMediaType>
|
||||
public interface IMediaTypeRepository : IContentTypeCompositionRepository<IMediaType>
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets all entities of the specified <see cref="PropertyType"/> query
|
||||
|
||||
@@ -3,7 +3,7 @@ using Umbraco.Core.Models;
|
||||
|
||||
namespace Umbraco.Core.Persistence.Repositories
|
||||
{
|
||||
public interface IMemberTypeRepository : IRepositoryQueryable<int, IMemberType>, IReadRepository<Guid, IMemberType>
|
||||
public interface IMemberTypeRepository : IContentTypeCompositionRepository<IMemberType>
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using Umbraco.Core.Models;
|
||||
|
||||
namespace Umbraco.Core.Persistence.Repositories
|
||||
@@ -22,6 +23,7 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
/// <param name="alias"></param>
|
||||
/// <returns></returns>
|
||||
[Obsolete("Use GetDescendants instead")]
|
||||
[EditorBrowsable(EditorBrowsableState.Never)]
|
||||
TemplateNode GetTemplateNode(string alias);
|
||||
|
||||
/// <summary>
|
||||
@@ -31,6 +33,7 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
/// <param name="alias"></param>
|
||||
/// <returns></returns>
|
||||
[Obsolete("Use GetDescendants instead")]
|
||||
[EditorBrowsable(EditorBrowsableState.Never)]
|
||||
TemplateNode FindTemplateInTree(TemplateNode anyNode, string alias);
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Umbraco.Core.Cache;
|
||||
using Umbraco.Core.Logging;
|
||||
using Umbraco.Core.Models;
|
||||
using Umbraco.Core.Models.EntityBase;
|
||||
@@ -20,23 +21,18 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
{
|
||||
public LanguageRepository(IDatabaseUnitOfWork work, CacheHelper cache, ILogger logger, ISqlSyntaxProvider sqlSyntax)
|
||||
: base(work, cache, logger, sqlSyntax)
|
||||
{
|
||||
//Custom cache options for better performance
|
||||
_cacheOptions = new RepositoryCacheOptions
|
||||
{
|
||||
GetAllCacheAllowZeroCount = true,
|
||||
GetAllCacheValidateCount = false
|
||||
};
|
||||
{
|
||||
}
|
||||
|
||||
private readonly RepositoryCacheOptions _cacheOptions;
|
||||
|
||||
/// <summary>
|
||||
/// Returns the repository cache options
|
||||
/// </summary>
|
||||
protected override RepositoryCacheOptions RepositoryCacheOptions
|
||||
private FullDataSetRepositoryCachePolicyFactory<ILanguage, int> _cachePolicyFactory;
|
||||
protected override IRepositoryCachePolicyFactory<ILanguage, int> CachePolicyFactory
|
||||
{
|
||||
get { return _cacheOptions; }
|
||||
get
|
||||
{
|
||||
//Use a FullDataSet cache policy - this will cache the entire GetAll result in a single collection
|
||||
return _cachePolicyFactory ?? (_cachePolicyFactory = new FullDataSetRepositoryCachePolicyFactory<ILanguage, int>(
|
||||
RuntimeCache, GetEntityId, () => PerformGetAll(), false));
|
||||
}
|
||||
}
|
||||
|
||||
#region Overrides of RepositoryBase<int,Language>
|
||||
@@ -159,13 +155,13 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
|
||||
public ILanguage GetByCultureName(string cultureName)
|
||||
{
|
||||
//use the underlying GetAll which will force cache all domains
|
||||
//use the underlying GetAll which will force cache all languages
|
||||
return GetAll().FirstOrDefault(x => x.CultureName.InvariantEquals(cultureName));
|
||||
}
|
||||
|
||||
public ILanguage GetByIsoCode(string isoCode)
|
||||
{
|
||||
//use the underlying GetAll which will force cache all domains
|
||||
//use the underlying GetAll which will force cache all languages
|
||||
return GetAll().FirstOrDefault(x => x.IsoCode.InvariantEquals(isoCode));
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Umbraco.Core.Cache;
|
||||
using Umbraco.Core.Logging;
|
||||
using Umbraco.Core.Models;
|
||||
using Umbraco.Core.Models.EntityBase;
|
||||
@@ -24,46 +25,54 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
{
|
||||
}
|
||||
|
||||
#region Overrides of RepositoryBase<int,IMedia>
|
||||
private FullDataSetRepositoryCachePolicyFactory<IMediaType, int> _cachePolicyFactory;
|
||||
protected override IRepositoryCachePolicyFactory<IMediaType, int> CachePolicyFactory
|
||||
{
|
||||
get
|
||||
{
|
||||
//Use a FullDataSet cache policy - this will cache the entire GetAll result in a single collection
|
||||
return _cachePolicyFactory ?? (_cachePolicyFactory = new FullDataSetRepositoryCachePolicyFactory<IMediaType, int>(
|
||||
RuntimeCache, GetEntityId, () => PerformGetAll(),
|
||||
//allow this cache to expire
|
||||
expires: true));
|
||||
}
|
||||
}
|
||||
|
||||
protected override IMediaType PerformGet(int id)
|
||||
{
|
||||
var contentTypes = ContentTypeQueryMapper.GetMediaTypes(
|
||||
new[] { id }, Database, SqlSyntax, this);
|
||||
|
||||
var contentType = contentTypes.SingleOrDefault();
|
||||
return contentType;
|
||||
//use the underlying GetAll which will force cache all content types
|
||||
return GetAll().FirstOrDefault(x => x.Id == id);
|
||||
}
|
||||
|
||||
protected override IEnumerable<IMediaType> PerformGetAll(params int[] ids)
|
||||
{
|
||||
if (ids.Any())
|
||||
{
|
||||
return ContentTypeQueryMapper.GetMediaTypes(ids, Database, SqlSyntax, this);
|
||||
}
|
||||
else
|
||||
{
|
||||
var sql = new Sql().Select("id").From<NodeDto>().Where<NodeDto>(dto => dto.NodeObjectType == NodeObjectTypeId);
|
||||
var allIds = Database.Fetch<int>(sql).ToArray();
|
||||
return ContentTypeQueryMapper.GetMediaTypes(allIds, Database, SqlSyntax, this);
|
||||
//NOTE: This logic should never be executed according to our cache policy
|
||||
return ContentTypeQueryMapper.GetMediaTypes(Database, SqlSyntax, this)
|
||||
.Where(x => ids.Contains(x.Id));
|
||||
}
|
||||
|
||||
return ContentTypeQueryMapper.GetMediaTypes(Database, SqlSyntax, this);
|
||||
}
|
||||
|
||||
protected override IEnumerable<IMediaType> PerformGetByQuery(IQuery<IMediaType> query)
|
||||
{
|
||||
var sqlClause = GetBaseQuery(false);
|
||||
var translator = new SqlTranslator<IMediaType>(sqlClause, query);
|
||||
var sql = translator.Translate()
|
||||
.OrderBy<NodeDto>(x => x.Text);
|
||||
var sql = translator.Translate();
|
||||
|
||||
var dtos = Database.Fetch<ContentTypeDto, NodeDto>(sql);
|
||||
return dtos.Any()
|
||||
? GetAll(dtos.DistinctBy(x => x.NodeId).Select(x => x.NodeId).ToArray())
|
||||
: Enumerable.Empty<IMediaType>();
|
||||
|
||||
return
|
||||
//This returns a lookup from the GetAll cached looup
|
||||
(dtos.Any()
|
||||
? GetAll(dtos.DistinctBy(x => x.NodeId).Select(x => x.NodeId).ToArray())
|
||||
: Enumerable.Empty<IMediaType>())
|
||||
//order the result by name
|
||||
.OrderBy(x => x.Name);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Gets all entities of the specified <see cref="PropertyType"/> query
|
||||
@@ -168,25 +177,33 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
|
||||
protected override IMediaType PerformGet(Guid id)
|
||||
{
|
||||
var contentTypes = ContentTypeQueryMapper.GetMediaTypes(
|
||||
new[] { id }, Database, SqlSyntax, this);
|
||||
|
||||
var contentType = contentTypes.SingleOrDefault();
|
||||
return contentType;
|
||||
//use the underlying GetAll which will force cache all content types
|
||||
return GetAll().FirstOrDefault(x => x.Key == id);
|
||||
}
|
||||
|
||||
protected override IEnumerable<IMediaType> PerformGetAll(params Guid[] ids)
|
||||
{
|
||||
//use the underlying GetAll which will force cache all content types
|
||||
|
||||
if (ids.Any())
|
||||
{
|
||||
return ContentTypeQueryMapper.GetMediaTypes(ids, Database, SqlSyntax, this);
|
||||
return GetAll().Where(x => ids.Contains(x.Key));
|
||||
}
|
||||
else
|
||||
{
|
||||
var sql = new Sql().Select("id").From<NodeDto>(SqlSyntax).Where<NodeDto>(dto => dto.NodeObjectType == NodeObjectTypeId);
|
||||
var allIds = Database.Fetch<int>(sql).ToArray();
|
||||
return ContentTypeQueryMapper.GetMediaTypes(allIds, Database, SqlSyntax, this);
|
||||
return GetAll();
|
||||
}
|
||||
}
|
||||
|
||||
protected override bool PerformExists(Guid id)
|
||||
{
|
||||
return GetAll().FirstOrDefault(x => x.Key == id) != null;
|
||||
}
|
||||
|
||||
protected override IMediaType PerformGet(string alias)
|
||||
{
|
||||
//use the underlying GetAll which will force cache all content types
|
||||
return GetAll().FirstOrDefault(x => x.Alias.InvariantEquals(alias));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -20,13 +20,9 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
|
||||
internal class MemberGroupRepository : PetaPocoRepositoryBase<int, IMemberGroup>, IMemberGroupRepository
|
||||
{
|
||||
private readonly CacheHelper _cacheHelper;
|
||||
|
||||
public MemberGroupRepository(IDatabaseUnitOfWork work, CacheHelper cache, ILogger logger, ISqlSyntaxProvider sqlSyntax, CacheHelper cacheHelper)
|
||||
public MemberGroupRepository(IDatabaseUnitOfWork work, CacheHelper cache, ILogger logger, ISqlSyntaxProvider sqlSyntax)
|
||||
: base(work, cache, logger, sqlSyntax)
|
||||
{
|
||||
if (cacheHelper == null) throw new ArgumentNullException("cacheHelper");
|
||||
_cacheHelper = cacheHelper;
|
||||
}
|
||||
|
||||
private readonly MemberGroupFactory _modelFactory = new MemberGroupFactory();
|
||||
@@ -135,7 +131,7 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
|
||||
public IMemberGroup GetByName(string name)
|
||||
{
|
||||
return _cacheHelper.RuntimeCache.GetCacheItem<IMemberGroup>(
|
||||
return RuntimeCache.GetCacheItem<IMemberGroup>(
|
||||
string.Format("{0}.{1}", typeof (IMemberGroup).FullName, name),
|
||||
() =>
|
||||
{
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using log4net;
|
||||
using Umbraco.Core.Cache;
|
||||
using Umbraco.Core.Logging;
|
||||
using Umbraco.Core.Models.EntityBase;
|
||||
using Umbraco.Core.Models;
|
||||
@@ -26,25 +27,23 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
{
|
||||
}
|
||||
|
||||
#region Overrides of RepositoryBase<int, IMemberType>
|
||||
|
||||
private FullDataSetRepositoryCachePolicyFactory<IMemberType, int> _cachePolicyFactory;
|
||||
protected override IRepositoryCachePolicyFactory<IMemberType, int> CachePolicyFactory
|
||||
{
|
||||
get
|
||||
{
|
||||
//Use a FullDataSet cache policy - this will cache the entire GetAll result in a single collection
|
||||
return _cachePolicyFactory ?? (_cachePolicyFactory = new FullDataSetRepositoryCachePolicyFactory<IMemberType, int>(
|
||||
RuntimeCache, GetEntityId, () => PerformGetAll(),
|
||||
//allow this cache to expire
|
||||
expires: true));
|
||||
}
|
||||
}
|
||||
|
||||
protected override IMemberType PerformGet(int id)
|
||||
{
|
||||
var sql = GetBaseQuery(false);
|
||||
sql.Where(GetBaseWhereClause(), new { Id = id });
|
||||
sql.OrderByDescending<NodeDto>(x => x.NodeId);
|
||||
|
||||
var dtos =
|
||||
Database.Fetch<MemberTypeReadOnlyDto, PropertyTypeReadOnlyDto, PropertyTypeGroupReadOnlyDto, MemberTypeReadOnlyDto>(
|
||||
new PropertyTypePropertyGroupRelator().Map, sql);
|
||||
|
||||
if (dtos == null || dtos.Any() == false)
|
||||
return null;
|
||||
|
||||
var factory = new MemberTypeReadOnlyFactory();
|
||||
var member = factory.BuildEntity(dtos.First());
|
||||
|
||||
return member;
|
||||
//use the underlying GetAll which will force cache all content types
|
||||
return GetAll().FirstOrDefault(x => x.Id == id);
|
||||
}
|
||||
|
||||
protected override IEnumerable<IMemberType> PerformGetAll(params int[] ids)
|
||||
@@ -52,10 +51,11 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
var sql = GetBaseQuery(false);
|
||||
if (ids.Any())
|
||||
{
|
||||
//NOTE: This logic should never be executed according to our cache policy
|
||||
var statement = string.Join(" OR ", ids.Select(x => string.Format("umbracoNode.id='{0}'", x)));
|
||||
sql.Where(statement);
|
||||
}
|
||||
sql.OrderByDescending<NodeDto>(x => x.NodeId);
|
||||
sql.OrderByDescending<NodeDto>(x => x.NodeId, SqlSyntax);
|
||||
|
||||
var dtos =
|
||||
Database.Fetch<MemberTypeReadOnlyDto, PropertyTypeReadOnlyDto, PropertyTypeGroupReadOnlyDto, MemberTypeReadOnlyDto>(
|
||||
@@ -71,7 +71,7 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
var subquery = translator.Translate();
|
||||
var sql = GetBaseQuery(false)
|
||||
.Append(new Sql("WHERE umbracoNode.id IN (" + subquery.SQL + ")", subquery.Arguments))
|
||||
.OrderBy<NodeDto>(x => x.SortOrder);
|
||||
.OrderBy<NodeDto>(x => x.SortOrder, SqlSyntax);
|
||||
|
||||
var dtos =
|
||||
Database.Fetch<MemberTypeReadOnlyDto, PropertyTypeReadOnlyDto, PropertyTypeGroupReadOnlyDto, MemberTypeReadOnlyDto>(
|
||||
@@ -79,11 +79,7 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
|
||||
return BuildFromDtos(dtos);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Overrides of PetaPocoRepositoryBase<int, IMemberType>
|
||||
|
||||
|
||||
protected override Sql GetBaseQuery(bool isCount)
|
||||
{
|
||||
var sql = new Sql();
|
||||
@@ -157,11 +153,7 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
{
|
||||
get { return new Guid(Constants.ObjectTypes.MemberType); }
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Unit of Work Implementation
|
||||
|
||||
|
||||
protected override void PersistNewItem(IMemberType entity)
|
||||
{
|
||||
ValidateAlias(entity);
|
||||
@@ -238,8 +230,7 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
|
||||
entity.ResetDirtyProperties();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Override so we can specify explicit db type's on any property types that are built-in.
|
||||
@@ -262,38 +253,33 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
|
||||
protected override IMemberType PerformGet(Guid id)
|
||||
{
|
||||
var sql = GetBaseQuery(false);
|
||||
sql.Where("umbracoNode.uniqueID = @Id", new { Id = id });
|
||||
sql.OrderByDescending<NodeDto>(x => x.NodeId);
|
||||
|
||||
var dtos =
|
||||
Database.Fetch<MemberTypeReadOnlyDto, PropertyTypeReadOnlyDto, PropertyTypeGroupReadOnlyDto, MemberTypeReadOnlyDto>(
|
||||
new PropertyTypePropertyGroupRelator().Map, sql);
|
||||
|
||||
if (dtos == null || dtos.Any() == false)
|
||||
return null;
|
||||
|
||||
var factory = new MemberTypeReadOnlyFactory();
|
||||
var member = factory.BuildEntity(dtos.First());
|
||||
|
||||
return member;
|
||||
//use the underlying GetAll which will force cache all content types
|
||||
return GetAll().FirstOrDefault(x => x.Key == id);
|
||||
}
|
||||
|
||||
protected override IEnumerable<IMemberType> PerformGetAll(params Guid[] ids)
|
||||
{
|
||||
var sql = GetBaseQuery(false);
|
||||
//use the underlying GetAll which will force cache all content types
|
||||
|
||||
if (ids.Any())
|
||||
{
|
||||
var statement = string.Join(" OR ", ids.Select(x => string.Format("umbracoNode.uniqueID='{0}'", x)));
|
||||
sql.Where(statement);
|
||||
return GetAll().Where(x => ids.Contains(x.Key));
|
||||
}
|
||||
sql.OrderByDescending<NodeDto>(x => x.NodeId, SqlSyntax);
|
||||
else
|
||||
{
|
||||
return GetAll();
|
||||
}
|
||||
}
|
||||
|
||||
var dtos =
|
||||
Database.Fetch<MemberTypeReadOnlyDto, PropertyTypeReadOnlyDto, PropertyTypeGroupReadOnlyDto, MemberTypeReadOnlyDto>(
|
||||
new PropertyTypePropertyGroupRelator().Map, sql);
|
||||
protected override bool PerformExists(Guid id)
|
||||
{
|
||||
return GetAll().FirstOrDefault(x => x.Key == id) != null;
|
||||
}
|
||||
|
||||
return BuildFromDtos(dtos);
|
||||
protected override IMemberType PerformGet(string alias)
|
||||
{
|
||||
//use the underlying GetAll which will force cache all content types
|
||||
return GetAll().FirstOrDefault(x => x.Alias.InvariantEquals(alias));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -26,13 +26,14 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
where TEntity : class, IAggregateRoot
|
||||
{
|
||||
private readonly IDatabaseUnitOfWork _unitOfWork;
|
||||
private readonly CacheHelper _cache;
|
||||
private readonly IRuntimeCacheProvider _runtimeCache;
|
||||
private readonly ISqlSyntaxProvider _sqlSyntax;
|
||||
|
||||
internal PermissionRepository(IDatabaseUnitOfWork unitOfWork, CacheHelper cache, ISqlSyntaxProvider sqlSyntax)
|
||||
{
|
||||
_unitOfWork = unitOfWork;
|
||||
_cache = cache;
|
||||
//Make this repository use an isolated cache
|
||||
_runtimeCache = cache.IsolatedRuntimeCache.GetOrCreateCache<EntityPermission>();
|
||||
_sqlSyntax = sqlSyntax;
|
||||
}
|
||||
|
||||
@@ -45,7 +46,7 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
public IEnumerable<EntityPermission> GetUserPermissionsForEntities(int userId, params int[] entityIds)
|
||||
{
|
||||
var entityIdKey = string.Join(",", entityIds.Select(x => x.ToString(CultureInfo.InvariantCulture)));
|
||||
return _cache.RuntimeCache.GetCacheItem<IEnumerable<EntityPermission>>(
|
||||
return _runtimeCache.GetCacheItem<IEnumerable<EntityPermission>>(
|
||||
string.Format("{0}{1}{2}", CacheKeys.UserPermissionsCacheKey, userId, entityIdKey),
|
||||
() =>
|
||||
{
|
||||
|
||||
@@ -17,33 +17,24 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
{
|
||||
public PublicAccessRepository(IDatabaseUnitOfWork work, CacheHelper cache, ILogger logger, ISqlSyntaxProvider sqlSyntax)
|
||||
: base(work, cache, logger, sqlSyntax)
|
||||
{
|
||||
_options = new RepositoryCacheOptions
|
||||
{
|
||||
//We want to ensure that a zero count gets cached, even if there is nothing in the db we don't want it to lookup nothing each time
|
||||
GetAllCacheAllowZeroCount = true,
|
||||
//Set to 1000 just to ensure that all of them are cached, The GetAll on this repository gets called *A lot*, we want max performance
|
||||
GetAllCacheThresholdLimit = 1000,
|
||||
//Override to false so that a Count check against the db is NOT performed when doing a GetAll without params, we just want to
|
||||
// return the raw cache without validation. The GetAll on this repository gets called *A lot*, we want max performance
|
||||
GetAllCacheValidateCount = false
|
||||
};
|
||||
{
|
||||
}
|
||||
|
||||
private readonly RepositoryCacheOptions _options;
|
||||
private FullDataSetRepositoryCachePolicyFactory<PublicAccessEntry, Guid> _cachePolicyFactory;
|
||||
protected override IRepositoryCachePolicyFactory<PublicAccessEntry, Guid> CachePolicyFactory
|
||||
{
|
||||
get
|
||||
{
|
||||
//Use a FullDataSet cache policy - this will cache the entire GetAll result in a single collection
|
||||
return _cachePolicyFactory ?? (_cachePolicyFactory = new FullDataSetRepositoryCachePolicyFactory<PublicAccessEntry, Guid>(
|
||||
RuntimeCache, GetEntityId, () => PerformGetAll(), false));
|
||||
}
|
||||
}
|
||||
|
||||
protected override PublicAccessEntry PerformGet(Guid id)
|
||||
{
|
||||
var sql = GetBaseQuery(false);
|
||||
sql.Where(GetBaseWhereClause(), new { Id = id });
|
||||
|
||||
var taskDto = Database.Fetch<AccessDto, AccessRuleDto, AccessDto>(new AccessRulesRelator().Map, sql).FirstOrDefault();
|
||||
if (taskDto == null)
|
||||
return null;
|
||||
|
||||
var factory = new PublicAccessEntryFactory();
|
||||
var entity = factory.BuildEntity(taskDto);
|
||||
return entity;
|
||||
//return from GetAll - this will be cached as a collection
|
||||
return GetAll().FirstOrDefault(x => x.Key == id);
|
||||
}
|
||||
|
||||
protected override IEnumerable<PublicAccessEntry> PerformGetAll(params Guid[] ids)
|
||||
@@ -102,15 +93,6 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
get { throw new NotImplementedException(); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the repository cache options
|
||||
/// </summary>
|
||||
protected override RepositoryCacheOptions RepositoryCacheOptions
|
||||
{
|
||||
get { return _options; }
|
||||
}
|
||||
|
||||
|
||||
protected override void PersistNewItem(PublicAccessEntry entity)
|
||||
{
|
||||
entity.AddingEntity();
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Umbraco.Core.Cache;
|
||||
using Umbraco.Core.Collections;
|
||||
using Umbraco.Core.Logging;
|
||||
using Umbraco.Core.Models.EntityBase;
|
||||
|
||||
@@ -22,11 +24,7 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
if (logger == null) throw new ArgumentNullException("logger");
|
||||
Logger = logger;
|
||||
_work = work;
|
||||
|
||||
//IMPORTANT: We will force the DeepCloneRuntimeCacheProvider to be used here which is a wrapper for the underlying
|
||||
// runtime cache to ensure that anything that can be deep cloned in/out is done so, this also ensures that our tracks
|
||||
// changes entities are reset.
|
||||
_cache = new CacheHelper(new DeepCloneRuntimeCacheProvider(cache.RuntimeCache), cache.StaticCache, cache.RequestCache);
|
||||
_cache = cache;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -84,9 +82,41 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
{
|
||||
}
|
||||
|
||||
private readonly RepositoryCacheOptions _cacheOptions = new RepositoryCacheOptions();
|
||||
|
||||
#region IRepository<TEntity> Members
|
||||
protected virtual TId GetEntityId(TEntity entity)
|
||||
{
|
||||
return (TId)(object)entity.Id;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The runtime cache used for this repo by default is the isolated cache for this type
|
||||
/// </summary>
|
||||
protected override IRuntimeCacheProvider RuntimeCache
|
||||
{
|
||||
get { return RepositoryCache.IsolatedRuntimeCache.GetOrCreateCache<TEntity>(); }
|
||||
}
|
||||
|
||||
private IRepositoryCachePolicyFactory<TEntity, TId> _cachePolicyFactory;
|
||||
/// <summary>
|
||||
/// Returns the Cache Policy for the repository
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The Cache Policy determines how each entity or entity collection is cached
|
||||
/// </remarks>
|
||||
protected virtual IRepositoryCachePolicyFactory<TEntity, TId> CachePolicyFactory
|
||||
{
|
||||
get
|
||||
{
|
||||
return _cachePolicyFactory ?? (_cachePolicyFactory = new DefaultRepositoryCachePolicyFactory<TEntity, TId>(
|
||||
RuntimeCache,
|
||||
new RepositoryCachePolicyOptions(() =>
|
||||
{
|
||||
//Get count of all entities of current type (TEntity) to ensure cached result is correct
|
||||
var query = Query<TEntity>.Builder.Where(x => x.Id != 0);
|
||||
return PerformCount(query);
|
||||
})));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds or Updates an entity of type TEntity
|
||||
@@ -119,23 +149,16 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
|
||||
protected abstract TEntity PerformGet(TId id);
|
||||
/// <summary>
|
||||
/// Gets an entity by the passed in Id utilizing the repository's runtime cache
|
||||
/// Gets an entity by the passed in Id utilizing the repository's cache policy
|
||||
/// </summary>
|
||||
/// <param name="id"></param>
|
||||
/// <returns></returns>
|
||||
public TEntity Get(TId id)
|
||||
{
|
||||
var cacheKey = GetCacheIdKey<TEntity>(id);
|
||||
var fromCache = RuntimeCache.GetCacheItem<TEntity>(cacheKey);
|
||||
|
||||
if (fromCache != null) return fromCache;
|
||||
|
||||
var entity = PerformGet(id);
|
||||
if (entity == null) return null;
|
||||
|
||||
RuntimeCache.InsertCacheItem(cacheKey, () => entity);
|
||||
|
||||
return entity;
|
||||
using (var p = CachePolicyFactory.CreatePolicy())
|
||||
{
|
||||
return p.Get(id, PerformGet);
|
||||
}
|
||||
}
|
||||
|
||||
protected abstract IEnumerable<TEntity> PerformGetAll(params TId[] ids);
|
||||
@@ -158,88 +181,13 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
throw new InvalidOperationException("Cannot perform a query with more than 2000 parameters");
|
||||
}
|
||||
|
||||
if (ids.Any())
|
||||
using (var p = CachePolicyFactory.CreatePolicy())
|
||||
{
|
||||
var entities = ids.Select(x => RuntimeCache.GetCacheItem<TEntity>(GetCacheIdKey<TEntity>(x))).ToArray();
|
||||
|
||||
if (ids.Count().Equals(entities.Count()) && entities.Any(x => x == null) == false)
|
||||
return entities;
|
||||
}
|
||||
else
|
||||
{
|
||||
var allEntities = RuntimeCache.GetCacheItemsByKeySearch<TEntity>(GetCacheTypeKey<TEntity>())
|
||||
.WhereNotNull()
|
||||
.ToArray();
|
||||
|
||||
if (allEntities.Any())
|
||||
{
|
||||
|
||||
if (RepositoryCacheOptions.GetAllCacheValidateCount)
|
||||
{
|
||||
//Get count of all entities of current type (TEntity) to ensure cached result is correct
|
||||
var query = Query<TEntity>.Builder.Where(x => x.Id != 0);
|
||||
int totalCount = PerformCount(query);
|
||||
|
||||
if (allEntities.Count() == totalCount)
|
||||
return allEntities;
|
||||
}
|
||||
else
|
||||
{
|
||||
return allEntities;
|
||||
}
|
||||
}
|
||||
else if (RepositoryCacheOptions.GetAllCacheAllowZeroCount)
|
||||
{
|
||||
//if the repository allows caching a zero count, then check the zero count cache
|
||||
var zeroCount = RuntimeCache.GetCacheItem<TEntity[]>(GetCacheTypeKey<TEntity>());
|
||||
if (zeroCount != null && zeroCount.Any() == false)
|
||||
{
|
||||
//there is a zero count cache so return an empty list
|
||||
return Enumerable.Empty<TEntity>();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
var entityCollection = PerformGetAll(ids)
|
||||
//ensure we don't include any null refs in the returned collection!
|
||||
.WhereNotNull()
|
||||
.ToArray();
|
||||
|
||||
//We need to put a threshold here! IF there's an insane amount of items
|
||||
// coming back here we don't want to chuck it all into memory, this added cache here
|
||||
// is more for convenience when paging stuff temporarily
|
||||
|
||||
if (entityCollection.Length > RepositoryCacheOptions.GetAllCacheThresholdLimit)
|
||||
return entityCollection;
|
||||
|
||||
if (entityCollection.Length == 0 && RepositoryCacheOptions.GetAllCacheAllowZeroCount)
|
||||
{
|
||||
//there was nothing returned but we want to cache a zero count result so add an TEntity[] to the cache
|
||||
// to signify that there is a zero count cache
|
||||
RuntimeCache.InsertCacheItem(GetCacheTypeKey<TEntity>(), () => new TEntity[] {});
|
||||
}
|
||||
|
||||
foreach (var entity in entityCollection)
|
||||
{
|
||||
if (entity != null)
|
||||
{
|
||||
var localCopy = entity;
|
||||
RuntimeCache.InsertCacheItem(GetCacheIdKey<TEntity>(entity.Id), () => localCopy);
|
||||
}
|
||||
}
|
||||
|
||||
return entityCollection;
|
||||
var result = p.GetAll(ids, PerformGetAll);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the repository cache options
|
||||
/// </summary>
|
||||
protected virtual RepositoryCacheOptions RepositoryCacheOptions
|
||||
{
|
||||
get { return _cacheOptions; }
|
||||
}
|
||||
|
||||
|
||||
protected abstract IEnumerable<TEntity> PerformGetByQuery(IQuery<TEntity> query);
|
||||
/// <summary>
|
||||
/// Gets a list of entities by the passed in query
|
||||
@@ -261,12 +209,10 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
/// <returns></returns>
|
||||
public bool Exists(TId id)
|
||||
{
|
||||
var fromCache = RuntimeCache.GetCacheItem<TEntity>(GetCacheIdKey<TEntity>(id));
|
||||
if (fromCache != null)
|
||||
using (var p = CachePolicyFactory.CreatePolicy())
|
||||
{
|
||||
return true;
|
||||
return p.Exists(id, PerformExists);
|
||||
}
|
||||
return PerformExists(id);
|
||||
}
|
||||
|
||||
protected abstract int PerformCount(IQuery<TEntity> query);
|
||||
@@ -279,34 +225,19 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
{
|
||||
return PerformCount(query);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region IUnitOfWorkRepository Members
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Unit of work method that tells the repository to persist the new entity
|
||||
/// </summary>
|
||||
/// <param name="entity"></param>
|
||||
public virtual void PersistNewItem(IEntity entity)
|
||||
{
|
||||
try
|
||||
{
|
||||
PersistNewItem((TEntity)entity);
|
||||
RuntimeCache.InsertCacheItem(GetCacheIdKey<TEntity>(entity.Id), () => entity);
|
||||
//If there's a GetAll zero count cache, ensure it is cleared
|
||||
RuntimeCache.ClearCacheItem(GetCacheTypeKey<TEntity>());
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
//if an exception is thrown we need to remove the entry from cache, this is ONLY a work around because of the way
|
||||
// that we cache entities: http://issues.umbraco.org/issue/U4-4259
|
||||
RuntimeCache.ClearCacheItem(GetCacheIdKey<TEntity>(entity.Id));
|
||||
//If there's a GetAll zero count cache, ensure it is cleared
|
||||
RuntimeCache.ClearCacheItem(GetCacheTypeKey<TEntity>());
|
||||
throw;
|
||||
}
|
||||
var casted = (TEntity)entity;
|
||||
|
||||
using (var p = CachePolicyFactory.CreatePolicy())
|
||||
{
|
||||
p.CreateOrUpdate(casted, PersistNewItem);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -315,23 +246,12 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
/// <param name="entity"></param>
|
||||
public virtual void PersistUpdatedItem(IEntity entity)
|
||||
{
|
||||
try
|
||||
{
|
||||
PersistUpdatedItem((TEntity)entity);
|
||||
RuntimeCache.InsertCacheItem(GetCacheIdKey<TEntity>(entity.Id), () => entity);
|
||||
//If there's a GetAll zero count cache, ensure it is cleared
|
||||
RuntimeCache.ClearCacheItem(GetCacheTypeKey<TEntity>());
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
//if an exception is thrown we need to remove the entry from cache, this is ONLY a work around because of the way
|
||||
// that we cache entities: http://issues.umbraco.org/issue/U4-4259
|
||||
RuntimeCache.ClearCacheItem(GetCacheIdKey<TEntity>(entity.Id));
|
||||
//If there's a GetAll zero count cache, ensure it is cleared
|
||||
RuntimeCache.ClearCacheItem(GetCacheTypeKey<TEntity>());
|
||||
throw;
|
||||
}
|
||||
var casted = (TEntity)entity;
|
||||
|
||||
using (var p = CachePolicyFactory.CreatePolicy())
|
||||
{
|
||||
p.CreateOrUpdate(casted, PersistUpdatedItem);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -340,21 +260,19 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
/// <param name="entity"></param>
|
||||
public virtual void PersistDeletedItem(IEntity entity)
|
||||
{
|
||||
PersistDeletedItem((TEntity)entity);
|
||||
RuntimeCache.ClearCacheItem(GetCacheIdKey<TEntity>(entity.Id));
|
||||
//If there's a GetAll zero count cache, ensure it is cleared
|
||||
RuntimeCache.ClearCacheItem(GetCacheTypeKey<TEntity>());
|
||||
var casted = (TEntity)entity;
|
||||
|
||||
using (var p = CachePolicyFactory.CreatePolicy())
|
||||
{
|
||||
p.Remove(casted, PersistDeletedItem);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Abstract IUnitOfWorkRepository Methods
|
||||
|
||||
|
||||
protected abstract void PersistNewItem(TEntity item);
|
||||
protected abstract void PersistUpdatedItem(TEntity item);
|
||||
protected abstract void PersistDeletedItem(TEntity item);
|
||||
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// Dispose disposable properties
|
||||
|
||||
@@ -4,6 +4,7 @@ using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using Umbraco.Core.Cache;
|
||||
using Umbraco.Core.Configuration;
|
||||
using Umbraco.Core.Configuration.UmbracoSettings;
|
||||
using Umbraco.Core.IO;
|
||||
@@ -32,7 +33,6 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
private readonly ITemplatesSection _templateConfig;
|
||||
private readonly ViewHelper _viewHelper;
|
||||
private readonly MasterPageHelper _masterPageHelper;
|
||||
private readonly RepositoryCacheOptions _cacheOptions;
|
||||
|
||||
internal TemplateRepository(IDatabaseUnitOfWork work, CacheHelper cache, ILogger logger, ISqlSyntaxProvider sqlSyntax, IFileSystem masterpageFileSystem, IFileSystem viewFileSystem, ITemplatesSection templateConfig)
|
||||
: base(work, cache, logger, sqlSyntax)
|
||||
@@ -41,41 +41,27 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
_viewsFileSystem = viewFileSystem;
|
||||
_templateConfig = templateConfig;
|
||||
_viewHelper = new ViewHelper(_viewsFileSystem);
|
||||
_masterPageHelper = new MasterPageHelper(_masterpagesFileSystem);
|
||||
|
||||
_cacheOptions = new RepositoryCacheOptions
|
||||
{
|
||||
//Allow a zero count cache entry because GetAll() gets used quite a lot and we want to ensure
|
||||
// if there are no templates, that it doesn't keep going to the db.
|
||||
GetAllCacheAllowZeroCount = true,
|
||||
//GetAll gets called a lot, we want to ensure that all templates are in the cache, default is 100 which
|
||||
// would normally be fine but we'll increase it in case people have a ton of templates.
|
||||
GetAllCacheThresholdLimit = 500
|
||||
};
|
||||
_masterPageHelper = new MasterPageHelper(_masterpagesFileSystem);
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Returns the repository cache options
|
||||
/// </summary>
|
||||
protected override RepositoryCacheOptions RepositoryCacheOptions
|
||||
private FullDataSetRepositoryCachePolicyFactory<ITemplate, int> _cachePolicyFactory;
|
||||
protected override IRepositoryCachePolicyFactory<ITemplate, int> CachePolicyFactory
|
||||
{
|
||||
get { return _cacheOptions; }
|
||||
get
|
||||
{
|
||||
//Use a FullDataSet cache policy - this will cache the entire GetAll result in a single collection
|
||||
return _cachePolicyFactory ?? (_cachePolicyFactory = new FullDataSetRepositoryCachePolicyFactory<ITemplate, int>(
|
||||
RuntimeCache, GetEntityId, () => PerformGetAll(), false));
|
||||
}
|
||||
}
|
||||
|
||||
#region Overrides of RepositoryBase<int,ITemplate>
|
||||
|
||||
protected override ITemplate PerformGet(int id)
|
||||
{
|
||||
var sql = GetBaseQuery(false).Where<TemplateDto>(x => x.NodeId == id);
|
||||
var result = Database.Fetch<TemplateDto, NodeDto>(sql).FirstOrDefault();
|
||||
if (result == null) return null;
|
||||
|
||||
//look up the simple template definitions that have a master template assigned, this is used
|
||||
// later to populate the template item's properties
|
||||
var childIds = GetAxisDefinitions(result).ToArray();
|
||||
|
||||
return MapFromDto(result, childIds);
|
||||
//use the underlying GetAll which will force cache all templates
|
||||
return base.GetAll().FirstOrDefault(x => x.Id == id);
|
||||
}
|
||||
|
||||
protected override IEnumerable<ITemplate> PerformGetAll(params int[] ids)
|
||||
@@ -479,126 +465,102 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
|
||||
public ITemplate Get(string alias)
|
||||
{
|
||||
var sql = GetBaseQuery(false).Where<TemplateDto>(x => x.Alias == alias);
|
||||
|
||||
var dto = Database.Fetch<TemplateDto, NodeDto>(sql).FirstOrDefault();
|
||||
|
||||
if (dto == null)
|
||||
return null;
|
||||
|
||||
return MapFromDto(dto, GetAxisDefinitions(dto).ToArray());
|
||||
return GetAll(alias).FirstOrDefault();
|
||||
}
|
||||
|
||||
public IEnumerable<ITemplate> GetAll(params string[] aliases)
|
||||
{
|
||||
var sql = GetBaseQuery(false);
|
||||
//We must call the base (normal) GetAll method
|
||||
// which is cached. This is a specialized method and unfortunatley with the params[] it
|
||||
// overlaps with the normal GetAll method.
|
||||
if (aliases.Any() == false) return base.GetAll();
|
||||
|
||||
if (aliases.Any())
|
||||
{
|
||||
sql.Where("cmsTemplate.alias IN (@aliases)", new {aliases = aliases});
|
||||
}
|
||||
|
||||
var dtos = Database.Fetch<TemplateDto, NodeDto>(sql).ToArray();
|
||||
if (dtos.Length == 0) return Enumerable.Empty<ITemplate>();
|
||||
|
||||
var axisDefos = GetAxisDefinitions(dtos).ToArray();
|
||||
return dtos.Select(x => MapFromDto(x, axisDefos));
|
||||
//return from base.GetAll, this is all cached
|
||||
return base.GetAll().Where(x => aliases.InvariantContains(x.Alias));
|
||||
}
|
||||
|
||||
public IEnumerable<ITemplate> GetChildren(int masterTemplateId)
|
||||
{
|
||||
var sql = GetBaseQuery(false);
|
||||
if (masterTemplateId <= 0)
|
||||
{
|
||||
sql.Where<NodeDto>(x => x.ParentId <= 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
sql.Where<NodeDto>(x => x.ParentId == masterTemplateId);
|
||||
}
|
||||
//return from base.GetAll, this is all cached
|
||||
var all = base.GetAll().ToArray();
|
||||
|
||||
var dtos = Database.Fetch<TemplateDto, NodeDto>(sql).ToArray();
|
||||
if (dtos.Length == 0) return Enumerable.Empty<ITemplate>();
|
||||
if (masterTemplateId <= 0) return all.Where(x => x.MasterTemplateAlias.IsNullOrWhiteSpace());
|
||||
|
||||
var axisDefos = GetAxisDefinitions(dtos).ToArray();
|
||||
return dtos.Select(x => MapFromDto(x, axisDefos));
|
||||
var parent = all.FirstOrDefault(x => x.Id == masterTemplateId);
|
||||
if (parent == null) return Enumerable.Empty<ITemplate>();
|
||||
|
||||
var children = all.Where(x => x.MasterTemplateAlias.InvariantEquals(parent.Alias));
|
||||
return children;
|
||||
}
|
||||
|
||||
public IEnumerable<ITemplate> GetChildren(string alias)
|
||||
{
|
||||
var sql = GetBaseQuery(false);
|
||||
if (alias.IsNullOrWhiteSpace())
|
||||
{
|
||||
sql.Where<NodeDto>(x => x.ParentId <= 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
//unfortunately SQLCE doesn't support scalar subqueries in the where clause, otherwise we could have done this
|
||||
// in a single query, now we have to lookup the path to acheive the same thing
|
||||
var parent = Database.ExecuteScalar<int?>(new Sql().Select("nodeId").From<TemplateDto>(SqlSyntax).Where<TemplateDto>(dto => dto.Alias == alias));
|
||||
if (parent.HasValue == false) return Enumerable.Empty<ITemplate>();
|
||||
|
||||
sql.Where<NodeDto>(x => x.ParentId == parent.Value);
|
||||
}
|
||||
|
||||
var dtos = Database.Fetch<TemplateDto, NodeDto>(sql).ToArray();
|
||||
if (dtos.Length == 0) return Enumerable.Empty<ITemplate>();
|
||||
|
||||
var axisDefos = GetAxisDefinitions(dtos).ToArray();
|
||||
return dtos.Select(x => MapFromDto(x, axisDefos));
|
||||
//return from base.GetAll, this is all cached
|
||||
return base.GetAll().Where(x => alias.IsNullOrWhiteSpace()
|
||||
? x.MasterTemplateAlias.IsNullOrWhiteSpace()
|
||||
: x.MasterTemplateAlias.InvariantEquals(alias));
|
||||
}
|
||||
|
||||
public IEnumerable<ITemplate> GetDescendants(int masterTemplateId)
|
||||
{
|
||||
var sql = GetBaseQuery(false);
|
||||
//return from base.GetAll, this is all cached
|
||||
var all = base.GetAll().ToArray();
|
||||
var descendants = new List<ITemplate>();
|
||||
if (masterTemplateId > 0)
|
||||
{
|
||||
//unfortunately SQLCE doesn't support scalar subqueries in the where clause, otherwise we could have done this
|
||||
// in a single query, now we have to lookup the path to acheive the same thing
|
||||
var path = Database.ExecuteScalar<string>(
|
||||
new Sql().Select(SqlSyntax.GetQuotedColumnName("path"))
|
||||
.From<TemplateDto>(SqlSyntax)
|
||||
.InnerJoin<NodeDto>(SqlSyntax)
|
||||
.On<TemplateDto, NodeDto>(SqlSyntax, dto => dto.NodeId, dto => dto.NodeId)
|
||||
.Where<NodeDto>(dto => dto.NodeId == masterTemplateId));
|
||||
|
||||
if (path.IsNullOrWhiteSpace()) return Enumerable.Empty<ITemplate>();
|
||||
|
||||
sql.Where(@"(umbracoNode." + SqlSyntax.GetQuotedColumnName("path") + @" LIKE @query)", new { query = path + ",%" });
|
||||
var parent = all.FirstOrDefault(x => x.Id == masterTemplateId);
|
||||
if (parent == null) return Enumerable.Empty<ITemplate>();
|
||||
//recursively add all children with a level
|
||||
AddChildren(all, descendants, parent.Alias);
|
||||
}
|
||||
else
|
||||
{
|
||||
descendants.AddRange(all.Where(x => x.MasterTemplateAlias.IsNullOrWhiteSpace()));
|
||||
foreach (var parent in descendants)
|
||||
{
|
||||
//recursively add all children with a level
|
||||
AddChildren(all, descendants, parent.Alias);
|
||||
}
|
||||
}
|
||||
|
||||
sql.OrderBy("umbracoNode." + SqlSyntax.GetQuotedColumnName("level"));
|
||||
|
||||
var dtos = Database.Fetch<TemplateDto, NodeDto>(sql).ToArray();
|
||||
if (dtos.Length == 0) return Enumerable.Empty<ITemplate>();
|
||||
|
||||
var axisDefos = GetAxisDefinitions(dtos).ToArray();
|
||||
return dtos.Select(x => MapFromDto(x, axisDefos));
|
||||
|
||||
//return the list - it will be naturally ordered by level
|
||||
return descendants;
|
||||
}
|
||||
|
||||
|
||||
public IEnumerable<ITemplate> GetDescendants(string alias)
|
||||
{
|
||||
var sql = GetBaseQuery(false);
|
||||
var all = base.GetAll().ToArray();
|
||||
var descendants = new List<ITemplate>();
|
||||
if (alias.IsNullOrWhiteSpace() == false)
|
||||
{
|
||||
//unfortunately SQLCE doesn't support scalar subqueries in the where clause, otherwise we could have done this
|
||||
// in a single query, now we have to lookup the path to acheive the same thing
|
||||
var path = Database.ExecuteScalar<string>(
|
||||
"SELECT umbracoNode.path FROM cmsTemplate INNER JOIN umbracoNode ON cmsTemplate.nodeId = umbracoNode.id WHERE cmsTemplate.alias = @alias", new { alias = alias });
|
||||
|
||||
if (path.IsNullOrWhiteSpace()) return Enumerable.Empty<ITemplate>();
|
||||
|
||||
sql.Where(@"(umbracoNode." + SqlSyntax.GetQuotedColumnName("path") + @" LIKE @query)", new {query = path + ",%" });
|
||||
var parent = all.FirstOrDefault(x => x.Alias.InvariantEquals(alias));
|
||||
if (parent == null) return Enumerable.Empty<ITemplate>();
|
||||
//recursively add all children
|
||||
AddChildren(all, descendants, parent.Alias);
|
||||
}
|
||||
else
|
||||
{
|
||||
descendants.AddRange(all.Where(x => x.MasterTemplateAlias.IsNullOrWhiteSpace()));
|
||||
foreach (var parent in descendants)
|
||||
{
|
||||
//recursively add all children with a level
|
||||
AddChildren(all, descendants, parent.Alias);
|
||||
}
|
||||
}
|
||||
//return the list - it will be naturally ordered by level
|
||||
return descendants;
|
||||
}
|
||||
|
||||
sql.OrderBy("umbracoNode." + SqlSyntax.GetQuotedColumnName("level"));
|
||||
|
||||
var dtos = Database.Fetch<TemplateDto, NodeDto>(sql).ToArray();
|
||||
if (dtos.Length == 0) return Enumerable.Empty<ITemplate>();
|
||||
|
||||
var axisDefos = GetAxisDefinitions(dtos).ToArray();
|
||||
return dtos.Select(x => MapFromDto(x, axisDefos));
|
||||
private void AddChildren(ITemplate[] all, List<ITemplate> descendants, string masterAlias)
|
||||
{
|
||||
var c = all.Where(x => x.MasterTemplateAlias.InvariantEquals(masterAlias)).ToArray();
|
||||
descendants.AddRange(c);
|
||||
if (c.Any() == false) return;
|
||||
//recurse through all children
|
||||
foreach (var child in c)
|
||||
{
|
||||
AddChildren(all, descendants, child.Alias);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -610,9 +572,9 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
public TemplateNode GetTemplateNode(string alias)
|
||||
{
|
||||
//first get all template objects
|
||||
var allTemplates = GetAll().ToArray();
|
||||
var allTemplates = base.GetAll().ToArray();
|
||||
|
||||
var selfTemplate = allTemplates.SingleOrDefault(x => x.Alias == alias);
|
||||
var selfTemplate = allTemplates.SingleOrDefault(x => x.Alias.InvariantEquals(alias));
|
||||
if (selfTemplate == null)
|
||||
{
|
||||
return null;
|
||||
@@ -621,11 +583,11 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
var top = selfTemplate;
|
||||
while (top.MasterTemplateAlias.IsNullOrWhiteSpace() == false)
|
||||
{
|
||||
top = allTemplates.Single(x => x.Alias == top.MasterTemplateAlias);
|
||||
top = allTemplates.Single(x => x.Alias.InvariantEquals(top.MasterTemplateAlias));
|
||||
}
|
||||
|
||||
var topNode = new TemplateNode(allTemplates.Single(x => x.Id == top.Id));
|
||||
var childTemplates = allTemplates.Where(x => x.MasterTemplateAlias == top.Alias);
|
||||
var childTemplates = allTemplates.Where(x => x.MasterTemplateAlias.InvariantEquals(top.Alias));
|
||||
//This now creates the hierarchy recursively
|
||||
topNode.Children = CreateChildren(topNode, childTemplates, allTemplates);
|
||||
|
||||
@@ -633,10 +595,11 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
return FindTemplateInTree(topNode, alias);
|
||||
}
|
||||
|
||||
[Obsolete("Only used by obsolete code")]
|
||||
private static TemplateNode WalkTree(TemplateNode current, string alias)
|
||||
{
|
||||
//now walk the tree to find the node
|
||||
if (current.Template.Alias == alias)
|
||||
if (current.Template.Alias.InvariantEquals(alias))
|
||||
{
|
||||
return current;
|
||||
}
|
||||
@@ -682,14 +645,16 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
public RenderingEngine DetermineTemplateRenderingEngine(ITemplate template)
|
||||
{
|
||||
var engine = _templateConfig.DefaultRenderingEngine;
|
||||
|
||||
if (template.Content.IsNullOrWhiteSpace() == false && MasterPageHelper.IsMasterPageSyntax(template.Content))
|
||||
var viewHelper = new ViewHelper(_viewsFileSystem);
|
||||
if (viewHelper.ViewExists(template) == false)
|
||||
{
|
||||
//there is a design but its definitely a webforms design
|
||||
return RenderingEngine.WebForms;
|
||||
if (template.Content.IsNullOrWhiteSpace() == false && MasterPageHelper.IsMasterPageSyntax(template.Content))
|
||||
{
|
||||
//there is a design but its definitely a webforms design and we haven't got a MVC view already for it
|
||||
return RenderingEngine.WebForms;
|
||||
}
|
||||
}
|
||||
|
||||
var viewHelper = new ViewHelper(_viewsFileSystem);
|
||||
var masterPageHelper = new MasterPageHelper(_masterpagesFileSystem);
|
||||
|
||||
switch (engine)
|
||||
@@ -766,7 +731,7 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
|
||||
//get this node's children
|
||||
var local = childTemplate;
|
||||
var kids = allTemplates.Where(x => x.MasterTemplateAlias == local.Alias);
|
||||
var kids = allTemplates.Where(x => x.MasterTemplateAlias.InvariantEquals(local.Alias));
|
||||
|
||||
//recurse
|
||||
child.Children = CreateChildren(child, kids, allTemplates);
|
||||
@@ -796,7 +761,7 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
|
||||
private bool AliasAlreadExists(ITemplate template)
|
||||
{
|
||||
var sql = GetBaseQuery(true).Where<TemplateDto>(x => x.Alias == template.Alias && x.NodeId != template.Id);
|
||||
var sql = GetBaseQuery(true).Where<TemplateDto>(x => x.Alias.InvariantEquals(template.Alias) && x.NodeId != template.Id);
|
||||
var count = Database.ExecuteScalar<int>(sql);
|
||||
return count > 0;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using Umbraco.Core.Configuration;
|
||||
using System;
|
||||
using Umbraco.Core.Cache;
|
||||
using Umbraco.Core.Configuration.UmbracoSettings;
|
||||
using Umbraco.Core.IO;
|
||||
using Umbraco.Core.Logging;
|
||||
@@ -18,6 +19,7 @@ namespace Umbraco.Core.Persistence
|
||||
private readonly ILogger _logger;
|
||||
private readonly ISqlSyntaxProvider _sqlSyntax;
|
||||
private readonly CacheHelper _cacheHelper;
|
||||
private readonly CacheHelper _noCache;
|
||||
private readonly IUmbracoSettingsSection _settings;
|
||||
|
||||
#region Ctors
|
||||
@@ -29,7 +31,28 @@ namespace Umbraco.Core.Persistence
|
||||
//if (sqlSyntax == null) throw new ArgumentNullException("sqlSyntax");
|
||||
if (settings == null) throw new ArgumentNullException("settings");
|
||||
|
||||
_cacheHelper = cacheHelper;
|
||||
_cacheHelper = cacheHelper;
|
||||
|
||||
//IMPORTANT: We will force the DeepCloneRuntimeCacheProvider to be used here which is a wrapper for the underlying
|
||||
// runtime cache to ensure that anything that can be deep cloned in/out is done so, this also ensures that our tracks
|
||||
// changes entities are reset.
|
||||
if ((_cacheHelper.RuntimeCache is DeepCloneRuntimeCacheProvider) == false)
|
||||
{
|
||||
var origRuntimeCache = cacheHelper.RuntimeCache;
|
||||
_cacheHelper.RuntimeCache = new DeepCloneRuntimeCacheProvider(origRuntimeCache);
|
||||
}
|
||||
//If the factory for isolated cache doesn't return DeepCloneRuntimeCacheProvider, then ensure it does
|
||||
if (_cacheHelper.IsolatedRuntimeCache.CacheFactory.Method.ReturnType != typeof (DeepCloneRuntimeCacheProvider))
|
||||
{
|
||||
var origFactory = cacheHelper.IsolatedRuntimeCache.CacheFactory;
|
||||
_cacheHelper.IsolatedRuntimeCache.CacheFactory = type =>
|
||||
{
|
||||
var cache = origFactory(type);
|
||||
return new DeepCloneRuntimeCacheProvider(cache);
|
||||
};
|
||||
}
|
||||
|
||||
_noCache = CacheHelper.CreateDisabledCacheHelper();
|
||||
_logger = logger;
|
||||
_sqlSyntax = sqlSyntax;
|
||||
_settings = settings;
|
||||
@@ -53,6 +76,7 @@ namespace Umbraco.Core.Persistence
|
||||
{
|
||||
if (cacheHelper == null) throw new ArgumentNullException("cacheHelper");
|
||||
_cacheHelper = cacheHelper;
|
||||
_noCache = CacheHelper.CreateDisabledCacheHelper();
|
||||
}
|
||||
|
||||
[Obsolete("Use the ctor specifying all dependencies instead")]
|
||||
@@ -80,15 +104,15 @@ namespace Umbraco.Core.Persistence
|
||||
|
||||
public virtual ITaskRepository CreateTaskRepository(IDatabaseUnitOfWork uow)
|
||||
{
|
||||
return new TaskRepository(uow,
|
||||
CacheHelper.CreateDisabledCacheHelper(), //never cache
|
||||
return new TaskRepository(uow,
|
||||
_noCache, //never cache
|
||||
_logger, _sqlSyntax);
|
||||
}
|
||||
|
||||
public virtual IAuditRepository CreateAuditRepository(IDatabaseUnitOfWork uow)
|
||||
{
|
||||
return new AuditRepository(uow,
|
||||
CacheHelper.CreateDisabledCacheHelper(), //never cache
|
||||
_noCache, //never cache
|
||||
_logger, _sqlSyntax);
|
||||
}
|
||||
|
||||
@@ -128,7 +152,6 @@ namespace Umbraco.Core.Persistence
|
||||
{
|
||||
return new DataTypeDefinitionRepository(
|
||||
uow,
|
||||
_cacheHelper,
|
||||
_cacheHelper,
|
||||
_logger, _sqlSyntax,
|
||||
CreateContentTypeRepository(uow));
|
||||
@@ -140,8 +163,7 @@ namespace Umbraco.Core.Persistence
|
||||
uow,
|
||||
_cacheHelper,
|
||||
_logger,
|
||||
_sqlSyntax,
|
||||
CreateLanguageRepository(uow));
|
||||
_sqlSyntax);
|
||||
}
|
||||
|
||||
public virtual ILanguageRepository CreateLanguageRepository(IDatabaseUnitOfWork uow)
|
||||
@@ -175,7 +197,7 @@ namespace Umbraco.Core.Persistence
|
||||
{
|
||||
return new RelationRepository(
|
||||
uow,
|
||||
CacheHelper.CreateDisabledCacheHelper(), //never cache
|
||||
_noCache, //never cache
|
||||
_logger, _sqlSyntax,
|
||||
CreateRelationTypeRepository(uow));
|
||||
}
|
||||
@@ -184,7 +206,7 @@ namespace Umbraco.Core.Persistence
|
||||
{
|
||||
return new RelationTypeRepository(
|
||||
uow,
|
||||
CacheHelper.CreateDisabledCacheHelper(), //never cache
|
||||
_noCache, //never cache
|
||||
_logger, _sqlSyntax);
|
||||
}
|
||||
|
||||
@@ -222,7 +244,7 @@ namespace Umbraco.Core.Persistence
|
||||
{
|
||||
return new MigrationEntryRepository(
|
||||
uow,
|
||||
CacheHelper.CreateDisabledCacheHelper(), //never cache
|
||||
_noCache, //never cache
|
||||
_logger, _sqlSyntax);
|
||||
}
|
||||
|
||||
@@ -283,8 +305,7 @@ namespace Umbraco.Core.Persistence
|
||||
{
|
||||
return new MemberGroupRepository(uow,
|
||||
_cacheHelper,
|
||||
_logger, _sqlSyntax,
|
||||
_cacheHelper);
|
||||
_logger, _sqlSyntax);
|
||||
}
|
||||
|
||||
public virtual IEntityRepository CreateEntityRepository(IDatabaseUnitOfWork uow)
|
||||
@@ -292,15 +313,15 @@ namespace Umbraco.Core.Persistence
|
||||
return new EntityRepository(uow);
|
||||
}
|
||||
|
||||
public IDomainRepository CreateDomainRepository(IDatabaseUnitOfWork uow)
|
||||
public virtual IDomainRepository CreateDomainRepository(IDatabaseUnitOfWork uow)
|
||||
{
|
||||
return new DomainRepository(uow, _cacheHelper, _logger, _sqlSyntax);
|
||||
}
|
||||
|
||||
public ITaskTypeRepository CreateTaskTypeRepository(IDatabaseUnitOfWork uow)
|
||||
{
|
||||
return new TaskTypeRepository(uow,
|
||||
CacheHelper.CreateDisabledCacheHelper(), //never cache
|
||||
return new TaskTypeRepository(uow,
|
||||
_noCache, //never cache
|
||||
_logger, _sqlSyntax);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -117,7 +117,7 @@ namespace Umbraco.Core.Persistence
|
||||
|
||||
public override void OnException(Exception x)
|
||||
{
|
||||
_logger.Info<UmbracoDatabase>(x.StackTrace);
|
||||
_logger.Error<UmbracoDatabase>("Database exception occurred", x);
|
||||
base.OnException(x);
|
||||
}
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@ using System;
|
||||
|
||||
namespace Umbraco.Core.PropertyEditors
|
||||
{
|
||||
/// <summary>
|
||||
/// Maps a property source value to a data object.
|
||||
/// </summary>
|
||||
// todo: drop IPropertyEditorValueConverter support (when?).
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
using Umbraco.Core.Models.PublishedContent;
|
||||
|
||||
namespace Umbraco.Core.PropertyEditors.ValueConverters
|
||||
{
|
||||
/// <summary>
|
||||
/// We need this property converter so that we always force the value of a label to be a string
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Without a property converter defined for the label type, the value will be converted with
|
||||
/// the `ConvertUsingDarkMagic` method which will try to parse the value into it's correct type, but this
|
||||
/// can cause issues if the string is detected as a number and then strips leading zeros.
|
||||
/// Example: http://issues.umbraco.org/issue/U4-7929
|
||||
/// </remarks>
|
||||
[DefaultPropertyValueConverter]
|
||||
[PropertyValueType(typeof (string))]
|
||||
[PropertyValueCache(PropertyCacheValue.All, PropertyCacheLevel.Content)]
|
||||
public class LabelValueConverter : PropertyValueConverterBase
|
||||
{
|
||||
public override bool IsConverter(PublishedPropertyType propertyType)
|
||||
{
|
||||
return Constants.PropertyEditors.NoEditAlias.Equals(propertyType.PropertyEditorAlias);
|
||||
}
|
||||
public override object ConvertDataToSource(PublishedPropertyType propertyType, object source, bool preview)
|
||||
{
|
||||
return source == null ? string.Empty : source.ToString();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,6 +2,7 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Specialized;
|
||||
using System.ComponentModel;
|
||||
using System.Linq;
|
||||
using System.Net.Http;
|
||||
using System.Net.Http.Headers;
|
||||
@@ -15,7 +16,6 @@ using Microsoft.Owin;
|
||||
using Newtonsoft.Json;
|
||||
using Umbraco.Core.Configuration;
|
||||
using Umbraco.Core.Models.Membership;
|
||||
using Microsoft.Owin;
|
||||
using Umbraco.Core.Logging;
|
||||
|
||||
namespace Umbraco.Core.Security
|
||||
@@ -157,9 +157,6 @@ namespace Umbraco.Core.Security
|
||||
return new HttpContextWrapper(http).GetCurrentIdentity(authenticateRequestIfNotFound);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This clears the forms authentication cookie
|
||||
/// </summary>
|
||||
public static void UmbracoLogout(this HttpContextBase http)
|
||||
{
|
||||
if (http == null) throw new ArgumentNullException("http");
|
||||
@@ -170,6 +167,8 @@ namespace Umbraco.Core.Security
|
||||
/// This clears the forms authentication cookie for webapi since cookies are handled differently
|
||||
/// </summary>
|
||||
/// <param name="response"></param>
|
||||
[Obsolete("Use OWIN IAuthenticationManager.SignOut instead")]
|
||||
[EditorBrowsable(EditorBrowsableState.Never)]
|
||||
public static void UmbracoLogoutWebApi(this HttpResponseMessage response)
|
||||
{
|
||||
if (response == null) throw new ArgumentNullException("response");
|
||||
@@ -195,11 +194,8 @@ namespace Umbraco.Core.Security
|
||||
response.Headers.AddCookies(new[] { authCookie, prevCookie, extLoginCookie });
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This adds the forms authentication cookie for webapi since cookies are handled differently
|
||||
/// </summary>
|
||||
/// <param name="response"></param>
|
||||
/// <param name="user"></param>
|
||||
[Obsolete("Use WebSecurity.SetPrincipalForRequest")]
|
||||
[EditorBrowsable(EditorBrowsableState.Never)]
|
||||
public static FormsAuthenticationTicket UmbracoLoginWebApi(this HttpResponseMessage response, IUser user)
|
||||
{
|
||||
if (response == null) throw new ArgumentNullException("response");
|
||||
@@ -250,26 +246,29 @@ namespace Umbraco.Core.Security
|
||||
if (http == null) throw new ArgumentNullException("http");
|
||||
new HttpContextWrapper(http).UmbracoLogout();
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Renews the Umbraco authentication ticket
|
||||
/// This will force ticket renewal in the OWIN pipeline
|
||||
/// </summary>
|
||||
/// <param name="http"></param>
|
||||
/// <returns></returns>
|
||||
public static bool RenewUmbracoAuthTicket(this HttpContextBase http)
|
||||
{
|
||||
if (http == null) throw new ArgumentNullException("http");
|
||||
return RenewAuthTicket(http,
|
||||
UmbracoConfig.For.UmbracoSettings().Security.AuthCookieName,
|
||||
UmbracoConfig.For.UmbracoSettings().Security.AuthCookieDomain,
|
||||
//Umbraco has always persisted it's original cookie for 1 day so we'll keep it that way
|
||||
1440);
|
||||
http.Items["umbraco-force-auth"] = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This will force ticket renewal in the OWIN pipeline
|
||||
/// </summary>
|
||||
/// <param name="http"></param>
|
||||
/// <returns></returns>
|
||||
internal static bool RenewUmbracoAuthTicket(this HttpContext http)
|
||||
{
|
||||
if (http == null) throw new ArgumentNullException("http");
|
||||
return new HttpContextWrapper(http).RenewUmbracoAuthTicket();
|
||||
http.Items["umbraco-force-auth"] = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -358,7 +357,9 @@ namespace Umbraco.Core.Security
|
||||
{
|
||||
//TODO: Do we need to do more here?? need to make sure that the forms cookie is gone, but is that
|
||||
// taken care of in our custom middleware somehow?
|
||||
ctx.Authentication.SignOut();
|
||||
ctx.Authentication.SignOut(
|
||||
Core.Constants.Security.BackOfficeAuthenticationType,
|
||||
Core.Constants.Security.BackOfficeExternalAuthenticationType);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -390,8 +391,7 @@ namespace Umbraco.Core.Security
|
||||
//ensure there's def an expired cookie
|
||||
http.Response.Cookies.Add(new HttpCookie(c) { Expires = DateTime.Now.AddYears(-1) });
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
private static FormsAuthenticationTicket GetAuthTicket(this HttpContextBase http, string cookieName)
|
||||
@@ -432,51 +432,6 @@ namespace Umbraco.Core.Security
|
||||
return FormsAuthentication.Decrypt(formsCookie);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Renews the forms authentication ticket & cookie
|
||||
/// </summary>
|
||||
/// <param name="http"></param>
|
||||
/// <param name="cookieName"></param>
|
||||
/// <param name="cookieDomain"></param>
|
||||
/// <param name="minutesPersisted"></param>
|
||||
/// <returns>true if there was a ticket to renew otherwise false if there was no ticket</returns>
|
||||
private static bool RenewAuthTicket(this HttpContextBase http, string cookieName, string cookieDomain, int minutesPersisted)
|
||||
{
|
||||
if (http == null) throw new ArgumentNullException("http");
|
||||
//get the ticket
|
||||
var ticket = GetAuthTicket(http, cookieName);
|
||||
//renew the ticket
|
||||
var renewed = FormsAuthentication.RenewTicketIfOld(ticket);
|
||||
if (renewed == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
//get the request cookie to get it's expiry date,
|
||||
//NOTE: this will never be null becaues we already do this
|
||||
// check in teh GetAuthTicket.
|
||||
var formsCookie = http.Request.Cookies[cookieName];
|
||||
|
||||
//encrypt it
|
||||
var hash = FormsAuthentication.Encrypt(renewed);
|
||||
//write it to the response
|
||||
var cookie = new HttpCookie(cookieName, hash)
|
||||
{
|
||||
Expires = DateTime.Now.AddMinutes(minutesPersisted),
|
||||
Domain = cookieDomain
|
||||
};
|
||||
|
||||
if (GlobalSettings.UseSSL)
|
||||
cookie.Secure = true;
|
||||
|
||||
//ensure http only, this should only be able to be accessed via the server
|
||||
cookie.HttpOnly = true;
|
||||
|
||||
//rewrite the cooke
|
||||
http.Response.Cookies.Set(cookie);
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a custom FormsAuthentication ticket with the data specified
|
||||
/// </summary>
|
||||
|
||||
@@ -9,6 +9,12 @@ namespace Umbraco.Core.Security
|
||||
{
|
||||
public class BackOfficeClaimsIdentityFactory : ClaimsIdentityFactory<BackOfficeIdentityUser, int>
|
||||
{
|
||||
public BackOfficeClaimsIdentityFactory()
|
||||
{
|
||||
SecurityStampClaimType = Constants.Security.SessionIdClaimType;
|
||||
UserNameClaimType = ClaimTypes.Name;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a ClaimsIdentity from a user
|
||||
/// </summary>
|
||||
@@ -20,7 +26,7 @@ namespace Umbraco.Core.Security
|
||||
|
||||
var umbracoIdentity = new UmbracoBackOfficeIdentity(baseIdentity,
|
||||
//set a new session id
|
||||
new UserData(Guid.NewGuid().ToString("N"))
|
||||
new UserData
|
||||
{
|
||||
Id = user.Id,
|
||||
Username = user.UserName,
|
||||
@@ -29,7 +35,8 @@ namespace Umbraco.Core.Security
|
||||
Culture = user.Culture,
|
||||
Roles = user.Roles.Select(x => x.RoleId).ToArray(),
|
||||
StartContentNode = user.StartContentId,
|
||||
StartMediaNode = user.StartMediaId
|
||||
StartMediaNode = user.StartMediaId,
|
||||
SessionId = user.SecurityStamp
|
||||
});
|
||||
|
||||
return umbracoIdentity;
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user