Compare commits
389 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| f61b7579a2 | |||
| 86a3780c61 | |||
| 552909fde2 | |||
| 7adae4d345 | |||
| d747cf69c3 | |||
| bc5e3b4d66 | |||
| febeb82d10 | |||
| 1c2b2c1409 | |||
| b4eb9f333e | |||
| af00a421cd | |||
| fed06c88e8 | |||
| 5dec2861d9 | |||
| 23b3bb37ee | |||
| 4dabb606e7 | |||
| d5f66ebe02 | |||
| c7783d1cd1 | |||
| 2104b109f2 | |||
| 578ca7d41d | |||
| bd38dde1fc | |||
| 42c23da9f5 | |||
| cab1536a30 | |||
| 33d35fd9f7 | |||
| cc885c1231 | |||
| 422dd1a627 | |||
| 759e444624 | |||
| 2a7ef5de26 | |||
| 43e892563d | |||
| 4d1e64b279 | |||
| 975c97cfb0 | |||
| 0c613347ae | |||
| 83f75fdaff | |||
| 487792920b | |||
| 6ae06ad2e0 | |||
| 8387c435d4 | |||
| 5807bbccc4 | |||
| 1bfc041bbe | |||
| a5ca058cb2 | |||
| 88a1ad04aa | |||
| e619c402b4 | |||
| c9294e5281 | |||
| 0074f57970 | |||
| 9b4a6f2761 | |||
| f889b5206c | |||
| 507d7dd4f0 | |||
| 55c5b41693 | |||
| b58d8677b3 | |||
| f30d7fc68c | |||
| f5a204d1de | |||
| d504d8f54e | |||
| 7c049b55a8 | |||
| b519d68e1a | |||
| deb8d652c5 | |||
| 051b3f2e6a | |||
| 21f947efbd | |||
| a68f4e284a | |||
| 90b43584d2 | |||
| 9461e35028 | |||
| 12dd01dd29 | |||
| c64e3a8908 | |||
| d554417fde | |||
| 74e8390bda | |||
| 4f9bbdf7f7 | |||
| 67fc8b6215 | |||
| 3840ac2fed | |||
| 7c64f1aa0d | |||
| 771f8d92f2 | |||
| 82c18955b0 | |||
| eedcfc122b | |||
| 8cf3110708 | |||
| 0dc0e68174 | |||
| 5226762528 | |||
| 7889f82a9a | |||
| aaa9c8f194 | |||
| 3d1a44b32c | |||
| 724ec9ab73 | |||
| aa0eaf590c | |||
| 2b82520067 | |||
| 5aed6334c4 | |||
| 91157caf0e | |||
| dbe489c0f2 | |||
| 2652a583e0 | |||
| bc1758f569 | |||
| a898c2d697 | |||
| e612bcae58 | |||
| d27fee8a5b | |||
| 7aef8cbb67 | |||
| a7e28a97d9 | |||
| 308a61895e | |||
| 8e5c57eb60 | |||
| 9c5526f281 | |||
| 9c803cc234 | |||
| f24fd42a69 | |||
| 9b7f459c44 | |||
| 273a5cef28 | |||
| 57945fcec6 | |||
| ade3c4ee6c | |||
| 7da7c1e5a1 | |||
| 8fe904d780 | |||
| 1901fbb6ed | |||
| 1de52482e3 | |||
| 1c0d4001ab | |||
| f04d70f2d9 | |||
| 55098deac0 | |||
| 58c5618d30 | |||
| d0cbeb4a0a | |||
| ce0821e2d6 | |||
| b6e9db7363 | |||
| 9f710a957d | |||
| b7578e2b98 | |||
| 97e8973cbd | |||
| 2dc45c3b9f | |||
| 49c2ae3673 | |||
| 5f22c81994 | |||
| f13427f5fb | |||
| 2785eaeda5 | |||
| bb31eb7c35 | |||
| a31cfb86f8 | |||
| 4feb876463 | |||
| e4802b592f | |||
| a8914dcfdb | |||
| 548f9fd01f | |||
| 843796c9a4 | |||
| 7648c76f6f | |||
| 308ed3bba7 | |||
| 7bcb2b11f6 | |||
| e06cda98e9 | |||
| fb02a5b06d | |||
| c060856782 | |||
| 311edd3279 | |||
| 1c89f8c8dd | |||
| 857063ebd8 | |||
| 24063c403a | |||
| 0f930d7342 | |||
| c36000c4dd | |||
| 999de5ae1d | |||
| 00982d4278 | |||
| cda47ed536 | |||
| f4f1f6774d | |||
| a20cc60c10 | |||
| 846ac573cc | |||
| 92acc3d331 | |||
| ed4db61288 | |||
| ba6705a706 | |||
| dc980bd78e | |||
| c9f99d7f42 | |||
| 73e219b23c | |||
| 07e0de5824 | |||
| 79a103662f | |||
| 2ba5627e2c | |||
| 122295c89f | |||
| 2669c68df0 | |||
| 389b29599b | |||
| 912a6a52cc | |||
| 925c343a30 | |||
| 351796fd6b | |||
| e58d9a9535 | |||
| 3fa7c4d7ef | |||
| b6b2a53361 | |||
| 4647ed2551 | |||
| e7e6ad6d0b | |||
| 0e727f91da | |||
| a71ec8dd81 | |||
| f816a63b54 | |||
| 138991d951 | |||
| 92ce064d30 | |||
| ab3bfa4e92 | |||
| fa8f3985b8 | |||
| 179523224a | |||
| 19e4bee34e | |||
| f1709f6066 | |||
| f2be235f85 | |||
| 175c785705 | |||
| 72aebd3ecf | |||
| c8408760df | |||
| 5834649abc | |||
| 2eda70542f | |||
| a2a17faca1 | |||
| a6c2844d12 | |||
| 802cbb82c4 | |||
| 6f16b98b32 | |||
| 42f5507fe8 | |||
| 72910cb35e | |||
| 7be30ad6ac | |||
| 5a8e37f5b4 | |||
| f87ccc0a53 | |||
| 40ed77103f | |||
| 483762a984 | |||
| a3c7b1fcfe | |||
| 145ca54dcf | |||
| 02785eea42 | |||
| 1aecb66167 | |||
| 96244f9f7b | |||
| e0cc59d212 | |||
| dd2946419b | |||
| d0ea8e83ad | |||
| 3c7d3b2f63 | |||
| a65ad3c72a | |||
| f6a7b25e87 | |||
| d97cc36c53 | |||
| 0e06245177 | |||
| 5a81a9eed7 | |||
| 38e7d7e697 | |||
| 820069d5d5 | |||
| 49f3979d18 | |||
| 055e209e0b | |||
| 5579563105 | |||
| cda9d0f9f2 | |||
| 49b7f15e3d | |||
| 8ae700eedf | |||
| 411bbcc192 | |||
| d598fdce54 | |||
| db86f6e1ee | |||
| c16ce4ce1f | |||
| 86021c5052 | |||
| de87701c95 | |||
| a03e0dfb53 | |||
| 5060e709d1 | |||
| c073191f69 | |||
| b4450ca7f2 | |||
| 7004765acf | |||
| 77a91bc017 | |||
| 14b96da588 | |||
| c21837e906 | |||
| 99324035e5 | |||
| 8b9a5000be | |||
| 32892cf5e0 | |||
| c82bddb7dd | |||
| 478ca30948 | |||
| f2fd45576b | |||
| 6bf5f4dbdf | |||
| 32d757b13c | |||
| 882d5ec7f5 | |||
| ce69497a68 | |||
| 1d43cf491b | |||
| d5a687cf3d | |||
| c9082ce511 | |||
| 46c7737c17 | |||
| 06c829eb27 | |||
| e439302b93 | |||
| 6a1d7d2586 | |||
| 27283754c0 | |||
| ff6ea0b42e | |||
| 384a31e3c6 | |||
| 69b913c608 | |||
| 83757c60b5 | |||
| 5b2212829c | |||
| 26acecb720 | |||
| 4a6844b949 | |||
| ac9b484d3d | |||
| 97a76d5ad4 | |||
| d4fab66411 | |||
| 4cd287616e | |||
| d22afe07db | |||
| fa56f1a59d | |||
| 546209ddb5 | |||
| 1e7c44ead0 | |||
| 90448f1c78 | |||
| 6dc34e1167 | |||
| 6956e01861 | |||
| f3b364022a | |||
| 56ae2b709d | |||
| 7c42cd26c2 | |||
| 13758418ac | |||
| 6bd1a77b96 | |||
| 77b04d74d4 | |||
| e95cb14d48 | |||
| 86b2dac231 | |||
| 54bda6f12a | |||
| 2f67a485aa | |||
| f192f241cf | |||
| bf797bccc7 | |||
| 4336f5201d | |||
| 0acc202740 | |||
| 431f91b900 | |||
| ed019a90e2 | |||
| d4acc2c79b | |||
| b05c4711ce | |||
| 74c167ed03 | |||
| 4ad0c1bb89 | |||
| 67da15330e | |||
| 8d598cd37c | |||
| 690af8e494 | |||
| 30c2f95844 | |||
| a458e7daf0 | |||
| 0078a5d8cc | |||
| 09085e3bcf | |||
| a31b03b3e2 | |||
| 9d06ce53a4 | |||
| d0e8e77fc5 | |||
| d85cb9e15e | |||
| 798105998e | |||
| 9ef53536bf | |||
| 547351a719 | |||
| 371c4ae1a3 | |||
| 060137c798 | |||
| f4994a5133 | |||
| 19a4cf774b | |||
| ab00ba8a12 | |||
| 57e2a19023 | |||
| daa80a7bf6 | |||
| 8152e7cb92 | |||
| adccb4161e | |||
| 72a0187295 | |||
| 24d6287918 | |||
| 55900a3464 | |||
| 6bbfb65100 | |||
| 36e9bb4d78 | |||
| 639d04381e | |||
| 2465eb233e | |||
| 7f905bce0e | |||
| 205ea22daa | |||
| cca3b9032f | |||
| 4fc3398e49 | |||
| 2d5254e169 | |||
| c5033710ca | |||
| c824d27728 | |||
| ac54affa57 | |||
| f7d7a43720 | |||
| 6028907a02 | |||
| e14ca4f3d0 | |||
| a97b566065 | |||
| 26a95a8ca1 | |||
| e3308e86f0 | |||
| 1d98507785 | |||
| 096c3aeec6 | |||
| 7ca5c48ae7 | |||
| 4a1a216b3c | |||
| f96d6aed9d | |||
| 192aae71a2 | |||
| bd8af539b4 | |||
| 479d859a75 | |||
| 1050af5376 | |||
| 82dbfb769e | |||
| 55eb996c19 | |||
| ac4de99f30 | |||
| a3150b7d4e | |||
| 1701eb2450 | |||
| 20ac21aef2 | |||
| 4ac20de1dd | |||
| 848eaf5e27 | |||
| f3e78e990f | |||
| a6996922b7 | |||
| 434703f4ec | |||
| 2f7b532209 | |||
| abebb40461 | |||
| 3b54db9b49 | |||
| 1defc1e0e4 | |||
| a7e626f4a7 | |||
| 04f6a01196 | |||
| ef64fbb839 | |||
| 2424a1c04b | |||
| c8c7de065d | |||
| 61d71d0552 | |||
| a09755ef7f | |||
| afae06a866 | |||
| 051af5c48e | |||
| 1fb05fcca7 | |||
| b51e571803 | |||
| a275567dba | |||
| 2a95ed3c7a | |||
| 9965aa461a | |||
| 8de8b53039 | |||
| 27eeb383b4 | |||
| 4f5347fa4d | |||
| 994f91e496 | |||
| 4ab7f768fa | |||
| a020a02b26 | |||
| 49d24dada7 | |||
| ef9dd6d803 | |||
| ca777b26de | |||
| 1f636d05d1 | |||
| 7495cd3f2c | |||
| 5f68f26d2f | |||
| a5af5ba1a9 | |||
| 1261f6b159 | |||
| a162f21a70 | |||
| ace111aefb | |||
| dfe3798aab | |||
| 975535a9e2 | |||
| e7f7631d1e | |||
| 5446e4b0fc | |||
| 79c2400d27 | |||
| 23304aca0b | |||
| 845dd4673c | |||
| 14e402878a | |||
| 5a2687d520 | |||
| 4c330c8654 | |||
| ca1f43d048 | |||
| e962a48138 |
@@ -15,6 +15,8 @@
|
||||
<language>en-US</language>
|
||||
<tags>umbraco</tags>
|
||||
<dependencies>
|
||||
<!--Must be this way because 2.0.5 is actually 1.2.15.0 and 2.0.6 is actually 2.0.6 !-->
|
||||
<dependency id="log4net" version="[2.0.5,2.0.6)" />
|
||||
<dependency id="Microsoft.AspNet.Mvc" version="[5.2.3,6.0.0)" />
|
||||
<dependency id="Microsoft.AspNet.WebApi" version="[5.2.3,6.0.0)" />
|
||||
<dependency id="Microsoft.AspNet.Identity.Owin" version="[2.2.1, 3.0.0)" />
|
||||
@@ -28,52 +30,51 @@
|
||||
<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.9.2, 2.0.0)" />
|
||||
<dependency id="ClientDependency-Mvc5" version="[1.8.0, 2.0.0)" />
|
||||
<!-- AutoMapper can not be updated due to: https://github.com/AutoMapper/AutoMapper/issues/373#issuecomment-127644405 -->
|
||||
<dependency id="ClientDependency-Mvc5" version="[1.8.0.0, 2.0.0)" />
|
||||
<dependency id="AutoMapper" version="[3.3.1, 4.0.0)" />
|
||||
<dependency id="Newtonsoft.Json" version="[6.0.8, 10.0.0)" />
|
||||
<dependency id="Examine" version="[0.1.80, 1.0.0)" />
|
||||
<dependency id="ImageProcessor" version="[2.5.1, 3.0.0)" />
|
||||
<dependency id="ImageProcessor.Web" version="[4.8.0, 5.0.0)" />
|
||||
<dependency id="semver" version="[1.1.2, 2.0.0)" />
|
||||
<dependency id="Newtonsoft.Json" version="[9.0.1, 10.0.0)" />
|
||||
<dependency id="Examine" version="[0.1.81, 1.0.0)" />
|
||||
<dependency id="ImageProcessor" version="[2.5.2, 3.0.0)" />
|
||||
<dependency id="ImageProcessor.Web" version="[4.8.2, 5.0.0)" />
|
||||
<dependency id="semver" version="[2.0.4, 3.0.0)" />
|
||||
<dependency id="UrlRewritingNet" version="[2.0.7, 3.0.0)" />
|
||||
<!-- Markdown can not be updated due to: https://github.com/hey-red/markdownsharp/issues/71#issuecomment-233585487 -->
|
||||
<dependency id="Markdown" version="[1.14.4, 2.0.0)" />
|
||||
<dependency id="Markdown" version="[1.14.7, 2.0.0)" />
|
||||
<dependency id="System.Threading.Tasks.Dataflow" version="[4.6.0, 5.0.0)" />
|
||||
</dependencies>
|
||||
</metadata>
|
||||
<files>
|
||||
<file src="..\_BuildOutput\WebApp\bin\businesslogic.dll" target="lib\businesslogic.dll" />
|
||||
<file src="..\_BuildOutput\WebApp\bin\businesslogic.xml" target="lib\businesslogic.xml" />
|
||||
<file src="..\_BuildOutput\WebApp\bin\cms.dll" target="lib\cms.dll" />
|
||||
<file src="..\_BuildOutput\WebApp\bin\cms.xml" target="lib\cms.xml" />
|
||||
<file src="..\_BuildOutput\WebApp\bin\controls.dll" target="lib\controls.dll" />
|
||||
<file src="..\_BuildOutput\WebApp\bin\controls.xml" target="lib\controls.xml" />
|
||||
<file src="..\_BuildOutput\WebApp\bin\interfaces.dll" target="lib\interfaces.dll" />
|
||||
<file src="..\_BuildOutput\WebApp\bin\interfaces.xml" target="lib\interfaces.xml" />
|
||||
<file src="..\_BuildOutput\WebApp\bin\log4net.dll" target="lib\log4net.dll" />
|
||||
<file src="..\_BuildOutput\WebApp\bin\Microsoft.ApplicationBlocks.Data.dll" target="lib\Microsoft.ApplicationBlocks.Data.dll" />
|
||||
<file src="..\_BuildOutput\WebApp\bin\SQLCE4Umbraco.dll" target="lib\SQLCE4Umbraco.dll" />
|
||||
<file src="..\_BuildOutput\WebApp\bin\SQLCE4Umbraco.xml" target="lib\SQLCE4Umbraco.xml" />
|
||||
<file src="..\_BuildOutput\WebApp\bin\System.Data.SqlServerCe.dll" target="lib\System.Data.SqlServerCe.dll" />
|
||||
<file src="..\_BuildOutput\WebApp\bin\System.Data.SqlServerCe.Entity.dll" target="lib\System.Data.SqlServerCe.Entity.dll" />
|
||||
<file src="..\_BuildOutput\WebApp\bin\TidyNet.dll" target="lib\TidyNet.dll" />
|
||||
<file src="..\_BuildOutput\WebApp\bin\Umbraco.Core.dll" target="lib\Umbraco.Core.dll" />
|
||||
<file src="..\_BuildOutput\WebApp\bin\Umbraco.Core.xml" target="lib\Umbraco.Core.xml" />
|
||||
<file src="..\_BuildOutput\WebApp\bin\umbraco.DataLayer.dll" target="lib\umbraco.DataLayer.dll" />
|
||||
<file src="..\_BuildOutput\WebApp\bin\umbraco.DataLayer.xml" target="lib\umbraco.DataLayer.xml" />
|
||||
<file src="..\_BuildOutput\WebApp\bin\umbraco.dll" target="lib\umbraco.dll" />
|
||||
<file src="..\_BuildOutput\WebApp\bin\umbraco.xml" target="lib\umbraco.xml" />
|
||||
<file src="..\_BuildOutput\WebApp\bin\umbraco.editorControls.dll" target="lib\umbraco.editorControls.dll" />
|
||||
<file src="..\_BuildOutput\WebApp\bin\umbraco.editorControls.xml" target="lib\umbraco.editorControls.xml" />
|
||||
<file src="..\_BuildOutput\WebApp\bin\umbraco.MacroEngines.dll" target="lib\umbraco.MacroEngines.dll" />
|
||||
<file src="..\_BuildOutput\WebApp\bin\umbraco.MacroEngines.xml" target="lib\umbraco.MacroEngines.xml" />
|
||||
<file src="..\_BuildOutput\WebApp\bin\umbraco.providers.dll" target="lib\umbraco.providers.dll" />
|
||||
<file src="..\_BuildOutput\WebApp\bin\umbraco.providers.xml" target="lib\umbraco.providers.xml" />
|
||||
<file src="..\_BuildOutput\WebApp\bin\Umbraco.Web.UI.dll" target="lib\Umbraco.Web.UI.dll" />
|
||||
<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\businesslogic.dll" target="lib\net45\businesslogic.dll" />
|
||||
<file src="..\_BuildOutput\WebApp\bin\businesslogic.xml" target="lib\net45\businesslogic.xml" />
|
||||
<file src="..\_BuildOutput\WebApp\bin\cms.dll" target="lib\net45\cms.dll" />
|
||||
<file src="..\_BuildOutput\WebApp\bin\cms.xml" target="lib\net45\cms.xml" />
|
||||
<file src="..\_BuildOutput\WebApp\bin\controls.dll" target="lib\net45\controls.dll" />
|
||||
<file src="..\_BuildOutput\WebApp\bin\controls.xml" target="lib\net45\controls.xml" />
|
||||
<file src="..\_BuildOutput\WebApp\bin\interfaces.dll" target="lib\net45\interfaces.dll" />
|
||||
<file src="..\_BuildOutput\WebApp\bin\interfaces.xml" target="lib\net45\interfaces.xml" />
|
||||
<file src="..\_BuildOutput\WebApp\bin\log4net.dll" target="lib\net45\log4net.dll" />
|
||||
<file src="..\_BuildOutput\WebApp\bin\Microsoft.ApplicationBlocks.Data.dll" target="lib\net45\Microsoft.ApplicationBlocks.Data.dll" />
|
||||
<file src="..\_BuildOutput\WebApp\bin\SQLCE4Umbraco.dll" target="lib\net45\SQLCE4Umbraco.dll" />
|
||||
<file src="..\_BuildOutput\WebApp\bin\SQLCE4Umbraco.xml" target="lib\net45\SQLCE4Umbraco.xml" />
|
||||
<file src="..\_BuildOutput\WebApp\bin\System.Data.SqlServerCe.dll" target="lib\net45\System.Data.SqlServerCe.dll" />
|
||||
<file src="..\_BuildOutput\WebApp\bin\System.Data.SqlServerCe.Entity.dll" target="lib\net45\System.Data.SqlServerCe.Entity.dll" />
|
||||
<file src="..\_BuildOutput\WebApp\bin\TidyNet.dll" target="lib\net45\TidyNet.dll" />
|
||||
<file src="..\_BuildOutput\WebApp\bin\Umbraco.Core.dll" target="lib\net45\Umbraco.Core.dll" />
|
||||
<file src="..\_BuildOutput\WebApp\bin\Umbraco.Core.xml" target="lib\net45\Umbraco.Core.xml" />
|
||||
<file src="..\_BuildOutput\WebApp\bin\umbraco.DataLayer.dll" target="lib\net45\umbraco.DataLayer.dll" />
|
||||
<file src="..\_BuildOutput\WebApp\bin\umbraco.DataLayer.xml" target="lib\net45\umbraco.DataLayer.xml" />
|
||||
<file src="..\_BuildOutput\WebApp\bin\umbraco.dll" target="lib\net45\umbraco.dll" />
|
||||
<file src="..\_BuildOutput\WebApp\bin\umbraco.xml" target="lib\net45\umbraco.xml" />
|
||||
<file src="..\_BuildOutput\WebApp\bin\umbraco.editorControls.dll" target="lib\net45\umbraco.editorControls.dll" />
|
||||
<file src="..\_BuildOutput\WebApp\bin\umbraco.editorControls.xml" target="lib\net45\umbraco.editorControls.xml" />
|
||||
<file src="..\_BuildOutput\WebApp\bin\umbraco.MacroEngines.dll" target="lib\net45\umbraco.MacroEngines.dll" />
|
||||
<file src="..\_BuildOutput\WebApp\bin\umbraco.MacroEngines.xml" target="lib\net45\umbraco.MacroEngines.xml" />
|
||||
<file src="..\_BuildOutput\WebApp\bin\umbraco.providers.dll" target="lib\net45\umbraco.providers.dll" />
|
||||
<file src="..\_BuildOutput\WebApp\bin\umbraco.providers.xml" target="lib\net45\umbraco.providers.xml" />
|
||||
<file src="..\_BuildOutput\WebApp\bin\Umbraco.Web.UI.dll" target="lib\net45\Umbraco.Web.UI.dll" />
|
||||
<file src="..\_BuildOutput\WebApp\bin\Umbraco.Web.UI.xml" target="lib\net45\Umbraco.Web.UI.xml" />
|
||||
<file src="..\_BuildOutput\WebApp\bin\UmbracoExamine.dll" target="lib\net45\UmbracoExamine.dll" />
|
||||
<file src="..\_BuildOutput\WebApp\bin\UmbracoExamine.xml" target="lib\net45\UmbracoExamine.xml" />
|
||||
<file src="tools\install.core.ps1" target="tools\install.ps1" />
|
||||
|
||||
<!-- Added to be able to produce a symbols package -->
|
||||
@@ -104,4 +105,4 @@
|
||||
<file src="..\_BuildOutput\bin\UmbracoExamine.pdb" target="lib" />
|
||||
<file src="..\..\src\UmbracoExamine\**\*.cs" exclude="..\..\src\**\TemporaryGeneratedFile*.cs" target="src\UmbracoExamine" />
|
||||
</files>
|
||||
</package>
|
||||
</package>
|
||||
|
||||
@@ -16,9 +16,10 @@
|
||||
<tags>umbraco</tags>
|
||||
<dependencies>
|
||||
<dependency id="UmbracoCms.Core" version="[$version$]" />
|
||||
<dependency id="Newtonsoft.Json" version="[6.0.8, 10.0.0)" />
|
||||
<dependency id="Newtonsoft.Json" version="[9.0.1, 10.0.0)" />
|
||||
<dependency id="Umbraco.ModelsBuilder" version="[3.0.5, 4.0.0)" />
|
||||
<dependency id="Microsoft.AspNet.SignalR.Core" version="[2.2.1, 3.0.0)" />
|
||||
<dependency id="ImageProcessor.Web.Config" version="[2.3.0, 3.0.0)" />
|
||||
</dependencies>
|
||||
</metadata>
|
||||
<files>
|
||||
@@ -45,6 +46,7 @@
|
||||
<file src="tools\umbracoSettings.config.install.xdt" target="Content\config\umbracoSettings.config.install.xdt" />
|
||||
<file src="tools\Views.Web.config.install.xdt" target="Views\Web.config.install.xdt" />
|
||||
<file src="tools\processing.config.install.xdt" target="Content\Config\imageprocessor\processing.config.install.xdt" />
|
||||
<file src="tools\cache.config.install.xdt" target="Content\Config\imageprocessor\cache.config.install.xdt" />
|
||||
<file src="build\**" target="build" />
|
||||
</files>
|
||||
</package>
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
<?xml version="1.0"?>
|
||||
<caching xmlns:xdt="http://schemas.microsoft.com/XML-Document-Transform">
|
||||
<caches>
|
||||
<cache name="DiskCache" trimCache="false" xdt:Transform="SetAttributes(trimCache)" xdt:Locator="Match(name)" />
|
||||
</caches>
|
||||
</caching>
|
||||
@@ -1,3 +1,3 @@
|
||||
# Usage: on line 2 put the release version, on line 3 put the version comment (example: beta)
|
||||
7.6.0
|
||||
alpha055
|
||||
alpha066
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<Project ToolsVersion="12.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<PropertyGroup>
|
||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
||||
@@ -10,7 +10,7 @@
|
||||
<AppDesignerFolder>Properties</AppDesignerFolder>
|
||||
<RootNamespace>SQLCE4Umbraco</RootNamespace>
|
||||
<AssemblyName>SQLCE4Umbraco</AssemblyName>
|
||||
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
|
||||
<TargetFrameworkVersion>v4.6.2</TargetFrameworkVersion>
|
||||
<FileAlignment>512</FileAlignment>
|
||||
<SccProjectName>
|
||||
</SccProjectName>
|
||||
|
||||
@@ -240,7 +240,7 @@ namespace SqlCE4Umbraco
|
||||
{
|
||||
var cmd = trx == null ? new SqlCeCommand(commandText, conn) : new SqlCeCommand(commandText, conn, trx);
|
||||
AttachParameters(cmd, commandParameters);
|
||||
return cmd.ExecuteReader(CommandBehavior.CloseConnection);
|
||||
return cmd.ExecuteReader();
|
||||
}
|
||||
catch
|
||||
{
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
|
||||
<dependentAssembly>
|
||||
<assemblyIdentity name="Newtonsoft.Json" publicKeyToken="30ad4fe6b2a6aeed" culture="neutral" />
|
||||
<bindingRedirect oldVersion="0.0.0.0-6.0.0.0" newVersion="6.0.0.0" />
|
||||
<bindingRedirect oldVersion="0.0.0.0-9.0.0.0" newVersion="9.0.0.0" />
|
||||
</dependentAssembly>
|
||||
<dependentAssembly>
|
||||
<assemblyIdentity name="System.Web.Mvc" publicKeyToken="31bf3856ad364e35" culture="neutral" />
|
||||
@@ -32,4 +32,4 @@
|
||||
</dependentAssembly>
|
||||
</assemblyBinding>
|
||||
</runtime>
|
||||
</configuration>
|
||||
<startup><supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.6.2" /></startup></configuration>
|
||||
|
||||
+1
-1
@@ -12,4 +12,4 @@ using System.Resources;
|
||||
[assembly: AssemblyVersion("1.0.*")]
|
||||
|
||||
[assembly: AssemblyFileVersion("7.6.0")]
|
||||
[assembly: AssemblyInformationalVersion("7.6.0-alpha055")]
|
||||
[assembly: AssemblyInformationalVersion("7.6.0-alpha066")]
|
||||
@@ -7,6 +7,7 @@ using Umbraco.Core.Configuration;
|
||||
using Umbraco.Core.Logging;
|
||||
using Umbraco.Core.ObjectResolution;
|
||||
using Umbraco.Core.Profiling;
|
||||
using Umbraco.Core.Scoping;
|
||||
using Umbraco.Core.Services;
|
||||
using Umbraco.Core.Sync;
|
||||
|
||||
@@ -162,6 +163,9 @@ namespace Umbraco.Core
|
||||
/// </summary>
|
||||
public static ApplicationContext Current { get; internal set; }
|
||||
|
||||
// fixme
|
||||
internal IScopeProvider ScopeProvider { get { return _databaseContext == null ? null : _databaseContext.ScopeProvider; } }
|
||||
|
||||
/// <summary>
|
||||
/// Returns the application wide cache accessor
|
||||
/// </summary>
|
||||
@@ -418,10 +422,17 @@ namespace Umbraco.Core
|
||||
this.ApplicationCache = null;
|
||||
if (_databaseContext != null) //need to check the internal field here
|
||||
{
|
||||
if (_databaseContext.ScopeProvider.AmbientScope != null)
|
||||
{
|
||||
var scope = _databaseContext.ScopeProvider.AmbientScope;
|
||||
scope.Dispose();
|
||||
}
|
||||
/*
|
||||
if (DatabaseContext.IsDatabaseConfigured && DatabaseContext.Database != null)
|
||||
{
|
||||
DatabaseContext.Database.Dispose();
|
||||
}
|
||||
}
|
||||
*/
|
||||
}
|
||||
this.DatabaseContext = null;
|
||||
this.Services = null;
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
using System;
|
||||
using System.Reflection;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Web;
|
||||
using Umbraco.Core;
|
||||
|
||||
namespace Umbraco.Core
|
||||
{
|
||||
/// <summary>
|
||||
/// Manages any assembly binding redirects that cannot be done via config
|
||||
/// </summary>
|
||||
internal class BindingRedirects
|
||||
{
|
||||
public static void Initialize()
|
||||
{
|
||||
// this only gets called when an assembly can't be resolved
|
||||
AppDomain.CurrentDomain.AssemblyResolve += CurrentDomain_AssemblyResolve;
|
||||
}
|
||||
|
||||
private static readonly Regex Log4NetAssemblyPattern = new Regex("log4net, Version=([\\d\\.]+?), Culture=neutral, PublicKeyToken=null", RegexOptions.Compiled);
|
||||
private const string Log4NetReplacement = "log4net, Version=1.2.15.0, Culture=neutral, PublicKeyToken=669e0ddf0bb1aa2a";
|
||||
|
||||
/// <summary>
|
||||
/// This is used to do an assembly binding redirect via code - normally required due to signature changes in assemblies
|
||||
/// </summary>
|
||||
/// <param name="sender"></param>
|
||||
/// <param name="args"></param>
|
||||
/// <returns></returns>
|
||||
private static Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args)
|
||||
{
|
||||
//log4net:
|
||||
// Use regex to match and replace
|
||||
if (Log4NetAssemblyPattern.IsMatch(args.Name) && args.Name != Log4NetReplacement)
|
||||
{
|
||||
return Assembly.Load(Log4NetAssemblyPattern.Replace(args.Name, Log4NetReplacement));
|
||||
}
|
||||
|
||||
//AutoMapper:
|
||||
// ensure the assembly is indeed AutoMapper and that the PublicKeyToken is null before trying to Load again
|
||||
// do NOT just replace this with 'return Assembly', as it will cause an infinite loop -> stackoverflow
|
||||
if (args.Name.StartsWith("AutoMapper") && args.Name.EndsWith("PublicKeyToken=null"))
|
||||
return Assembly.Load(args.Name.Replace(", PublicKeyToken=null", ", PublicKeyToken=be96cd2c38ef1005"));
|
||||
|
||||
return null;
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,23 +1,26 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Umbraco.Core.Logging;
|
||||
using Umbraco.Core.Models.EntityBase;
|
||||
using Umbraco.Core.Scoping;
|
||||
|
||||
namespace Umbraco.Core.Cache
|
||||
{
|
||||
/// <summary>
|
||||
/// The default cache policy for retrieving a single entity
|
||||
/// Represents the default cache policy.
|
||||
/// </summary>
|
||||
/// <typeparam name="TEntity"></typeparam>
|
||||
/// <typeparam name="TId"></typeparam>
|
||||
/// <typeparam name="TEntity">The type of the entity.</typeparam>
|
||||
/// <typeparam name="TId">The type of the identifier.</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.
|
||||
/// <para>The default cache policy caches entities with a 5 minutes sliding expiration.</para>
|
||||
/// <para>Each entity is cached individually.</para>
|
||||
/// <para>If options.GetAllCacheAllowZeroCount then a 'zero-count' array is cached when GetAll finds nothing.</para>
|
||||
/// <para>If options.GetAllCacheValidateCount then we check against the db when getting many entities.</para>
|
||||
/// </remarks>
|
||||
internal class DefaultRepositoryCachePolicy<TEntity, TId> : RepositoryCachePolicyBase<TEntity, TId>
|
||||
where TEntity : class, IAggregateRoot
|
||||
{
|
||||
private static readonly TEntity[] EmptyEntities = new TEntity[0]; // const
|
||||
private readonly RepositoryCachePolicyOptions _options;
|
||||
|
||||
public DefaultRepositoryCachePolicy(IRuntimeCacheProvider cache, RepositoryCachePolicyOptions options)
|
||||
@@ -27,242 +30,223 @@ namespace Umbraco.Core.Cache
|
||||
_options = options;
|
||||
}
|
||||
|
||||
protected string GetCacheIdKey(object id)
|
||||
public override IRepositoryCachePolicy<TEntity, TId> Scoped(IRuntimeCacheProvider runtimeCache, IScope scope)
|
||||
{
|
||||
if (id == null) throw new ArgumentNullException("id");
|
||||
|
||||
return string.Format("{0}{1}", GetCacheTypeKey(), id);
|
||||
return new ScopedRepositoryCachePolicy<TEntity, TId>(this, runtimeCache, scope);
|
||||
}
|
||||
|
||||
protected string GetCacheTypeKey()
|
||||
protected string GetEntityCacheKey(object id)
|
||||
{
|
||||
if (id == null) throw new ArgumentNullException("id");
|
||||
return GetEntityTypeCacheKey() + id;
|
||||
}
|
||||
|
||||
protected string GetEntityTypeCacheKey()
|
||||
{
|
||||
return string.Format("uRepo_{0}_", typeof(TEntity).Name);
|
||||
}
|
||||
|
||||
public override void CreateOrUpdate(TEntity entity, Action<TEntity> persistMethod)
|
||||
protected virtual void InsertEntity(string cacheKey, TEntity entity)
|
||||
{
|
||||
Cache.InsertCacheItem(cacheKey, () => entity, TimeSpan.FromMinutes(5), true);
|
||||
}
|
||||
|
||||
protected virtual void InsertEntities(TId[] ids, TEntity[] entities)
|
||||
{
|
||||
if (ids.Length == 0 && entities.Length == 0 && _options.GetAllCacheAllowZeroCount)
|
||||
{
|
||||
// getting all of them, and finding nothing.
|
||||
// if we can cache a zero count, cache an empty array,
|
||||
// for as long as the cache is not cleared (no expiration)
|
||||
Cache.InsertCacheItem(GetEntityTypeCacheKey(), () => EmptyEntities);
|
||||
}
|
||||
else
|
||||
{
|
||||
// individually cache each item
|
||||
foreach (var entity in entities)
|
||||
{
|
||||
var capture = entity;
|
||||
Cache.InsertCacheItem(GetEntityCacheKey(entity.Id), () => capture, TimeSpan.FromMinutes(5), true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Create(TEntity entity, Action<TEntity> persistNew)
|
||||
{
|
||||
if (entity == null) throw new ArgumentNullException("entity");
|
||||
if (persistMethod == null) throw new ArgumentNullException("persistMethod");
|
||||
|
||||
try
|
||||
{
|
||||
persistMethod(entity);
|
||||
persistNew(entity);
|
||||
|
||||
//set the disposal action
|
||||
SetCacheAction(() =>
|
||||
// just to be safe, we cannot cache an item without an identity
|
||||
if (entity.HasIdentity)
|
||||
{
|
||||
//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());
|
||||
});
|
||||
|
||||
Cache.InsertCacheItem(GetEntityCacheKey(entity.Id), () => entity, TimeSpan.FromMinutes(5), true);
|
||||
}
|
||||
|
||||
// if there's a GetAllCacheAllowZeroCount cache, ensure it is cleared
|
||||
Cache.ClearCacheItem(GetEntityTypeCacheKey());
|
||||
}
|
||||
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 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(GetEntityCacheKey(entity.Id));
|
||||
|
||||
// if there's a GetAllCacheAllowZeroCount cache, ensure it is cleared
|
||||
Cache.ClearCacheItem(GetEntityTypeCacheKey());
|
||||
|
||||
//If there's a GetAllCacheAllowZeroCount cache, ensure it is cleared
|
||||
Cache.ClearCacheItem(GetCacheTypeKey());
|
||||
});
|
||||
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
public override void Remove(TEntity entity, Action<TEntity> persistMethod)
|
||||
/// <inheritdoc />
|
||||
public override void Update(TEntity entity, Action<TEntity> persistUpdated)
|
||||
{
|
||||
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(() =>
|
||||
persistUpdated(entity);
|
||||
|
||||
// just to be safe, we cannot cache an item without an identity
|
||||
if (entity.HasIdentity)
|
||||
{
|
||||
Cache.ClearCacheItem(cacheKey);
|
||||
//If there's a GetAllCacheAllowZeroCount cache, ensure it is cleared
|
||||
Cache.ClearCacheItem(GetCacheTypeKey());
|
||||
});
|
||||
Cache.InsertCacheItem(GetEntityCacheKey(entity.Id), () => entity, TimeSpan.FromMinutes(5), true);
|
||||
}
|
||||
|
||||
// if there's a GetAllCacheAllowZeroCount cache, ensure it is cleared
|
||||
Cache.ClearCacheItem(GetEntityTypeCacheKey());
|
||||
}
|
||||
catch
|
||||
{
|
||||
// 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(GetEntityCacheKey(entity.Id));
|
||||
|
||||
// if there's a GetAllCacheAllowZeroCount cache, ensure it is cleared
|
||||
Cache.ClearCacheItem(GetEntityTypeCacheKey());
|
||||
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
public override TEntity Get(TId id, Func<TId, TEntity> getFromRepo)
|
||||
/// <inheritdoc />
|
||||
public override void Delete(TEntity entity, Action<TEntity> persistDeleted)
|
||||
{
|
||||
if (getFromRepo == null) throw new ArgumentNullException("getFromRepo");
|
||||
if (entity == null) throw new ArgumentNullException("entity");
|
||||
|
||||
var cacheKey = GetCacheIdKey(id);
|
||||
try
|
||||
{
|
||||
persistDeleted(entity);
|
||||
}
|
||||
finally
|
||||
{
|
||||
// whatever happens, clear the cache
|
||||
var cacheKey = GetEntityCacheKey(entity.Id);
|
||||
Cache.ClearCacheItem(cacheKey);
|
||||
// if there's a GetAllCacheAllowZeroCount cache, ensure it is cleared
|
||||
Cache.ClearCacheItem(GetEntityTypeCacheKey());
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override TEntity Get(TId id, Func<TId, TEntity> performGet, Func<TId[], IEnumerable<TEntity>> performGetAll)
|
||||
{
|
||||
var cacheKey = GetEntityCacheKey(id);
|
||||
var fromCache = Cache.GetCacheItem<TEntity>(cacheKey);
|
||||
|
||||
// if found in cache then return else fetch and cache
|
||||
if (fromCache != null)
|
||||
return fromCache;
|
||||
|
||||
var entity = getFromRepo(id);
|
||||
var entity = performGet(id);
|
||||
|
||||
//set the disposal action
|
||||
SetCacheAction(cacheKey, entity);
|
||||
if (entity != null && entity.HasIdentity)
|
||||
InsertEntity(cacheKey, entity);
|
||||
|
||||
return entity;
|
||||
}
|
||||
|
||||
public override TEntity Get(TId id)
|
||||
/// <inheritdoc />
|
||||
public override TEntity GetCached(TId id)
|
||||
{
|
||||
var cacheKey = GetCacheIdKey(id);
|
||||
var cacheKey = GetEntityCacheKey(id);
|
||||
return Cache.GetCacheItem<TEntity>(cacheKey);
|
||||
}
|
||||
|
||||
public override bool Exists(TId id, Func<TId, bool> getFromRepo)
|
||||
/// <inheritdoc />
|
||||
public override bool Exists(TId id, Func<TId, bool> performExists, Func<TId[], IEnumerable<TEntity>> performGetAll)
|
||||
{
|
||||
if (getFromRepo == null) throw new ArgumentNullException("getFromRepo");
|
||||
|
||||
var cacheKey = GetCacheIdKey(id);
|
||||
// if found in cache the return else check
|
||||
var cacheKey = GetEntityCacheKey(id);
|
||||
var fromCache = Cache.GetCacheItem<TEntity>(cacheKey);
|
||||
return fromCache != null || getFromRepo(id);
|
||||
return fromCache != null || performExists(id);
|
||||
}
|
||||
|
||||
public override TEntity[] GetAll(TId[] ids, Func<TId[], IEnumerable<TEntity>> getFromRepo)
|
||||
/// <inheritdoc />
|
||||
public override TEntity[] GetAll(TId[] ids, Func<TId[], IEnumerable<TEntity>> performGetAll)
|
||||
{
|
||||
if (getFromRepo == null) throw new ArgumentNullException("getFromRepo");
|
||||
|
||||
if (ids.Any())
|
||||
if (ids.Length > 0)
|
||||
{
|
||||
var entities = ids.Select(Get).ToArray();
|
||||
if (ids.Length.Equals(entities.Length) && entities.Any(x => x == null) == false)
|
||||
return entities;
|
||||
// try to get each entity from the cache
|
||||
// if we can find all of them, return
|
||||
var entities = ids.Select(GetCached).WhereNotNull().ToArray();
|
||||
if (ids.Length.Equals(entities.Length))
|
||||
return entities; // no need for null checks, we are not caching nulls
|
||||
}
|
||||
else
|
||||
{
|
||||
var allEntities = GetAllFromCache();
|
||||
if (allEntities.Any())
|
||||
// get everything we have
|
||||
var entities = Cache.GetCacheItemsByKeySearch<TEntity>(GetEntityTypeCacheKey())
|
||||
.ToArray(); // no need for null checks, we are not caching nulls
|
||||
|
||||
if (entities.Length > 0)
|
||||
{
|
||||
// if some of them were in the cache...
|
||||
if (_options.GetAllCacheValidateCount)
|
||||
{
|
||||
//Get count of all entities of current type (TEntity) to ensure cached result is correct
|
||||
// need to validate the count, get the actual count and return if ok
|
||||
var totalCount = _options.PerformCount();
|
||||
if (allEntities.Length == totalCount)
|
||||
return allEntities;
|
||||
if (entities.Length == totalCount)
|
||||
return entities;
|
||||
}
|
||||
else
|
||||
{
|
||||
return allEntities;
|
||||
// no need to validate, just return what we have and assume it's all there is
|
||||
return entities;
|
||||
}
|
||||
}
|
||||
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[] {};
|
||||
}
|
||||
// if none of them were in the cache
|
||||
// and we allow zero count - check for the special (empty) entry
|
||||
var empty = Cache.GetCacheItem<TEntity[]>(GetEntityTypeCacheKey());
|
||||
if (empty != null) return empty;
|
||||
}
|
||||
}
|
||||
|
||||
//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()
|
||||
// cache failed, get from repo and cache
|
||||
var repoEntities = performGetAll(ids)
|
||||
.WhereNotNull() // exclude nulls!
|
||||
.Where(x => x.HasIdentity) // be safe, though would be weird...
|
||||
.ToArray();
|
||||
|
||||
//set the disposal action
|
||||
SetCacheAction(ids, entityCollection);
|
||||
// note: if empty & allow zero count, will cache a special (empty) entry
|
||||
InsertEntities(ids, repoEntities);
|
||||
|
||||
return entityCollection;
|
||||
return repoEntities;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up the zero count cache, must return null if it doesn't exist
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
protected bool HasZeroCountCache()
|
||||
/// <inheritdoc />
|
||||
public override void ClearAll()
|
||||
{
|
||||
var zeroCount = Cache.GetCacheItem<TEntity[]>(GetCacheTypeKey());
|
||||
return (zeroCount != null && zeroCount.Any() == false);
|
||||
// fixme the cache should NOT contain anything else so we can clean all, can't we?
|
||||
Cache.ClearAllCache();
|
||||
//Cache.ClearCacheByKeySearch(GetEntityTypeCacheKey());
|
||||
}
|
||||
|
||||
/// <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);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,27 +0,0 @@
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3,227 +3,178 @@ using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Umbraco.Core.Collections;
|
||||
using Umbraco.Core.Models.EntityBase;
|
||||
using Umbraco.Core.Scoping;
|
||||
|
||||
namespace Umbraco.Core.Cache
|
||||
{
|
||||
/// <summary>
|
||||
/// A caching policy that caches an entire dataset as a single collection
|
||||
/// Represents a caching policy that caches the entire entities set as a single collection.
|
||||
/// </summary>
|
||||
/// <typeparam name="TEntity"></typeparam>
|
||||
/// <typeparam name="TId"></typeparam>
|
||||
/// <typeparam name="TEntity">The type of the entity.</typeparam>
|
||||
/// <typeparam name="TId">The type of the identifier.</typeparam>
|
||||
/// <remarks>
|
||||
/// <para>Caches the entire set of entities as a single collection.</para>
|
||||
/// <para>Used by Content-, Media- and MemberTypeRepository, DataTypeRepository, DomainRepository,
|
||||
/// LanguageRepository, PublicAccessRepository, TemplateRepository... things that make sense to
|
||||
/// keep as a whole in memory.</para>
|
||||
/// </remarks>
|
||||
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 Func<TEntity, TId> _entityGetId;
|
||||
private readonly bool _expires;
|
||||
|
||||
public FullDataSetRepositoryCachePolicy(IRuntimeCacheProvider cache, Func<TEntity, TId> getEntityId, Func<IEnumerable<TEntity>> getAllFromRepo, bool expires)
|
||||
public FullDataSetRepositoryCachePolicy(IRuntimeCacheProvider cache, Func<TEntity, TId> entityGetId, bool expires)
|
||||
: base(cache)
|
||||
{
|
||||
_getEntityId = getEntityId;
|
||||
_getAllFromRepo = getAllFromRepo;
|
||||
_entityGetId = entityGetId;
|
||||
_expires = expires;
|
||||
}
|
||||
|
||||
private bool? _hasZeroCountCache;
|
||||
public override IRepositoryCachePolicy<TEntity, TId> Scoped(IRuntimeCacheProvider runtimeCache, IScope scope)
|
||||
{
|
||||
return new ScopedRepositoryCachePolicy<TEntity, TId>(this, runtimeCache, scope);
|
||||
}
|
||||
|
||||
protected static readonly TId[] EmptyIds = new TId[0]; // const
|
||||
|
||||
protected string GetCacheTypeKey()
|
||||
protected string GetEntityTypeCacheKey()
|
||||
{
|
||||
return string.Format("uRepo_{0}_", typeof(TEntity).Name);
|
||||
}
|
||||
|
||||
public override void CreateOrUpdate(TEntity entity, Action<TEntity> persistMethod)
|
||||
protected void InsertEntities(TEntity[] entities)
|
||||
{
|
||||
if (entity == null) throw new ArgumentNullException("entity");
|
||||
if (persistMethod == null) throw new ArgumentNullException("persistMethod");
|
||||
// cache is expected to be a deep-cloning cache ie it deep-clones whatever is
|
||||
// IDeepCloneable when it goes in, and out. it also resets dirty properties,
|
||||
// making sure that no 'dirty' entity is cached.
|
||||
//
|
||||
// this policy is caching the entire list of entities. to ensure that entities
|
||||
// are properly deep-clones when cached, it uses a DeepCloneableList. however,
|
||||
// we don't want to deep-clone *each* entity in the list when fetching it from
|
||||
// cache as that would not be efficient for Get(id). so the DeepCloneableList is
|
||||
// set to ListCloneBehavior.CloneOnce ie it will clone *once* when inserting,
|
||||
// and then will *not* clone when retrieving.
|
||||
|
||||
try
|
||||
if (_expires)
|
||||
{
|
||||
persistMethod(entity);
|
||||
|
||||
//set the disposal action
|
||||
SetCacheAction(() =>
|
||||
{
|
||||
//Clear all
|
||||
Cache.ClearCacheItem(GetCacheTypeKey());
|
||||
});
|
||||
Cache.InsertCacheItem(GetEntityTypeCacheKey(), () => new DeepCloneableList<TEntity>(entities), TimeSpan.FromMinutes(5), true);
|
||||
}
|
||||
catch
|
||||
else
|
||||
{
|
||||
//set the disposal action
|
||||
SetCacheAction(() =>
|
||||
{
|
||||
//Clear all
|
||||
Cache.ClearCacheItem(GetCacheTypeKey());
|
||||
});
|
||||
throw;
|
||||
Cache.InsertCacheItem(GetEntityTypeCacheKey(), () => new DeepCloneableList<TEntity>(entities));
|
||||
}
|
||||
}
|
||||
|
||||
public override void Remove(TEntity entity, Action<TEntity> persistMethod)
|
||||
/// <inheritdoc />
|
||||
public override void Create(TEntity entity, Action<TEntity> persistNew)
|
||||
{
|
||||
if (entity == null) throw new ArgumentNullException("entity");
|
||||
if (persistMethod == null) throw new ArgumentNullException("persistMethod");
|
||||
|
||||
try
|
||||
{
|
||||
persistMethod(entity);
|
||||
persistNew(entity);
|
||||
}
|
||||
finally
|
||||
{
|
||||
//set the disposal action
|
||||
SetCacheAction(() =>
|
||||
{
|
||||
//Clear all
|
||||
Cache.ClearCacheItem(GetCacheTypeKey());
|
||||
});
|
||||
ClearAll();
|
||||
}
|
||||
}
|
||||
|
||||
public override TEntity Get(TId id, Func<TId, TEntity> getFromRepo)
|
||||
/// <inheritdoc />
|
||||
public override void Update(TEntity entity, Action<TEntity> persistUpdated)
|
||||
{
|
||||
//Force get all with cache
|
||||
var found = GetAll(new TId[] { }, ids => _getAllFromRepo().WhereNotNull());
|
||||
if (entity == null) throw new ArgumentNullException("entity");
|
||||
|
||||
//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())
|
||||
try
|
||||
{
|
||||
return allEntities;
|
||||
persistUpdated(entity);
|
||||
}
|
||||
|
||||
//check the zero count cache
|
||||
if (HasZeroCountCache())
|
||||
finally
|
||||
{
|
||||
//there is a zero count cache so return an empty list
|
||||
return new TEntity[] { };
|
||||
ClearAll();
|
||||
}
|
||||
|
||||
//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)
|
||||
/// <inheritdoc />
|
||||
public override void Delete(TEntity entity, Action<TEntity> persistDeleted)
|
||||
{
|
||||
//No-op
|
||||
}
|
||||
if (entity == null) throw new ArgumentNullException("entity");
|
||||
|
||||
/// <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(() =>
|
||||
try
|
||||
{
|
||||
//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));
|
||||
}
|
||||
});
|
||||
persistDeleted(entity);
|
||||
}
|
||||
finally
|
||||
{
|
||||
ClearAll();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up the zero count cache, must return null if it doesn't exist
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
protected bool HasZeroCountCache()
|
||||
/// <inheritdoc />
|
||||
public override TEntity Get(TId id, Func<TId, TEntity> performGet, Func<TId[], IEnumerable<TEntity>> performGetAll)
|
||||
{
|
||||
if (_hasZeroCountCache.HasValue)
|
||||
return _hasZeroCountCache.Value;
|
||||
// get all from the cache, then look for the entity
|
||||
var all = GetAllCached(performGetAll);
|
||||
var entity = all.FirstOrDefault(x => _entityGetId(x).Equals(id));
|
||||
|
||||
_hasZeroCountCache = Cache.GetCacheItem<DeepCloneableList<TEntity>>(GetCacheTypeKey()) != null;
|
||||
return _hasZeroCountCache.Value;
|
||||
// see note in InsertEntities - what we get here is the original
|
||||
// cached entity, not a clone, so we need to manually ensure it is deep-cloned.
|
||||
return entity == null ? null : (TEntity) entity.DeepClone();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This policy will cache the full data set as a single collection
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
protected TEntity[] GetAllFromCache()
|
||||
/// <inheritdoc />
|
||||
public override TEntity GetCached(TId id)
|
||||
{
|
||||
var found = Cache.GetCacheItem<DeepCloneableList<TEntity>>(GetCacheTypeKey());
|
||||
// get all from the cache -- and only the cache, then look for the entity
|
||||
var all = Cache.GetCacheItem<DeepCloneableList<TEntity>>(GetEntityTypeCacheKey());
|
||||
var entity = all == null ? null : all.FirstOrDefault(x => _entityGetId(x).Equals(id));
|
||||
|
||||
//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();
|
||||
// see note in InsertEntities - what we get here is the original
|
||||
// cached entity, not a clone, so we need to manually ensure it is deep-cloned.
|
||||
return entity == null ? null : (TEntity)entity.DeepClone();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool Exists(TId id, Func<TId, bool> performExits, Func<TId[], IEnumerable<TEntity>> performGetAll)
|
||||
{
|
||||
// get all as one set, then look for the entity
|
||||
var all = GetAllCached(performGetAll);
|
||||
return all.Any(x => _entityGetId(x).Equals(id));
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override TEntity[] GetAll(TId[] ids, Func<TId[], IEnumerable<TEntity>> performGetAll)
|
||||
{
|
||||
// get all as one set, from cache if possible, else repo
|
||||
var all = GetAllCached(performGetAll);
|
||||
|
||||
// if ids have been specified, filter
|
||||
if (ids.Length > 0) all = all.Where(x => ids.Contains(_entityGetId(x)));
|
||||
|
||||
// and return
|
||||
// see note in SetCacheActionToInsertEntities - what we get here is the original
|
||||
// cached entities, not clones, so we need to manually ensure they are deep-cloned.
|
||||
return all.Select(x => (TEntity) x.DeepClone()).ToArray();
|
||||
}
|
||||
|
||||
// does NOT clone anything, so be nice with the returned values
|
||||
private IEnumerable<TEntity> GetAllCached(Func<TId[], IEnumerable<TEntity>> performGetAll)
|
||||
{
|
||||
// try the cache first
|
||||
var all = Cache.GetCacheItem<DeepCloneableList<TEntity>>(GetEntityTypeCacheKey());
|
||||
if (all != null) return all.ToArray();
|
||||
|
||||
// else get from repo and cache
|
||||
var entities = performGetAll(EmptyIds).WhereNotNull().ToArray();
|
||||
InsertEntities(entities); // may be an empty array...
|
||||
return entities;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void ClearAll()
|
||||
{
|
||||
Cache.ClearCacheItem(GetEntityTypeCacheKey());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,33 +0,0 @@
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,18 +1,90 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Umbraco.Core.Models.EntityBase;
|
||||
using Umbraco.Core.Scoping;
|
||||
|
||||
namespace Umbraco.Core.Cache
|
||||
{
|
||||
internal interface IRepositoryCachePolicy<TEntity, TId> : IDisposable
|
||||
internal interface IRepositoryCachePolicy<TEntity, TId>
|
||||
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);
|
||||
// note:
|
||||
// at the moment each repository instance creates its corresponding cache policy instance
|
||||
// we could reduce allocations by using static cache policy instances but then we would need
|
||||
// to modify all methods here to pass the repository and cache eg:
|
||||
//
|
||||
// TEntity Get(TRepository repository, IRuntimeCacheProvider cache, TId id);
|
||||
//
|
||||
// it is not *that* complicated but then RepositoryBase needs to have a TRepository generic
|
||||
// type parameter and it all becomes convoluted - keeping it simple for the time being.
|
||||
|
||||
// fixme explain
|
||||
IRepositoryCachePolicy<TEntity, TId> Scoped(IRuntimeCacheProvider runtimeCache, IScope scope);
|
||||
|
||||
/// <summary>
|
||||
/// Gets an entity from the cache, else from the repository.
|
||||
/// </summary>
|
||||
/// <param name="id">The identifier.</param>
|
||||
/// <param name="performGet">The repository PerformGet method.</param>
|
||||
/// <param name="performGetAll">The repository PerformGetAll method.</param>
|
||||
/// <returns>The entity with the specified identifier, if it exits, else null.</returns>
|
||||
/// <remarks>First considers the cache then the repository.</remarks>
|
||||
TEntity Get(TId id, Func<TId, TEntity> performGet, Func<TId[], IEnumerable<TEntity>> performGetAll);
|
||||
|
||||
/// <summary>
|
||||
/// Gets an entity from the cache.
|
||||
/// </summary>
|
||||
/// <param name="id">The identifier.</param>
|
||||
/// <returns>The entity with the specified identifier, if it is in the cache already, else null.</returns>
|
||||
/// <remarks>Does not consider the repository at all.</remarks>
|
||||
TEntity GetCached(TId id);
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether an entity with a specified identifier exists.
|
||||
/// </summary>
|
||||
/// <param name="id">The identifier.</param>
|
||||
/// <param name="performExists">The repository PerformExists method.</param>
|
||||
/// <param name="performGetAll">The repository PerformGetAll method.</param>
|
||||
/// <returns>A value indicating whether an entity with the specified identifier exists.</returns>
|
||||
/// <remarks>First considers the cache then the repository.</remarks>
|
||||
bool Exists(TId id, Func<TId, bool> performExists, Func<TId[], IEnumerable<TEntity>> performGetAll);
|
||||
|
||||
/// <summary>
|
||||
/// Creates an entity.
|
||||
/// </summary>
|
||||
/// <param name="entity">The entity.</param>
|
||||
/// <param name="persistNew">The repository PersistNewItem method.</param>
|
||||
/// <remarks>Creates the entity in the repository, and updates the cache accordingly.</remarks>
|
||||
void Create(TEntity entity, Action<TEntity> persistNew);
|
||||
|
||||
/// <summary>
|
||||
/// Updates an entity.
|
||||
/// </summary>
|
||||
/// <param name="entity">The entity.</param>
|
||||
/// <param name="persistUpdated">The reopsitory PersistUpdatedItem method.</param>
|
||||
/// <remarks>Updates the entity in the repository, and updates the cache accordingly.</remarks>
|
||||
void Update(TEntity entity, Action<TEntity> persistUpdated);
|
||||
|
||||
/// <summary>
|
||||
/// Removes an entity.
|
||||
/// </summary>
|
||||
/// <param name="entity">The entity.</param>
|
||||
/// <param name="persistDeleted">The repository PersistDeletedItem method.</param>
|
||||
/// <remarks>Removes the entity from the repository and clears the cache.</remarks>
|
||||
void Delete(TEntity entity, Action<TEntity> persistDeleted);
|
||||
|
||||
/// <summary>
|
||||
/// Gets entities.
|
||||
/// </summary>
|
||||
/// <param name="ids">The identifiers.</param>
|
||||
/// <param name="performGetAll">The repository PerformGetAll method.</param>
|
||||
/// <returns>If <paramref name="ids"/> is empty, all entities, else the entities with the specified identifiers.</returns>
|
||||
/// <remarks>Get all the entities. Either from the cache or the repository depending on the implementation.</remarks>
|
||||
TEntity[] GetAll(TId[] ids, Func<TId[], IEnumerable<TEntity>> performGetAll);
|
||||
|
||||
/// <summary>
|
||||
/// Clears the entire cache.
|
||||
/// </summary>
|
||||
void ClearAll();
|
||||
}
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
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,62 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Umbraco.Core.Models.EntityBase;
|
||||
using Umbraco.Core.Scoping;
|
||||
|
||||
namespace Umbraco.Core.Cache
|
||||
{
|
||||
internal class NoRepositoryCachePolicy<TEntity, TId> : IRepositoryCachePolicy<TEntity, TId>
|
||||
where TEntity : class, IAggregateRoot
|
||||
{
|
||||
private static readonly NoRepositoryCachePolicy<TEntity, TId> StaticInstance = new NoRepositoryCachePolicy<TEntity, TId>();
|
||||
|
||||
private NoRepositoryCachePolicy()
|
||||
{ }
|
||||
|
||||
public static NoRepositoryCachePolicy<TEntity, TId> Instance { get { return StaticInstance; } }
|
||||
|
||||
public IRepositoryCachePolicy<TEntity, TId> Scoped(IRuntimeCacheProvider runtimeCache, IScope scope)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public TEntity Get(TId id, Func<TId, TEntity> performGet, Func<TId[], IEnumerable<TEntity>> performGetAll)
|
||||
{
|
||||
return performGet(id);
|
||||
}
|
||||
|
||||
public TEntity GetCached(TId id)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
public bool Exists(TId id, Func<TId, bool> performExists, Func<TId[], IEnumerable<TEntity>> performGetAll)
|
||||
{
|
||||
return performExists(id);
|
||||
}
|
||||
|
||||
public void Create(TEntity entity, Action<TEntity> persistNew)
|
||||
{
|
||||
persistNew(entity);
|
||||
}
|
||||
|
||||
public void Update(TEntity entity, Action<TEntity> persistUpdated)
|
||||
{
|
||||
persistUpdated(entity);
|
||||
}
|
||||
|
||||
public void Delete(TEntity entity, Action<TEntity> persistDeleted)
|
||||
{
|
||||
persistDeleted(entity);
|
||||
}
|
||||
|
||||
public TEntity[] GetAll(TId[] ids, Func<TId[], IEnumerable<TEntity>> performGetAll)
|
||||
{
|
||||
return performGetAll(ids).ToArray();
|
||||
}
|
||||
|
||||
public void ClearAll()
|
||||
{ }
|
||||
}
|
||||
}
|
||||
@@ -1,27 +0,0 @@
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,48 +1,51 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Umbraco.Core.Models.EntityBase;
|
||||
using Umbraco.Core.Scoping;
|
||||
|
||||
namespace Umbraco.Core.Cache
|
||||
{
|
||||
internal abstract class RepositoryCachePolicyBase<TEntity, TId> : DisposableObject, IRepositoryCachePolicy<TEntity, TId>
|
||||
/// <summary>
|
||||
/// A base class for repository cache policies.
|
||||
/// </summary>
|
||||
/// <typeparam name="TEntity">The type of the entity.</typeparam>
|
||||
/// <typeparam name="TId">The type of the identifier.</typeparam>
|
||||
internal abstract class RepositoryCachePolicyBase<TEntity, TId> : IRepositoryCachePolicy<TEntity, TId>
|
||||
where TEntity : class, IAggregateRoot
|
||||
{
|
||||
private Action _action;
|
||||
|
||||
protected RepositoryCachePolicyBase(IRuntimeCacheProvider cache)
|
||||
{
|
||||
if (cache == null) throw new ArgumentNullException("cache");
|
||||
|
||||
if (cache == null) throw new ArgumentNullException("cache");
|
||||
Cache = cache;
|
||||
}
|
||||
|
||||
public abstract IRepositoryCachePolicy<TEntity, TId> Scoped(IRuntimeCacheProvider runtimeCache, IScope scope);
|
||||
|
||||
protected IRuntimeCacheProvider Cache { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// The disposal performs the caching
|
||||
/// </summary>
|
||||
protected override void DisposeResources()
|
||||
{
|
||||
if (_action != null)
|
||||
{
|
||||
_action();
|
||||
}
|
||||
}
|
||||
/// <inheritdoc />
|
||||
public abstract TEntity Get(TId id, Func<TId, TEntity> performGet, Func<TId[], IEnumerable<TEntity>> performGetAll);
|
||||
|
||||
/// <summary>
|
||||
/// Sets the action to execute on disposal
|
||||
/// </summary>
|
||||
/// <param name="action"></param>
|
||||
protected void SetCacheAction(Action action)
|
||||
{
|
||||
_action = action;
|
||||
}
|
||||
/// <inheritdoc />
|
||||
public abstract TEntity GetCached(TId id);
|
||||
|
||||
/// <inheritdoc />
|
||||
public abstract bool Exists(TId id, Func<TId, bool> performExists, Func<TId[], IEnumerable<TEntity>> performGetAll);
|
||||
|
||||
/// <inheritdoc />
|
||||
public abstract void Create(TEntity entity, Action<TEntity> persistNew);
|
||||
|
||||
/// <inheritdoc />
|
||||
public abstract void Update(TEntity entity, Action<TEntity> persistUpdated);
|
||||
|
||||
/// <inheritdoc />
|
||||
public abstract void Delete(TEntity entity, Action<TEntity> persistDeleted);
|
||||
|
||||
/// <inheritdoc />
|
||||
public abstract TEntity[] GetAll(TId[] ids, Func<TId[], IEnumerable<TEntity>> performGetAll);
|
||||
|
||||
/// <inheritdoc />
|
||||
public abstract void ClearAll();
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
@@ -2,6 +2,9 @@ using System;
|
||||
|
||||
namespace Umbraco.Core.Cache
|
||||
{
|
||||
/// <summary>
|
||||
/// Specifies how a repository cache policy should cache entities.
|
||||
/// </summary>
|
||||
internal class RepositoryCachePolicyOptions
|
||||
{
|
||||
/// <summary>
|
||||
|
||||
@@ -0,0 +1,75 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Umbraco.Core.Models.EntityBase;
|
||||
using Umbraco.Core.Scoping;
|
||||
|
||||
namespace Umbraco.Core.Cache
|
||||
{
|
||||
internal class ScopedRepositoryCachePolicy<TEntity, TId> : IRepositoryCachePolicy<TEntity, TId>
|
||||
where TEntity : class, IAggregateRoot
|
||||
{
|
||||
private readonly IRepositoryCachePolicy<TEntity, TId> _cachePolicy;
|
||||
private readonly IRuntimeCacheProvider _globalIsolatedCache;
|
||||
private readonly IScope _scope;
|
||||
|
||||
public ScopedRepositoryCachePolicy(IRepositoryCachePolicy<TEntity, TId> cachePolicy, IRuntimeCacheProvider globalIsolatedCache, IScope scope)
|
||||
{
|
||||
_cachePolicy = cachePolicy;
|
||||
_globalIsolatedCache = globalIsolatedCache;
|
||||
_scope = scope;
|
||||
}
|
||||
|
||||
public IRepositoryCachePolicy<TEntity, TId> Scoped(IRuntimeCacheProvider runtimeCache, IScope scope)
|
||||
{
|
||||
throw new InvalidOperationException(); // obviously
|
||||
}
|
||||
|
||||
public TEntity Get(TId id, Func<TId, TEntity> performGet, Func<TId[], IEnumerable<TEntity>> performGetAll)
|
||||
{
|
||||
// loads into the local cache only, ok for now
|
||||
return _cachePolicy.Get(id, performGet, performGetAll);
|
||||
}
|
||||
|
||||
public TEntity GetCached(TId id)
|
||||
{
|
||||
// loads into the local cache only, ok for now
|
||||
return _cachePolicy.GetCached(id);
|
||||
}
|
||||
|
||||
public bool Exists(TId id, Func<TId, bool> performExists, Func<TId[], IEnumerable<TEntity>> performGetAll)
|
||||
{
|
||||
// loads into the local cache only, ok for now
|
||||
return _cachePolicy.Exists(id, performExists, performGetAll);
|
||||
}
|
||||
|
||||
public void Create(TEntity entity, Action<TEntity> persistNew)
|
||||
{
|
||||
// writes into the local cache
|
||||
_cachePolicy.Create(entity, persistNew);
|
||||
}
|
||||
|
||||
public void Update(TEntity entity, Action<TEntity> persistUpdated)
|
||||
{
|
||||
// writes into the local cache
|
||||
_cachePolicy.Update(entity, persistUpdated);
|
||||
}
|
||||
|
||||
public void Delete(TEntity entity, Action<TEntity> persistDeleted)
|
||||
{
|
||||
// deletes the local cache
|
||||
_cachePolicy.Delete(entity, persistDeleted);
|
||||
}
|
||||
|
||||
public TEntity[] GetAll(TId[] ids, Func<TId[], IEnumerable<TEntity>> performGetAll)
|
||||
{
|
||||
// loads into the local cache only, ok for now
|
||||
return _cachePolicy.GetAll(ids, performGetAll);
|
||||
}
|
||||
|
||||
public void ClearAll()
|
||||
{
|
||||
// clears the local cache
|
||||
_cachePolicy.ClearAll();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,24 +1,27 @@
|
||||
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
|
||||
/// Represents a special policy that does not cache the result of GetAll.
|
||||
/// </summary>
|
||||
/// <typeparam name="TEntity"></typeparam>
|
||||
/// <typeparam name="TId"></typeparam>
|
||||
/// <typeparam name="TEntity">The type of the entity.</typeparam>
|
||||
/// <typeparam name="TId">The type of the identifier.</typeparam>
|
||||
/// <remarks>
|
||||
/// <para>Overrides the default repository cache policy and does not writes the result of GetAll
|
||||
/// to cache, but only the result of individual Gets. It does read the cache for GetAll, though.</para>
|
||||
/// <para>Used by DictionaryRepository.</para>
|
||||
/// </remarks>
|
||||
internal class SingleItemsOnlyRepositoryCachePolicy<TEntity, TId> : DefaultRepositoryCachePolicy<TEntity, TId>
|
||||
where TEntity : class, IAggregateRoot
|
||||
{
|
||||
public SingleItemsOnlyRepositoryCachePolicy(IRuntimeCacheProvider cache, RepositoryCachePolicyOptions options) : base(cache, options)
|
||||
public SingleItemsOnlyRepositoryCachePolicy(IRuntimeCacheProvider cache, RepositoryCachePolicyOptions options)
|
||||
: base(cache, options)
|
||||
{ }
|
||||
|
||||
protected override void InsertEntities(TId[] ids, TEntity[] entities)
|
||||
{
|
||||
}
|
||||
|
||||
protected override void SetCacheAction(TId[] ids, TEntity[] entityCollection)
|
||||
{
|
||||
//no-op
|
||||
// nop
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -21,6 +21,10 @@ namespace Umbraco.Core
|
||||
private static readonly ICacheProvider NullRequestCache = new NullCacheProvider();
|
||||
private static readonly ICacheProvider NullStaticCache = new NullCacheProvider();
|
||||
private static readonly IRuntimeCacheProvider NullRuntimeCache = new NullCacheProvider();
|
||||
private static readonly IsolatedRuntimeCache NullIsolatedCache = new IsolatedRuntimeCache(_ => NullRuntimeCache);
|
||||
private static readonly CacheHelper NullCache = new CacheHelper(NullRuntimeCache, NullStaticCache, NullRequestCache, NullIsolatedCache);
|
||||
|
||||
public static CacheHelper NoCache { get { return NullCache; } }
|
||||
|
||||
/// <summary>
|
||||
/// Creates a cache helper with disabled caches
|
||||
@@ -31,7 +35,10 @@ namespace Umbraco.Core
|
||||
/// </remarks>
|
||||
public static CacheHelper CreateDisabledCacheHelper()
|
||||
{
|
||||
return new CacheHelper(NullRuntimeCache, NullStaticCache, NullRequestCache, new IsolatedRuntimeCache(t => NullRuntimeCache));
|
||||
// do *not* return NoCache
|
||||
// NoCache is a special instance that is detected by RepositoryBase and disables all cache policies
|
||||
// CreateDisabledCacheHelper is used in tests to use no cache, *but* keep all cache policies
|
||||
return new CacheHelper(NullRuntimeCache, NullStaticCache, NullRequestCache, NullIsolatedCache);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
using System;
|
||||
|
||||
namespace Umbraco.Core.CodeAnnotations
|
||||
{
|
||||
[AttributeUsage(AttributeTargets.Field, AllowMultiple = false, Inherited = false)]
|
||||
internal class UmbracoUdiTypeAttribute : Attribute
|
||||
{
|
||||
public string UdiType { get; private set; }
|
||||
|
||||
public UmbracoUdiTypeAttribute(string udiType)
|
||||
{
|
||||
UdiType = udiType;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -159,6 +159,12 @@ namespace Umbraco.Core.Configuration.UmbracoSettings
|
||||
get { return GetOptionalTextElement("defaultDocumentTypeProperty", "Textstring"); }
|
||||
}
|
||||
|
||||
[ConfigurationProperty("showDeprecatedPropertyEditors")]
|
||||
internal InnerTextConfigurationElement<bool> ShowDeprecatedPropertyEditors
|
||||
{
|
||||
get { return GetOptionalTextElement("showDeprecatedPropertyEditors", false); }
|
||||
}
|
||||
|
||||
[ConfigurationProperty("EnableInheritedDocumentTypes")]
|
||||
internal InnerTextConfigurationElement<bool> EnableInheritedDocumentTypes
|
||||
{
|
||||
@@ -306,6 +312,11 @@ namespace Umbraco.Core.Configuration.UmbracoSettings
|
||||
get { return DefaultDocumentTypeProperty; }
|
||||
}
|
||||
|
||||
bool IContentSection.ShowDeprecatedPropertyEditors
|
||||
{
|
||||
get { return ShowDeprecatedPropertyEditors; }
|
||||
}
|
||||
|
||||
bool IContentSection.EnableInheritedDocumentTypes
|
||||
{
|
||||
get { return EnableInheritedDocumentTypes; }
|
||||
|
||||
@@ -60,6 +60,12 @@ namespace Umbraco.Core.Configuration.UmbracoSettings
|
||||
|
||||
string DefaultDocumentTypeProperty { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The default for this is false but if you would like deprecated property editors displayed
|
||||
/// in the data type editor you can enable this
|
||||
/// </summary>
|
||||
bool ShowDeprecatedPropertyEditors { get; }
|
||||
|
||||
bool EnableInheritedDocumentTypes { get; }
|
||||
|
||||
bool EnableInheritedMediaTypes { get; }
|
||||
|
||||
@@ -24,7 +24,7 @@ namespace Umbraco.Core.Configuration
|
||||
/// Gets the version comment (like beta or RC).
|
||||
/// </summary>
|
||||
/// <value>The version comment.</value>
|
||||
public static string CurrentComment { get { return "alpha055"; } }
|
||||
public static string CurrentComment { get { return "alpha066"; } }
|
||||
|
||||
// Get the version of the umbraco.dll by looking at a class in that dll
|
||||
// Had to do it like this due to medium trust issues, see: http://haacked.com/archive/2010/11/04/assembly-location-and-medium-trust.aspx
|
||||
|
||||
@@ -112,9 +112,15 @@
|
||||
|
||||
public const string Languages = "languages";
|
||||
|
||||
public const string PartialViews = "partialViews";
|
||||
|
||||
public const string PartialViewMacros = "partialViewMacros";
|
||||
|
||||
public const string Scripts = "scripts";
|
||||
|
||||
//TODO: Fill in the rest!
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -109,6 +109,11 @@ namespace Umbraco.Core
|
||||
/// </summary>
|
||||
public const string Member = "39EB0F98-B348-42A1-8662-E7EB18487560";
|
||||
|
||||
/// <summary>
|
||||
/// Guid for a Media Type object.
|
||||
/// </summary>
|
||||
public static readonly Guid MemberGuid = new Guid(Member);
|
||||
|
||||
/// <summary>
|
||||
/// Guid for a Member Group object.
|
||||
/// </summary>
|
||||
@@ -162,6 +167,36 @@ namespace Umbraco.Core
|
||||
/// Guid for a relation type.
|
||||
/// </summary>
|
||||
public static readonly Guid RelationTypeGuid = new Guid(RelationType);
|
||||
|
||||
/// <summary>
|
||||
/// Guid for a Forms Form.
|
||||
/// </summary>
|
||||
public const string FormsForm = "F5A9F787-6593-46F0-B8FF-BFD9BCA9F6BB";
|
||||
|
||||
/// <summary>
|
||||
/// Guid for a Forms Form.
|
||||
/// </summary>
|
||||
public static readonly Guid FormsFormGuid = new Guid(FormsForm);
|
||||
|
||||
/// <summary>
|
||||
/// Guid for a Forms PreValue Source.
|
||||
/// </summary>
|
||||
public const string FormsPreValue = "42D7BF9B-A362-4FEE-B45A-674D5C064B70";
|
||||
|
||||
/// <summary>
|
||||
/// Guid for a Forms PreValue Source.
|
||||
/// </summary>
|
||||
public static readonly Guid FormsPreValueGuid = new Guid(FormsPreValue);
|
||||
|
||||
/// <summary>
|
||||
/// Guid for a Forms DataSource.
|
||||
/// </summary>
|
||||
public const string FormsDataSource = "CFED6CE4-9359-443E-9977-9956FEB1D867";
|
||||
|
||||
/// <summary>
|
||||
/// Guid for a Forms DataSource.
|
||||
/// </summary>
|
||||
public static readonly Guid FormsDataSourceGuid = new Guid(FormsDataSource);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -42,10 +42,14 @@ namespace Umbraco.Core
|
||||
[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 ContentPicker = "158AA029-24ED-4948-939E-C3DA209E5FBA";
|
||||
|
||||
|
||||
[Obsolete("This is an obsoleted content picker, use ContentPicker2Alias instead")]
|
||||
public const string ContentPickerAlias = "Umbraco.ContentPickerAlias";
|
||||
|
||||
/// <summary>
|
||||
/// Alias for the Content Picker datatype.
|
||||
/// </summary>
|
||||
public const string ContentPickerAlias = "Umbraco.ContentPickerAlias";
|
||||
public const string ContentPicker2Alias = "Umbraco.ContentPicker2";
|
||||
|
||||
/// <summary>
|
||||
/// Guid for the Date datatype.
|
||||
@@ -192,11 +196,15 @@ namespace Umbraco.Core
|
||||
[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 MediaPicker = "EAD69342-F06D-4253-83AC-28000225583B";
|
||||
|
||||
[Obsolete("This is an obsoleted picker, use MediaPicker2Alias instead")]
|
||||
public const string MediaPickerAlias = "Umbraco.MediaPicker";
|
||||
|
||||
/// <summary>
|
||||
/// Alias for the Media Picker datatype.
|
||||
/// </summary>
|
||||
public const string MediaPickerAlias = "Umbraco.MediaPicker";
|
||||
public const string MediaPicker2Alias = "Umbraco.MediaPicker2";
|
||||
|
||||
[Obsolete("This is an obsoleted picker, use MediaPicker2Alias instead")]
|
||||
public const string MultipleMediaPickerAlias = "Umbraco.MultipleMediaPicker";
|
||||
|
||||
/// <summary>
|
||||
@@ -205,26 +213,32 @@ namespace Umbraco.Core
|
||||
[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 MemberPicker = "39F533E4-0551-4505-A64B-E0425C5CE775";
|
||||
|
||||
[Obsolete("This is an obsoleted picker, use MemberPicker2Alias instead")]
|
||||
public const string MemberPickerAlias = "Umbraco.MemberPicker";
|
||||
|
||||
/// <summary>
|
||||
/// Alias for the Member Picker datatype.
|
||||
/// </summary>
|
||||
public const string MemberPickerAlias = "Umbraco.MemberPicker";
|
||||
public const string MemberPicker2Alias = "Umbraco.MemberPicker2";
|
||||
|
||||
/// <summary>
|
||||
/// Alias for the Member Group Picker datatype.
|
||||
/// </summary>
|
||||
public const string MemberGroupPickerAlias = "Umbraco.MemberGroupPicker";
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Guid for the Multi-Node Tree Picker 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 MultiNodeTreePicker = "7E062C13-7C41-4AD9-B389-41D88AEEF87C";
|
||||
|
||||
[Obsolete("This is an obsoleted picker, use MultiNodeTreePicker2Alias instead")]
|
||||
public const string MultiNodeTreePickerAlias = "Umbraco.MultiNodeTreePicker";
|
||||
|
||||
/// <summary>
|
||||
/// Alias for the Multi-Node Tree Picker datatype
|
||||
/// </summary>
|
||||
public const string MultiNodeTreePickerAlias = "Umbraco.MultiNodeTreePicker";
|
||||
public const string MultiNodeTreePicker2Alias = "Umbraco.MultiNodeTreePicker2";
|
||||
|
||||
/// <summary>
|
||||
/// Guid for the Multiple Textstring datatype.
|
||||
|
||||
@@ -28,6 +28,7 @@ using Umbraco.Core.PropertyEditors.ValueConverters;
|
||||
using Umbraco.Core.Publishing;
|
||||
using Umbraco.Core.Macros;
|
||||
using Umbraco.Core.Manifest;
|
||||
using Umbraco.Core.Scoping;
|
||||
using Umbraco.Core.Services;
|
||||
using Umbraco.Core.Sync;
|
||||
using Umbraco.Core.Strings;
|
||||
@@ -108,8 +109,11 @@ namespace Umbraco.Core
|
||||
var dbFactory = new DefaultDatabaseFactory(Constants.System.UmbracoConnectionName, ProfilingLogger.Logger);
|
||||
Database.Mapper = new PetaPocoMapper();
|
||||
|
||||
var scopeProvider = new ScopeProvider(dbFactory);
|
||||
dbFactory.ScopeProvider = scopeProvider;
|
||||
|
||||
var dbContext = new DatabaseContext(
|
||||
dbFactory,
|
||||
scopeProvider,
|
||||
ProfilingLogger.Logger,
|
||||
SqlSyntaxProviders.CreateDefault(ProfilingLogger.Logger));
|
||||
|
||||
@@ -117,7 +121,7 @@ namespace Umbraco.Core
|
||||
dbContext.Initialize();
|
||||
|
||||
//get the service context
|
||||
var serviceContext = CreateServiceContext(dbContext, dbFactory);
|
||||
var serviceContext = CreateServiceContext(dbContext, scopeProvider);
|
||||
|
||||
//set property and singleton from response
|
||||
ApplicationContext.Current = ApplicationContext = CreateApplicationContext(dbContext, serviceContext);
|
||||
@@ -160,17 +164,16 @@ namespace Umbraco.Core
|
||||
/// Creates and returns the service context for the app
|
||||
/// </summary>
|
||||
/// <param name="dbContext"></param>
|
||||
/// <param name="dbFactory"></param>
|
||||
/// <param name="scopeProvider"></param>
|
||||
/// <returns></returns>
|
||||
protected virtual ServiceContext CreateServiceContext(DatabaseContext dbContext, IDatabaseFactory dbFactory)
|
||||
protected virtual ServiceContext CreateServiceContext(DatabaseContext dbContext, IScopeProvider scopeProvider)
|
||||
{
|
||||
//default transient factory
|
||||
var msgFactory = new TransientMessagesFactory();
|
||||
return new ServiceContext(
|
||||
new RepositoryFactory(ApplicationCache, ProfilingLogger.Logger, dbContext.SqlSyntax, UmbracoConfig.For.UmbracoSettings()),
|
||||
new PetaPocoUnitOfWorkProvider(dbFactory),
|
||||
new FileUnitOfWorkProvider(),
|
||||
new PublishingStrategy(msgFactory, ProfilingLogger.Logger),
|
||||
new PetaPocoUnitOfWorkProvider(scopeProvider),
|
||||
new FileUnitOfWorkProvider(scopeProvider),
|
||||
ApplicationCache,
|
||||
ProfilingLogger.Logger,
|
||||
msgFactory);
|
||||
|
||||
@@ -14,6 +14,7 @@ using Umbraco.Core.Persistence;
|
||||
using Umbraco.Core.Persistence.Migrations;
|
||||
using Umbraco.Core.Persistence.Migrations.Initial;
|
||||
using Umbraco.Core.Persistence.SqlSyntax;
|
||||
using Umbraco.Core.Scoping;
|
||||
using Umbraco.Core.Services;
|
||||
|
||||
namespace Umbraco.Core
|
||||
@@ -26,7 +27,7 @@ namespace Umbraco.Core
|
||||
/// </remarks>
|
||||
public class DatabaseContext
|
||||
{
|
||||
private readonly IDatabaseFactory _factory;
|
||||
internal readonly IScopeProviderInternal ScopeProvider;
|
||||
private readonly ILogger _logger;
|
||||
private readonly SqlSyntaxProviders _syntaxProviders;
|
||||
private bool _configured;
|
||||
@@ -40,6 +41,19 @@ namespace Umbraco.Core
|
||||
/// </summary>
|
||||
private const int ConnectionCheckMinutes = 1;
|
||||
|
||||
#region Compatibility with 7.5
|
||||
|
||||
// note: the ctors accepting IDatabaseFactory are here only for backward compatibility purpose
|
||||
//
|
||||
// problem: IDatabaseFactory2 adds the CreateNewDatabase() method which creates a new database
|
||||
// 'cos IDatabaseFactory CreateDatabase() is supposed to also manage the ambient thing. We
|
||||
// want to keep these ctors for backward compatibility reasons (in case ppl use them in tests)
|
||||
// so we need to create a scope provider (else nothing would work) and so we need a IDatabaseFactory2,
|
||||
// so...?
|
||||
// solution: wrap IDatabaseFactory and pretend we have a IDatabaseFactory2, it *should* work in most
|
||||
// cases but really, it depends on what ppl are doing in their tests... yet, cannot really see any
|
||||
// other way to do it?
|
||||
|
||||
[Obsolete("Use the constructor specifying all dependencies instead")]
|
||||
public DatabaseContext(IDatabaseFactory factory)
|
||||
: this(factory, LoggerResolver.Current.Logger, new SqlSyntaxProviders(new ISqlSyntaxProvider[]
|
||||
@@ -48,8 +62,7 @@ namespace Umbraco.Core
|
||||
new SqlCeSyntaxProvider(),
|
||||
new SqlServerSyntaxProvider()
|
||||
}))
|
||||
{
|
||||
}
|
||||
{ }
|
||||
|
||||
/// <summary>
|
||||
/// Default constructor
|
||||
@@ -63,7 +76,11 @@ namespace Umbraco.Core
|
||||
if (logger == null) throw new ArgumentNullException("logger");
|
||||
if (syntaxProviders == null) throw new ArgumentNullException("syntaxProviders");
|
||||
|
||||
_factory = factory;
|
||||
var asDbFactory2 = factory as IDatabaseFactory2;
|
||||
ScopeProvider = asDbFactory2 == null
|
||||
? new ScopeProvider(new DatabaseFactoryWrapper(factory))
|
||||
: new ScopeProvider(asDbFactory2);
|
||||
|
||||
_logger = logger;
|
||||
_syntaxProviders = syntaxProviders;
|
||||
}
|
||||
@@ -80,22 +97,86 @@ namespace Umbraco.Core
|
||||
_providerName = providerName;
|
||||
SqlSyntax = sqlSyntax;
|
||||
SqlSyntaxContext.SqlSyntaxProvider = SqlSyntax;
|
||||
_factory = factory;
|
||||
|
||||
var asDbFactory2 = factory as IDatabaseFactory2;
|
||||
ScopeProvider = asDbFactory2 == null
|
||||
? new ScopeProvider(new DatabaseFactoryWrapper(factory))
|
||||
: new ScopeProvider(asDbFactory2);
|
||||
|
||||
_logger = logger;
|
||||
_configured = true;
|
||||
}
|
||||
|
||||
#if DEBUG_DATABASES
|
||||
public List<UmbracoDatabase> Databases
|
||||
private class DatabaseFactoryWrapper : IDatabaseFactory2
|
||||
{
|
||||
get
|
||||
private readonly IDatabaseFactory _factory;
|
||||
|
||||
public DatabaseFactoryWrapper(IDatabaseFactory factory)
|
||||
{
|
||||
var factory = _factory as DefaultDatabaseFactory;
|
||||
if (factory == null) throw new NotSupportedException();
|
||||
return factory.Databases;
|
||||
_factory = factory;
|
||||
}
|
||||
|
||||
public UmbracoDatabase CreateDatabase()
|
||||
{
|
||||
return _factory.CreateDatabase();
|
||||
}
|
||||
|
||||
public UmbracoDatabase CreateNewDatabase()
|
||||
{
|
||||
return CreateDatabase();
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_factory.Dispose();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#endregion
|
||||
|
||||
[Obsolete("Use the constructor specifying all dependencies instead")]
|
||||
internal DatabaseContext(IScopeProviderInternal scopeProvider)
|
||||
: this(scopeProvider, LoggerResolver.Current.Logger, new SqlSyntaxProviders(new ISqlSyntaxProvider[]
|
||||
{
|
||||
new MySqlSyntaxProvider(LoggerResolver.Current.Logger),
|
||||
new SqlCeSyntaxProvider(),
|
||||
new SqlServerSyntaxProvider()
|
||||
}))
|
||||
{ }
|
||||
|
||||
/// <summary>
|
||||
/// Default constructor
|
||||
/// </summary>
|
||||
/// <param name="scopeProvider"></param>
|
||||
/// <param name="logger"></param>
|
||||
/// <param name="syntaxProviders"></param>
|
||||
internal DatabaseContext(IScopeProviderInternal scopeProvider, ILogger logger, SqlSyntaxProviders syntaxProviders)
|
||||
{
|
||||
if (scopeProvider == null) throw new ArgumentNullException("scopeProvider");
|
||||
if (logger == null) throw new ArgumentNullException("logger");
|
||||
if (syntaxProviders == null) throw new ArgumentNullException("syntaxProviders");
|
||||
|
||||
ScopeProvider = scopeProvider;
|
||||
_logger = logger;
|
||||
_syntaxProviders = syntaxProviders;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a configured DatabaseContext
|
||||
/// </summary>
|
||||
/// <param name="scopeProvider"></param>
|
||||
/// <param name="logger"></param>
|
||||
/// <param name="sqlSyntax"></param>
|
||||
/// <param name="providerName"></param>
|
||||
internal DatabaseContext(IScopeProviderInternal scopeProvider, ILogger logger, ISqlSyntaxProvider sqlSyntax, string providerName)
|
||||
{
|
||||
_providerName = providerName;
|
||||
SqlSyntax = sqlSyntax;
|
||||
SqlSyntaxContext.SqlSyntaxProvider = SqlSyntax;
|
||||
ScopeProvider = scopeProvider;
|
||||
_logger = logger;
|
||||
_configured = true;
|
||||
}
|
||||
|
||||
public ISqlSyntaxProvider SqlSyntax { get; private set; }
|
||||
|
||||
@@ -110,7 +191,17 @@ namespace Umbraco.Core
|
||||
/// </remarks>
|
||||
public virtual UmbracoDatabase Database
|
||||
{
|
||||
get { return _factory.CreateDatabase(); }
|
||||
get
|
||||
{
|
||||
if (IsDatabaseConfigured == false)
|
||||
{
|
||||
throw new InvalidOperationException("Cannot create a database instance, there is no available connection string");
|
||||
}
|
||||
|
||||
return ScopeProvider.GetAmbientOrNoScope().Database;
|
||||
//var scope = ScopeProvider.AmbientScope;
|
||||
//return scope != null ? scope.Database : ScopeProvider.CreateNoScope().Database;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -123,6 +214,8 @@ namespace Umbraco.Core
|
||||
/// will be properly removed from call context and does not interfere with anything else. In most case
|
||||
/// it is not replacing anything, just temporarily installing a database in context.</para>
|
||||
/// </remarks>
|
||||
// fixme - this should just entirely be replaced by Scope?
|
||||
/*
|
||||
public virtual IDisposable UseSafeDatabase(bool force = false)
|
||||
{
|
||||
var factory = _factory as DefaultDatabaseFactory;
|
||||
@@ -138,6 +231,7 @@ namespace Umbraco.Core
|
||||
// create a new, temp, database (will be disposed with UsingDatabase)
|
||||
return new UsingDatabase(null, factory.CreateDatabase());
|
||||
}
|
||||
*/
|
||||
|
||||
/// <summary>
|
||||
/// Boolean indicating whether the database has been configured
|
||||
@@ -159,7 +253,7 @@ namespace Umbraco.Core
|
||||
|
||||
//Don't check again if the timeout period hasn't elapsed
|
||||
//this ensures we don't keep checking the connection too many times in a row like during startup.
|
||||
//Do check if the _connectionLastChecked is null which means we're just initializing or it could
|
||||
//Do check if the _connectionLastChecked is null which means we're just initializing or it could
|
||||
//not connect last time it was checked.
|
||||
if ((_connectionLastChecked.HasValue && (DateTime.Now - _connectionLastChecked.Value).TotalMinutes > ConnectionCheckMinutes)
|
||||
|| _connectionLastChecked.HasValue == false)
|
||||
@@ -202,7 +296,7 @@ namespace Umbraco.Core
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new InvalidOperationException("Can't find a connection string with the name '" + Constants.System.UmbracoConnectionName + "'");
|
||||
throw new NullReferenceException("Can't find a connection string with the name '" + Constants.System.UmbracoConnectionName + "'");
|
||||
}
|
||||
return _providerName;
|
||||
}
|
||||
@@ -809,6 +903,7 @@ namespace Umbraco.Core
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
private class UsingDatabase : IDisposable
|
||||
{
|
||||
private readonly UmbracoDatabase _orig;
|
||||
@@ -831,5 +926,6 @@ namespace Umbraco.Core
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
}
|
||||
*/
|
||||
}
|
||||
}
|
||||
@@ -16,6 +16,7 @@ namespace Umbraco.Core.Deploy
|
||||
if (udi == null)
|
||||
throw new ArgumentNullException("udi");
|
||||
Udi = udi;
|
||||
Name = Udi.ToString();
|
||||
|
||||
Dependencies = dependencies ?? Enumerable.Empty<ArtifactDependency>();
|
||||
_checksum = new Lazy<string>(GetChecksum);
|
||||
@@ -48,5 +49,8 @@ namespace Umbraco.Core.Deploy
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
public string Name { get; set; }
|
||||
public string Alias { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -4,5 +4,8 @@ namespace Umbraco.Core.Deploy
|
||||
/// Represents an artifact ie an object that can be transfered between environments.
|
||||
/// </summary>
|
||||
public interface IArtifact : IArtifactSignature
|
||||
{ }
|
||||
{
|
||||
string Name { get; }
|
||||
string Alias { get; }
|
||||
}
|
||||
}
|
||||
@@ -66,10 +66,20 @@ namespace Umbraco.Core.Deploy
|
||||
/// <returns>The length of the file, or -1 if the file does not exist.</returns>
|
||||
Task<long> GetFileLengthAsync(StringUdi udi, CancellationToken token);
|
||||
|
||||
// fixme - doc
|
||||
void GetFiles(IEnumerable<StringUdi> udis, IFileStore fileStore);
|
||||
/// <summary>
|
||||
/// Gets files and store them using a file store.
|
||||
/// </summary>
|
||||
/// <param name="udis">The udis of the files to get.</param>
|
||||
/// <param name="fileTypes">A collection of file types which can store the files.</param>
|
||||
void GetFiles(IEnumerable<StringUdi> udis, IFileTypeCollection fileTypes);
|
||||
|
||||
Task GetFilesAsync(IEnumerable<StringUdi> udis, IFileStore fileStore, CancellationToken token);
|
||||
/// <summary>
|
||||
/// Gets files and store them using a file store.
|
||||
/// </summary>
|
||||
/// <param name="udis">The udis of the files to get.</param>
|
||||
/// <param name="fileTypes">A collection of file types which can store the files.</param>
|
||||
/// <param name="token">A cancellation token.</param>
|
||||
Task GetFilesAsync(IEnumerable<StringUdi> udis, IFileTypeCollection fileTypes, CancellationToken token);
|
||||
|
||||
///// <summary>
|
||||
///// Gets the content of a file as a bytes array.
|
||||
|
||||
@@ -1,9 +0,0 @@
|
||||
using System.IO;
|
||||
|
||||
namespace Umbraco.Core.Deploy
|
||||
{
|
||||
public interface IFileStore
|
||||
{
|
||||
void SaveStream(StringUdi udi, Stream stream);
|
||||
}
|
||||
}
|
||||
@@ -11,12 +11,12 @@ namespace Umbraco.Core.Deploy
|
||||
public interface IGridCellValueConnector
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether the connector supports a specified grid editor alias.
|
||||
/// Gets a value indicating whether the connector supports a specified grid editor view.
|
||||
/// </summary>
|
||||
/// <param name="alias">The grid editor alias.</param>
|
||||
/// <remarks>A value indicating whether the connector supports the grid editor alias.</remarks>
|
||||
/// <remarks>Note that <paramref name="alias" /> can be string.Empty to indicate the "default" connector.</remarks>
|
||||
bool IsConnector(string alias);
|
||||
/// <param name="view">The grid editor view. It needs to be the view instead of the alias as the view is really what identifies what kind of connector should be used. Alias can be anything and you can have multiple different aliases using the same kind of view.</param>
|
||||
/// <remarks>A value indicating whether the connector supports the grid editor view.</remarks>
|
||||
/// <remarks>Note that <paramref name="view" /> can be string.Empty to indicate the "default" connector.</remarks>
|
||||
bool IsConnector(string view);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the value to be deployed from the control value as a string.
|
||||
|
||||
@@ -1,8 +0,0 @@
|
||||
namespace Umbraco.Core.Deploy
|
||||
{
|
||||
public interface IPrettyArtifact
|
||||
{
|
||||
string Name { get; }
|
||||
string Alias { get; }
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
@@ -67,22 +68,15 @@ namespace Umbraco.Core
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>The for each.</summary>
|
||||
/// <param name="items">The items.</param>
|
||||
/// <param name="func">The func.</param>
|
||||
/// <typeparam name="TItem">item type</typeparam>
|
||||
/// <typeparam name="TResult">Result type</typeparam>
|
||||
/// <returns>the Results</returns>
|
||||
[EditorBrowsable(EditorBrowsableState.Never)]
|
||||
[Obsolete("Use a normal foreach loop instead, this adds more allocations than necessary")]
|
||||
public static TResult[] ForEach<TItem, TResult>(this IEnumerable<TItem> items, Func<TItem, TResult> func)
|
||||
{
|
||||
return items.Select(func).ToArray();
|
||||
}
|
||||
|
||||
/// <summary>The for each.</summary>
|
||||
/// <param name="items">The items.</param>
|
||||
/// <param name="action">The action.</param>
|
||||
/// <typeparam name="TItem">Item type</typeparam>
|
||||
/// <returns>list of TItem</returns>
|
||||
[EditorBrowsable(EditorBrowsableState.Never)]
|
||||
[Obsolete("Use a normal foreach loop instead, this adds more allocations than necessary")]
|
||||
public static IEnumerable<TItem> ForEach<TItem>(this IEnumerable<TItem> items, Action<TItem> action)
|
||||
{
|
||||
if (items != null)
|
||||
@@ -101,6 +95,7 @@ namespace Umbraco.Core
|
||||
/// <param name="f">The select child.</param>
|
||||
/// <typeparam name="T">Item type</typeparam>
|
||||
/// <returns>list of TItem</returns>
|
||||
[EditorBrowsable(EditorBrowsableState.Never)]
|
||||
[Obsolete("Do not use, use SelectRecursive instead which has far less potential of re-iterating an iterator which may cause significantly more SQL queries")]
|
||||
public static IEnumerable<T> FlattenList<T>(this IEnumerable<T> e, Func<T, IEnumerable<T>> f)
|
||||
{
|
||||
|
||||
@@ -10,8 +10,8 @@ namespace Umbraco.Core.Events
|
||||
/// Event args for that can support cancellation
|
||||
/// </summary>
|
||||
[HostProtection(SecurityAction.LinkDemand, SharedState = true)]
|
||||
public class CancellableEventArgs : EventArgs
|
||||
{
|
||||
public class CancellableEventArgs : EventArgs, IEquatable<CancellableEventArgs>
|
||||
{
|
||||
private bool _cancel;
|
||||
|
||||
public CancellableEventArgs(bool canCancel, EventMessages messages, IDictionary<string, object> additionalData)
|
||||
@@ -98,6 +98,36 @@ namespace Umbraco.Core.Events
|
||||
/// This allows for a bit of flexibility in our event raising - it's not pretty but we need to maintain backwards compatibility
|
||||
/// so we cannot change the strongly typed nature for some events.
|
||||
/// </remarks>
|
||||
public ReadOnlyDictionary<string, object> AdditionalData { get; private set; }
|
||||
}
|
||||
public ReadOnlyDictionary<string, object> AdditionalData { get; private set; }
|
||||
|
||||
public bool Equals(CancellableEventArgs other)
|
||||
{
|
||||
if (ReferenceEquals(null, other)) return false;
|
||||
if (ReferenceEquals(this, other)) return true;
|
||||
return Equals(AdditionalData, other.AdditionalData);
|
||||
}
|
||||
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
if (ReferenceEquals(null, obj)) return false;
|
||||
if (ReferenceEquals(this, obj)) return true;
|
||||
if (obj.GetType() != this.GetType()) return false;
|
||||
return Equals((CancellableEventArgs) obj);
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return (AdditionalData != null ? AdditionalData.GetHashCode() : 0);
|
||||
}
|
||||
|
||||
public static bool operator ==(CancellableEventArgs left, CancellableEventArgs right)
|
||||
{
|
||||
return Equals(left, right);
|
||||
}
|
||||
|
||||
public static bool operator !=(CancellableEventArgs left, CancellableEventArgs right)
|
||||
{
|
||||
return !Equals(left, right);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,3 +1,4 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Security.Permissions;
|
||||
using Umbraco.Core.Models;
|
||||
@@ -10,7 +11,7 @@ namespace Umbraco.Core.Events
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
[HostProtection(SecurityAction.LinkDemand, SharedState = true)]
|
||||
public class CancellableObjectEventArgs<T> : CancellableEventArgs
|
||||
public class CancellableObjectEventArgs<T> : CancellableEventArgs, IEquatable<CancellableObjectEventArgs<T>>
|
||||
{
|
||||
public CancellableObjectEventArgs(T eventObject, bool canCancel, EventMessages messages, IDictionary<string, object> additionalData)
|
||||
: base(canCancel, messages, additionalData)
|
||||
@@ -48,5 +49,37 @@ namespace Umbraco.Core.Events
|
||||
/// </remarks>
|
||||
protected T EventObject { get; set; }
|
||||
|
||||
public bool Equals(CancellableObjectEventArgs<T> other)
|
||||
{
|
||||
if (ReferenceEquals(null, other)) return false;
|
||||
if (ReferenceEquals(this, other)) return true;
|
||||
return base.Equals(other) && EqualityComparer<T>.Default.Equals(EventObject, other.EventObject);
|
||||
}
|
||||
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
if (ReferenceEquals(null, obj)) return false;
|
||||
if (ReferenceEquals(this, obj)) return true;
|
||||
if (obj.GetType() != this.GetType()) return false;
|
||||
return Equals((CancellableObjectEventArgs<T>) obj);
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
unchecked
|
||||
{
|
||||
return (base.GetHashCode() * 397) ^ EqualityComparer<T>.Default.GetHashCode(EventObject);
|
||||
}
|
||||
}
|
||||
|
||||
public static bool operator ==(CancellableObjectEventArgs<T> left, CancellableObjectEventArgs<T> right)
|
||||
{
|
||||
return Equals(left, right);
|
||||
}
|
||||
|
||||
public static bool operator !=(CancellableObjectEventArgs<T> left, CancellableObjectEventArgs<T> right)
|
||||
{
|
||||
return !Equals(left, right);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,9 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Umbraco.Core.Events
|
||||
{
|
||||
public class CopyEventArgs<TEntity> : CancellableObjectEventArgs<TEntity>
|
||||
public class CopyEventArgs<TEntity> : CancellableObjectEventArgs<TEntity>, IEquatable<CopyEventArgs<TEntity>>
|
||||
{
|
||||
public CopyEventArgs(TEntity original, TEntity copy, bool canCancel, int parentId)
|
||||
: base(original, canCancel)
|
||||
@@ -43,5 +46,42 @@ namespace Umbraco.Core.Events
|
||||
public int ParentId { get; private set; }
|
||||
|
||||
public bool RelateToOriginal { get; set; }
|
||||
|
||||
public bool Equals(CopyEventArgs<TEntity> other)
|
||||
{
|
||||
if (ReferenceEquals(null, other)) return false;
|
||||
if (ReferenceEquals(this, other)) return true;
|
||||
return base.Equals(other) && EqualityComparer<TEntity>.Default.Equals(Copy, other.Copy) && ParentId == other.ParentId && RelateToOriginal == other.RelateToOriginal;
|
||||
}
|
||||
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
if (ReferenceEquals(null, obj)) return false;
|
||||
if (ReferenceEquals(this, obj)) return true;
|
||||
if (obj.GetType() != this.GetType()) return false;
|
||||
return Equals((CopyEventArgs<TEntity>) obj);
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
unchecked
|
||||
{
|
||||
int hashCode = base.GetHashCode();
|
||||
hashCode = (hashCode * 397) ^ EqualityComparer<TEntity>.Default.GetHashCode(Copy);
|
||||
hashCode = (hashCode * 397) ^ ParentId;
|
||||
hashCode = (hashCode * 397) ^ RelateToOriginal.GetHashCode();
|
||||
return hashCode;
|
||||
}
|
||||
}
|
||||
|
||||
public static bool operator ==(CopyEventArgs<TEntity> left, CopyEventArgs<TEntity> right)
|
||||
{
|
||||
return Equals(left, right);
|
||||
}
|
||||
|
||||
public static bool operator !=(CopyEventArgs<TEntity> left, CopyEventArgs<TEntity> right)
|
||||
{
|
||||
return !Equals(left, right);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,9 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Umbraco.Core.Events
|
||||
{
|
||||
public class DeleteEventArgs<TEntity> : CancellableObjectEventArgs<IEnumerable<TEntity>>
|
||||
public class DeleteEventArgs<TEntity> : CancellableObjectEventArgs<IEnumerable<TEntity>>, IEquatable<DeleteEventArgs<TEntity>>, IDeletingMediaFilesEventArgs
|
||||
{
|
||||
/// <summary>
|
||||
/// Constructor accepting multiple entities that are used in the delete operation
|
||||
@@ -99,10 +100,43 @@ namespace Umbraco.Core.Events
|
||||
/// <summary>
|
||||
/// A list of media files that can be added to during a deleted operation for which Umbraco will ensure are removed
|
||||
/// </summary>
|
||||
public List<string> MediaFilesToDelete { get; private set; }
|
||||
public List<string> MediaFilesToDelete { get; private set; }
|
||||
|
||||
public bool Equals(DeleteEventArgs<TEntity> other)
|
||||
{
|
||||
if (ReferenceEquals(null, other)) return false;
|
||||
if (ReferenceEquals(this, other)) return true;
|
||||
return base.Equals(other) && MediaFilesToDelete.Equals(other.MediaFilesToDelete);
|
||||
}
|
||||
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
if (ReferenceEquals(null, obj)) return false;
|
||||
if (ReferenceEquals(this, obj)) return true;
|
||||
if (obj.GetType() != this.GetType()) return false;
|
||||
return Equals((DeleteEventArgs<TEntity>) obj);
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
unchecked
|
||||
{
|
||||
return (base.GetHashCode() * 397) ^ MediaFilesToDelete.GetHashCode();
|
||||
}
|
||||
}
|
||||
|
||||
public static bool operator ==(DeleteEventArgs<TEntity> left, DeleteEventArgs<TEntity> right)
|
||||
{
|
||||
return Equals(left, right);
|
||||
}
|
||||
|
||||
public static bool operator !=(DeleteEventArgs<TEntity> left, DeleteEventArgs<TEntity> right)
|
||||
{
|
||||
return !Equals(left, right);
|
||||
}
|
||||
}
|
||||
|
||||
public class DeleteEventArgs : CancellableEventArgs
|
||||
public class DeleteEventArgs : CancellableEventArgs, IEquatable<DeleteEventArgs>
|
||||
{
|
||||
public DeleteEventArgs(int id, bool canCancel, EventMessages eventMessages)
|
||||
: base(canCancel, eventMessages)
|
||||
@@ -125,5 +159,38 @@ namespace Umbraco.Core.Events
|
||||
/// Gets the Id of the object being deleted.
|
||||
/// </summary>
|
||||
public int Id { get; private set; }
|
||||
|
||||
public bool Equals(DeleteEventArgs other)
|
||||
{
|
||||
if (ReferenceEquals(null, other)) return false;
|
||||
if (ReferenceEquals(this, other)) return true;
|
||||
return base.Equals(other) && Id == other.Id;
|
||||
}
|
||||
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
if (ReferenceEquals(null, obj)) return false;
|
||||
if (ReferenceEquals(this, obj)) return true;
|
||||
if (obj.GetType() != this.GetType()) return false;
|
||||
return Equals((DeleteEventArgs) obj);
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
unchecked
|
||||
{
|
||||
return (base.GetHashCode() * 397) ^ Id;
|
||||
}
|
||||
}
|
||||
|
||||
public static bool operator ==(DeleteEventArgs left, DeleteEventArgs right)
|
||||
{
|
||||
return Equals(left, right);
|
||||
}
|
||||
|
||||
public static bool operator !=(DeleteEventArgs left, DeleteEventArgs right)
|
||||
{
|
||||
return !Equals(left, right);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,7 +2,7 @@ using System;
|
||||
|
||||
namespace Umbraco.Core.Events
|
||||
{
|
||||
public class DeleteRevisionsEventArgs : DeleteEventArgs
|
||||
public class DeleteRevisionsEventArgs : DeleteEventArgs, IEquatable<DeleteRevisionsEventArgs>
|
||||
{
|
||||
public DeleteRevisionsEventArgs(int id, bool canCancel, Guid specificVersion = default(Guid), bool deletePriorVersions = false, DateTime dateToRetain = default(DateTime))
|
||||
: base(id, canCancel)
|
||||
@@ -31,5 +31,42 @@ namespace Umbraco.Core.Events
|
||||
{
|
||||
get { return SpecificVersion != default(Guid); }
|
||||
}
|
||||
|
||||
public bool Equals(DeleteRevisionsEventArgs other)
|
||||
{
|
||||
if (ReferenceEquals(null, other)) return false;
|
||||
if (ReferenceEquals(this, other)) return true;
|
||||
return base.Equals(other) && DateToRetain.Equals(other.DateToRetain) && DeletePriorVersions == other.DeletePriorVersions && SpecificVersion.Equals(other.SpecificVersion);
|
||||
}
|
||||
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
if (ReferenceEquals(null, obj)) return false;
|
||||
if (ReferenceEquals(this, obj)) return true;
|
||||
if (obj.GetType() != this.GetType()) return false;
|
||||
return Equals((DeleteRevisionsEventArgs) obj);
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
unchecked
|
||||
{
|
||||
int hashCode = base.GetHashCode();
|
||||
hashCode = (hashCode * 397) ^ DateToRetain.GetHashCode();
|
||||
hashCode = (hashCode * 397) ^ DeletePriorVersions.GetHashCode();
|
||||
hashCode = (hashCode * 397) ^ SpecificVersion.GetHashCode();
|
||||
return hashCode;
|
||||
}
|
||||
}
|
||||
|
||||
public static bool operator ==(DeleteRevisionsEventArgs left, DeleteRevisionsEventArgs right)
|
||||
{
|
||||
return Equals(left, right);
|
||||
}
|
||||
|
||||
public static bool operator !=(DeleteRevisionsEventArgs left, DeleteRevisionsEventArgs right)
|
||||
{
|
||||
return !Equals(left, right);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
using System;
|
||||
|
||||
namespace Umbraco.Core.Events
|
||||
{
|
||||
internal class EventDefinition : EventDefinitionBase
|
||||
{
|
||||
private readonly EventHandler _trackedEvent;
|
||||
private readonly object _sender;
|
||||
private readonly EventArgs _args;
|
||||
|
||||
public EventDefinition(EventHandler trackedEvent, object sender, EventArgs args, string eventName = null)
|
||||
: base(sender, args, eventName)
|
||||
{
|
||||
_trackedEvent = trackedEvent;
|
||||
_sender = sender;
|
||||
_args = args;
|
||||
}
|
||||
|
||||
public override void RaiseEvent()
|
||||
{
|
||||
if (_trackedEvent != null)
|
||||
{
|
||||
_trackedEvent(_sender, _args);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal class EventDefinition<TEventArgs> : EventDefinitionBase
|
||||
{
|
||||
private readonly EventHandler<TEventArgs> _trackedEvent;
|
||||
private readonly object _sender;
|
||||
private readonly TEventArgs _args;
|
||||
|
||||
public EventDefinition(EventHandler<TEventArgs> trackedEvent, object sender, TEventArgs args, string eventName = null)
|
||||
: base(sender, args, eventName)
|
||||
{
|
||||
_trackedEvent = trackedEvent;
|
||||
_sender = sender;
|
||||
_args = args;
|
||||
}
|
||||
|
||||
public override void RaiseEvent()
|
||||
{
|
||||
if (_trackedEvent != null)
|
||||
{
|
||||
_trackedEvent(_sender, _args);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal class EventDefinition<TSender, TEventArgs> : EventDefinitionBase
|
||||
{
|
||||
private readonly TypedEventHandler<TSender, TEventArgs> _trackedEvent;
|
||||
private readonly TSender _sender;
|
||||
private readonly TEventArgs _args;
|
||||
|
||||
public EventDefinition(TypedEventHandler<TSender, TEventArgs> trackedEvent, TSender sender, TEventArgs args, string eventName = null)
|
||||
: base(sender, args, eventName)
|
||||
{
|
||||
_trackedEvent = trackedEvent;
|
||||
_sender = sender;
|
||||
_args = args;
|
||||
}
|
||||
|
||||
public override void RaiseEvent()
|
||||
{
|
||||
if (_trackedEvent != null)
|
||||
{
|
||||
_trackedEvent(_sender, _args);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
using System;
|
||||
using System.Reflection;
|
||||
|
||||
namespace Umbraco.Core.Events
|
||||
{
|
||||
public abstract class EventDefinitionBase : IEventDefinition, IEquatable<EventDefinitionBase>
|
||||
{
|
||||
protected EventDefinitionBase(object sender, object args, string eventName = null)
|
||||
{
|
||||
if (sender == null) throw new ArgumentNullException("sender");
|
||||
if (args == null) throw new ArgumentNullException("args");
|
||||
Sender = sender;
|
||||
Args = args;
|
||||
EventName = eventName;
|
||||
|
||||
if (EventName.IsNullOrWhiteSpace())
|
||||
{
|
||||
var findResult = EventNameExtractor.FindEvent(sender, args,
|
||||
//don't match "Ing" suffixed names
|
||||
exclude:EventNameExtractor.MatchIngNames);
|
||||
|
||||
if (findResult.Success == false)
|
||||
throw new AmbiguousMatchException("Could not automatically find the event name, the event name will need to be explicitly registered for this event definition. Error: " + findResult.Result.Error);
|
||||
EventName = findResult.Result.Name;
|
||||
}
|
||||
}
|
||||
|
||||
public object Sender { get; private set; }
|
||||
public object Args { get; private set; }
|
||||
public string EventName { get; private set; }
|
||||
|
||||
public abstract void RaiseEvent();
|
||||
|
||||
public bool Equals(EventDefinitionBase other)
|
||||
{
|
||||
if (ReferenceEquals(null, other)) return false;
|
||||
if (ReferenceEquals(this, other)) return true;
|
||||
return Args.Equals(other.Args) && string.Equals(EventName, other.EventName) && Sender.Equals(other.Sender);
|
||||
}
|
||||
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
if (ReferenceEquals(null, obj)) return false;
|
||||
if (ReferenceEquals(this, obj)) return true;
|
||||
if (obj.GetType() != this.GetType()) return false;
|
||||
return Equals((EventDefinitionBase) obj);
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
unchecked
|
||||
{
|
||||
var hashCode = Args.GetHashCode();
|
||||
hashCode = (hashCode * 397) ^ EventName.GetHashCode();
|
||||
hashCode = (hashCode * 397) ^ Sender.GetHashCode();
|
||||
return hashCode;
|
||||
}
|
||||
}
|
||||
|
||||
public static bool operator ==(EventDefinitionBase left, EventDefinitionBase right)
|
||||
{
|
||||
return Equals(left, right);
|
||||
}
|
||||
|
||||
public static bool operator !=(EventDefinitionBase left, EventDefinitionBase right)
|
||||
{
|
||||
return Equals(left, right) == false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
namespace Umbraco.Core.Events
|
||||
{
|
||||
/// <summary>
|
||||
/// The filter used in the <see cref="IEventDispatcher"/> GetEvents method which determines
|
||||
/// how the result list is filtered
|
||||
/// </summary>
|
||||
public enum EventDefinitionFilter
|
||||
{
|
||||
/// <summary>
|
||||
/// Returns all events tracked
|
||||
/// </summary>
|
||||
All,
|
||||
|
||||
/// <summary>
|
||||
/// Deduplicates events and only returns the first duplicate instance tracked
|
||||
/// </summary>
|
||||
FirstIn,
|
||||
|
||||
/// <summary>
|
||||
/// Deduplicates events and only returns the last duplicate instance tracked
|
||||
/// </summary>
|
||||
LastIn
|
||||
}
|
||||
}
|
||||
@@ -2,6 +2,8 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Runtime.CompilerServices;
|
||||
using Umbraco.Core.Persistence.UnitOfWork;
|
||||
|
||||
namespace Umbraco.Core.Events
|
||||
{
|
||||
@@ -10,73 +12,40 @@ namespace Umbraco.Core.Events
|
||||
/// </summary>
|
||||
public static class EventExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Raises the event and returns a boolean value indicating if the event was cancelled
|
||||
/// </summary>
|
||||
/// <typeparam name="TSender"></typeparam>
|
||||
/// <typeparam name="TArgs"></typeparam>
|
||||
/// <param name="eventHandler"></param>
|
||||
/// <param name="args"></param>
|
||||
/// <param name="sender"></param>
|
||||
/// <returns></returns>
|
||||
public static bool IsRaisedEventCancelled<TSender, TArgs>(
|
||||
this TypedEventHandler<TSender, TArgs> eventHandler,
|
||||
TArgs args,
|
||||
TSender sender)
|
||||
where TArgs : CancellableEventArgs
|
||||
{
|
||||
if (eventHandler != null)
|
||||
eventHandler(sender, args);
|
||||
// keep these two for backward compatibility reasons but understand that
|
||||
// they are *not* part of any scope / event dispatcher / anything...
|
||||
|
||||
/// <summary>
|
||||
/// Raises a cancelable event and returns a value indicating whether the event should be cancelled.
|
||||
/// </summary>
|
||||
/// <typeparam name="TSender">The type of the event source.</typeparam>
|
||||
/// <typeparam name="TArgs">The type of the event data.</typeparam>
|
||||
/// <param name="eventHandler">The event handler.</param>
|
||||
/// <param name="args">The event source.</param>
|
||||
/// <param name="sender">The event data.</param>
|
||||
/// <returns>A value indicating whether the cancelable event should be cancelled</returns>
|
||||
/// <remarks>A cancelable event is raised by a component when it is about to perform an action that can be canceled.</remarks>
|
||||
public static bool IsRaisedEventCancelled<TSender, TArgs>(this TypedEventHandler<TSender, TArgs> eventHandler, TArgs args, TSender sender)
|
||||
where TArgs : CancellableEventArgs
|
||||
{
|
||||
if (eventHandler == null) return args.Cancel;
|
||||
eventHandler(sender, args);
|
||||
return args.Cancel;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Raises the event
|
||||
/// Raises an event.
|
||||
/// </summary>
|
||||
/// <typeparam name="TSender"></typeparam>
|
||||
/// <typeparam name="TArgs"></typeparam>
|
||||
/// <param name="eventHandler"></param>
|
||||
/// <param name="args"></param>
|
||||
/// <param name="sender"></param>
|
||||
public static void RaiseEvent<TSender, TArgs>(
|
||||
this TypedEventHandler<TSender, TArgs> eventHandler,
|
||||
TArgs args,
|
||||
TSender sender)
|
||||
where TArgs : EventArgs
|
||||
{
|
||||
if (eventHandler != null)
|
||||
eventHandler(sender, args);
|
||||
}
|
||||
|
||||
// moves the last handler that was added to an instance event, to first position
|
||||
public static void PromoteLastHandler(object sender, string eventName)
|
||||
{
|
||||
var fieldInfo = sender.GetType().GetField(eventName, BindingFlags.Instance | BindingFlags.NonPublic);
|
||||
if (fieldInfo == null) throw new InvalidOperationException("No event named " + eventName + ".");
|
||||
PromoteLastHandler(sender, fieldInfo);
|
||||
}
|
||||
|
||||
// moves the last handler that was added to a static event, to first position
|
||||
public static void PromoteLastHandler<TSender>(string eventName)
|
||||
/// <typeparam name="TSender">The type of the event source.</typeparam>
|
||||
/// <typeparam name="TArgs">The type of the event data.</typeparam>
|
||||
/// <param name="eventHandler">The event handler.</param>
|
||||
/// <param name="args">The event source.</param>
|
||||
/// <param name="sender">The event data.</param>
|
||||
public static void RaiseEvent<TSender, TArgs>(this TypedEventHandler<TSender, TArgs> eventHandler, TArgs args, TSender sender)
|
||||
where TArgs : EventArgs
|
||||
{
|
||||
var fieldInfo = typeof(TSender).GetField(eventName, BindingFlags.Static | BindingFlags.NonPublic);
|
||||
if (fieldInfo == null) throw new InvalidOperationException("No event named " + eventName + ".");
|
||||
PromoteLastHandler(null, fieldInfo);
|
||||
}
|
||||
|
||||
private static void PromoteLastHandler(object sender, FieldInfo fieldInfo)
|
||||
{
|
||||
var d = fieldInfo.GetValue(sender) as Delegate;
|
||||
if (d == null) return;
|
||||
|
||||
var l = d.GetInvocationList();
|
||||
var x = l[l.Length - 1];
|
||||
for (var i = l.Length - 1; i > 0; i--)
|
||||
l[i] = l[i - 1];
|
||||
l[0] = x;
|
||||
|
||||
fieldInfo.SetValue(sender, Delegate.Combine(l));
|
||||
if (eventHandler == null) return;
|
||||
eventHandler(sender, args);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,159 @@
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace Umbraco.Core.Events
|
||||
{
|
||||
/// <summary>
|
||||
/// There is actually no way to discover an event name in c# at the time of raising the event. It is possible
|
||||
/// to get the event name from the handler that is being executed based on the event being raised, however that is not
|
||||
/// what we want in this case. We need to find the event name before it is being raised - you would think that it's possible
|
||||
/// with reflection or anything but that is not the case, the delegate that defines an event has no info attached to it, it
|
||||
/// is literally just an event.
|
||||
///
|
||||
/// So what this does is take the sender and event args objects, looks up all public/static events on the sender that have
|
||||
/// a generic event handler with generic arguments (but only) one, then we match the type of event arguments with the ones
|
||||
/// being passed in. As it turns out, in our services this will work for the majority of our events! In some cases it may not
|
||||
/// work and we'll have to supply a string but hopefully this saves a bit of magic strings.
|
||||
///
|
||||
/// We can also write tests to validate these are all working correctly for all services.
|
||||
/// </summary>
|
||||
internal class EventNameExtractor
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// Finds the event name on the sender that matches the args type
|
||||
/// </summary>
|
||||
/// <param name="senderType"></param>
|
||||
/// <param name="argsType"></param>
|
||||
/// <param name="exclude">
|
||||
/// A filter to exclude matched event names, this filter should return true to exclude the event name from being matched
|
||||
/// </param>
|
||||
/// <returns>
|
||||
/// null if not found or an ambiguous match
|
||||
/// </returns>
|
||||
public static Attempt<EventNameExtractorResult> FindEvent(Type senderType, Type argsType, Func<string, bool> exclude)
|
||||
{
|
||||
var found = MatchedEventNames.GetOrAdd(new Tuple<Type, Type>(senderType, argsType), tuple =>
|
||||
{
|
||||
var events = CandidateEvents.GetOrAdd(senderType, t =>
|
||||
{
|
||||
return t.GetEvents(BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic)
|
||||
//we can only look for events handlers with generic types because that is the only
|
||||
// way that we can try to find a matching event based on the arg type passed in
|
||||
.Where(x => x.EventHandlerType.IsGenericType)
|
||||
.Select(x => new EventInfoArgs(x, x.EventHandlerType.GetGenericArguments()))
|
||||
//we are only looking for event handlers that have more than one generic argument
|
||||
.Where(x =>
|
||||
{
|
||||
if (x.GenericArgs.Length == 1) return true;
|
||||
|
||||
//special case for our own TypedEventHandler
|
||||
if (x.EventInfo.EventHandlerType.GetGenericTypeDefinition() == typeof(TypedEventHandler<,>) && x.GenericArgs.Length == 2)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
})
|
||||
.ToArray();
|
||||
});
|
||||
|
||||
return events.Where(x =>
|
||||
{
|
||||
if (x.GenericArgs.Length == 1 && x.GenericArgs[0] == tuple.Item2)
|
||||
return true;
|
||||
|
||||
//special case for our own TypedEventHandler
|
||||
if (x.EventInfo.EventHandlerType.GetGenericTypeDefinition() == typeof(TypedEventHandler<,>)
|
||||
&& x.GenericArgs.Length == 2
|
||||
&& x.GenericArgs[1] == tuple.Item2)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}).Select(x => x.EventInfo.Name).ToArray();
|
||||
});
|
||||
|
||||
var filtered = found.Where(x => exclude(x) == false).ToArray();
|
||||
|
||||
if (filtered.Length == 0)
|
||||
return Attempt.Fail(new EventNameExtractorResult(EventNameExtractorError.NoneFound));
|
||||
|
||||
if (filtered.Length == 1)
|
||||
return Attempt.Succeed(new EventNameExtractorResult(filtered[0]));
|
||||
|
||||
//there's more than one left so it's ambiguous!
|
||||
return Attempt.Fail(new EventNameExtractorResult(EventNameExtractorError.Ambiguous));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Finds the event name on the sender that matches the args type
|
||||
/// </summary>
|
||||
/// <param name="sender"></param>
|
||||
/// <param name="args"></param>
|
||||
/// <param name="exclude">
|
||||
/// A filter to exclude matched event names, this filter should return true to exclude the event name from being matched
|
||||
/// </param>
|
||||
/// <returns>
|
||||
/// null if not found or an ambiguous match
|
||||
/// </returns>
|
||||
public static Attempt<EventNameExtractorResult> FindEvent(object sender, object args, Func<string, bool> exclude)
|
||||
{
|
||||
return FindEvent(sender.GetType(), args.GetType(), exclude);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Return true if the event is named with an ING name such as "Saving" or "RollingBack"
|
||||
/// </summary>
|
||||
/// <param name="eventName"></param>
|
||||
/// <returns></returns>
|
||||
internal static bool MatchIngNames(string eventName)
|
||||
{
|
||||
var splitter = new Regex(@"(?<!^)(?=[A-Z])");
|
||||
var words = splitter.Split(eventName);
|
||||
if (words.Length == 0)
|
||||
return false;
|
||||
return words[0].EndsWith("ing");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Return true if the event is not named with an ING name such as "Saving" or "RollingBack"
|
||||
/// </summary>
|
||||
/// <param name="eventName"></param>
|
||||
/// <returns></returns>
|
||||
internal static bool MatchNonIngNames(string eventName)
|
||||
{
|
||||
var splitter = new Regex(@"(?<!^)(?=[A-Z])");
|
||||
var words = splitter.Split(eventName);
|
||||
if (words.Length == 0)
|
||||
return false;
|
||||
return words[0].EndsWith("ing") == false;
|
||||
}
|
||||
|
||||
private class EventInfoArgs
|
||||
{
|
||||
public EventInfo EventInfo { get; private set; }
|
||||
public Type[] GenericArgs { get; private set; }
|
||||
|
||||
public EventInfoArgs(EventInfo eventInfo, Type[] genericArgs)
|
||||
{
|
||||
EventInfo = eventInfo;
|
||||
GenericArgs = genericArgs;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Used to cache all candidate events for a given type so we don't re-look them up
|
||||
/// </summary>
|
||||
private static readonly ConcurrentDictionary<Type, EventInfoArgs[]> CandidateEvents = new ConcurrentDictionary<Type, EventInfoArgs[]>();
|
||||
|
||||
/// <summary>
|
||||
/// Used to cache all matched event names by (sender type + arg type) so we don't re-look them up
|
||||
/// </summary>
|
||||
private static readonly ConcurrentDictionary<Tuple<Type, Type>, string[]> MatchedEventNames = new ConcurrentDictionary<Tuple<Type, Type>, string[]>();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
namespace Umbraco.Core.Events
|
||||
{
|
||||
internal enum EventNameExtractorError
|
||||
{
|
||||
NoneFound,
|
||||
Ambiguous
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
namespace Umbraco.Core.Events
|
||||
{
|
||||
internal class EventNameExtractorResult
|
||||
{
|
||||
public EventNameExtractorError? Error { get; private set; }
|
||||
public string Name { get; private set; }
|
||||
|
||||
public EventNameExtractorResult(string name)
|
||||
{
|
||||
Name = name;
|
||||
}
|
||||
|
||||
public EventNameExtractorResult(EventNameExtractorError error)
|
||||
{
|
||||
Error = error;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,9 +1,10 @@
|
||||
using System.Collections.Generic;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Xml.Linq;
|
||||
|
||||
namespace Umbraco.Core.Events
|
||||
{
|
||||
public class ExportEventArgs<TEntity> : CancellableObjectEventArgs<IEnumerable<TEntity>>
|
||||
public class ExportEventArgs<TEntity> : CancellableObjectEventArgs<IEnumerable<TEntity>>, IEquatable<ExportEventArgs<TEntity>>
|
||||
{
|
||||
/// <summary>
|
||||
/// Constructor accepting a single entity instance
|
||||
@@ -48,5 +49,38 @@ namespace Umbraco.Core.Events
|
||||
/// Returns the xml relating to the export event
|
||||
/// </summary>
|
||||
public XElement Xml { get; private set; }
|
||||
|
||||
public bool Equals(ExportEventArgs<TEntity> other)
|
||||
{
|
||||
if (ReferenceEquals(null, other)) return false;
|
||||
if (ReferenceEquals(this, other)) return true;
|
||||
return base.Equals(other) && Equals(Xml, other.Xml);
|
||||
}
|
||||
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
if (ReferenceEquals(null, obj)) return false;
|
||||
if (ReferenceEquals(this, obj)) return true;
|
||||
if (obj.GetType() != this.GetType()) return false;
|
||||
return Equals((ExportEventArgs<TEntity>) obj);
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
unchecked
|
||||
{
|
||||
return (base.GetHashCode() * 397) ^ (Xml != null ? Xml.GetHashCode() : 0);
|
||||
}
|
||||
}
|
||||
|
||||
public static bool operator ==(ExportEventArgs<TEntity> left, ExportEventArgs<TEntity> right)
|
||||
{
|
||||
return Equals(left, right);
|
||||
}
|
||||
|
||||
public static bool operator !=(ExportEventArgs<TEntity> left, ExportEventArgs<TEntity> right)
|
||||
{
|
||||
return !Equals(left, right);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Umbraco.Core.Events
|
||||
{
|
||||
internal interface IDeletingMediaFilesEventArgs
|
||||
{
|
||||
List<string> MediaFilesToDelete { get; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
using System;
|
||||
|
||||
namespace Umbraco.Core.Events
|
||||
{
|
||||
public interface IEventDefinition
|
||||
{
|
||||
object Sender { get; }
|
||||
object Args { get; }
|
||||
string EventName { get; }
|
||||
|
||||
void RaiseEvent();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,98 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Umbraco.Core.Events
|
||||
{
|
||||
/// <summary>
|
||||
/// Dispatches events from within a scope.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>The name of the event is auto-magically discovered by matching the sender type, args type, and
|
||||
/// eventHandler type. If the match is not unique, then the name parameter must be used to specify the
|
||||
/// name in an explicit way.</para>
|
||||
/// <para>What happens when an event is dispatched depends on the scope settings. It can be anything from
|
||||
/// "trigger immediately" to "just ignore". Refer to the scope documentation for more details.</para>
|
||||
/// </remarks>
|
||||
public interface IEventDispatcher
|
||||
{
|
||||
// not sure about the Dispatch & DispatchCancelable signatures at all for now
|
||||
// nor about the event name thing, etc - but let's keep it like this
|
||||
|
||||
/// <summary>
|
||||
/// Dispatches a cancelable event.
|
||||
/// </summary>
|
||||
/// <param name="eventHandler">The event handler.</param>
|
||||
/// <param name="sender">The object that raised the event.</param>
|
||||
/// <param name="args">The event data.</param>
|
||||
/// <param name="name">The optional name of the event.</param>
|
||||
/// <returns>A value indicating whether the cancelable event was cancelled.</returns>
|
||||
/// <remarks>See general remarks on the interface.</remarks>
|
||||
bool DispatchCancelable(EventHandler eventHandler, object sender, CancellableEventArgs args, string name = null);
|
||||
|
||||
/// <summary>
|
||||
/// Dispatches a cancelable event.
|
||||
/// </summary>
|
||||
/// <param name="eventHandler">The event handler.</param>
|
||||
/// <param name="sender">The object that raised the event.</param>
|
||||
/// <param name="args">The event data.</param>
|
||||
/// <param name="name">The optional name of the event.</param>
|
||||
/// <returns>A value indicating whether the cancelable event was cancelled.</returns>
|
||||
/// <remarks>See general remarks on the interface.</remarks>
|
||||
bool DispatchCancelable<TArgs>(EventHandler<TArgs> eventHandler, object sender, TArgs args, string name = null)
|
||||
where TArgs : CancellableEventArgs;
|
||||
|
||||
/// <summary>
|
||||
/// Dispatches a cancelable event.
|
||||
/// </summary>
|
||||
/// <param name="eventHandler">The event handler.</param>
|
||||
/// <param name="sender">The object that raised the event.</param>
|
||||
/// <param name="args">The event data.</param>
|
||||
/// <param name="name">The optional name of the event.</param>
|
||||
/// <returns>A value indicating whether the cancelable event was cancelled.</returns>
|
||||
/// <remarks>See general remarks on the interface.</remarks>
|
||||
bool DispatchCancelable<TSender, TArgs>(TypedEventHandler<TSender, TArgs> eventHandler, TSender sender, TArgs args, string name = null)
|
||||
where TArgs : CancellableEventArgs;
|
||||
|
||||
/// <summary>
|
||||
/// Dispatches an event.
|
||||
/// </summary>
|
||||
/// <param name="eventHandler">The event handler.</param>
|
||||
/// <param name="sender">The object that raised the event.</param>
|
||||
/// <param name="args">The event data.</param>
|
||||
/// <param name="name">The optional name of the event.</param>
|
||||
/// <remarks>See general remarks on the interface.</remarks>
|
||||
void Dispatch(EventHandler eventHandler, object sender, EventArgs args, string name = null);
|
||||
|
||||
/// <summary>
|
||||
/// Dispatches an event.
|
||||
/// </summary>
|
||||
/// <param name="eventHandler">The event handler.</param>
|
||||
/// <param name="sender">The object that raised the event.</param>
|
||||
/// <param name="args">The event data.</param>
|
||||
/// <param name="name">The optional name of the event.</param>
|
||||
/// <remarks>See general remarks on the interface.</remarks>
|
||||
void Dispatch<TArgs>(EventHandler<TArgs> eventHandler, object sender, TArgs args, string name = null);
|
||||
|
||||
/// <summary>
|
||||
/// Dispatches an event.
|
||||
/// </summary>
|
||||
/// <param name="eventHandler">The event handler.</param>
|
||||
/// <param name="sender">The object that raised the event.</param>
|
||||
/// <param name="args">The event data.</param>
|
||||
/// <param name="name">The optional name of the event.</param>
|
||||
/// <remarks>See general remarks on the interface.</remarks>
|
||||
void Dispatch<TSender, TArgs>(TypedEventHandler<TSender, TArgs> eventHandler, TSender sender, TArgs args, string name = null);
|
||||
|
||||
/// <summary>
|
||||
/// Notifies the dispatcher that the scope is exiting.
|
||||
/// </summary>
|
||||
/// <param name="completed">A value indicating whether the scope completed.</param>
|
||||
void ScopeExit(bool completed);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the collected events.
|
||||
/// </summary>
|
||||
/// <returns>The collected events.</returns>
|
||||
IEnumerable<IEventDefinition> GetEvents(EventDefinitionFilter filter);
|
||||
}
|
||||
}
|
||||
@@ -1,9 +1,10 @@
|
||||
using System.Collections.Generic;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Xml.Linq;
|
||||
|
||||
namespace Umbraco.Core.Events
|
||||
{
|
||||
public class ImportEventArgs<TEntity> : CancellableObjectEventArgs<IEnumerable<TEntity>>
|
||||
public class ImportEventArgs<TEntity> : CancellableObjectEventArgs<IEnumerable<TEntity>>, IEquatable<ImportEventArgs<TEntity>>
|
||||
{
|
||||
/// <summary>
|
||||
/// Constructor accepting an XElement with the xml being imported
|
||||
@@ -46,5 +47,38 @@ namespace Umbraco.Core.Events
|
||||
/// Returns the xml relating to the import event
|
||||
/// </summary>
|
||||
public XElement Xml { get; private set; }
|
||||
|
||||
public bool Equals(ImportEventArgs<TEntity> other)
|
||||
{
|
||||
if (ReferenceEquals(null, other)) return false;
|
||||
if (ReferenceEquals(this, other)) return true;
|
||||
return base.Equals(other) && Equals(Xml, other.Xml);
|
||||
}
|
||||
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
if (ReferenceEquals(null, obj)) return false;
|
||||
if (ReferenceEquals(this, obj)) return true;
|
||||
if (obj.GetType() != this.GetType()) return false;
|
||||
return Equals((ImportEventArgs<TEntity>) obj);
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
unchecked
|
||||
{
|
||||
return (base.GetHashCode() * 397) ^ (Xml != null ? Xml.GetHashCode() : 0);
|
||||
}
|
||||
}
|
||||
|
||||
public static bool operator ==(ImportEventArgs<TEntity> left, ImportEventArgs<TEntity> right)
|
||||
{
|
||||
return Equals(left, right);
|
||||
}
|
||||
|
||||
public static bool operator !=(ImportEventArgs<TEntity> left, ImportEventArgs<TEntity> right)
|
||||
{
|
||||
return !Equals(left, right);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,9 +1,10 @@
|
||||
using System.Collections.Generic;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Umbraco.Core.Packaging.Models;
|
||||
|
||||
namespace Umbraco.Core.Events
|
||||
{
|
||||
internal class ImportPackageEventArgs<TEntity> : CancellableObjectEventArgs<IEnumerable<TEntity>>
|
||||
internal class ImportPackageEventArgs<TEntity> : CancellableObjectEventArgs<IEnumerable<TEntity>>, IEquatable<ImportPackageEventArgs<TEntity>>
|
||||
{
|
||||
private readonly MetaData _packageMetaData;
|
||||
|
||||
@@ -21,6 +22,44 @@ namespace Umbraco.Core.Events
|
||||
public MetaData PackageMetaData
|
||||
{
|
||||
get { return _packageMetaData; }
|
||||
}
|
||||
|
||||
public IEnumerable<TEntity> InstallationSummary
|
||||
{
|
||||
get { return EventObject; }
|
||||
}
|
||||
|
||||
public bool Equals(ImportPackageEventArgs<TEntity> other)
|
||||
{
|
||||
if (ReferenceEquals(null, other)) return false;
|
||||
if (ReferenceEquals(this, other)) return true;
|
||||
return base.Equals(other) && _packageMetaData.Equals(other._packageMetaData);
|
||||
}
|
||||
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
if (ReferenceEquals(null, obj)) return false;
|
||||
if (ReferenceEquals(this, obj)) return true;
|
||||
if (obj.GetType() != this.GetType()) return false;
|
||||
return Equals((ImportPackageEventArgs<TEntity>) obj);
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
unchecked
|
||||
{
|
||||
return (base.GetHashCode() * 397) ^ _packageMetaData.GetHashCode();
|
||||
}
|
||||
}
|
||||
|
||||
public static bool operator ==(ImportPackageEventArgs<TEntity> left, ImportPackageEventArgs<TEntity> right)
|
||||
{
|
||||
return Equals(left, right);
|
||||
}
|
||||
|
||||
public static bool operator !=(ImportPackageEventArgs<TEntity> left, ImportPackageEventArgs<TEntity> right)
|
||||
{
|
||||
return !Equals(left, right);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -7,7 +7,7 @@ using Umbraco.Core.Persistence.Migrations;
|
||||
|
||||
namespace Umbraco.Core.Events
|
||||
{
|
||||
public class MigrationEventArgs : CancellableObjectEventArgs<IList<IMigration>>
|
||||
public class MigrationEventArgs : CancellableObjectEventArgs<IList<IMigration>>, IEquatable<MigrationEventArgs>
|
||||
{
|
||||
/// <summary>
|
||||
/// Constructor accepting multiple migrations that are used in the migration runner
|
||||
@@ -141,5 +141,43 @@ namespace Umbraco.Core.Events
|
||||
public string ProductName { get; private set; }
|
||||
|
||||
internal MigrationContext MigrationContext { get; private set; }
|
||||
|
||||
public bool Equals(MigrationEventArgs other)
|
||||
{
|
||||
if (ReferenceEquals(null, other)) return false;
|
||||
if (ReferenceEquals(this, other)) return true;
|
||||
return base.Equals(other) && ConfiguredSemVersion.Equals(other.ConfiguredSemVersion) && MigrationContext.Equals(other.MigrationContext) && string.Equals(ProductName, other.ProductName) && TargetSemVersion.Equals(other.TargetSemVersion);
|
||||
}
|
||||
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
if (ReferenceEquals(null, obj)) return false;
|
||||
if (ReferenceEquals(this, obj)) return true;
|
||||
if (obj.GetType() != this.GetType()) return false;
|
||||
return Equals((MigrationEventArgs) obj);
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
unchecked
|
||||
{
|
||||
int hashCode = base.GetHashCode();
|
||||
hashCode = (hashCode * 397) ^ ConfiguredSemVersion.GetHashCode();
|
||||
hashCode = (hashCode * 397) ^ MigrationContext.GetHashCode();
|
||||
hashCode = (hashCode * 397) ^ ProductName.GetHashCode();
|
||||
hashCode = (hashCode * 397) ^ TargetSemVersion.GetHashCode();
|
||||
return hashCode;
|
||||
}
|
||||
}
|
||||
|
||||
public static bool operator ==(MigrationEventArgs left, MigrationEventArgs right)
|
||||
{
|
||||
return Equals(left, right);
|
||||
}
|
||||
|
||||
public static bool operator !=(MigrationEventArgs left, MigrationEventArgs right)
|
||||
{
|
||||
return !Equals(left, right);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4,7 +4,7 @@ using System.Linq;
|
||||
|
||||
namespace Umbraco.Core.Events
|
||||
{
|
||||
public class MoveEventArgs<TEntity> : CancellableObjectEventArgs<TEntity>
|
||||
public class MoveEventArgs<TEntity> : CancellableObjectEventArgs<TEntity>, IEquatable<MoveEventArgs<TEntity>>
|
||||
{
|
||||
/// <summary>
|
||||
/// Constructor accepting a collection of MoveEventInfo objects
|
||||
@@ -123,5 +123,38 @@ namespace Umbraco.Core.Events
|
||||
/// </summary>
|
||||
[Obsolete("Retrieve the ParentId from the MoveInfoCollection property instead")]
|
||||
public int ParentId { get; private set; }
|
||||
|
||||
public bool Equals(MoveEventArgs<TEntity> other)
|
||||
{
|
||||
if (ReferenceEquals(null, other)) return false;
|
||||
if (ReferenceEquals(this, other)) return true;
|
||||
return base.Equals(other) && MoveInfoCollection.Equals(other.MoveInfoCollection);
|
||||
}
|
||||
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
if (ReferenceEquals(null, obj)) return false;
|
||||
if (ReferenceEquals(this, obj)) return true;
|
||||
if (obj.GetType() != this.GetType()) return false;
|
||||
return Equals((MoveEventArgs<TEntity>) obj);
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
unchecked
|
||||
{
|
||||
return (base.GetHashCode() * 397) ^ MoveInfoCollection.GetHashCode();
|
||||
}
|
||||
}
|
||||
|
||||
public static bool operator ==(MoveEventArgs<TEntity> left, MoveEventArgs<TEntity> right)
|
||||
{
|
||||
return Equals(left, right);
|
||||
}
|
||||
|
||||
public static bool operator !=(MoveEventArgs<TEntity> left, MoveEventArgs<TEntity> right)
|
||||
{
|
||||
return !Equals(left, right);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,9 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Umbraco.Core.Events
|
||||
{
|
||||
public class MoveEventInfo<TEntity>
|
||||
public class MoveEventInfo<TEntity> : IEquatable<MoveEventInfo<TEntity>>
|
||||
{
|
||||
public MoveEventInfo(TEntity entity, string originalPath, int newParentId)
|
||||
{
|
||||
@@ -12,5 +15,41 @@ namespace Umbraco.Core.Events
|
||||
public TEntity Entity { get; set; }
|
||||
public string OriginalPath { get; set; }
|
||||
public int NewParentId { get; set; }
|
||||
|
||||
public bool Equals(MoveEventInfo<TEntity> other)
|
||||
{
|
||||
if (ReferenceEquals(null, other)) return false;
|
||||
if (ReferenceEquals(this, other)) return true;
|
||||
return EqualityComparer<TEntity>.Default.Equals(Entity, other.Entity) && NewParentId == other.NewParentId && string.Equals(OriginalPath, other.OriginalPath);
|
||||
}
|
||||
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
if (ReferenceEquals(null, obj)) return false;
|
||||
if (ReferenceEquals(this, obj)) return true;
|
||||
if (obj.GetType() != this.GetType()) return false;
|
||||
return Equals((MoveEventInfo<TEntity>) obj);
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
unchecked
|
||||
{
|
||||
var hashCode = EqualityComparer<TEntity>.Default.GetHashCode(Entity);
|
||||
hashCode = (hashCode * 397) ^ NewParentId;
|
||||
hashCode = (hashCode * 397) ^ OriginalPath.GetHashCode();
|
||||
return hashCode;
|
||||
}
|
||||
}
|
||||
|
||||
public static bool operator ==(MoveEventInfo<TEntity> left, MoveEventInfo<TEntity> right)
|
||||
{
|
||||
return Equals(left, right);
|
||||
}
|
||||
|
||||
public static bool operator !=(MoveEventInfo<TEntity> left, MoveEventInfo<TEntity> right)
|
||||
{
|
||||
return !Equals(left, right);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,10 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Umbraco.Core.Models;
|
||||
|
||||
namespace Umbraco.Core.Events
|
||||
{
|
||||
public class NewEventArgs<TEntity> : CancellableObjectEventArgs<TEntity>
|
||||
public class NewEventArgs<TEntity> : CancellableObjectEventArgs<TEntity>, IEquatable<NewEventArgs<TEntity>>
|
||||
{
|
||||
|
||||
|
||||
@@ -84,5 +86,42 @@ namespace Umbraco.Core.Events
|
||||
/// Gets or Sets the parent IContent object.
|
||||
/// </summary>
|
||||
public TEntity Parent { get; private set; }
|
||||
|
||||
public bool Equals(NewEventArgs<TEntity> other)
|
||||
{
|
||||
if (ReferenceEquals(null, other)) return false;
|
||||
if (ReferenceEquals(this, other)) return true;
|
||||
return base.Equals(other) && string.Equals(Alias, other.Alias) && EqualityComparer<TEntity>.Default.Equals(Parent, other.Parent) && ParentId == other.ParentId;
|
||||
}
|
||||
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
if (ReferenceEquals(null, obj)) return false;
|
||||
if (ReferenceEquals(this, obj)) return true;
|
||||
if (obj.GetType() != this.GetType()) return false;
|
||||
return Equals((NewEventArgs<TEntity>) obj);
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
unchecked
|
||||
{
|
||||
int hashCode = base.GetHashCode();
|
||||
hashCode = (hashCode * 397) ^ Alias.GetHashCode();
|
||||
hashCode = (hashCode * 397) ^ EqualityComparer<TEntity>.Default.GetHashCode(Parent);
|
||||
hashCode = (hashCode * 397) ^ ParentId;
|
||||
return hashCode;
|
||||
}
|
||||
}
|
||||
|
||||
public static bool operator ==(NewEventArgs<TEntity> left, NewEventArgs<TEntity> right)
|
||||
{
|
||||
return Equals(left, right);
|
||||
}
|
||||
|
||||
public static bool operator !=(NewEventArgs<TEntity> left, NewEventArgs<TEntity> right)
|
||||
{
|
||||
return !Equals(left, right);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace Umbraco.Core.Events
|
||||
{
|
||||
/// <summary>
|
||||
/// This event manager supports event cancellation and will raise the events as soon as they are tracked, it does not store tracked events
|
||||
/// </summary>
|
||||
internal class PassThroughEventDispatcher : IEventDispatcher
|
||||
{
|
||||
public bool DispatchCancelable(EventHandler eventHandler, object sender, CancellableEventArgs args, string eventName = null)
|
||||
{
|
||||
if (eventHandler == null) return args.Cancel;
|
||||
eventHandler(sender, args);
|
||||
return args.Cancel;
|
||||
}
|
||||
|
||||
public bool DispatchCancelable<TArgs>(EventHandler<TArgs> eventHandler, object sender, TArgs args, string eventName = null)
|
||||
where TArgs : CancellableEventArgs
|
||||
{
|
||||
if (eventHandler == null) return args.Cancel;
|
||||
eventHandler(sender, args);
|
||||
return args.Cancel;
|
||||
}
|
||||
|
||||
public bool DispatchCancelable<TSender, TArgs>(TypedEventHandler<TSender, TArgs> eventHandler, TSender sender, TArgs args, string eventName = null)
|
||||
where TArgs : CancellableEventArgs
|
||||
{
|
||||
if (eventHandler == null) return args.Cancel;
|
||||
eventHandler(sender, args);
|
||||
return args.Cancel;
|
||||
}
|
||||
|
||||
public void Dispatch(EventHandler eventHandler, object sender, EventArgs args, string eventName = null)
|
||||
{
|
||||
if (eventHandler == null) return;
|
||||
eventHandler(sender, args);
|
||||
}
|
||||
|
||||
public void Dispatch<TArgs>(EventHandler<TArgs> eventHandler, object sender, TArgs args, string eventName = null)
|
||||
{
|
||||
if (eventHandler == null) return;
|
||||
eventHandler(sender, args);
|
||||
}
|
||||
|
||||
public void Dispatch<TSender, TArgs>(TypedEventHandler<TSender, TArgs> eventHandler, TSender sender, TArgs args, string eventName = null)
|
||||
{
|
||||
if (eventHandler == null) return;
|
||||
eventHandler(sender, args);
|
||||
}
|
||||
|
||||
public IEnumerable<IEventDefinition> GetEvents(EventDefinitionFilter filter)
|
||||
{
|
||||
return Enumerable.Empty<IEventDefinition>();
|
||||
}
|
||||
|
||||
public void ScopeExit(bool completed)
|
||||
{ }
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,9 @@
|
||||
using System.Collections.Generic;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Umbraco.Core.Events
|
||||
{
|
||||
public class PublishEventArgs<TEntity> : CancellableObjectEventArgs<IEnumerable<TEntity>>
|
||||
public class PublishEventArgs<TEntity> : CancellableObjectEventArgs<IEnumerable<TEntity>>, IEquatable<PublishEventArgs<TEntity>>
|
||||
{
|
||||
/// <summary>
|
||||
/// Constructor accepting multiple entities that are used in the publish operation
|
||||
@@ -101,5 +102,38 @@ namespace Umbraco.Core.Events
|
||||
}
|
||||
|
||||
public bool IsAllRepublished { get; private set; }
|
||||
|
||||
public bool Equals(PublishEventArgs<TEntity> other)
|
||||
{
|
||||
if (ReferenceEquals(null, other)) return false;
|
||||
if (ReferenceEquals(this, other)) return true;
|
||||
return base.Equals(other) && IsAllRepublished == other.IsAllRepublished;
|
||||
}
|
||||
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
if (ReferenceEquals(null, obj)) return false;
|
||||
if (ReferenceEquals(this, obj)) return true;
|
||||
if (obj.GetType() != this.GetType()) return false;
|
||||
return Equals((PublishEventArgs<TEntity>) obj);
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
unchecked
|
||||
{
|
||||
return (base.GetHashCode() * 397) ^ IsAllRepublished.GetHashCode();
|
||||
}
|
||||
}
|
||||
|
||||
public static bool operator ==(PublishEventArgs<TEntity> left, PublishEventArgs<TEntity> right)
|
||||
{
|
||||
return Equals(left, right);
|
||||
}
|
||||
|
||||
public static bool operator !=(PublishEventArgs<TEntity> left, PublishEventArgs<TEntity> right)
|
||||
{
|
||||
return !Equals(left, right);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -5,7 +5,7 @@ using Umbraco.Core.Models;
|
||||
|
||||
namespace Umbraco.Core.Events
|
||||
{
|
||||
public class RecycleBinEventArgs : CancellableEventArgs
|
||||
public class RecycleBinEventArgs : CancellableEventArgs, IEquatable<RecycleBinEventArgs>, IDeletingMediaFilesEventArgs
|
||||
{
|
||||
public RecycleBinEventArgs(Guid nodeObjectType, Dictionary<int, IEnumerable<Property>> allPropertyData, bool emptiedSuccessfully)
|
||||
: base(false)
|
||||
@@ -97,6 +97,8 @@ namespace Umbraco.Core.Events
|
||||
/// </remarks>
|
||||
public List<string> Files { get; private set; }
|
||||
|
||||
public List<string> MediaFilesToDelete { get { return Files; } }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the list of all property data associated with a content id
|
||||
/// </summary>
|
||||
@@ -122,5 +124,44 @@ namespace Umbraco.Core.Events
|
||||
{
|
||||
get { return NodeObjectType == new Guid(Constants.ObjectTypes.Media); }
|
||||
}
|
||||
|
||||
public bool Equals(RecycleBinEventArgs other)
|
||||
{
|
||||
if (ReferenceEquals(null, other)) return false;
|
||||
if (ReferenceEquals(this, other)) return true;
|
||||
return base.Equals(other) && AllPropertyData.Equals(other.AllPropertyData) && Files.Equals(other.Files) && Ids.Equals(other.Ids) && NodeObjectType.Equals(other.NodeObjectType) && RecycleBinEmptiedSuccessfully == other.RecycleBinEmptiedSuccessfully;
|
||||
}
|
||||
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
if (ReferenceEquals(null, obj)) return false;
|
||||
if (ReferenceEquals(this, obj)) return true;
|
||||
if (obj.GetType() != this.GetType()) return false;
|
||||
return Equals((RecycleBinEventArgs) obj);
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
unchecked
|
||||
{
|
||||
int hashCode = base.GetHashCode();
|
||||
hashCode = (hashCode * 397) ^ AllPropertyData.GetHashCode();
|
||||
hashCode = (hashCode * 397) ^ Files.GetHashCode();
|
||||
hashCode = (hashCode * 397) ^ Ids.GetHashCode();
|
||||
hashCode = (hashCode * 397) ^ NodeObjectType.GetHashCode();
|
||||
hashCode = (hashCode * 397) ^ RecycleBinEmptiedSuccessfully.GetHashCode();
|
||||
return hashCode;
|
||||
}
|
||||
}
|
||||
|
||||
public static bool operator ==(RecycleBinEventArgs left, RecycleBinEventArgs right)
|
||||
{
|
||||
return Equals(left, right);
|
||||
}
|
||||
|
||||
public static bool operator !=(RecycleBinEventArgs left, RecycleBinEventArgs right)
|
||||
{
|
||||
return !Equals(left, right);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -17,5 +17,7 @@ namespace Umbraco.Core.Events
|
||||
{
|
||||
get { return EventObject; }
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
@@ -116,5 +116,7 @@ namespace Umbraco.Core.Events
|
||||
{
|
||||
get { return EventObject; }
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
using Umbraco.Core.IO;
|
||||
|
||||
namespace Umbraco.Core.Events
|
||||
{
|
||||
/// <summary>
|
||||
/// This event manager is created for each scope and is aware of if it is nested in an outer scope
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The outer scope is the only scope that can raise events, the inner scope's will defer to the outer scope
|
||||
/// </remarks>
|
||||
internal class ScopeEventDispatcher : ScopeEventDispatcherBase
|
||||
{
|
||||
public ScopeEventDispatcher()
|
||||
: base(true)
|
||||
{ }
|
||||
|
||||
protected override void ScopeExitCompleted()
|
||||
{
|
||||
// fixme - we'd need to de-duplicate events somehow, etc - and the deduplication should be last in wins
|
||||
|
||||
foreach (var e in GetEvents(EventDefinitionFilter.All))
|
||||
{
|
||||
e.RaiseEvent();
|
||||
|
||||
// fixme - not sure I like doing it here - but then where? how?
|
||||
var delete = e.Args as IDeletingMediaFilesEventArgs;
|
||||
if (delete != null && delete.MediaFilesToDelete.Count > 0)
|
||||
MediaFileSystem.DeleteMediaFiles(delete.MediaFilesToDelete);
|
||||
}
|
||||
}
|
||||
|
||||
private MediaFileSystem _mediaFileSystem;
|
||||
|
||||
private MediaFileSystem MediaFileSystem
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_mediaFileSystem != null) return _mediaFileSystem;
|
||||
|
||||
// fixme - insane! reading config goes cross AppDomain and serializes context?
|
||||
using (new SafeCallContext())
|
||||
{
|
||||
return _mediaFileSystem = FileSystemProviderManager.Current.MediaFileSystem;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,101 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace Umbraco.Core.Events
|
||||
{
|
||||
public abstract class ScopeEventDispatcherBase : IEventDispatcher
|
||||
{
|
||||
private List<IEventDefinition> _events;
|
||||
private readonly bool _raiseCancelable;
|
||||
|
||||
protected ScopeEventDispatcherBase(bool raiseCancelable)
|
||||
{
|
||||
_raiseCancelable = raiseCancelable;
|
||||
}
|
||||
|
||||
private List<IEventDefinition> Events { get { return _events ?? (_events = new List<IEventDefinition>()); } }
|
||||
|
||||
public bool DispatchCancelable(EventHandler eventHandler, object sender, CancellableEventArgs args, string eventName = null)
|
||||
{
|
||||
if (eventHandler == null) return args.Cancel;
|
||||
if (_raiseCancelable == false) return args.Cancel;
|
||||
eventHandler(sender, args);
|
||||
return args.Cancel;
|
||||
}
|
||||
|
||||
public bool DispatchCancelable<TArgs>(EventHandler<TArgs> eventHandler, object sender, TArgs args, string eventName = null)
|
||||
where TArgs : CancellableEventArgs
|
||||
{
|
||||
if (eventHandler == null) return args.Cancel;
|
||||
if (_raiseCancelable == false) return args.Cancel;
|
||||
eventHandler(sender, args);
|
||||
return args.Cancel;
|
||||
}
|
||||
|
||||
public bool DispatchCancelable<TSender, TArgs>(TypedEventHandler<TSender, TArgs> eventHandler, TSender sender, TArgs args, string eventName = null)
|
||||
where TArgs : CancellableEventArgs
|
||||
{
|
||||
if (eventHandler == null) return args.Cancel;
|
||||
if (_raiseCancelable == false) return args.Cancel;
|
||||
eventHandler(sender, args);
|
||||
return args.Cancel;
|
||||
}
|
||||
|
||||
public void Dispatch(EventHandler eventHandler, object sender, EventArgs args, string eventName = null)
|
||||
{
|
||||
if (eventHandler == null) return;
|
||||
Events.Add(new EventDefinition(eventHandler, sender, args, eventName));
|
||||
}
|
||||
|
||||
public void Dispatch<TArgs>(EventHandler<TArgs> eventHandler, object sender, TArgs args, string eventName = null)
|
||||
{
|
||||
if (eventHandler == null) return;
|
||||
Events.Add(new EventDefinition<TArgs>(eventHandler, sender, args, eventName));
|
||||
}
|
||||
|
||||
public void Dispatch<TSender, TArgs>(TypedEventHandler<TSender, TArgs> eventHandler, TSender sender, TArgs args, string eventName = null)
|
||||
{
|
||||
if (eventHandler == null) return;
|
||||
Events.Add(new EventDefinition<TSender, TArgs>(eventHandler, sender, args, eventName));
|
||||
}
|
||||
|
||||
public IEnumerable<IEventDefinition> GetEvents(EventDefinitionFilter filter)
|
||||
{
|
||||
if (_events == null)
|
||||
return Enumerable.Empty<IEventDefinition>();
|
||||
|
||||
switch (filter)
|
||||
{
|
||||
case EventDefinitionFilter.All:
|
||||
return _events;
|
||||
case EventDefinitionFilter.FirstIn:
|
||||
var l1 = new OrderedHashSet<IEventDefinition>();
|
||||
foreach (var e in _events)
|
||||
{
|
||||
l1.Add(e);
|
||||
}
|
||||
return l1;
|
||||
case EventDefinitionFilter.LastIn:
|
||||
var l2 = new OrderedHashSet<IEventDefinition>(keepOldest: false);
|
||||
foreach (var e in _events)
|
||||
{
|
||||
l2.Add(e);
|
||||
}
|
||||
return l2;
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException("filter", filter, null);
|
||||
}
|
||||
}
|
||||
|
||||
public void ScopeExit(bool completed)
|
||||
{
|
||||
if (_events == null) return;
|
||||
if (completed)
|
||||
ScopeExitCompleted();
|
||||
_events.Clear();
|
||||
}
|
||||
|
||||
protected abstract void ScopeExitCompleted();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
using System;
|
||||
using Umbraco.Core.Scoping;
|
||||
|
||||
namespace Umbraco.Core.Events
|
||||
{
|
||||
/// <summary>
|
||||
/// Stores the instance of EventMessages in the current scope.
|
||||
/// </summary>
|
||||
internal class ScopeLifespanMessagesFactory : IEventMessagesFactory
|
||||
{
|
||||
public const string ContextKey = "Umbraco.Core.Events.ScopeLifespanMessagesFactory";
|
||||
|
||||
private readonly IHttpContextAccessor _contextAccessor;
|
||||
private readonly IScopeProviderInternal _scopeProvider;
|
||||
|
||||
public static ScopeLifespanMessagesFactory Current { get; private set; }
|
||||
|
||||
public ScopeLifespanMessagesFactory(IHttpContextAccessor contextAccesor, IScopeProvider scopeProvider)
|
||||
{
|
||||
if (contextAccesor == null) throw new ArgumentNullException("contextAccesor");
|
||||
if (scopeProvider == null) throw new ArgumentNullException("scopeProvider");
|
||||
if (scopeProvider is IScopeProviderInternal == false) throw new ArgumentException("Not IScopeProviderInternal.", "scopeProvider");
|
||||
_contextAccessor = contextAccesor;
|
||||
_scopeProvider = (IScopeProviderInternal) scopeProvider;
|
||||
Current = this;
|
||||
}
|
||||
|
||||
public EventMessages Get()
|
||||
{
|
||||
var messages = GetFromHttpContext();
|
||||
if (messages != null) return messages;
|
||||
|
||||
var scope = _scopeProvider.GetAmbientOrNoScope();
|
||||
return scope.Messages;
|
||||
}
|
||||
|
||||
public EventMessages GetFromHttpContext()
|
||||
{
|
||||
if (_contextAccessor == null || _contextAccessor.Value == null) return null;
|
||||
return (EventMessages)_contextAccessor.Value.Items[ContextKey];
|
||||
}
|
||||
|
||||
public EventMessages TryGet()
|
||||
{
|
||||
var messages = GetFromHttpContext();
|
||||
if (messages != null) return messages;
|
||||
|
||||
var scope = _scopeProvider.AmbientScope;
|
||||
return scope == null ? null : scope.MessagesOrNull;
|
||||
}
|
||||
|
||||
public void Set(EventMessages messages)
|
||||
{
|
||||
if (_contextAccessor.Value == null) return;
|
||||
_contextAccessor.Value.Items[ContextKey] = messages;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,6 +2,6 @@ using System;
|
||||
|
||||
namespace Umbraco.Core.Events
|
||||
{
|
||||
[Serializable]
|
||||
[Serializable]
|
||||
public delegate void TypedEventHandler<in TSender, in TEventArgs>(TSender sender, TEventArgs e);
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
using System.Collections.Generic;
|
||||
using Umbraco.Core.Packaging.Models;
|
||||
|
||||
namespace Umbraco.Core.Events
|
||||
{
|
||||
internal class UninstallPackageEventArgs<TEntity> : CancellableObjectEventArgs<IEnumerable<TEntity>>
|
||||
{
|
||||
private readonly MetaData _packageMetaData;
|
||||
|
||||
public UninstallPackageEventArgs(TEntity eventObject, bool canCancel)
|
||||
: base(new[] { eventObject }, canCancel)
|
||||
{
|
||||
}
|
||||
|
||||
public UninstallPackageEventArgs(TEntity eventObject, MetaData packageMetaData)
|
||||
: base(new[] { eventObject })
|
||||
{
|
||||
_packageMetaData = packageMetaData;
|
||||
}
|
||||
|
||||
public MetaData PackageMetaData
|
||||
{
|
||||
get { return _packageMetaData; }
|
||||
}
|
||||
|
||||
public IEnumerable<TEntity> UninstallationSummary
|
||||
{
|
||||
get { return EventObject; }
|
||||
}
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -1,7 +1,7 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// <auto-generated>
|
||||
// This code was generated by a tool.
|
||||
// Runtime Version:4.0.30319.0
|
||||
// Runtime Version:4.0.30319.42000
|
||||
//
|
||||
// Changes to this file may cause incorrect behavior and will be lost if
|
||||
// the code is regenerated.
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
using System.Web;
|
||||
|
||||
namespace Umbraco.Core
|
||||
{
|
||||
public interface IHttpContextAccessor
|
||||
{
|
||||
HttpContextBase Value { get; }
|
||||
}
|
||||
}
|
||||
@@ -5,9 +5,10 @@ using System.Configuration;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using Umbraco.Core.Configuration;
|
||||
using Umbraco.Core.Scoping;
|
||||
|
||||
namespace Umbraco.Core.IO
|
||||
{
|
||||
{
|
||||
public class FileSystemProviderManager
|
||||
{
|
||||
private readonly FileSystemProvidersSection _config;
|
||||
@@ -40,6 +41,12 @@ namespace Umbraco.Core.IO
|
||||
get { return Instance; }
|
||||
}
|
||||
|
||||
private IScopeProviderInternal ScopeProvider
|
||||
{
|
||||
// fixme - 'course this is bad, but enough for now
|
||||
get { return ApplicationContext.Current == null ? null : ApplicationContext.Current.ScopeProvider as IScopeProviderInternal; }
|
||||
}
|
||||
|
||||
internal FileSystemProviderManager()
|
||||
{
|
||||
_config = (FileSystemProvidersSection) ConfigurationManager.GetSection("umbracoConfiguration/FileSystemProviders");
|
||||
@@ -52,13 +59,13 @@ namespace Umbraco.Core.IO
|
||||
_masterPagesFileSystem = new PhysicalFileSystem(SystemDirectories.Masterpages);
|
||||
_mvcViewsFileSystem = new PhysicalFileSystem(SystemDirectories.MvcViews);
|
||||
|
||||
_macroPartialFileSystem = _macroPartialFileSystemWrapper = new ShadowWrapper(_macroPartialFileSystem, "Views/MacroPartials");
|
||||
_partialViewsFileSystem = _partialViewsFileSystemWrapper = new ShadowWrapper(_partialViewsFileSystem, "Views/Partials");
|
||||
_stylesheetsFileSystem = _stylesheetsFileSystemWrapper = new ShadowWrapper(_stylesheetsFileSystem, "css");
|
||||
_scriptsFileSystem = _scriptsFileSystemWrapper = new ShadowWrapper(_scriptsFileSystem, "scripts");
|
||||
_xsltFileSystem = _xsltFileSystemWrapper = new ShadowWrapper(_xsltFileSystem, "xslt");
|
||||
_masterPagesFileSystem = _masterPagesFileSystemWrapper = new ShadowWrapper(_masterPagesFileSystem, "masterpages");
|
||||
_mvcViewsFileSystem = _mvcViewsFileSystemWrapper = new ShadowWrapper(_mvcViewsFileSystem, "Views");
|
||||
_macroPartialFileSystem = _macroPartialFileSystemWrapper = new ShadowWrapper(_macroPartialFileSystem, "Views/MacroPartials", ScopeProvider);
|
||||
_partialViewsFileSystem = _partialViewsFileSystemWrapper = new ShadowWrapper(_partialViewsFileSystem, "Views/Partials", ScopeProvider);
|
||||
_stylesheetsFileSystem = _stylesheetsFileSystemWrapper = new ShadowWrapper(_stylesheetsFileSystem, "css", ScopeProvider);
|
||||
_scriptsFileSystem = _scriptsFileSystemWrapper = new ShadowWrapper(_scriptsFileSystem, "scripts", ScopeProvider);
|
||||
_xsltFileSystem = _xsltFileSystemWrapper = new ShadowWrapper(_xsltFileSystem, "xslt", ScopeProvider);
|
||||
_masterPagesFileSystem = _masterPagesFileSystemWrapper = new ShadowWrapper(_masterPagesFileSystem, "masterpages", ScopeProvider);
|
||||
_mvcViewsFileSystem = _mvcViewsFileSystemWrapper = new ShadowWrapper(_mvcViewsFileSystem, "Views", ScopeProvider);
|
||||
|
||||
// filesystems obtained from GetFileSystemProvider are already wrapped and do not need to be wrapped again
|
||||
MediaFileSystem = GetFileSystemProvider<MediaFileSystem>();
|
||||
@@ -92,7 +99,7 @@ namespace Umbraco.Core.IO
|
||||
}
|
||||
|
||||
private readonly ConcurrentDictionary<string, ProviderConstructionInfo> _providerLookup = new ConcurrentDictionary<string, ProviderConstructionInfo>();
|
||||
private readonly ConcurrentDictionary<Type, string> _aliases = new ConcurrentDictionary<Type, string>();
|
||||
private readonly ConcurrentDictionary<Type, string> _aliases = new ConcurrentDictionary<Type, string>();
|
||||
|
||||
/// <summary>
|
||||
/// Gets an underlying (non-typed) filesystem supporting a strongly-typed filesystem.
|
||||
@@ -121,7 +128,7 @@ namespace Umbraco.Core.IO
|
||||
|
||||
// find a ctor matching the config parameters
|
||||
var paramCount = providerConfig.Parameters != null ? providerConfig.Parameters.Count : 0;
|
||||
var constructor = providerType.GetConstructors().SingleOrDefault(x
|
||||
var constructor = providerType.GetConstructors().SingleOrDefault(x
|
||||
=> x.GetParameters().Length == paramCount && x.GetParameters().All(y => providerConfig.Parameters.AllKeys.Contains(y.Name)));
|
||||
if (constructor == null)
|
||||
throw new InvalidOperationException(string.Format("Type {0} has no ctor matching the {1} configuration parameter(s).", providerType.FullName, paramCount));
|
||||
@@ -129,7 +136,7 @@ namespace Umbraco.Core.IO
|
||||
var parameters = new object[paramCount];
|
||||
if (providerConfig.Parameters != null) // keeps ReSharper happy
|
||||
for (var i = 0; i < paramCount; i++)
|
||||
parameters[i] = providerConfig.Parameters[providerConfig.Parameters.AllKeys[i]].Value;
|
||||
parameters[i] = providerConfig.Parameters[providerConfig.Parameters.AllKeys[i]].Value;
|
||||
|
||||
return new ProviderConstructionInfo
|
||||
{
|
||||
@@ -159,7 +166,7 @@ namespace Umbraco.Core.IO
|
||||
var alias = _aliases.GetOrAdd(typeof (TFileSystem), fsType =>
|
||||
{
|
||||
// validate the ctor
|
||||
var constructor = fsType.GetConstructors().SingleOrDefault(x
|
||||
var constructor = fsType.GetConstructors().SingleOrDefault(x
|
||||
=> x.GetParameters().Length == 1 && TypeHelper.IsTypeAssignableFrom<IFileSystem>(x.GetParameters().Single().ParameterType));
|
||||
if (constructor == null)
|
||||
throw new InvalidOperationException("Type " + fsType.FullName + " must inherit from FileSystemWrapper and have a constructor that accepts one parameter of type " + typeof(IFileSystem).FullName + ".");
|
||||
@@ -176,8 +183,8 @@ namespace Umbraco.Core.IO
|
||||
// so we are double-wrapping here
|
||||
// could be optimized by having FileSystemWrapper inherit from ShadowWrapper, maybe
|
||||
var innerFs = GetUnderlyingFileSystemProvider(alias);
|
||||
var shadowWrapper = new ShadowWrapper(innerFs, "typed/" + alias);
|
||||
var fs = (TFileSystem) Activator.CreateInstance(typeof (TFileSystem), innerFs);
|
||||
var shadowWrapper = new ShadowWrapper(innerFs, "typed/" + alias, ScopeProvider);
|
||||
var fs = (TFileSystem) Activator.CreateInstance(typeof (TFileSystem), shadowWrapper);
|
||||
_wrappers.Add(shadowWrapper); // keeping a weak reference to the wrapper
|
||||
return fs;
|
||||
}
|
||||
@@ -186,25 +193,7 @@ namespace Umbraco.Core.IO
|
||||
|
||||
#region Shadow
|
||||
|
||||
// note
|
||||
// shadowing is thread-safe, but entering and exiting shadow mode is not, and there is only one
|
||||
// global shadow for the entire application, so great care should be taken to ensure that the
|
||||
// application is *not* doing anything else when using a shadow.
|
||||
// shadow applies to well-known filesystems *only* - at the moment, any other filesystem that would
|
||||
// be created directly (via ctor) or via GetFileSystemProvider<T> is *not* shadowed.
|
||||
|
||||
// shadow must be enabled in an app event handler before anything else ie before any filesystem
|
||||
// is actually created and used - after, it is too late - enabling shadow has a neglictible perfs
|
||||
// impact.
|
||||
// NO! by the time an app event handler is instanciated it is already too late, see note in ctor.
|
||||
//internal void EnableShadow()
|
||||
//{
|
||||
// if (_mvcViewsFileSystem != null) // test one of the fs...
|
||||
// throw new InvalidOperationException("Cannot enable shadow once filesystems have been created.");
|
||||
// _shadowEnabled = true;
|
||||
//}
|
||||
|
||||
public ICompletable Shadow(Guid id)
|
||||
internal ICompletable Shadow(Guid id)
|
||||
{
|
||||
var typed = _wrappers.ToArray();
|
||||
var wrappers = new ShadowWrapper[typed.Length + 7];
|
||||
@@ -218,7 +207,7 @@ namespace Umbraco.Core.IO
|
||||
wrappers[i++] = _masterPagesFileSystemWrapper;
|
||||
wrappers[i] = _mvcViewsFileSystemWrapper;
|
||||
|
||||
return ShadowFileSystemsScope.CreateScope(id, wrappers);
|
||||
return new ShadowFileSystems(id, wrappers);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
@@ -109,5 +109,24 @@ namespace Umbraco.Core.IO
|
||||
var wrapped2 = Wrapped as IFileSystem2;
|
||||
return wrapped2 == null ? Wrapped.GetSize(path) : wrapped2.GetSize(path);
|
||||
}
|
||||
|
||||
// explicitely implementing - not breaking
|
||||
bool IFileSystem2.CanAddPhysical
|
||||
{
|
||||
get
|
||||
{
|
||||
var wrapped2 = Wrapped as IFileSystem2;
|
||||
return wrapped2 != null && wrapped2.CanAddPhysical;
|
||||
}
|
||||
}
|
||||
|
||||
// explicitely implementing - not breaking
|
||||
void IFileSystem2.AddFile(string path, string physicalPath, bool overrideIfExists, bool copy)
|
||||
{
|
||||
var wrapped2 = Wrapped as IFileSystem2;
|
||||
if (wrapped2 == null)
|
||||
throw new NotSupportedException();
|
||||
wrapped2.AddFile(path, physicalPath, overrideIfExists, copy);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -44,6 +44,10 @@ namespace Umbraco.Core.IO
|
||||
{
|
||||
long GetSize(string path);
|
||||
|
||||
bool CanAddPhysical { get; }
|
||||
|
||||
void AddFile(string path, string physicalPath, bool overrideIfExists = true, bool copy = false);
|
||||
|
||||
// TODO: implement these
|
||||
//
|
||||
//void CreateDirectory(string path);
|
||||
|
||||
@@ -4,11 +4,11 @@ using System.Globalization;
|
||||
using System.Reflection;
|
||||
using System.IO;
|
||||
using System.Configuration;
|
||||
using System.Linq;
|
||||
using System.Web;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Web.Hosting;
|
||||
using Umbraco.Core.Configuration;
|
||||
using Umbraco.Core.Logging;
|
||||
|
||||
namespace Umbraco.Core.IO
|
||||
{
|
||||
@@ -351,7 +351,54 @@ namespace Umbraco.Core.IO
|
||||
writer.Write(contents);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if a given path is a full path including drive letter
|
||||
/// </summary>
|
||||
/// <param name="path"></param>
|
||||
/// <returns></returns>
|
||||
// From: http://stackoverflow.com/a/35046453/5018
|
||||
internal static bool IsFullPath(this string path)
|
||||
{
|
||||
return string.IsNullOrWhiteSpace(path) == false
|
||||
&& path.IndexOfAny(Path.GetInvalidPathChars().ToArray()) == -1
|
||||
&& Path.IsPathRooted(path)
|
||||
&& Path.GetPathRoot(path).Equals(Path.DirectorySeparatorChar.ToString(), StringComparison.Ordinal) == false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get properly formatted relative path from an existing absolute or relative path
|
||||
/// </summary>
|
||||
/// <param name="path"></param>
|
||||
/// <returns></returns>
|
||||
internal static string GetRelativePath(this string path)
|
||||
{
|
||||
if (path.IsFullPath())
|
||||
{
|
||||
var rootDirectory = GetRootDirectorySafe();
|
||||
var relativePath = path.ToLowerInvariant().Replace(rootDirectory.ToLowerInvariant(), string.Empty);
|
||||
path = relativePath;
|
||||
}
|
||||
|
||||
return path.EnsurePathIsApplicationRootPrefixed();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Ensures that a path has `~/` as prefix
|
||||
/// </summary>
|
||||
/// <param name="path"></param>
|
||||
/// <returns></returns>
|
||||
internal static string EnsurePathIsApplicationRootPrefixed(this string path)
|
||||
{
|
||||
if (path.StartsWith("~/"))
|
||||
return path;
|
||||
if (path.StartsWith("/") == false && path.StartsWith("\\") == false)
|
||||
path = string.Format("/{0}", path);
|
||||
if (path.StartsWith("~") == false)
|
||||
path = string.Format("~{0}", path);
|
||||
return path;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Umbraco.Core.Configuration;
|
||||
using Umbraco.Core.Configuration.UmbracoSettings;
|
||||
using Umbraco.Core.Logging;
|
||||
@@ -24,6 +25,7 @@ namespace Umbraco.Core.IO
|
||||
{
|
||||
private readonly IContentSection _contentConfig;
|
||||
private readonly UploadAutoFillProperties _uploadAutoFillProperties;
|
||||
private readonly ILogger _logger;
|
||||
|
||||
private readonly object _folderCounterLock = new object();
|
||||
private long _folderCounter;
|
||||
@@ -42,6 +44,7 @@ namespace Umbraco.Core.IO
|
||||
public MediaFileSystem(IFileSystem wrapped, IContentSection contentConfig, ILogger logger)
|
||||
: base(wrapped)
|
||||
{
|
||||
_logger = logger;
|
||||
_contentConfig = contentConfig;
|
||||
_uploadAutoFillProperties = new UploadAutoFillProperties(this, logger, contentConfig);
|
||||
}
|
||||
@@ -99,7 +102,7 @@ namespace Umbraco.Core.IO
|
||||
}
|
||||
else
|
||||
{
|
||||
// new scheme: path is "<cuid>-<puid>/<filename>" OR "<cuid>-<puid>-<filename>"
|
||||
// new scheme: path is "<xuid>/<filename>" where xuid is a combination of cuid and puid
|
||||
// default media filesystem maps to "~/media/<filepath>"
|
||||
// assumes that cuid and puid keys can be trusted - and that a single property type
|
||||
// for a single content cannot store two different files with the same name
|
||||
@@ -435,6 +438,64 @@ namespace Umbraco.Core.IO
|
||||
}
|
||||
}
|
||||
|
||||
public void DeleteMediaFiles(IEnumerable<string> files)
|
||||
{
|
||||
files = files.Distinct();
|
||||
|
||||
Parallel.ForEach(files, file =>
|
||||
{
|
||||
try
|
||||
{
|
||||
if (file.IsNullOrWhiteSpace()) return;
|
||||
|
||||
if (FileExists(file) == false) return;
|
||||
DeleteFile(file, true);
|
||||
|
||||
if (UseTheNewMediaPathScheme == false)
|
||||
{
|
||||
// old scheme: filepath is "<int>/<filename>" OR "<int>-<filename>"
|
||||
// remove the directory if any
|
||||
var dir = Path.GetDirectoryName(file);
|
||||
if (string.IsNullOrWhiteSpace(dir) == false)
|
||||
DeleteDirectory(dir, true);
|
||||
}
|
||||
else
|
||||
{
|
||||
// new scheme: path is "<xuid>/<filename>" where xuid is a combination of cuid and puid
|
||||
// remove the directory
|
||||
var dir = Path.GetDirectoryName(file);
|
||||
DeleteDirectory(dir, true);
|
||||
}
|
||||
|
||||
// I don't even understand...
|
||||
/*
|
||||
|
||||
var relativeFilePath = GetRelativePath(file); // fixme - should be relative already
|
||||
if (FileExists(relativeFilePath) == false) return;
|
||||
|
||||
var parentDirectory = Path.GetDirectoryName(relativeFilePath);
|
||||
|
||||
// don't want to delete the media folder if not using directories.
|
||||
if (_contentSection.UploadAllowDirectories && parentDirectory != GetRelativePath("/"))
|
||||
{
|
||||
//issue U4-771: if there is a parent directory the recursive parameter should be true
|
||||
DeleteDirectory(parentDirectory, string.IsNullOrEmpty(parentDirectory) == false);
|
||||
}
|
||||
else
|
||||
{
|
||||
DeleteFile(file, true);
|
||||
}
|
||||
|
||||
*/
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
_logger.Error<MediaFileSystem>("Failed to delete attached file \"" + file + "\".", e);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
#endregion
|
||||
|
||||
#region GenerateThumbnails
|
||||
|
||||
@@ -364,6 +364,29 @@ namespace Umbraco.Core.IO
|
||||
return file.Exists ? file.Length : -1;
|
||||
}
|
||||
|
||||
public bool CanAddPhysical { get { return true; } }
|
||||
|
||||
public void AddFile(string path, string physicalPath, bool overrideIfExists = true, bool copy = false)
|
||||
{
|
||||
var fullPath = GetFullPath(path);
|
||||
|
||||
if (File.Exists(fullPath))
|
||||
{
|
||||
if (overrideIfExists == false)
|
||||
throw new InvalidOperationException(string.Format("A file at path '{0}' already exists", path));
|
||||
File.Delete(fullPath);
|
||||
}
|
||||
|
||||
var directory = Path.GetDirectoryName(fullPath);
|
||||
if (directory == null) throw new InvalidOperationException("Could not get directory.");
|
||||
Directory.CreateDirectory(directory); // ensure it exists
|
||||
|
||||
if (copy)
|
||||
File.Copy(physicalPath, fullPath);
|
||||
else
|
||||
File.Move(physicalPath, fullPath);
|
||||
}
|
||||
|
||||
#region Helper Methods
|
||||
|
||||
protected virtual void EnsureDirectory(string path)
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace Umbraco.Core.IO
|
||||
{
|
||||
@@ -33,8 +34,16 @@ namespace Umbraco.Core.IO
|
||||
{
|
||||
try
|
||||
{
|
||||
using (var stream = _sfs.OpenFile(kvp.Key))
|
||||
_fs.AddFile(kvp.Key, stream, true);
|
||||
var fs2 = _fs as IFileSystem2;
|
||||
if (fs2 != null && fs2.CanAddPhysical)
|
||||
{
|
||||
fs2.AddFile(kvp.Key, _sfs.GetFullPath(kvp.Key)); // overwrite, move
|
||||
}
|
||||
else
|
||||
{
|
||||
using (var stream = _sfs.OpenFile(kvp.Key))
|
||||
_fs.AddFile(kvp.Key, stream, true);
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
@@ -201,19 +210,20 @@ namespace Umbraco.Core.IO
|
||||
|
||||
public IEnumerable<string> GetFiles(string path)
|
||||
{
|
||||
var normPath = NormPath(path);
|
||||
var shadows = Nodes.Where(kvp => IsChild(normPath, kvp.Key)).ToArray();
|
||||
var files = _fs.GetFiles(path);
|
||||
return files
|
||||
.Except(shadows.Where(kvp => (kvp.Value.IsFile && kvp.Value.IsDelete) || kvp.Value.IsDir)
|
||||
.Select(kvp => kvp.Key))
|
||||
.Union(shadows.Where(kvp => kvp.Value.IsFile && kvp.Value.IsExist).Select(kvp => kvp.Key))
|
||||
.Distinct();
|
||||
return GetFiles(path, null);
|
||||
}
|
||||
|
||||
public IEnumerable<string> GetFiles(string path, string filter)
|
||||
{
|
||||
return _fs.GetFiles(path, filter);
|
||||
var normPath = NormPath(path);
|
||||
var shadows = Nodes.Where(kvp => IsChild(normPath, kvp.Key)).ToArray();
|
||||
var files = filter != null ? _fs.GetFiles(path, filter) : _fs.GetFiles(path);
|
||||
var regexFilter = FilterToRegex(filter);
|
||||
return files
|
||||
.Except(shadows.Where(kvp => (kvp.Value.IsFile && kvp.Value.IsDelete) || kvp.Value.IsDir)
|
||||
.Select(kvp => kvp.Key))
|
||||
.Union(shadows.Where(kvp => kvp.Value.IsFile && kvp.Value.IsExist && FilterByRegex(kvp.Key, regexFilter)).Select(kvp => kvp.Key))
|
||||
.Distinct();
|
||||
}
|
||||
|
||||
public Stream OpenFile(string path)
|
||||
@@ -245,6 +255,9 @@ namespace Umbraco.Core.IO
|
||||
|
||||
public string GetFullPath(string path)
|
||||
{
|
||||
ShadowNode sf;
|
||||
if (Nodes.TryGetValue(NormPath(path), out sf))
|
||||
return sf.IsDir || sf.IsDelete ? null : _sfs.GetFullPath(path);
|
||||
return _fs.GetFullPath(path);
|
||||
}
|
||||
|
||||
@@ -282,5 +295,63 @@ namespace Umbraco.Core.IO
|
||||
if (sf.IsDelete || sf.IsDir) throw new InvalidOperationException("Invalid path.");
|
||||
return _sfs.GetSize(path);
|
||||
}
|
||||
|
||||
public bool CanAddPhysical { get { return true; } }
|
||||
|
||||
public void AddFile(string path, string physicalPath, bool overrideIfExists = true, bool copy = false)
|
||||
{
|
||||
ShadowNode sf;
|
||||
var normPath = NormPath(path);
|
||||
if (Nodes.TryGetValue(normPath, out sf) && sf.IsExist && (sf.IsDir || overrideIfExists == false))
|
||||
throw new InvalidOperationException(string.Format("A file at path '{0}' already exists", path));
|
||||
|
||||
var parts = normPath.Split('/');
|
||||
for (var i = 0; i < parts.Length - 1; i++)
|
||||
{
|
||||
var dirPath = string.Join("/", parts.Take(i + 1));
|
||||
ShadowNode sd;
|
||||
if (Nodes.TryGetValue(dirPath, out sd))
|
||||
{
|
||||
if (sd.IsFile) throw new InvalidOperationException("Invalid path.");
|
||||
if (sd.IsDelete) Nodes[dirPath] = new ShadowNode(false, true);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (_fs.DirectoryExists(dirPath)) continue;
|
||||
if (_fs.FileExists(dirPath)) throw new InvalidOperationException("Invalid path.");
|
||||
Nodes[dirPath] = new ShadowNode(false, true);
|
||||
}
|
||||
}
|
||||
|
||||
_sfs.AddFile(path, physicalPath, overrideIfExists, copy);
|
||||
Nodes[normPath] = new ShadowNode(false, false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Helper function for filtering keys by Regex if a filter is specified.
|
||||
/// </summary>
|
||||
/// <param name="input"></param>
|
||||
/// <param name="regexFilter"></param>
|
||||
/// <returns></returns>
|
||||
internal static bool FilterByRegex(string input, string regexFilter)
|
||||
{
|
||||
if (regexFilter == null) return true;
|
||||
return regexFilter != string.Empty && Regex.IsMatch(input, regexFilter);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Transforms a filter pattern into a Regex pattern
|
||||
/// </summary>
|
||||
/// <param name="pattern"></param>
|
||||
/// <returns></returns>
|
||||
/// <remarks>
|
||||
/// Appending '$' only if not containing wildcard is stupid and broken.
|
||||
/// It is however what seems to be what they're doing in .NET so we need to match the functionality.
|
||||
/// </remarks>
|
||||
internal static string FilterToRegex(string pattern)
|
||||
{
|
||||
if (pattern == null) return null;
|
||||
return "^" + Regex.Escape(pattern).Replace("\\*", ".*").Replace("\\?", ".") + (pattern.Contains("*") ? string.Empty : "$");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,73 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Umbraco.Core.Logging;
|
||||
|
||||
namespace Umbraco.Core.IO
|
||||
{
|
||||
internal class ShadowFileSystems : ICompletable
|
||||
{
|
||||
// note: taking a reference to the _manager instead of using manager.Current
|
||||
// to avoid using Current everywhere but really, we support only 1 scope at
|
||||
// a time, not multiple scopes in case of multiple managers (not supported)
|
||||
|
||||
private static readonly object Locker = new object();
|
||||
private static Guid _currentId = Guid.Empty;
|
||||
private readonly Guid _id;
|
||||
private readonly ShadowWrapper[] _wrappers;
|
||||
private bool _completed;
|
||||
|
||||
public ShadowFileSystems(Guid id, ShadowWrapper[] wrappers)
|
||||
{
|
||||
lock (Locker)
|
||||
{
|
||||
if (_currentId != Guid.Empty)
|
||||
throw new InvalidOperationException("Already shadowing.");
|
||||
_currentId = id;
|
||||
|
||||
LogHelper.Debug<ShadowFileSystems>("Shadow " + id + ".");
|
||||
_id = id;
|
||||
_wrappers = wrappers;
|
||||
foreach (var wrapper in _wrappers)
|
||||
wrapper.Shadow(id);
|
||||
}
|
||||
}
|
||||
|
||||
public void Complete()
|
||||
{
|
||||
_completed = true;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
lock (Locker)
|
||||
{
|
||||
LogHelper.Debug<ShadowFileSystems>("UnShadow " + _id + " (" + (_completed ? "complete" : "abort") + ").");
|
||||
|
||||
var exceptions = new List<Exception>();
|
||||
foreach (var wrapper in _wrappers)
|
||||
{
|
||||
try
|
||||
{
|
||||
// this may throw an AggregateException if some of the changes could not be applied
|
||||
wrapper.UnShadow(_completed);
|
||||
}
|
||||
catch (AggregateException ae)
|
||||
{
|
||||
exceptions.Add(ae);
|
||||
}
|
||||
}
|
||||
|
||||
_currentId = Guid.Empty;
|
||||
|
||||
if (exceptions.Count > 0)
|
||||
throw new AggregateException(_completed ? "Failed to apply all changes (see exceptions)." : "Failed to abort (see exceptions).", exceptions);
|
||||
}
|
||||
}
|
||||
|
||||
// for tests
|
||||
internal static void ResetId()
|
||||
{
|
||||
_currentId = Guid.Empty;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,114 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.Remoting.Messaging;
|
||||
using Umbraco.Core.Logging;
|
||||
|
||||
namespace Umbraco.Core.IO
|
||||
{
|
||||
internal class ShadowFileSystemsScope : ICompletable
|
||||
{
|
||||
// note: taking a reference to the _manager instead of using manager.Current
|
||||
// to avoid using Current everywhere but really, we support only 1 scope at
|
||||
// a time, not multiple scopes in case of multiple managers (not supported)
|
||||
|
||||
private const string ItemKey = "Umbraco.Core.IO.ShadowFileSystemsScope";
|
||||
private static readonly object Locker = new object();
|
||||
private readonly Guid _id;
|
||||
private readonly ShadowWrapper[] _wrappers;
|
||||
|
||||
static ShadowFileSystemsScope()
|
||||
{
|
||||
SafeCallContext.Register(
|
||||
() =>
|
||||
{
|
||||
var scope = CallContext.LogicalGetData(ItemKey);
|
||||
CallContext.FreeNamedDataSlot(ItemKey);
|
||||
return scope;
|
||||
},
|
||||
o =>
|
||||
{
|
||||
if (CallContext.LogicalGetData(ItemKey) != null) throw new InvalidOperationException();
|
||||
if (o != null) CallContext.LogicalSetData(ItemKey, o);
|
||||
});
|
||||
}
|
||||
|
||||
private ShadowFileSystemsScope(Guid id, ShadowWrapper[] wrappers)
|
||||
{
|
||||
LogHelper.Debug<ShadowFileSystemsScope>("Shadow " + id + ".");
|
||||
_id = id;
|
||||
_wrappers = wrappers;
|
||||
foreach (var wrapper in _wrappers)
|
||||
wrapper.Shadow(id);
|
||||
}
|
||||
|
||||
// internal for tests + FileSystemProviderManager
|
||||
// do NOT use otherwise
|
||||
internal static ShadowFileSystemsScope CreateScope(Guid id, ShadowWrapper[] wrappers)
|
||||
{
|
||||
lock (Locker)
|
||||
{
|
||||
if (CallContext.LogicalGetData(ItemKey) != null) throw new InvalidOperationException("Already shadowing.");
|
||||
CallContext.LogicalSetData(ItemKey, ItemKey); // value does not matter
|
||||
}
|
||||
return new ShadowFileSystemsScope(id, wrappers);
|
||||
}
|
||||
|
||||
internal static bool InScope
|
||||
{
|
||||
get { return NoScope == false; }
|
||||
}
|
||||
|
||||
internal static bool NoScope
|
||||
{
|
||||
get { return CallContext.LogicalGetData(ItemKey) == null; }
|
||||
}
|
||||
|
||||
public void Complete()
|
||||
{
|
||||
lock (Locker)
|
||||
{
|
||||
LogHelper.Debug<ShadowFileSystemsScope>("UnShadow " + _id + " (complete).");
|
||||
|
||||
var exceptions = new List<Exception>();
|
||||
foreach (var wrapper in _wrappers)
|
||||
{
|
||||
try
|
||||
{
|
||||
// this may throw an AggregateException if some of the changes could not be applied
|
||||
wrapper.UnShadow(true);
|
||||
}
|
||||
catch (AggregateException ae)
|
||||
{
|
||||
exceptions.Add(ae);
|
||||
}
|
||||
}
|
||||
|
||||
if (exceptions.Count > 0)
|
||||
throw new AggregateException("Failed to apply all changes (see exceptions).", exceptions);
|
||||
|
||||
// last, & *only* if successful (otherwise we'll unshadow & cleanup as best as we can)
|
||||
CallContext.FreeNamedDataSlot(ItemKey);
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
lock (Locker)
|
||||
{
|
||||
if (CallContext.LogicalGetData(ItemKey) == null) return;
|
||||
|
||||
try
|
||||
{
|
||||
LogHelper.Debug<ShadowFileSystemsScope>("UnShadow " + _id + " (abort)");
|
||||
foreach (var wrapper in _wrappers)
|
||||
wrapper.UnShadow(false); // should not throw
|
||||
}
|
||||
finally
|
||||
{
|
||||
// last, & always
|
||||
CallContext.FreeNamedDataSlot(ItemKey);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,20 +2,23 @@
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using Umbraco.Core.Scoping;
|
||||
|
||||
namespace Umbraco.Core.IO
|
||||
{
|
||||
internal class ShadowWrapper : IFileSystem2
|
||||
{
|
||||
private readonly IScopeProviderInternal _scopeProvider;
|
||||
private readonly IFileSystem _innerFileSystem;
|
||||
private readonly string _shadowPath;
|
||||
private ShadowFileSystem _shadowFileSystem;
|
||||
private string _shadowDir;
|
||||
|
||||
public ShadowWrapper(IFileSystem innerFileSystem, string shadowPath)
|
||||
public ShadowWrapper(IFileSystem innerFileSystem, string shadowPath, IScopeProviderInternal scopeProvider)
|
||||
{
|
||||
_innerFileSystem = innerFileSystem;
|
||||
_shadowPath = shadowPath;
|
||||
_scopeProvider = scopeProvider;
|
||||
}
|
||||
|
||||
internal void Shadow(Guid id)
|
||||
@@ -62,7 +65,14 @@ namespace Umbraco.Core.IO
|
||||
|
||||
private IFileSystem FileSystem
|
||||
{
|
||||
get { return ShadowFileSystemsScope.NoScope ? _innerFileSystem : _shadowFileSystem; }
|
||||
get
|
||||
{
|
||||
var isScoped = _scopeProvider != null && _scopeProvider.AmbientScope != null && _scopeProvider.AmbientScope.ScopedFileSystems;
|
||||
|
||||
return isScoped
|
||||
? _shadowFileSystem
|
||||
: _innerFileSystem;
|
||||
}
|
||||
}
|
||||
|
||||
public IEnumerable<string> GetDirectories(string path)
|
||||
@@ -154,5 +164,22 @@ namespace Umbraco.Core.IO
|
||||
var filesystem2 = filesystem as IFileSystem2;
|
||||
return filesystem2 == null ? filesystem.GetSize(path) : filesystem2.GetSize(path);
|
||||
}
|
||||
|
||||
public bool CanAddPhysical
|
||||
{
|
||||
get
|
||||
{
|
||||
var fileSystem2 = FileSystem as IFileSystem2;
|
||||
return fileSystem2 != null && fileSystem2.CanAddPhysical;
|
||||
}
|
||||
}
|
||||
|
||||
public void AddFile(string path, string physicalPath, bool overrideIfExists = true, bool copy = false)
|
||||
{
|
||||
var fileSystem2 = FileSystem as IFileSystem2;
|
||||
if (fileSystem2 == null)
|
||||
throw new NotSupportedException();
|
||||
fileSystem2.AddFile(path, physicalPath, overrideIfExists, copy);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -400,7 +400,7 @@ namespace Umbraco.Core.Models
|
||||
/// <summary>
|
||||
/// Set property values by alias with an annonymous object
|
||||
/// </summary>
|
||||
public static void PropertyValues(this IContent content, object value)
|
||||
public static void PropertyValues(this IContentBase content, object value)
|
||||
{
|
||||
if (value == null)
|
||||
throw new Exception("No properties has been passed in");
|
||||
|
||||
@@ -1,32 +1,73 @@
|
||||
using System.Collections.Generic;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Umbraco.Core.Services;
|
||||
|
||||
namespace Umbraco.Core.Models
|
||||
{
|
||||
//fixme: This whole thing needs to go, it's super hacky and doens't need to exist in the first place
|
||||
internal static class ContentTypeExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Get all descendant content types
|
||||
/// </summary>
|
||||
/// <param name="contentType"></param>
|
||||
/// <param name="contentTypeService"></param>
|
||||
/// <returns></returns>
|
||||
public static IEnumerable<IContentTypeBase> Descendants(this IContentTypeBase contentType)
|
||||
public static IEnumerable<IContentTypeBase> Descendants(this IContentTypeBase contentType, IContentTypeService contentTypeService)
|
||||
{
|
||||
var contentTypeService = ApplicationContext.Current.Services.ContentTypeService;
|
||||
var descendants = contentTypeService.GetContentTypeChildren(contentType.Id)
|
||||
.SelectRecursive(type => contentTypeService.GetContentTypeChildren(type.Id));
|
||||
return descendants;
|
||||
if (contentType is IContentType)
|
||||
{
|
||||
var descendants = contentTypeService.GetContentTypeChildren(contentType.Id)
|
||||
.SelectRecursive(type => contentTypeService.GetContentTypeChildren(type.Id));
|
||||
return descendants;
|
||||
}
|
||||
|
||||
if (contentType is IMediaType)
|
||||
{
|
||||
var descendants = contentTypeService.GetMediaTypeChildren(contentType.Id)
|
||||
.SelectRecursive(type => contentTypeService.GetMediaTypeChildren(type.Id));
|
||||
return descendants;
|
||||
}
|
||||
|
||||
//No other content types have children (i.e. member types)
|
||||
return Enumerable.Empty<IContentTypeBase>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get all descendant content types
|
||||
/// </summary>
|
||||
/// <param name="contentType"></param>
|
||||
/// <param name="contentTypeService"></param>
|
||||
/// <returns></returns>
|
||||
public static IEnumerable<IContentTypeBase> Descendants(this IContentTypeBase contentType, ContentTypeServiceBase contentTypeService)
|
||||
{
|
||||
var cService = contentTypeService as IContentTypeService;
|
||||
|
||||
return cService == null ? Enumerable.Empty<IContentTypeBase>() : contentType.Descendants(cService);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get all descendant and self content types
|
||||
/// </summary>
|
||||
/// <param name="contentType"></param>
|
||||
/// <param name="contentTypeService"></param>
|
||||
/// <returns></returns>
|
||||
public static IEnumerable<IContentTypeBase> DescendantsAndSelf(this IContentTypeBase contentType)
|
||||
public static IEnumerable<IContentTypeBase> DescendantsAndSelf(this IContentTypeBase contentType, IContentTypeService contentTypeService)
|
||||
{
|
||||
var descendantsAndSelf = new[] { contentType }.Concat(contentType.Descendants());
|
||||
var descendantsAndSelf = new[] { contentType }.Concat(contentType.Descendants(contentTypeService));
|
||||
return descendantsAndSelf;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get all descendant and self content types
|
||||
/// </summary>
|
||||
/// <param name="contentType"></param>
|
||||
/// <param name="contentTypeService"></param>
|
||||
/// <returns></returns>
|
||||
public static IEnumerable<IContentTypeBase> DescendantsAndSelf(this IContentTypeBase contentType, ContentTypeServiceBase contentTypeService)
|
||||
{
|
||||
var descendantsAndSelf = new[] { contentType }.Concat(contentType.Descendants(contentTypeService));
|
||||
return descendantsAndSelf;
|
||||
}
|
||||
|
||||
|
||||
@@ -13,6 +13,7 @@ namespace Umbraco.Core.Models.Identity
|
||||
public override void ConfigureMappings(IConfiguration config, ApplicationContext applicationContext)
|
||||
{
|
||||
config.CreateMap<IUser, BackOfficeIdentityUser>()
|
||||
.ForMember(user => user.LastLoginDateUtc, expression => expression.MapFrom(user => user.LastLoginDate.ToUniversalTime()))
|
||||
.ForMember(user => user.Email, expression => expression.MapFrom(user => user.Email))
|
||||
.ForMember(user => user.Id, expression => expression.MapFrom(user => user.Id))
|
||||
.ForMember(user => user.LockoutEndDateUtc, expression => expression.MapFrom(user => user.IsLockedOut ? DateTime.MaxValue.ToUniversalTime() : (DateTime?)null))
|
||||
|
||||
@@ -24,12 +24,17 @@ namespace Umbraco.Core.Models.Identity
|
||||
///
|
||||
/// </summary>
|
||||
public IdentityUser()
|
||||
{
|
||||
{
|
||||
this.Claims = new List<TClaim>();
|
||||
this.Roles = new List<TRole>();
|
||||
this.Logins = new List<TLogin>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Last login date
|
||||
/// </summary>
|
||||
public virtual DateTime? LastLoginDateUtc { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Email
|
||||
///
|
||||
|
||||
@@ -106,12 +106,13 @@ namespace Umbraco.Core.Models.PublishedContent
|
||||
/// <param name="propertyTypeAlias">The property type alias.</param>
|
||||
/// <param name="dataTypeDefinitionId">The datatype definition identifier.</param>
|
||||
/// <param name="propertyEditorAlias">The property editor alias.</param>
|
||||
/// <param name="initConverters">Generally used only for testing, in production this will always be true</param>
|
||||
/// <remarks>
|
||||
/// <para>The new published property type does not belong to a published content type.</para>
|
||||
/// <para>The values of <paramref name="dataTypeDefinitionId"/> and <paramref name="propertyEditorAlias"/> are
|
||||
/// assumed to be valid and consistent.</para>
|
||||
/// </remarks>
|
||||
internal PublishedPropertyType(string propertyTypeAlias, int dataTypeDefinitionId, string propertyEditorAlias)
|
||||
internal PublishedPropertyType(string propertyTypeAlias, int dataTypeDefinitionId, string propertyEditorAlias, bool initConverters = true)
|
||||
{
|
||||
// ContentType
|
||||
// - in unit tests, to be set by PublishedContentType when creating it
|
||||
@@ -122,7 +123,8 @@ namespace Umbraco.Core.Models.PublishedContent
|
||||
DataTypeId = dataTypeDefinitionId;
|
||||
PropertyEditorAlias = propertyEditorAlias;
|
||||
|
||||
InitializeConverters();
|
||||
if (initConverters)
|
||||
InitializeConverters();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
@@ -9,7 +9,7 @@ namespace Umbraco.Core.Models.Rdbms
|
||||
internal class DataTypePreValueDto
|
||||
{
|
||||
[Column("id")]
|
||||
[PrimaryKeyColumn(IdentitySeed = 6)]
|
||||
[PrimaryKeyColumn(IdentitySeed = 10)]
|
||||
public int Id { get; set; }
|
||||
|
||||
[Column("datatypeNodeId")]
|
||||
|
||||
@@ -22,6 +22,7 @@ namespace Umbraco.Core.Models.Rdbms
|
||||
[Column("LoginName")]
|
||||
[Length(1000)]
|
||||
[Constraint(Default = "''")]
|
||||
[Index(IndexTypes.NonClustered, Name = "IX_cmsMember_LoginName")]
|
||||
public string LoginName { get; set; }
|
||||
|
||||
[Column("Password")]
|
||||
|
||||
@@ -35,6 +35,7 @@ namespace Umbraco.Core.Models.Rdbms
|
||||
|
||||
[Column("path")]
|
||||
[Length(150)]
|
||||
[Index(IndexTypes.NonClustered, Name = "IX_umbracoNodePath")]
|
||||
public string Path { get; set; }
|
||||
|
||||
[Column("sortOrder")]
|
||||
|
||||
@@ -16,6 +16,7 @@ namespace Umbraco.Core.Models.Rdbms
|
||||
|
||||
[Column("parentId")]
|
||||
[ForeignKey(typeof(NodeDto), Name = "FK_umbracoRelation_umbracoNode")]
|
||||
[Index(IndexTypes.UniqueNonClustered, Name = "IX_umbracoRelation_parentChildType", ForColumns = "parentId,childId,relType")]
|
||||
public int ParentId { get; set; }
|
||||
|
||||
[Column("childId")]
|
||||
|
||||
@@ -29,11 +29,13 @@ namespace Umbraco.Core.Models.Rdbms
|
||||
public Guid ChildObjectType { get; set; }
|
||||
|
||||
[Column("name")]
|
||||
[Index(IndexTypes.UniqueNonClustered, Name = "IX_umbracoRelationType_name")]
|
||||
public string Name { get; set; }
|
||||
|
||||
[Column("alias")]
|
||||
[NullSetting(NullSetting = NullSettings.Null)]
|
||||
[Length(100)]
|
||||
[Index(IndexTypes.UniqueNonClustered, Name = "IX_umbracoRelationType_alias")]
|
||||
public string Alias { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -15,6 +15,7 @@ namespace Umbraco.Core.Models.Rdbms
|
||||
|
||||
[Column("nodeId")]
|
||||
[ForeignKey(typeof(NodeDto))]
|
||||
[Index(IndexTypes.NonClustered, Name = "IX_umbracoUser2NodePermission_nodeId")]
|
||||
public int NodeId { get; set; }
|
||||
|
||||
[Column("permission")]
|
||||
|
||||
@@ -113,6 +113,11 @@ namespace Umbraco.Core.Models
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// When resolved from EntityService this checks if the entity has the HasChildren flag
|
||||
/// </summary>
|
||||
/// <param name="entity"></param>
|
||||
/// <returns></returns>
|
||||
public static bool HasChildren(this IUmbracoEntity entity)
|
||||
{
|
||||
if (entity.AdditionalData.ContainsKey("HasChildren"))
|
||||
@@ -133,6 +138,11 @@ namespace Umbraco.Core.Models
|
||||
return entity.AdditionalData.GetValueIgnoreCase(key, defaultVal);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// When resolved from EntityService this checks if the entity has the IsContainer flag
|
||||
/// </summary>
|
||||
/// <param name="entity"></param>
|
||||
/// <returns></returns>
|
||||
public static bool IsContainer(this IUmbracoEntity entity)
|
||||
{
|
||||
if (entity.AdditionalData.ContainsKeyIgnoreCase("IsContainer") == false) return false;
|
||||
|
||||
@@ -26,7 +26,7 @@ namespace Umbraco.Core.Models
|
||||
/// <summary>
|
||||
/// Root
|
||||
/// </summary>
|
||||
[UmbracoObjectType(Constants.ObjectTypes.SystemRoot)]
|
||||
[UmbracoObjectType(Constants.ObjectTypes.SystemRoot)]
|
||||
[FriendlyName("Root")]
|
||||
ROOT,
|
||||
|
||||
@@ -35,6 +35,7 @@ namespace Umbraco.Core.Models
|
||||
/// </summary>
|
||||
[UmbracoObjectType(Constants.ObjectTypes.Document, typeof(IContent))]
|
||||
[FriendlyName("Document")]
|
||||
[UmbracoUdiType(Constants.UdiEntityType.Document)]
|
||||
Document,
|
||||
|
||||
/// <summary>
|
||||
@@ -42,6 +43,7 @@ namespace Umbraco.Core.Models
|
||||
/// </summary>
|
||||
[UmbracoObjectType(Constants.ObjectTypes.Media, typeof(IMedia))]
|
||||
[FriendlyName("Media")]
|
||||
[UmbracoUdiType(Constants.UdiEntityType.Media)]
|
||||
Media,
|
||||
|
||||
/// <summary>
|
||||
@@ -49,6 +51,7 @@ namespace Umbraco.Core.Models
|
||||
/// </summary>
|
||||
[UmbracoObjectType(Constants.ObjectTypes.MemberType, typeof(IMemberType))]
|
||||
[FriendlyName("Member Type")]
|
||||
[UmbracoUdiType(Constants.UdiEntityType.MemberType)]
|
||||
MemberType,
|
||||
|
||||
/// <summary>
|
||||
@@ -56,6 +59,7 @@ namespace Umbraco.Core.Models
|
||||
/// </summary>
|
||||
[UmbracoObjectType(Constants.ObjectTypes.Template, typeof(ITemplate))]
|
||||
[FriendlyName("Template")]
|
||||
[UmbracoUdiType(Constants.UdiEntityType.Template)]
|
||||
Template,
|
||||
|
||||
/// <summary>
|
||||
@@ -63,6 +67,7 @@ namespace Umbraco.Core.Models
|
||||
/// </summary>
|
||||
[UmbracoObjectType(Constants.ObjectTypes.MemberGroup)]
|
||||
[FriendlyName("Member Group")]
|
||||
[UmbracoUdiType(Constants.UdiEntityType.MemberGroup)]
|
||||
MemberGroup,
|
||||
|
||||
//TODO: What is a 'Content Item' supposed to be???
|
||||
@@ -80,6 +85,7 @@ namespace Umbraco.Core.Models
|
||||
/// </summary>
|
||||
[UmbracoObjectType(Constants.ObjectTypes.MediaType, typeof(IMediaType))]
|
||||
[FriendlyName("Media Type")]
|
||||
[UmbracoUdiType(Constants.UdiEntityType.MediaType)]
|
||||
MediaType,
|
||||
|
||||
/// <summary>
|
||||
@@ -87,13 +93,14 @@ namespace Umbraco.Core.Models
|
||||
/// </summary>
|
||||
[UmbracoObjectType(Constants.ObjectTypes.DocumentType, typeof(IContentType))]
|
||||
[FriendlyName("Document Type")]
|
||||
[UmbracoUdiType(Constants.UdiEntityType.DocumentType)]
|
||||
DocumentType,
|
||||
|
||||
/// <summary>
|
||||
/// Recycle Bin
|
||||
/// </summary>
|
||||
[UmbracoObjectType(Constants.ObjectTypes.ContentRecycleBin)]
|
||||
[FriendlyName("Recycle Bin")]
|
||||
[FriendlyName("Recycle Bin")]
|
||||
RecycleBin,
|
||||
|
||||
/// <summary>
|
||||
@@ -101,6 +108,7 @@ namespace Umbraco.Core.Models
|
||||
/// </summary>
|
||||
[UmbracoObjectType(Constants.ObjectTypes.Stylesheet)]
|
||||
[FriendlyName("Stylesheet")]
|
||||
[UmbracoUdiType(Constants.UdiEntityType.Stylesheet)]
|
||||
Stylesheet,
|
||||
|
||||
/// <summary>
|
||||
@@ -108,6 +116,7 @@ namespace Umbraco.Core.Models
|
||||
/// </summary>
|
||||
[UmbracoObjectType(Constants.ObjectTypes.Member, typeof(IMember))]
|
||||
[FriendlyName("Member")]
|
||||
[UmbracoUdiType(Constants.UdiEntityType.Member)]
|
||||
Member,
|
||||
|
||||
/// <summary>
|
||||
@@ -115,6 +124,7 @@ namespace Umbraco.Core.Models
|
||||
/// </summary>
|
||||
[UmbracoObjectType(Constants.ObjectTypes.DataType, typeof(IDataTypeDefinition))]
|
||||
[FriendlyName("Data Type")]
|
||||
[UmbracoUdiType(Constants.UdiEntityType.DataType)]
|
||||
DataType,
|
||||
|
||||
/// <summary>
|
||||
@@ -122,6 +132,7 @@ namespace Umbraco.Core.Models
|
||||
/// </summary>
|
||||
[UmbracoObjectType(Constants.ObjectTypes.DocumentTypeContainer)]
|
||||
[FriendlyName("Document Type Container")]
|
||||
[UmbracoUdiType(Constants.UdiEntityType.DocumentTypeContainer)]
|
||||
DocumentTypeContainer,
|
||||
|
||||
/// <summary>
|
||||
@@ -129,6 +140,7 @@ namespace Umbraco.Core.Models
|
||||
/// </summary>
|
||||
[UmbracoObjectType(Constants.ObjectTypes.MediaTypeContainer)]
|
||||
[FriendlyName("Media Type Container")]
|
||||
[UmbracoUdiType(Constants.UdiEntityType.MediaTypeContainer)]
|
||||
MediaTypeContainer,
|
||||
|
||||
/// <summary>
|
||||
@@ -136,6 +148,7 @@ namespace Umbraco.Core.Models
|
||||
/// </summary>
|
||||
[UmbracoObjectType(Constants.ObjectTypes.DataTypeContainer)]
|
||||
[FriendlyName("Data Type Container")]
|
||||
[UmbracoUdiType(Constants.UdiEntityType.DataTypeContainer)]
|
||||
DataTypeContainer,
|
||||
|
||||
/// <summary>
|
||||
@@ -143,6 +156,28 @@ namespace Umbraco.Core.Models
|
||||
/// </summary>
|
||||
[UmbracoObjectType(Constants.ObjectTypes.RelationType)]
|
||||
[FriendlyName("Relation Type")]
|
||||
RelationType
|
||||
[UmbracoUdiType(Constants.UdiEntityType.RelationType)]
|
||||
RelationType,
|
||||
|
||||
/// <summary>
|
||||
/// Forms Form
|
||||
/// </summary>
|
||||
[UmbracoObjectType(Constants.ObjectTypes.FormsForm)]
|
||||
[FriendlyName("Form")]
|
||||
FormsForm,
|
||||
|
||||
/// <summary>
|
||||
/// Forms PreValue
|
||||
/// </summary>
|
||||
[UmbracoObjectType(Constants.ObjectTypes.FormsPreValue)]
|
||||
[FriendlyName("PreValue")]
|
||||
FormsPreValue,
|
||||
|
||||
/// <summary>
|
||||
/// Forms DataSource
|
||||
/// </summary>
|
||||
[UmbracoObjectType(Constants.ObjectTypes.FormsDataSource)]
|
||||
[FriendlyName("DataSource")]
|
||||
FormsDataSource
|
||||
}
|
||||
}
|
||||
@@ -12,6 +12,7 @@ namespace Umbraco.Core.Models
|
||||
{
|
||||
//MUST be concurrent to avoid thread collisions!
|
||||
private static readonly ConcurrentDictionary<UmbracoObjectTypes, Guid> UmbracoObjectTypeCache = new ConcurrentDictionary<UmbracoObjectTypes, Guid>();
|
||||
private static readonly ConcurrentDictionary<UmbracoObjectTypes, string> UmbracoObjectTypeUdiCache = new ConcurrentDictionary<UmbracoObjectTypes, string>();
|
||||
|
||||
/// <summary>
|
||||
/// Get an UmbracoObjectTypes value from it's name
|
||||
@@ -43,6 +44,21 @@ namespace Umbraco.Core.Models
|
||||
return umbracoObjectType;
|
||||
}
|
||||
|
||||
public static string GetUdiType(Guid guid)
|
||||
{
|
||||
var umbracoObjectType = Constants.UdiEntityType.Unknown;
|
||||
|
||||
foreach (var name in Enum.GetNames(typeof(UmbracoObjectTypes)))
|
||||
{
|
||||
var objType = GetUmbracoObjectType(name);
|
||||
if (objType.GetGuid() == guid)
|
||||
{
|
||||
umbracoObjectType = GetUdiType(objType);
|
||||
}
|
||||
}
|
||||
return umbracoObjectType;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Extension method for the UmbracoObjectTypes enum to return the enum GUID
|
||||
/// </summary>
|
||||
@@ -68,6 +84,26 @@ namespace Umbraco.Core.Models
|
||||
});
|
||||
}
|
||||
|
||||
public static string GetUdiType(this UmbracoObjectTypes umbracoObjectType)
|
||||
{
|
||||
return UmbracoObjectTypeUdiCache.GetOrAdd(umbracoObjectType, types =>
|
||||
{
|
||||
var type = typeof(UmbracoObjectTypes);
|
||||
var memInfo = type.GetMember(umbracoObjectType.ToString());
|
||||
var attributes = memInfo[0].GetCustomAttributes(typeof(UmbracoUdiTypeAttribute),
|
||||
false);
|
||||
|
||||
if (attributes.Length == 0)
|
||||
return Constants.UdiEntityType.Unknown;
|
||||
|
||||
var attribute = ((UmbracoUdiTypeAttribute)attributes[0]);
|
||||
if (attribute == null)
|
||||
return Constants.UdiEntityType.Unknown;
|
||||
|
||||
return attribute.UdiType;
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Extension method for the UmbracoObjectTypes enum to return the enum name
|
||||
/// </summary>
|
||||
|
||||
@@ -647,5 +647,10 @@ namespace Umbraco.Core
|
||||
return "[GetPropertyValueException]";
|
||||
}
|
||||
}
|
||||
|
||||
internal static Guid AsGuid(this object value)
|
||||
{
|
||||
return value is Guid ? (Guid) value : Guid.Empty;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
using System.Collections.ObjectModel;
|
||||
|
||||
namespace Umbraco.Core
|
||||
{
|
||||
/// <summary>
|
||||
/// A custom collection similar to HashSet{T} which only contains unique items, however this collection keeps items in order
|
||||
/// and is customizable to keep the newest or oldest equatable item
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
internal class OrderedHashSet<T> : KeyedCollection<T, T>
|
||||
{
|
||||
private readonly bool _keepOldest;
|
||||
|
||||
public OrderedHashSet(bool keepOldest = true)
|
||||
{
|
||||
_keepOldest = keepOldest;
|
||||
}
|
||||
|
||||
protected override void InsertItem(int index, T item)
|
||||
{
|
||||
if (Dictionary == null)
|
||||
{
|
||||
base.InsertItem(index, item);
|
||||
}
|
||||
else
|
||||
{
|
||||
var exists = Dictionary.ContainsKey(item);
|
||||
|
||||
//if we want to keep the newest, then we need to remove the old item and add the new one
|
||||
if (exists == false)
|
||||
{
|
||||
base.InsertItem(index, item);
|
||||
}
|
||||
else if(_keepOldest == false)
|
||||
{
|
||||
if (Remove(item))
|
||||
{
|
||||
index--;
|
||||
}
|
||||
base.InsertItem(index, item);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected override T GetKeyForItem(T item)
|
||||
{
|
||||
return item;
|
||||
}
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user