Compare commits
891 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 4d3eef1124 | |||
| 3e6e6faf03 | |||
| b3f18bc677 | |||
| a48fe40675 | |||
| 1511ad58b2 | |||
| 2e9b751174 | |||
| d61ab97be3 | |||
| dd40d73b71 | |||
| 068bca84b7 | |||
| 0fda528b94 | |||
| 3552f63d87 | |||
| 01d04fde37 | |||
| 7f8c526136 | |||
| ea4be14f0c | |||
| 20df48e2f0 | |||
| 2c0f6f975c | |||
| fa7f91b39d | |||
| 72c5853fb1 | |||
| f03b1a22f9 | |||
| e7fb6e5beb | |||
| d0cc93d6a2 | |||
| 4adc2562b1 | |||
| ba6878666c | |||
| 792ab8d088 | |||
| 3d4409e52a | |||
| acd377956f | |||
| 3b6dcc3421 | |||
| cec4d41df7 | |||
| ff0bcffefa | |||
| 5dbc72897a | |||
| 83736bec01 | |||
| 2f953c3819 | |||
| 026d040a62 | |||
| 7747b8f64f | |||
| feb9f40237 | |||
| 651182f5de | |||
| 22b15a06f6 | |||
| cb09795280 | |||
| a5637af2d8 | |||
| ac59c5deb0 | |||
| 683ddf6d34 | |||
| eda33b1ac4 | |||
| ed0f884348 | |||
| b561351340 | |||
| 690ab29863 | |||
| 27cb1d5223 | |||
| c677f36f08 | |||
| 750f7a6b33 | |||
| dea804b402 | |||
| 3803d1d7da | |||
| 3e67b3034d | |||
| 1ada5210ad | |||
| 9a608674eb | |||
| 7dae412a29 | |||
| a1067e676f | |||
| ce649f54d7 | |||
| d159792256 | |||
| 606b5aa60a | |||
| a206652a0d | |||
| 545040fdf0 | |||
| 40d1a09609 | |||
| 2e25bc8fdb | |||
| 1618ac86b8 | |||
| a4adbca12c | |||
| 8728e2f918 | |||
| cd53682ddb | |||
| e2d5fbf545 | |||
| d339e7c814 | |||
| 078deb704b | |||
| 6858eb3330 | |||
| e267b7700a | |||
| 1e45abe71e | |||
| a556b4ed21 | |||
| 953c711ca4 | |||
| f06c9e8235 | |||
| f1a439e851 | |||
| a0e496e589 | |||
| e0dcc3afbc | |||
| 3634a77d61 | |||
| 846f4b5b6c | |||
| 2869f17eff | |||
| feefb052fd | |||
| 4d2d9156ba | |||
| 82ba1a24a3 | |||
| 357db9d0b6 | |||
| 61766287bc | |||
| ffbf8b2fd6 | |||
| f7a703195c | |||
| 509be9b431 | |||
| 10ef5eb7bc | |||
| 2849ecadb7 | |||
| 697f93c3ce | |||
| 18121e8726 | |||
| 81cdc71145 | |||
| 4069fc8d1f | |||
| 0a80c39962 | |||
| edee8134c2 | |||
| 30a73c0886 | |||
| f5ee02a3e9 | |||
| 9827b40bea | |||
| f9323c85e4 | |||
| 2677672e50 | |||
| a38e0dcf86 | |||
| fdd7227494 | |||
| 6b74e14cdd | |||
| 9d7b3b965a | |||
| 806008a7e9 | |||
| 45fa192c95 | |||
| 92235128f1 | |||
| 7622937cfd | |||
| bf41694572 | |||
| 7b897ce64c | |||
| 1ca950f721 | |||
| ffba271fd0 | |||
| ec08d3e910 | |||
| 0bfd032fad | |||
| 2bb58b6fca | |||
| 4d2d656b3d | |||
| fa625d72f9 | |||
| 1991380e02 | |||
| 3b8d6dc2c7 | |||
| 1faeb76c27 | |||
| 3a513668fd | |||
| 3ef51d7357 | |||
| 8b553dd067 | |||
| ccaf82d797 | |||
| f28ae52ae3 | |||
| cfd0973f00 | |||
| 1bfdd83ba7 | |||
| 6fefcacc61 | |||
| 3b639e61b2 | |||
| 9959ff667e | |||
| de1f86c7a1 | |||
| 28d8fd0531 | |||
| e4e76eb0bf | |||
| 677cc8c8fd | |||
| 3fa5c4e0b2 | |||
| 360393c464 | |||
| d3daf88a8d | |||
| 7a99ebd6d9 | |||
| a7298e2e5a | |||
| 15d7443c61 | |||
| f2b7e27b34 | |||
| 690d08aa8c | |||
| 6719d70cc0 | |||
| 3cdcf513ec | |||
| 43c426b46c | |||
| e2ac82abca | |||
| 64d1a484bd | |||
| 585b42af48 | |||
| 7c4412fc4b | |||
| 0be8039c8f | |||
| 668265f1ac | |||
| 4735f27a66 | |||
| 6377b9aa50 | |||
| 011be60205 | |||
| a272675ed8 | |||
| 80697cf9ca | |||
| f4534aa94c | |||
| 56dec485a5 | |||
| ffde0f48cf | |||
| 2956aee4e3 | |||
| 9bd8d729fa | |||
| fb9569d914 | |||
| 47974d49fe | |||
| 3f8b3031c2 | |||
| 7da41594fd | |||
| 9796b30bbb | |||
| c2c4763faa | |||
| c062d3b71b | |||
| ed83fb8216 | |||
| 326309e61e | |||
| c38030def2 | |||
| 949861a890 | |||
| 122069752a | |||
| ae83b0fbaf | |||
| 99e91a1053 | |||
| bad118ac6b | |||
| 5c614a88a2 | |||
| 3ef9635406 | |||
| 75a42fd0d6 | |||
| 84da5f43a2 | |||
| 000c2637d6 | |||
| 563867cd8b | |||
| 86c66511dd | |||
| 7d57521ad8 | |||
| e74e8f9d73 | |||
| 23b0c89848 | |||
| aebc30c7a3 | |||
| 44bc365fdd | |||
| 06defee410 | |||
| 955573256d | |||
| d963f5cd7d | |||
| 2467b5ee3b | |||
| f86eb4b741 | |||
| 33b99073f0 | |||
| 880636c5a6 | |||
| 6e344f335e | |||
| 6b205bd725 | |||
| 531306a091 | |||
| 4346f73159 | |||
| 4db6ce4b22 | |||
| 7c720460f5 | |||
| 961755336a | |||
| e0aff8a1e4 | |||
| 6fa0c627a5 | |||
| 66a63b3cbb | |||
| 21c5b6fe02 | |||
| ffed85640d | |||
| cdbf89ee57 | |||
| 056b13eb98 | |||
| 9467a67661 | |||
| 0f9f11bbcf | |||
| 5b6cc51516 | |||
| 80b83ab64b | |||
| c6751f0374 | |||
| d71c686aea | |||
| 4c5dfb42d5 | |||
| 0245efe3cb | |||
| 0b4ddf82aa | |||
| 3ff173732b | |||
| f3f7aa70d1 | |||
| cd82bc6d21 | |||
| c3a6c7c473 | |||
| 5538a2e793 | |||
| 1b7dc76df2 | |||
| 85c85cd1eb | |||
| ba5f2bc69d | |||
| 17883358b6 | |||
| fc0c6a1cd8 | |||
| 934d4020b2 | |||
| 8b03ba4f42 | |||
| 3caf84c63c | |||
| aae16f227e | |||
| 443002cdde | |||
| 75150f0bfa | |||
| af73fd8b68 | |||
| 139df63ddf | |||
| a49058fe00 | |||
| dcde00cec1 | |||
| bcc70f7a1e | |||
| 32bc2f1d69 | |||
| 4d3f67885a | |||
| a269e9d426 | |||
| af5a382f93 | |||
| dad910b8ab | |||
| 8a70c440f2 | |||
| fbdb1d5d6c | |||
| edaa5ebd43 | |||
| c12b270511 | |||
| c8fa2b3880 | |||
| 1c139b685a | |||
| 7a8b3dc9bc | |||
| a4dde1948f | |||
| 0f68818a19 | |||
| 414b65b269 | |||
| 2dd04799b2 | |||
| 1c901e00cc | |||
| c589c741e8 | |||
| c8923cbc27 | |||
| 063f405a06 | |||
| addfbcf2a7 | |||
| 8ee133bb75 | |||
| fa1185cbd8 | |||
| 68e0a414a0 | |||
| 89860bd786 | |||
| b5cfefa052 | |||
| b2c0f3c35a | |||
| a43f4b7b7f | |||
| 7ce3949852 | |||
| 4862419744 | |||
| 6b810c89f6 | |||
| 89b0a2fd89 | |||
| 8aceae9880 | |||
| 384d4d60f1 | |||
| 67caf81a74 | |||
| 763ab26446 | |||
| 67a8fa5f07 | |||
| 5c59317bf8 | |||
| a2ec53dd4f | |||
| db78c78df2 | |||
| 4558549d5a | |||
| d98801d5d1 | |||
| de9ecf6afc | |||
| 702790bf77 | |||
| c912e80134 | |||
| 098c856138 | |||
| b45306e1ff | |||
| 3499f39970 | |||
| 9115aa1a6d | |||
| 904cbbb906 | |||
| 8656d3e4b9 | |||
| 40988b4655 | |||
| 06dbea84d8 | |||
| f54cf32057 | |||
| 64dbf0207b | |||
| 206c88105e | |||
| 3177b979d3 | |||
| 4ab32dd270 | |||
| fec55e9d1c | |||
| a21aa079ff | |||
| ef4246478d | |||
| 137d859532 | |||
| 82b2821e27 | |||
| b270cefacc | |||
| b4e984fe6a | |||
| f62e4fa973 | |||
| ee8050f38f | |||
| b506b5c8e9 | |||
| d9e843f0ba | |||
| 66dd0d073c | |||
| 29c22cc4b5 | |||
| e29031a2c4 | |||
| 3ff0cca033 | |||
| fe16cc91fa | |||
| 7e6fdcb05e | |||
| d1e071b9cb | |||
| 2ce952bdbb | |||
| 6a9b8d5540 | |||
| af86a5d7b1 | |||
| 0f25029ff6 | |||
| 060e584503 | |||
| 743c0bb31c | |||
| 4bac3e19fe | |||
| b86cddda94 | |||
| 11103c0bc5 | |||
| 8210702474 | |||
| a42b660648 | |||
| f11d4fbedd | |||
| 9105c08a4c | |||
| 5aec753859 | |||
| eeaa9166ac | |||
| 751ace1168 | |||
| 51da5343ea | |||
| 34df5009e0 | |||
| e17b5e6a7b | |||
| a1c65f0518 | |||
| c5047148b3 | |||
| ed2126eef9 | |||
| ce4b56895e | |||
| 94db371234 | |||
| 103e226b51 | |||
| 57f2250f13 | |||
| bb1edd2051 | |||
| 14790559cb | |||
| 97457087c7 | |||
| 37eb66510f | |||
| 4cb4ff0617 | |||
| dc60eb8383 | |||
| 3f1b057f45 | |||
| f6179ff813 | |||
| 1735c5adca | |||
| 31e715e146 | |||
| 5f50bae65c | |||
| e6d8a83519 | |||
| 38df2017de | |||
| 33aa4e2062 | |||
| 1efa75c891 | |||
| d4514844f8 | |||
| 6b7a495d19 | |||
| 601eb0bf38 | |||
| 037e7e0797 | |||
| a7e32e97e3 | |||
| cacf87be2d | |||
| e981776e6d | |||
| 250276bbd4 | |||
| af29f61478 | |||
| 58fc51ae93 | |||
| 20813ef207 | |||
| d04f2a98b9 | |||
| f41f82adc5 | |||
| e6ae88c016 | |||
| 474dcd0754 | |||
| 369f6ac935 | |||
| 5a928aa459 | |||
| 416a9ed064 | |||
| b13c3aa3b9 | |||
| 99ae94a82f | |||
| 96440c9ee5 | |||
| b4c005c140 | |||
| eca823ea90 | |||
| 376fe0bf55 | |||
| e3cd85a6a2 | |||
| 69f805ecca | |||
| c037e2e09f | |||
| d8debf6789 | |||
| 282da69d61 | |||
| e9ed1bf17a | |||
| cfcfb75dcc | |||
| 181593f5ea | |||
| a311a54d3d | |||
| c83844468d | |||
| 80b5cfbb07 | |||
| 9911f03745 | |||
| aacb86bf92 | |||
| 59391f58ec | |||
| 0debf9c34f | |||
| 9fa17ef6cb | |||
| d227aa19b8 | |||
| d76336cd39 | |||
| e0a10080b1 | |||
| 807df434f6 | |||
| 0dc3920309 | |||
| 98d1aafb75 | |||
| d963917556 | |||
| 347d41c841 | |||
| f16aa38fff | |||
| 6a0d7ec8d6 | |||
| 62a7e0acd0 | |||
| 8e89d13142 | |||
| a5782a40af | |||
| c6af319805 | |||
| 516beedbbb | |||
| 7cd01fc50b | |||
| e9d12ae0d8 | |||
| 02e167c1f6 | |||
| baed01518c | |||
| f9a86385b5 | |||
| a0106ff811 | |||
| e0da5a9cb2 | |||
| 451d5eb484 | |||
| b8f7df1a96 | |||
| 425fd80e65 | |||
| 04bb778dc1 | |||
| 3f2be3342a | |||
| 3ba50e7bae | |||
| 7306ddef14 | |||
| 17bb29eb66 | |||
| 8bf3de738f | |||
| 2d37502a44 | |||
| d5c485205a | |||
| 9533198365 | |||
| 66f20e3800 | |||
| 9269a98806 | |||
| 7c97fddec5 | |||
| a567cf2a87 | |||
| 1e319d667b | |||
| cc039e365d | |||
| f91ff5dc31 | |||
| 9347a2edbb | |||
| 1923b68594 | |||
| 64a73ef528 | |||
| 4ea04f5ffe | |||
| 6b006e6fd7 | |||
| fc0654a80f | |||
| 4717faaf1b | |||
| e7d587f776 | |||
| dca0c85db5 | |||
| 5006115b2d | |||
| 058cec7551 | |||
| 2671709991 | |||
| ac2d8633af | |||
| 76bc312842 | |||
| 767719357e | |||
| 7b29bd67f3 | |||
| dc4a0fec80 | |||
| 4c3d700c54 | |||
| 6451f6ad68 | |||
| 7cd6882f28 | |||
| dc336728b5 | |||
| 49d5791825 | |||
| 6cfb864055 | |||
| 89c5d5d613 | |||
| 73b7729b82 | |||
| c66d49dbfe | |||
| 9a3b70149c | |||
| 98cc1c63ce | |||
| 128a08f169 | |||
| 4487e686d7 | |||
| ae32968efd | |||
| c1778918b5 | |||
| 680a4b9570 | |||
| ba6ba15732 | |||
| 291f526368 | |||
| ff61812e65 | |||
| d20ea58467 | |||
| c4f3dae317 | |||
| 4d5b330567 | |||
| a87e699f8d | |||
| 40287fd427 | |||
| 5d5f7a4516 | |||
| 61ef83c748 | |||
| f82329b371 | |||
| 0fee434ac2 | |||
| 3aab41b1e4 | |||
| 3abd4df8bd | |||
| ea239e6122 | |||
| 3fe99a402e | |||
| f9ad06be29 | |||
| e738738e54 | |||
| 699c651daa | |||
| 79299175c1 | |||
| 14e3976e88 | |||
| 76c86daf24 | |||
| a1ef40e4be | |||
| e4a0904de6 | |||
| f502912225 | |||
| c8a26bf6a3 | |||
| e06e50c4fc | |||
| 4236473b14 | |||
| 3285e161c0 | |||
| 607a52883a | |||
| 4651dfc763 | |||
| da74c0a163 | |||
| fbb7f18f23 | |||
| af43b35d17 | |||
| 72ea43758d | |||
| ee5af4998f | |||
| 565a020acf | |||
| 15bbe1b829 | |||
| c459a893a5 | |||
| 671b94dd06 | |||
| bc9dc911ed | |||
| cc8298d83c | |||
| b5c3fb1728 | |||
| 13dcfbbd2b | |||
| 18ea5b3768 | |||
| e1caf3a470 | |||
| 63a4e00f52 | |||
| cb8c1a31cb | |||
| 6c77749e32 | |||
| 4170713e29 | |||
| a3bca42117 | |||
| 80488e012f | |||
| b6cf182341 | |||
| 27e6e9b91c | |||
| 09b7186713 | |||
| c6389852ea | |||
| 49f10ccd32 | |||
| a8b037f2e0 | |||
| 17c055472d | |||
| 16bce4248d | |||
| a2e9ec0412 | |||
| 11c9abf359 | |||
| 12b7a9d874 | |||
| a90f32035d | |||
| 33b09c80aa | |||
| b2980cedc6 | |||
| 34b7f86b8b | |||
| 130f6d3a42 | |||
| 3ff9d6adbc | |||
| fd5de171c9 | |||
| 45ad51b8df | |||
| b02657f32c | |||
| 9f3784aaed | |||
| 2ab2a5a9f3 | |||
| 2d83ba06a5 | |||
| 06d5c60803 | |||
| fe18722e1f | |||
| c2e7fc2f10 | |||
| b595ec4d88 | |||
| 20ab14beb4 | |||
| a264342bcc | |||
| 08660704cc | |||
| 4c13dbf8b7 | |||
| 9a10863760 | |||
| c74cdb28d6 | |||
| 86430f1b01 | |||
| 55247fe31d | |||
| a806f40093 | |||
| 24020cfea3 | |||
| 9ec30860b0 | |||
| 06206c60d9 | |||
| 5937a7e976 | |||
| f64b8d680d | |||
| f9663a9e80 | |||
| c604cc5c2f | |||
| 520ebc3394 | |||
| e6bb999d83 | |||
| cdd1a0a4cb | |||
| 5e0fbe5634 | |||
| e5b3ca1907 | |||
| 9ddee615cc | |||
| 74f6a34651 | |||
| 7dee2802fe | |||
| b200cb463e | |||
| cdf5ddf539 | |||
| 5238293c09 | |||
| bfa7ab788b | |||
| 5a15c93195 | |||
| 77777c8007 | |||
| 5aa01e78ab | |||
| a21bee6683 | |||
| 56f1062db8 | |||
| e0878fdd9f | |||
| c9aac96c1a | |||
| 4df79ce55b | |||
| 15d2021786 | |||
| a8f6b9eeb9 | |||
| 3d4b403c0e | |||
| 957760d29a | |||
| a7603747a6 | |||
| e7754213f1 | |||
| ea82e9c67e | |||
| 182cc1ace7 | |||
| 205b9889f2 | |||
| 4a58fbfa1d | |||
| af534e87f3 | |||
| b6777e4635 | |||
| 9a66948857 | |||
| b5022bb183 | |||
| 2e9bc3bb2b | |||
| 98e487753f | |||
| 725b938de1 | |||
| 2ef4b77bd9 | |||
| fae9fc6e2d | |||
| 099c1ff2ea | |||
| 5716edcf80 | |||
| 6c32cf9529 | |||
| d925f856ae | |||
| 3de2196e01 | |||
| ac2d010df3 | |||
| eb4317a9c1 | |||
| c3caf7ff04 | |||
| f9cae9c4d8 | |||
| dd16af0989 | |||
| c118990a4c | |||
| ebf60071ab | |||
| f4b8d9bd6e | |||
| 5cf07ba6be | |||
| cb4eef54a4 | |||
| 7a541ba489 | |||
| d2647ea607 | |||
| a785cd1569 | |||
| d1f36346de | |||
| 761354dbf5 | |||
| 1e786ac311 | |||
| 0c28e01347 | |||
| 08566f32de | |||
| dc1a67bfa2 | |||
| 95df28bf64 | |||
| a027c3cb22 | |||
| f1c0aa2439 | |||
| c8fa881bc5 | |||
| b17db9f143 | |||
| 23b7e94ce3 | |||
| 8fe7c0da9e | |||
| 16ec58f338 | |||
| e8fd6a6ece | |||
| 5b943ca866 | |||
| abe3efa9a1 | |||
| ee4f4440d5 | |||
| f4d060f3eb | |||
| 85cb7fadfc | |||
| ac19ac7a6b | |||
| 0415a31d0e | |||
| 912716f889 | |||
| 5dc9bb752e | |||
| cf435011df | |||
| f63514e8ea | |||
| 1ddf2fa365 | |||
| 9d243c6c46 | |||
| 8f8da5a7ec | |||
| 7e1d8a6108 | |||
| 0fe33c120b | |||
| 10ea234dbf | |||
| 31f146fc6f | |||
| 67a632aa43 | |||
| dcd5decd3a | |||
| b6d4415f5c | |||
| 24471c32f9 | |||
| 0af73b0290 | |||
| b22fd4c5b1 | |||
| e977a8c656 | |||
| af6eb2724d | |||
| 237ee71177 | |||
| d5604ea427 | |||
| 16f8c61f28 | |||
| f066473f14 | |||
| 1a1641d300 | |||
| 3d46cd1186 | |||
| 1d200b6f2f | |||
| 86f0c7c7c9 | |||
| 72e1b5a0c6 | |||
| 94a78aec4b | |||
| f53a23553b | |||
| 42c432caa0 | |||
| e69b043cfa | |||
| 812d5c1391 | |||
| 8d510fd512 | |||
| c5c44cec37 | |||
| 82f81d1f4f | |||
| 32e3048717 | |||
| 90abe8c25f | |||
| 9f1d212375 | |||
| 9ca048f0ad | |||
| 85ab96f71a | |||
| 1ba8ad5fbb | |||
| 433e141761 | |||
| 9fa5821f07 | |||
| 447b3227a9 | |||
| ddfd9eeb18 | |||
| 6e0fa9f945 | |||
| 4d726167f6 | |||
| 1781528390 | |||
| c0214ac4ec | |||
| b2deb3def3 | |||
| 998e222881 | |||
| 58ba7d2d6d | |||
| edd9aa80ae | |||
| 7ddb77db66 | |||
| b25e015890 | |||
| 686250c586 | |||
| c5197dbe17 | |||
| d821401ea2 | |||
| 39316a345f | |||
| 349dc9a228 | |||
| 303c1e16b8 | |||
| c2b1ed205b | |||
| fee02af4eb | |||
| 6675bac1dc | |||
| 0d49a59cfe | |||
| 4770e903fb | |||
| baa22301e9 | |||
| e080965468 | |||
| 0c05836537 | |||
| c269f55074 | |||
| 177486418d | |||
| 3393b6b0da | |||
| 33bba57047 | |||
| 269f573b82 | |||
| 2ec367fd59 | |||
| 72dcb32ac2 | |||
| 1f68cad45b | |||
| 8ffbad4b49 | |||
| 24091991fa | |||
| d65e94f519 | |||
| 966416c0a5 | |||
| 09de4a3666 | |||
| f2f1d265ec | |||
| 642c6c1afe | |||
| c5945789f0 | |||
| 6742bf4708 | |||
| 5e83023ce5 | |||
| 05039fdd7c | |||
| 21954f7bc6 | |||
| 25d2b56ed7 | |||
| eeeb99b3b5 | |||
| 008d413302 | |||
| 186d5157ec | |||
| 35c7b224bd | |||
| 437408464f | |||
| 969dece8fc | |||
| a535a4c96f | |||
| c3ef39661b | |||
| e9b9fb15c1 | |||
| 3d4b54064f | |||
| 14303fab0c | |||
| 262ef8d305 | |||
| 6e6ac8a4d5 | |||
| a262f0b342 | |||
| 20b4975cfb | |||
| 757283a3ff | |||
| ea76c42c7b | |||
| 0196afa12a | |||
| 5d5c25f0e4 | |||
| 09c9fb4e8f | |||
| d44318f589 | |||
| 9112e2e626 | |||
| 9b56c7eab6 | |||
| f332e6530a | |||
| c228c24ba5 | |||
| 697aa03758 | |||
| c62689450b | |||
| e65a71a05a | |||
| 7a2a444579 | |||
| e16ebecda4 | |||
| 0ed4c1868b | |||
| 2bdbd6c7d9 | |||
| 23517d0c36 | |||
| 2238f81d83 | |||
| 37ce5c7a22 | |||
| 4ef10c8afe | |||
| e5145f3eb0 | |||
| 244dcff8da | |||
| 2e747caa10 | |||
| 93691f13df | |||
| 258d526435 | |||
| 357dc6ec8e | |||
| 232c81543a | |||
| 581f3a1893 | |||
| 9fc3efd73a | |||
| 9445f008bc | |||
| 36c281a19a | |||
| 3fc4f36859 | |||
| 02566589fe | |||
| 49637e62fe | |||
| 136673fd23 | |||
| c7351dfad7 | |||
| 575abc6abf | |||
| 6f63369e11 | |||
| 1995b25092 | |||
| f26d759c84 | |||
| 3f0dc7f652 | |||
| 8274883fa5 | |||
| 12b3d50717 | |||
| 86276206d5 | |||
| da719377df | |||
| 614dc8f4d1 | |||
| a3f41af3ba | |||
| 342232f21e | |||
| c3c9c10060 | |||
| 89fbad447b | |||
| 0aa38728fc | |||
| 23e4003af0 | |||
| 53b6fb7b56 | |||
| 6bbc2de43e | |||
| 0218e39664 | |||
| 91590c824f | |||
| d17b2cf884 | |||
| aed017641d | |||
| 2b82e74b90 | |||
| 37d190c97d | |||
| f1818aae1b | |||
| ee4312b7f2 | |||
| 2345a5e133 | |||
| cbe8f15007 | |||
| dd59038369 | |||
| c9942d7ca5 | |||
| ab8e6b8f33 | |||
| d21f10ec1e | |||
| 76d0ffb797 | |||
| 6437179a83 | |||
| cc45bbeca6 | |||
| 8cd9266ea6 | |||
| ad44feb6dd | |||
| 16ed85ecc9 | |||
| 9d45b10ee5 | |||
| e9f8cf25b9 | |||
| 19a3b8809e | |||
| 96e40b8b4f | |||
| 53aeb59eb4 | |||
| befb42cd68 | |||
| 15bab05e18 | |||
| 7d2e408363 | |||
| c1cc62543d | |||
| 6d61c93f62 | |||
| 38c8860e92 | |||
| 4719bfb4ad | |||
| b5719ab103 | |||
| c82573a697 | |||
| 6448554343 | |||
| c0b9e803d9 | |||
| ab06a22272 | |||
| eac56eba47 | |||
| 0e822bdc81 | |||
| ec4f6de631 | |||
| 3bb61294e9 | |||
| c477da95fd | |||
| fb03de37ce | |||
| 7e645f9a6a | |||
| f3e8359e36 | |||
| 89622998e0 | |||
| 2fd9efffa1 | |||
| 91465c6aa2 | |||
| f4eb8c7517 | |||
| bd3616239f | |||
| ef0744a849 | |||
| adeb859195 | |||
| aaec90e47a | |||
| d4d21c18b2 | |||
| 9d0f544840 | |||
| 3ecd564c57 | |||
| 9c23bbd87a | |||
| d3562b797e | |||
| ea1aefa09e | |||
| fe23502e67 | |||
| ad2764e3ac | |||
| 1628dfaa2f | |||
| 3ff888f77a | |||
| 9af3a10da8 | |||
| 0470a0ca51 | |||
| 7b6cda6b44 | |||
| 332d91b6dc | |||
| 85421cf714 | |||
| 97dd5433e0 | |||
| 0ae9a75b32 | |||
| 4c3b9c6f35 | |||
| a7bd0d97a7 | |||
| 5943c87fc6 | |||
| ac9d6431f4 | |||
| a6b16b4878 | |||
| 6abca67fc4 | |||
| 2738f45d14 | |||
| 34bbf95146 | |||
| ecd6c42924 | |||
| 03e34cde87 | |||
| 7d69e859f9 | |||
| 661e92218a | |||
| 3a2ac6c4d3 | |||
| f62b8a604e |
@@ -82,5 +82,7 @@ tools/NDepend/
|
||||
src/*.vspx
|
||||
src/*.psess
|
||||
NDependOut/*
|
||||
*.ndproj
|
||||
QueryResult.htm
|
||||
*.ndproj
|
||||
src/Umbraco.Tests/Views/web.config
|
||||
|
||||
+2
-2
@@ -1,6 +1,6 @@
|
||||
@ECHO OFF
|
||||
SET release=6.1.6
|
||||
SET comment=
|
||||
SET release=6.2.0
|
||||
SET comment=beta
|
||||
SET version=%release%
|
||||
|
||||
IF [%comment%] EQU [] (SET version=%release%) ELSE (SET version=%release%-%comment%)
|
||||
|
||||
@@ -12,7 +12,7 @@ using Umbraco.Core.Services;
|
||||
|
||||
namespace Umbraco.Core
|
||||
{
|
||||
/// <summary>
|
||||
/// <summary>
|
||||
/// the Umbraco Application context
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
@@ -20,73 +20,102 @@ namespace Umbraco.Core
|
||||
/// </remarks>
|
||||
public class ApplicationContext : IDisposable
|
||||
{
|
||||
/// <summary>
|
||||
/// Constructor
|
||||
/// </summary>
|
||||
internal ApplicationContext(DatabaseContext dbContext, ServiceContext serviceContext)
|
||||
: this(dbContext, serviceContext, true)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructor
|
||||
/// </summary>
|
||||
/// <param name="dbContext"></param>
|
||||
/// <param name="serviceContext"></param>
|
||||
/// <param name="enableCache"></param>
|
||||
internal ApplicationContext(DatabaseContext dbContext, ServiceContext serviceContext, bool enableCache)
|
||||
: this(enableCache)
|
||||
/// <param name="cache"></param>
|
||||
public ApplicationContext(DatabaseContext dbContext, ServiceContext serviceContext, CacheHelper cache)
|
||||
{
|
||||
if (dbContext == null) throw new ArgumentNullException("dbContext");
|
||||
if (serviceContext == null) throw new ArgumentNullException("serviceContext");
|
||||
|
||||
if (cache == null) throw new ArgumentNullException("cache");
|
||||
_databaseContext = dbContext;
|
||||
_services = serviceContext;
|
||||
_services = serviceContext;
|
||||
ApplicationCache = cache;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Empty constructor normally reserved for unit tests when a DatabaseContext or a ServiceContext is not
|
||||
/// necessarily required or needs to be set after construction.
|
||||
/// </summary>
|
||||
internal ApplicationContext() : this(true)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructor used to specify if we will enable application cache or not
|
||||
/// Creates a basic app context
|
||||
/// </summary>
|
||||
/// <param name="enableCache"></param>
|
||||
internal ApplicationContext(bool enableCache)
|
||||
/// <param name="cache"></param>
|
||||
public ApplicationContext(CacheHelper cache)
|
||||
{
|
||||
//create a new application cache from the HttpRuntime.Cache
|
||||
ApplicationCache = HttpRuntime.Cache == null
|
||||
? new CacheHelper(new System.Web.Caching.Cache(), enableCache)
|
||||
: new CacheHelper(HttpRuntime.Cache, enableCache);
|
||||
ApplicationCache = cache;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Singleton accessor
|
||||
/// </summary>
|
||||
public static ApplicationContext Current { get; internal set; }
|
||||
/// <summary>
|
||||
/// A method used to set and/or ensure that a global ApplicationContext singleton is created.
|
||||
/// </summary>
|
||||
/// <param name="appContext">
|
||||
/// The instance to set on the global application singleton
|
||||
/// </param>
|
||||
/// <param name="replaceContext">If set to true and the singleton is already set, it will be replaced</param>
|
||||
/// <returns></returns>
|
||||
/// <remarks>
|
||||
/// This is NOT thread safe
|
||||
/// </remarks>
|
||||
public static ApplicationContext EnsureContext(ApplicationContext appContext, bool replaceContext)
|
||||
{
|
||||
if (ApplicationContext.Current != null)
|
||||
{
|
||||
if (!replaceContext)
|
||||
return ApplicationContext.Current;
|
||||
}
|
||||
ApplicationContext.Current = appContext;
|
||||
return ApplicationContext.Current;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the application wide cache accessor
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Any caching that is done in the application (app wide) should be done through this property
|
||||
/// </remarks>
|
||||
public CacheHelper ApplicationCache { get; private set; }
|
||||
/// <summary>
|
||||
/// A method used to create and ensure that a global ApplicationContext singleton is created.
|
||||
/// </summary>
|
||||
/// <param name="cache"></param>
|
||||
/// <param name="replaceContext">
|
||||
/// If set to true will replace the current singleton instance - This should only be used for unit tests or on app
|
||||
/// startup if for some reason the boot manager is not the umbraco boot manager.
|
||||
/// </param>
|
||||
/// <param name="dbContext"></param>
|
||||
/// <param name="serviceContext"></param>
|
||||
/// <returns></returns>
|
||||
/// <remarks>
|
||||
/// This is NOT thread safe
|
||||
/// </remarks>
|
||||
public static ApplicationContext EnsureContext(DatabaseContext dbContext, ServiceContext serviceContext, CacheHelper cache, bool replaceContext)
|
||||
{
|
||||
if (ApplicationContext.Current != null)
|
||||
{
|
||||
if (!replaceContext)
|
||||
return ApplicationContext.Current;
|
||||
}
|
||||
var ctx = new ApplicationContext(dbContext, serviceContext, cache);
|
||||
ApplicationContext.Current = ctx;
|
||||
return ApplicationContext.Current;
|
||||
}
|
||||
|
||||
// IsReady is set to true by the boot manager once it has successfully booted
|
||||
/// <summary>
|
||||
/// Singleton accessor
|
||||
/// </summary>
|
||||
public static ApplicationContext Current { get; internal set; }
|
||||
|
||||
/// <summary>
|
||||
/// Returns the application wide cache accessor
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Any caching that is done in the application (app wide) should be done through this property
|
||||
/// </remarks>
|
||||
public CacheHelper ApplicationCache { get; private set; }
|
||||
|
||||
// IsReady is set to true by the boot manager once it has successfully booted
|
||||
// note - the original umbraco module checks on content.Instance in umbraco.dll
|
||||
// now, the boot task that setup the content store ensures that it is ready
|
||||
bool _isReady = false;
|
||||
readonly System.Threading.ManualResetEventSlim _isReadyEvent = new System.Threading.ManualResetEventSlim(false);
|
||||
private DatabaseContext _databaseContext;
|
||||
private ServiceContext _services;
|
||||
readonly ManualResetEventSlim _isReadyEvent = new ManualResetEventSlim(false);
|
||||
private DatabaseContext _databaseContext;
|
||||
private ServiceContext _services;
|
||||
|
||||
public bool IsReady
|
||||
public bool IsReady
|
||||
{
|
||||
get
|
||||
{
|
||||
@@ -96,14 +125,14 @@ namespace Umbraco.Core
|
||||
{
|
||||
AssertIsNotReady();
|
||||
_isReady = value;
|
||||
_isReadyEvent.Set();
|
||||
_isReadyEvent.Set();
|
||||
}
|
||||
}
|
||||
|
||||
public bool WaitForReady(int timeout)
|
||||
{
|
||||
return _isReadyEvent.WaitHandle.WaitOne(timeout);
|
||||
}
|
||||
public bool WaitForReady(int timeout)
|
||||
{
|
||||
return _isReadyEvent.WaitHandle.WaitOne(timeout);
|
||||
}
|
||||
|
||||
|
||||
// notes
|
||||
@@ -112,14 +141,14 @@ namespace Umbraco.Core
|
||||
// the system is configured if they match
|
||||
// if they don't, install runs, updates web.config (presumably) and updates GlobalSettings.ConfiguredStatus
|
||||
//
|
||||
// then there is Application["umbracoNeedConfiguration"] which makes no sense... getting rid of it...
|
||||
// then there is Application["umbracoNeedConfiguration"] which makes no sense... getting rid of it... SD: I have actually remove that now!
|
||||
//
|
||||
public bool IsConfigured
|
||||
{
|
||||
// fixme - let's do this for the time being
|
||||
// todo - we should not do this - ok for now
|
||||
get
|
||||
{
|
||||
return Configured;
|
||||
return Configured;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -135,45 +164,45 @@ namespace Umbraco.Core
|
||||
/// </remarks>
|
||||
internal string OriginalRequestUrl { get; set; }
|
||||
|
||||
private bool Configured
|
||||
{
|
||||
get
|
||||
{
|
||||
try
|
||||
{
|
||||
string configStatus = ConfigurationStatus;
|
||||
string currentVersion = UmbracoVersion.Current.ToString(3);
|
||||
private bool Configured
|
||||
{
|
||||
get
|
||||
{
|
||||
try
|
||||
{
|
||||
string configStatus = ConfigurationStatus;
|
||||
string currentVersion = UmbracoVersion.Current.ToString(3);
|
||||
|
||||
|
||||
if (currentVersion != configStatus)
|
||||
{
|
||||
LogHelper.Info<ApplicationContext>("CurrentVersion different from configStatus: '" + currentVersion + "','" + configStatus + "'");
|
||||
}
|
||||
|
||||
if (currentVersion != configStatus)
|
||||
{
|
||||
LogHelper.Info<ApplicationContext>("CurrentVersion different from configStatus: '" + currentVersion + "','" + configStatus + "'");
|
||||
}
|
||||
|
||||
return (configStatus == currentVersion);
|
||||
}
|
||||
catch
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private string ConfigurationStatus
|
||||
{
|
||||
get
|
||||
{
|
||||
try
|
||||
{
|
||||
return ConfigurationManager.AppSettings["umbracoConfigurationStatus"];
|
||||
}
|
||||
catch
|
||||
{
|
||||
return String.Empty;
|
||||
}
|
||||
}
|
||||
}
|
||||
return (configStatus == currentVersion);
|
||||
}
|
||||
catch
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private string ConfigurationStatus
|
||||
{
|
||||
get
|
||||
{
|
||||
try
|
||||
{
|
||||
return ConfigurationManager.AppSettings["umbracoConfigurationStatus"];
|
||||
}
|
||||
catch
|
||||
{
|
||||
return String.Empty;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void AssertIsReady()
|
||||
{
|
||||
@@ -187,39 +216,39 @@ namespace Umbraco.Core
|
||||
throw new Exception("ApplicationContext has already been initialized.");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the current DatabaseContext
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Internal set is generally only used for unit tests
|
||||
/// </remarks>
|
||||
public DatabaseContext DatabaseContext
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_databaseContext == null)
|
||||
throw new InvalidOperationException("The DatabaseContext has not been set on the ApplicationContext");
|
||||
return _databaseContext;
|
||||
}
|
||||
internal set { _databaseContext = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the current ServiceContext
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Internal set is generally only used for unit tests
|
||||
/// </remarks>
|
||||
public ServiceContext Services
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_services == null)
|
||||
throw new InvalidOperationException("The ServiceContext has not been set on the ApplicationContext");
|
||||
return _services;
|
||||
}
|
||||
internal set { _services = value; }
|
||||
}
|
||||
/// <summary>
|
||||
/// Gets the current DatabaseContext
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Internal set is generally only used for unit tests
|
||||
/// </remarks>
|
||||
public DatabaseContext DatabaseContext
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_databaseContext == null)
|
||||
throw new InvalidOperationException("The DatabaseContext has not been set on the ApplicationContext");
|
||||
return _databaseContext;
|
||||
}
|
||||
internal set { _databaseContext = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the current ServiceContext
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Internal set is generally only used for unit tests
|
||||
/// </remarks>
|
||||
public ServiceContext Services
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_services == null)
|
||||
throw new InvalidOperationException("The ServiceContext has not been set on the ApplicationContext");
|
||||
return _services;
|
||||
}
|
||||
internal set { _services = value; }
|
||||
}
|
||||
|
||||
|
||||
private volatile bool _disposed;
|
||||
@@ -245,26 +274,26 @@ namespace Umbraco.Core
|
||||
//clear the cache
|
||||
if (ApplicationCache != null)
|
||||
{
|
||||
ApplicationCache.ClearAllCache();
|
||||
ApplicationCache.ClearAllCache();
|
||||
}
|
||||
//reset all resolvers
|
||||
ResolverCollection.ResetAll();
|
||||
//reset resolution itself (though this should be taken care of by resetting any of the resolvers above)
|
||||
Resolution.Reset();
|
||||
|
||||
|
||||
//reset the instance objects
|
||||
this.ApplicationCache = null;
|
||||
if (_databaseContext != null) //need to check the internal field here
|
||||
{
|
||||
if (DatabaseContext.IsDatabaseConfigured)
|
||||
{
|
||||
DatabaseContext.Database.Dispose();
|
||||
}
|
||||
DatabaseContext.Database.Dispose();
|
||||
}
|
||||
}
|
||||
this.DatabaseContext = null;
|
||||
this.Services = null;
|
||||
this._isReady = false; //set the internal field
|
||||
|
||||
|
||||
// Indicate that the instance has been disposed.
|
||||
_disposed = true;
|
||||
}
|
||||
|
||||
+155
-55
@@ -2,65 +2,165 @@ using System;
|
||||
|
||||
namespace Umbraco.Core
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents the result of an operation attempt
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <remarks></remarks>
|
||||
[Serializable]
|
||||
public struct Attempt<T>
|
||||
{
|
||||
private readonly bool _success;
|
||||
private readonly T _result;
|
||||
private readonly Exception _error;
|
||||
/// <summary>
|
||||
/// Provides ways to create attempts.
|
||||
/// </summary>
|
||||
public static class Attempt
|
||||
{
|
||||
/// <summary>
|
||||
/// Creates a successful attempt with a result.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of the attempted operation result.</typeparam>
|
||||
/// <param name="result">The result of the attempt.</param>
|
||||
/// <returns>The successful attempt.</returns>
|
||||
public static Attempt<T> Succeed<T>(T result)
|
||||
{
|
||||
return Attempt<T>.Succeed(result);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether this <see cref="Attempt{T}"/> represents a successful operation.
|
||||
/// </summary>
|
||||
/// <remarks></remarks>
|
||||
public bool Success
|
||||
{
|
||||
get { return _success; }
|
||||
}
|
||||
/// <summary>
|
||||
/// Creates a failed attempt with a result.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of the attempted operation result.</typeparam>
|
||||
/// <param name="result">The result of the attempt.</param>
|
||||
/// <returns>The failed attempt.</returns>
|
||||
public static Attempt<T> Fail<T>(T result)
|
||||
{
|
||||
return Attempt<T>.Fail(result);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the error associated with an unsuccessful attempt.
|
||||
/// </summary>
|
||||
/// <value>The error.</value>
|
||||
public Exception Error { get { return _error; } }
|
||||
/// <summary>
|
||||
/// Creates a failed attempt with a result and an exception.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of the attempted operation result.</typeparam>
|
||||
/// <param name="result">The result of the attempt.</param>
|
||||
/// <param name="exception">The exception causing the failure of the attempt.</param>
|
||||
/// <returns>The failed attempt.</returns>
|
||||
public static Attempt<T> Fail<T>(T result, Exception exception)
|
||||
{
|
||||
return Attempt<T>.Fail(result, exception);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the parse result.
|
||||
/// </summary>
|
||||
/// <remarks></remarks>
|
||||
public T Result
|
||||
{
|
||||
get { return _result; }
|
||||
}
|
||||
/// <summary>
|
||||
/// Creates a successful or a failed attempt, with a result.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of the attempted operation result.</typeparam>
|
||||
/// <param name="success">A value indicating whether the attempt is successful.</param>
|
||||
/// <param name="result">The result of the attempt.</param>
|
||||
/// <returns>The attempt.</returns>
|
||||
public static Attempt<T> If<T>(bool success, T result)
|
||||
{
|
||||
return Attempt<T>.SucceedIf(success, result);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Represents an unsuccessful parse operation
|
||||
/// </summary>
|
||||
public static readonly Attempt<T> False = new Attempt<T>(false, default(T));
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="Attempt{T}"/> struct.
|
||||
/// </summary>
|
||||
/// <param name="success">If set to <c>true</c> this tuple represents a successful parse result.</param>
|
||||
/// <param name="result">The parse result.</param>
|
||||
/// <remarks></remarks>
|
||||
public Attempt(bool success, T result)
|
||||
{
|
||||
_success = success;
|
||||
_result = result;
|
||||
_error = null;
|
||||
}
|
||||
/// <summary>
|
||||
/// Executes an attempt function, with callbacks.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of the attempted operation result.</typeparam>
|
||||
/// <param name="attempt">The attempt returned by the attempt function.</param>
|
||||
/// <param name="onSuccess">An action to execute in case the attempt succeeds.</param>
|
||||
/// <param name="onFail">An action to execute in case the attempt fails.</param>
|
||||
/// <returns>The outcome of the attempt.</returns>
|
||||
/// <remarks>Runs <paramref name="onSuccess"/> or <paramref name="onFail"/> depending on the
|
||||
/// whether the attempt function reports a success or a failure.</remarks>
|
||||
public static Outcome Try<T>(Attempt<T> attempt, Action<T> onSuccess, Action<Exception> onFail = null)
|
||||
{
|
||||
if (attempt.Success)
|
||||
{
|
||||
onSuccess(attempt.Result);
|
||||
return Outcome.Success;
|
||||
}
|
||||
|
||||
public Attempt(Exception error)
|
||||
{
|
||||
_success = false;
|
||||
_result = default(T);
|
||||
_error = error;
|
||||
}
|
||||
}
|
||||
if (onFail != null)
|
||||
{
|
||||
onFail(attempt.Exception);
|
||||
}
|
||||
|
||||
return Outcome.Failure;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Represents the outcome of an attempt.
|
||||
/// </summary>
|
||||
/// <remarks>Can be a success or a failure, and allows for attempts chaining.</remarks>
|
||||
public struct Outcome
|
||||
{
|
||||
private readonly bool _success;
|
||||
|
||||
/// <summary>
|
||||
/// Gets an outcome representing a success.
|
||||
/// </summary>
|
||||
public static readonly Outcome Success = new Outcome(true);
|
||||
|
||||
/// <summary>
|
||||
/// Gets an outcome representing a failure.
|
||||
/// </summary>
|
||||
public static readonly Outcome Failure = new Outcome(false);
|
||||
|
||||
private Outcome(bool success)
|
||||
{
|
||||
_success = success;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Executes another attempt function, if the previous one failed, with callbacks.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of the attempted operation result.</typeparam>
|
||||
/// <param name="nextFunction">The attempt function to execute, returning an attempt.</param>
|
||||
/// <param name="onSuccess">An action to execute in case the attempt succeeds.</param>
|
||||
/// <param name="onFail">An action to execute in case the attempt fails.</param>
|
||||
/// <returns>If it executes, returns the outcome of the attempt, else returns a success outcome.</returns>
|
||||
/// <remarks>
|
||||
/// <para>Executes only if the previous attempt failed, else does not execute and return a success outcome.</para>
|
||||
/// <para>If it executes, then runs <paramref name="onSuccess"/> or <paramref name="onFail"/> depending on the
|
||||
/// whether the attempt function reports a success or a failure.</para>
|
||||
/// </remarks>
|
||||
public Outcome OnFailure<T>(Func<Attempt<T>> nextFunction, Action<T> onSuccess, Action<Exception> onFail = null)
|
||||
{
|
||||
return _success
|
||||
? Success
|
||||
: ExecuteNextFunction(nextFunction, onSuccess, onFail);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Executes another attempt function, if the previous one succeeded, with callbacks.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of the attempted operation result.</typeparam>
|
||||
/// <param name="nextFunction">The attempt function to execute, returning an attempt.</param>
|
||||
/// <param name="onSuccess">An action to execute in case the attempt succeeds.</param>
|
||||
/// <param name="onFail">An action to execute in case the attempt fails.</param>
|
||||
/// <returns>If it executes, returns the outcome of the attempt, else returns a failed outcome.</returns>
|
||||
/// <remarks>
|
||||
/// <para>Executes only if the previous attempt succeeded, else does not execute and return a success outcome.</para>
|
||||
/// <para>If it executes, then runs <paramref name="onSuccess"/> or <paramref name="onFail"/> depending on the
|
||||
/// whether the attempt function reports a success or a failure.</para>
|
||||
/// </remarks>
|
||||
public Outcome OnSuccess<T>(Func<Attempt<T>> nextFunction, Action<T> onSuccess, Action<Exception> onFail = null)
|
||||
{
|
||||
return _success
|
||||
? ExecuteNextFunction(nextFunction, onSuccess, onFail)
|
||||
: Failure;
|
||||
}
|
||||
|
||||
private static Outcome ExecuteNextFunction<T>(Func<Attempt<T>> nextFunction, Action<T> onSuccess, Action<Exception> onFail = null)
|
||||
{
|
||||
var attempt = nextFunction();
|
||||
|
||||
if (attempt.Success)
|
||||
{
|
||||
onSuccess(attempt.Result);
|
||||
return Success;
|
||||
}
|
||||
|
||||
if (onFail != null)
|
||||
{
|
||||
onFail(attempt.Exception);
|
||||
}
|
||||
|
||||
return Failure;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,174 @@
|
||||
using System;
|
||||
using Umbraco.Core.Dynamics;
|
||||
|
||||
namespace Umbraco.Core
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents the result of an operation attempt.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of the attempted operation result.</typeparam>
|
||||
[Serializable]
|
||||
public struct Attempt<T>
|
||||
{
|
||||
private readonly bool _success;
|
||||
private readonly T _result;
|
||||
private readonly Exception _exception;
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether this <see cref="Attempt{T}"/> was successful.
|
||||
/// </summary>
|
||||
public bool Success
|
||||
{
|
||||
get { return _success; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the exception associated with an unsuccessful attempt.
|
||||
/// </summary>
|
||||
public Exception Exception { get { return _exception; } }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the exception associated with an unsuccessful attempt.
|
||||
/// </summary>
|
||||
/// <remarks>Keep it for backward compatibility sake.</remarks>
|
||||
[Obsolete(".Error is obsolete, you should use .Exception instead.", false)]
|
||||
public Exception Error { get { return _exception; } }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the attempt result.
|
||||
/// </summary>
|
||||
public T Result
|
||||
{
|
||||
get { return _result; }
|
||||
}
|
||||
|
||||
// optimize, use a singleton failed attempt
|
||||
private static readonly Attempt<T> Failed = new Attempt<T>(false, default(T), null);
|
||||
|
||||
/// <summary>
|
||||
/// Represents an unsuccessful attempt.
|
||||
/// </summary>
|
||||
/// <remarks>Keep it for backward compatibility sake.</remarks>
|
||||
[Obsolete(".Failed is obsolete, you should use Attempt<T>.Fail() instead.", false)]
|
||||
public static readonly Attempt<T> False = Failed;
|
||||
|
||||
// private - use Succeed() or Fail() methods to create attempts
|
||||
private Attempt(bool success, T result, Exception exception)
|
||||
{
|
||||
_success = success;
|
||||
_result = result;
|
||||
_exception = exception;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initialize a new instance of the <see cref="Attempt{T}"/> struct with a result.
|
||||
/// </summary>
|
||||
/// <param name="success">A value indicating whether the attempt is successful.</param>
|
||||
/// <param name="result">The result of the attempt.</param>
|
||||
/// <remarks>Keep it for backward compatibility sake.</remarks>
|
||||
[Obsolete("Attempt ctors are obsolete, you should use Attempt<T>.Succeed(), Attempt<T>.Fail() or Attempt<T>.If() instead.", false)]
|
||||
public Attempt(bool success, T result)
|
||||
: this(success, result, null)
|
||||
{ }
|
||||
|
||||
/// <summary>
|
||||
/// Initialize a new instance of the <see cref="Attempt{T}"/> struct representing a failed attempt, with an exception.
|
||||
/// </summary>
|
||||
/// <param name="exception">The exception causing the failure of the attempt.</param>
|
||||
/// <remarks>Keep it for backward compatibility sake.</remarks>
|
||||
[Obsolete("Attempt ctors are obsolete, you should use Attempt<T>.Succeed(), Attempt<T>.Fail() or Attempt<T>.If() instead.", false)]
|
||||
public Attempt(Exception exception)
|
||||
: this(false, default(T), exception)
|
||||
{ }
|
||||
|
||||
/// <summary>
|
||||
/// Creates a successful attempt.
|
||||
/// </summary>
|
||||
/// <returns>The successful attempt.</returns>
|
||||
public static Attempt<T> Succeed()
|
||||
{
|
||||
return new Attempt<T>(true, default(T), null);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a successful attempt with a result.
|
||||
/// </summary>
|
||||
/// <param name="result">The result of the attempt.</param>
|
||||
/// <returns>The successful attempt.</returns>
|
||||
public static Attempt<T> Succeed(T result)
|
||||
{
|
||||
return new Attempt<T>(true, result, null);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a failed attempt.
|
||||
/// </summary>
|
||||
/// <returns>The failed attempt.</returns>
|
||||
public static Attempt<T> Fail()
|
||||
{
|
||||
return Failed;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a failed attempt with an exception.
|
||||
/// </summary>
|
||||
/// <param name="exception">The exception causing the failure of the attempt.</param>
|
||||
/// <returns>The failed attempt.</returns>
|
||||
public static Attempt<T> Fail(Exception exception)
|
||||
{
|
||||
return new Attempt<T>(false, default(T), exception);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a failed attempt with a result.
|
||||
/// </summary>
|
||||
/// <param name="result">The result of the attempt.</param>
|
||||
/// <returns>The failed attempt.</returns>
|
||||
public static Attempt<T> Fail(T result)
|
||||
{
|
||||
return new Attempt<T>(false, result, null);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a failed attempt with a result and an exception.
|
||||
/// </summary>
|
||||
/// <param name="result">The result of the attempt.</param>
|
||||
/// <param name="exception">The exception causing the failure of the attempt.</param>
|
||||
/// <returns>The failed attempt.</returns>
|
||||
public static Attempt<T> Fail(T result, Exception exception)
|
||||
{
|
||||
return new Attempt<T>(false, result, exception);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a successful or a failed attempt.
|
||||
/// </summary>
|
||||
/// <param name="condition">A value indicating whether the attempt is successful.</param>
|
||||
/// <returns>The attempt.</returns>
|
||||
public static Attempt<T> SucceedIf(bool condition)
|
||||
{
|
||||
return condition ? new Attempt<T>(true, default(T), null) : Failed;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a successful or a failed attempt, with a result.
|
||||
/// </summary>
|
||||
/// <param name="condition">A value indicating whether the attempt is successful.</param>
|
||||
/// <param name="result">The result of the attempt.</param>
|
||||
/// <returns>The attempt.</returns>
|
||||
public static Attempt<T> SucceedIf(bool condition, T result)
|
||||
{
|
||||
return new Attempt<T>(condition, result, null);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Implicity operator to check if the attempt was successful without having to access the 'success' property
|
||||
/// </summary>
|
||||
/// <param name="a"></param>
|
||||
/// <returns></returns>
|
||||
public static implicit operator bool(Attempt<T> a)
|
||||
{
|
||||
return a.Success;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,3 +1,5 @@
|
||||
using System;
|
||||
|
||||
namespace Umbraco.Core.Cache
|
||||
{
|
||||
|
||||
@@ -9,6 +11,7 @@ namespace Umbraco.Core.Cache
|
||||
public const string ApplicationTreeCacheKey = "ApplicationTreeCache";
|
||||
public const string ApplicationsCacheKey = "ApplicationCache";
|
||||
|
||||
[Obsolete("This is no longer used and will be removed from the codebase in the future")]
|
||||
public const string UserTypeCacheKey = "UserTypeCache";
|
||||
|
||||
public const string ContentItemCacheKey = "contentItem";
|
||||
@@ -24,13 +27,16 @@ namespace Umbraco.Core.Cache
|
||||
|
||||
public const string MemberLibraryCacheKey = "UL_GetMember";
|
||||
public const string MemberBusinessLogicCacheKey = "MemberCacheItem_";
|
||||
|
||||
|
||||
public const string TemplateFrontEndCacheKey = "template";
|
||||
public const string TemplateBusinessLogicCacheKey = "UmbracoTemplateCache";
|
||||
|
||||
public const string UserContextCacheKey = "UmbracoUserContext";
|
||||
public const string UserContextTimeoutCacheKey = "UmbracoUserContextTimeout";
|
||||
|
||||
[Obsolete("This is no longer used and will be removed from the codebase in the future")]
|
||||
public const string UserCacheKey = "UmbracoUser";
|
||||
|
||||
public const string UserPermissionsCacheKey = "UmbracoUserPermissions";
|
||||
|
||||
public const string ContentTypeCacheKey = "UmbracoContentType";
|
||||
@@ -47,6 +53,5 @@ namespace Umbraco.Core.Cache
|
||||
public const string StylesheetPropertyCacheKey = "UmbracoStylesheetProperty";
|
||||
|
||||
public const string DataTypeCacheKey = "UmbracoDataTypeDefinition";
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,24 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Umbraco.Core.Cache
|
||||
{
|
||||
/// <summary>
|
||||
/// An abstract class for implementing a basic cache provider
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// THIS MUST REMAIN INTERNAL UNTIL WE STREAMLINE HOW ALL CACHE IS HANDLED, WE NEED TO SUPPORT HTTP RUNTIME CACHE, IN MEMORY CACHE, ETC...
|
||||
/// </remarks>
|
||||
internal abstract class CacheProviderBase
|
||||
{
|
||||
public abstract void ClearAllCache();
|
||||
public abstract void ClearCacheItem(string key);
|
||||
public abstract void ClearCacheObjectTypes(string typeName);
|
||||
public abstract void ClearCacheObjectTypes<T>();
|
||||
public abstract void ClearCacheByKeySearch(string keyStartsWith);
|
||||
public abstract void ClearCacheByKeyExpression(string regexString);
|
||||
public abstract IEnumerable<T> GetCacheItemsByKeySearch<T>(string keyStartsWith);
|
||||
public abstract T GetCacheItem<T>(string cacheKey);
|
||||
public abstract T GetCacheItem<T>(string cacheKey, Func<T> getCacheItem);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Web.Caching;
|
||||
|
||||
namespace Umbraco.Core.Cache
|
||||
{
|
||||
/// <summary>
|
||||
/// Extensions for strongly typed access
|
||||
/// </summary>
|
||||
internal static class CacheProviderExtensions
|
||||
{
|
||||
public static T GetCacheItem<T>(this IRuntimeCacheProvider provider,
|
||||
string cacheKey,
|
||||
Func<T> getCacheItem,
|
||||
TimeSpan? timeout,
|
||||
bool isSliding = false,
|
||||
CacheItemPriority priority = CacheItemPriority.Normal,
|
||||
CacheItemRemovedCallback removedCallback = null,
|
||||
string[] dependentFiles = null)
|
||||
{
|
||||
var result = provider.GetCacheItem(cacheKey, () => getCacheItem(), timeout, isSliding, priority, removedCallback, dependentFiles);
|
||||
return result == null ? default(T) : result.TryConvertTo<T>().Result;
|
||||
}
|
||||
|
||||
public static void InsertCacheItem<T>(this IRuntimeCacheProvider provider,
|
||||
string cacheKey,
|
||||
Func<T> getCacheItem,
|
||||
TimeSpan? timeout = null,
|
||||
bool isSliding = false,
|
||||
CacheItemPriority priority = CacheItemPriority.Normal,
|
||||
CacheItemRemovedCallback removedCallback = null,
|
||||
string[] dependentFiles = null)
|
||||
{
|
||||
provider.InsertCacheItem(cacheKey, () => getCacheItem(), timeout, isSliding, priority, removedCallback, dependentFiles);
|
||||
}
|
||||
|
||||
public static IEnumerable<T> GetCacheItemsByKeySearch<T>(this ICacheProvider provider, string keyStartsWith)
|
||||
{
|
||||
var result = provider.GetCacheItemsByKeySearch(keyStartsWith);
|
||||
return result.Select(x => x.TryConvertTo<T>().Result);
|
||||
}
|
||||
|
||||
public static T GetCacheItem<T>(this ICacheProvider provider, string cacheKey)
|
||||
{
|
||||
var result = provider.GetCacheItem(cacheKey);
|
||||
if (result == null)
|
||||
{
|
||||
return default(T);
|
||||
}
|
||||
return result.TryConvertTo<T>().Result;
|
||||
}
|
||||
|
||||
public static T GetCacheItem<T>(this ICacheProvider provider, string cacheKey, Func<T> getCacheItem)
|
||||
{
|
||||
var result = provider.GetCacheItem(cacheKey, () => getCacheItem());
|
||||
if (result == null)
|
||||
{
|
||||
return default(T);
|
||||
}
|
||||
return result.TryConvertTo<T>().Result;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,198 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Threading;
|
||||
|
||||
namespace Umbraco.Core.Cache
|
||||
{
|
||||
internal abstract class DictionaryCacheProviderBase : ICacheProvider
|
||||
{
|
||||
protected static readonly ReaderWriterLockSlim Locker = new ReaderWriterLockSlim(LockRecursionPolicy.SupportsRecursion);
|
||||
protected abstract DictionaryCacheWrapper DictionaryCache { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Clears everything in umbraco's runtime cache
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Does not clear other stuff the user has put in httpruntime.cache!
|
||||
/// </remarks>
|
||||
public virtual void ClearAllCache()
|
||||
{
|
||||
using (new WriteLock(Locker))
|
||||
{
|
||||
var keysToRemove = DictionaryCache.Cast<object>()
|
||||
.Select(item => new DictionaryItemWrapper(item))
|
||||
.Where(c => c.Key is string && ((string)c.Key).StartsWith(CacheItemPrefix) && DictionaryCache[c.Key.ToString()] != null)
|
||||
.Select(c => c.Key)
|
||||
.ToList();
|
||||
|
||||
foreach (var k in keysToRemove)
|
||||
{
|
||||
DictionaryCache.Remove(k);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clears the item in umbraco's runtime cache with the given key
|
||||
/// </summary>
|
||||
/// <param name="key">Key</param>
|
||||
public virtual void ClearCacheItem(string key)
|
||||
{
|
||||
using (new WriteLock(Locker))
|
||||
{
|
||||
if (DictionaryCache[GetCacheKey(key)] == null) return;
|
||||
DictionaryCache.Remove(GetCacheKey(key)); ;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clears all objects in the System.Web.Cache with the System.Type name as the
|
||||
/// input parameter. (using [object].GetType())
|
||||
/// </summary>
|
||||
/// <param name="typeName">The name of the System.Type which should be cleared from cache ex "System.Xml.XmlDocument"</param>
|
||||
public virtual void ClearCacheObjectTypes(string typeName)
|
||||
{
|
||||
using (new WriteLock(Locker))
|
||||
{
|
||||
var keysToRemove = DictionaryCache
|
||||
.Cast<object>()
|
||||
.Select(item => new DictionaryItemWrapper(item))
|
||||
.Where(c =>
|
||||
{
|
||||
var k = c.Key.ToString();
|
||||
var v = DictionaryCache[k];
|
||||
return v != null && v.GetType().ToString().InvariantEquals(typeName);
|
||||
})
|
||||
.Select(c => c.Key)
|
||||
.ToList();
|
||||
|
||||
foreach (var k in keysToRemove)
|
||||
DictionaryCache.Remove(k);
|
||||
}
|
||||
}
|
||||
|
||||
public virtual void ClearCacheObjectTypes<T>()
|
||||
{
|
||||
using (new WriteLock(Locker))
|
||||
{
|
||||
var typeOfT = typeof(T);
|
||||
var keysToRemove = DictionaryCache
|
||||
.Cast<object>()
|
||||
.Select(item => new DictionaryItemWrapper(item))
|
||||
.Where(c =>
|
||||
{
|
||||
var k = c.Key.ToString();
|
||||
var v = DictionaryCache[k];
|
||||
return v != null && v.GetType() == typeOfT;
|
||||
})
|
||||
.Select(c => c.Key)
|
||||
.ToList();
|
||||
|
||||
foreach (var k in keysToRemove)
|
||||
DictionaryCache.Remove(k);
|
||||
}
|
||||
}
|
||||
|
||||
public virtual void ClearCacheObjectTypes<T>(Func<string, T, bool> predicate)
|
||||
{
|
||||
using (new WriteLock(Locker))
|
||||
{
|
||||
var typeOfT = typeof(T);
|
||||
var keysToRemove = DictionaryCache
|
||||
.Cast<object>()
|
||||
.Select(item => new DictionaryItemWrapper(item))
|
||||
.Where(c =>
|
||||
{
|
||||
var k = c.Key.ToString();
|
||||
var v = DictionaryCache[k];
|
||||
return v != null && v.GetType() == typeOfT && predicate(k, (T)v);
|
||||
})
|
||||
.Select(c => c.Key)
|
||||
.ToList();
|
||||
|
||||
foreach (var k in keysToRemove)
|
||||
DictionaryCache.Remove(k);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clears all cache items that starts with the key passed.
|
||||
/// </summary>
|
||||
/// <param name="keyStartsWith">The start of the key</param>
|
||||
public virtual void ClearCacheByKeySearch(string keyStartsWith)
|
||||
{
|
||||
var keysToRemove = DictionaryCache.Cast<object>()
|
||||
.Select(item => new DictionaryItemWrapper(item))
|
||||
.Where(c => c.Key is string && ((string)c.Key).InvariantStartsWith(string.Format("{0}-{1}", CacheItemPrefix, keyStartsWith)))
|
||||
.Select(c => c.Key)
|
||||
.ToList();
|
||||
|
||||
foreach (var k in keysToRemove)
|
||||
{
|
||||
DictionaryCache.Remove(k);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clears all cache items that have a key that matches the regular expression
|
||||
/// </summary>
|
||||
/// <param name="regexString"></param>
|
||||
public virtual void ClearCacheByKeyExpression(string regexString)
|
||||
{
|
||||
var keysToRemove = new List<object>();
|
||||
foreach (var item in DictionaryCache)
|
||||
{
|
||||
var c = new DictionaryItemWrapper(item);
|
||||
var s = c.Key as string;
|
||||
if (s != null)
|
||||
{
|
||||
var withoutPrefix = s.TrimStart(string.Format("{0}-", CacheItemPrefix));
|
||||
if (Regex.IsMatch(withoutPrefix, regexString))
|
||||
{
|
||||
keysToRemove.Add(c.Key);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var k in keysToRemove)
|
||||
{
|
||||
DictionaryCache.Remove(k);
|
||||
}
|
||||
}
|
||||
|
||||
public virtual IEnumerable<object> GetCacheItemsByKeySearch(string keyStartsWith)
|
||||
{
|
||||
return (from object item in DictionaryCache
|
||||
select new DictionaryItemWrapper(item)
|
||||
into c
|
||||
where c.Key is string && ((string) c.Key).InvariantStartsWith(string.Format("{0}-{1}", CacheItemPrefix, keyStartsWith))
|
||||
select c.Value).ToList();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a cache item by key, does not update the cache if it isn't there.
|
||||
/// </summary>
|
||||
/// <param name="cacheKey"></param>
|
||||
/// <returns></returns>
|
||||
public virtual object GetCacheItem(string cacheKey)
|
||||
{
|
||||
var result = DictionaryCache.Get(GetCacheKey(cacheKey));
|
||||
return result;
|
||||
}
|
||||
|
||||
public abstract object GetCacheItem(string cacheKey, Func<object> getCacheItem);
|
||||
|
||||
/// <summary>
|
||||
/// We prefix all cache keys with this so that we know which ones this class has created when
|
||||
/// using the HttpRuntime cache so that when we clear it we don't clear other entries we didn't create.
|
||||
/// </summary>
|
||||
protected const string CacheItemPrefix = "umbrtmche";
|
||||
|
||||
protected string GetCacheKey(string key)
|
||||
{
|
||||
return string.Format("{0}-{1}", CacheItemPrefix, key);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
|
||||
namespace Umbraco.Core.Cache
|
||||
{
|
||||
internal class DictionaryCacheWrapper : IEnumerable
|
||||
{
|
||||
private readonly IEnumerable _inner;
|
||||
private readonly Func<object, object> _get;
|
||||
private readonly Action<object> _remove;
|
||||
|
||||
public DictionaryCacheWrapper(
|
||||
IEnumerable inner,
|
||||
Func<object, object> get,
|
||||
Action<object> remove)
|
||||
{
|
||||
_inner = inner;
|
||||
_get = get;
|
||||
_remove = remove;
|
||||
}
|
||||
|
||||
public object this[object key]
|
||||
{
|
||||
get
|
||||
{
|
||||
return Get(key);
|
||||
}
|
||||
}
|
||||
|
||||
public object Get(object key)
|
||||
{
|
||||
return _get(key);
|
||||
}
|
||||
|
||||
public void Remove(object key)
|
||||
{
|
||||
_remove(key);
|
||||
}
|
||||
|
||||
public IEnumerator GetEnumerator()
|
||||
{
|
||||
return _inner.GetEnumerator();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
namespace Umbraco.Core.Cache
|
||||
{
|
||||
internal class DictionaryItemWrapper
|
||||
{
|
||||
public DictionaryItemWrapper(dynamic item)
|
||||
{
|
||||
Key = item.Key;
|
||||
Value = item.Value;
|
||||
}
|
||||
|
||||
public object Key { get; private set; }
|
||||
public object Value { get; private set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Web;
|
||||
|
||||
namespace Umbraco.Core.Cache
|
||||
{
|
||||
/// <summary>
|
||||
/// A cache provider that caches items in the HttpContext.Items
|
||||
/// </summary>
|
||||
internal class HttpRequestCacheProvider : DictionaryCacheProviderBase
|
||||
{
|
||||
private readonly Func<HttpContextBase> _context;
|
||||
|
||||
public HttpRequestCacheProvider(HttpContext context)
|
||||
{
|
||||
_context = () => new HttpContextWrapper(context);
|
||||
}
|
||||
|
||||
public HttpRequestCacheProvider(Func<HttpContextBase> context)
|
||||
{
|
||||
_context = context;
|
||||
}
|
||||
|
||||
protected override DictionaryCacheWrapper DictionaryCache
|
||||
{
|
||||
get
|
||||
{
|
||||
var ctx = _context();
|
||||
return new DictionaryCacheWrapper(
|
||||
ctx.Items,
|
||||
o => ctx.Items[o],
|
||||
o => ctx.Items.Remove(o));
|
||||
}
|
||||
}
|
||||
|
||||
public override object GetCacheItem(string cacheKey, Func<object> getCacheItem)
|
||||
{
|
||||
var ctx = _context();
|
||||
var ck = GetCacheKey(cacheKey);
|
||||
return ctx.Items[ck] ?? (ctx.Items[ck] = getCacheItem());
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,62 +1,37 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Threading;
|
||||
using System.Web;
|
||||
using System.Web.Caching;
|
||||
using Umbraco.Core.Logging;
|
||||
using CacheItemPriority = System.Web.Caching.CacheItemPriority;
|
||||
|
||||
namespace Umbraco.Core.Cache
|
||||
{
|
||||
/// <summary>
|
||||
/// A CacheProvider that wraps the logic of the HttpRuntime.Cache
|
||||
/// </summary>
|
||||
internal class HttpRuntimeCacheProvider : RuntimeCacheProviderBase
|
||||
internal class HttpRuntimeCacheProvider : DictionaryCacheProviderBase, IRuntimeCacheProvider
|
||||
{
|
||||
private readonly System.Web.Caching.Cache _cache;
|
||||
private static readonly object Locker = new object();
|
||||
|
||||
private readonly DictionaryCacheWrapper _wrapper;
|
||||
|
||||
public HttpRuntimeCacheProvider(System.Web.Caching.Cache cache)
|
||||
{
|
||||
_cache = cache;
|
||||
_wrapper = new DictionaryCacheWrapper(_cache, s => _cache.Get(s.ToString()), o => _cache.Remove(o.ToString()));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clears everything in umbraco's runtime cache, which means that not only
|
||||
/// umbraco content is removed, but also other cache items from pages running in
|
||||
/// the same application / website. Use with care :-)
|
||||
/// </summary>
|
||||
public override void ClearAllCache()
|
||||
protected override DictionaryCacheWrapper DictionaryCache
|
||||
{
|
||||
var cacheEnumerator = _cache.GetEnumerator();
|
||||
while (cacheEnumerator.MoveNext())
|
||||
{
|
||||
_cache.Remove(cacheEnumerator.Key.ToString());
|
||||
}
|
||||
get { return _wrapper; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clears the item in umbraco's runtime cache with the given key
|
||||
/// Clears all objects in the System.Web.Cache with the System.Type specified that satisfy the predicate
|
||||
/// </summary>
|
||||
/// <param name="key">Key</param>
|
||||
public override void ClearCacheItem(string key)
|
||||
{
|
||||
// NH 10 jan 2012
|
||||
// Patch by the always wonderful Stéphane Gay to avoid cache null refs
|
||||
lock (Locker)
|
||||
{
|
||||
if (_cache[key] == null) return;
|
||||
_cache.Remove(key); ;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Clears all objects in the System.Web.Cache with the System.Type name as the
|
||||
/// input parameter. (using [object].GetType())
|
||||
/// </summary>
|
||||
/// <param name="typeName">The name of the System.Type which should be cleared from cache ex "System.Xml.XmlDocument"</param>
|
||||
public override void ClearCacheObjectTypes(string typeName)
|
||||
public override void ClearCacheObjectTypes<T>(Func<string, T, bool> predicate)
|
||||
{
|
||||
try
|
||||
{
|
||||
@@ -64,8 +39,10 @@ namespace Umbraco.Core.Cache
|
||||
{
|
||||
foreach (DictionaryEntry c in _cache)
|
||||
{
|
||||
if (_cache[c.Key.ToString()] != null
|
||||
&& _cache[c.Key.ToString()].GetType().ToString().InvariantEquals(typeName))
|
||||
var key = c.Key.ToString();
|
||||
if (_cache[key] != null
|
||||
&& _cache[key] is T
|
||||
&& predicate(key, (T)_cache[key]))
|
||||
{
|
||||
_cache.Remove(c.Key.ToString());
|
||||
}
|
||||
@@ -78,279 +55,94 @@ namespace Umbraco.Core.Cache
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clears all objects in the System.Web.Cache with the System.Type specified
|
||||
/// </summary>
|
||||
public override void ClearCacheObjectTypes<T>()
|
||||
{
|
||||
try
|
||||
{
|
||||
lock (Locker)
|
||||
{
|
||||
foreach (DictionaryEntry c in _cache)
|
||||
{
|
||||
if (_cache[c.Key.ToString()] != null
|
||||
&& _cache[c.Key.ToString()].GetType() == typeof(T))
|
||||
{
|
||||
_cache.Remove(c.Key.ToString());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
LogHelper.Error<CacheHelper>("Cache clearing error", e);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clears all cache items that starts with the key passed.
|
||||
/// </summary>
|
||||
/// <param name="keyStartsWith">The start of the key</param>
|
||||
public override void ClearCacheByKeySearch(string keyStartsWith)
|
||||
{
|
||||
foreach (DictionaryEntry c in _cache)
|
||||
{
|
||||
if (c.Key is string && ((string)c.Key).InvariantStartsWith(keyStartsWith))
|
||||
{
|
||||
ClearCacheItem((string)c.Key);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clears all cache items that have a key that matches the regular expression
|
||||
/// </summary>
|
||||
/// <param name="regexString"></param>
|
||||
public override void ClearCacheByKeyExpression(string regexString)
|
||||
{
|
||||
foreach (DictionaryEntry c in _cache)
|
||||
{
|
||||
if (c.Key is string && Regex.IsMatch(((string)c.Key), regexString))
|
||||
{
|
||||
ClearCacheItem((string)c.Key);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override IEnumerable<T> GetCacheItemsByKeySearch<T>(string keyStartsWith)
|
||||
{
|
||||
return (from DictionaryEntry c in _cache
|
||||
where c.Key is string && ((string)c.Key).InvariantStartsWith(keyStartsWith)
|
||||
select c.Value.TryConvertTo<T>()
|
||||
into attempt
|
||||
where attempt.Success
|
||||
select attempt.Result).ToList();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a cache item by key, does not update the cache if it isn't there.
|
||||
/// </summary>
|
||||
/// <typeparam name="TT"></typeparam>
|
||||
/// <param name="cacheKey"></param>
|
||||
/// <returns></returns>
|
||||
public override TT GetCacheItem<TT>(string cacheKey)
|
||||
{
|
||||
var result = _cache.Get(cacheKey);
|
||||
if (result == null)
|
||||
{
|
||||
return default(TT);
|
||||
}
|
||||
return result.TryConvertTo<TT>().Result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets (and adds if necessary) an item from the cache with all of the default parameters
|
||||
/// </summary>
|
||||
/// <typeparam name="TT"></typeparam>
|
||||
/// <param name="cacheKey"></param>
|
||||
/// <param name="getCacheItem"></param>
|
||||
/// <returns></returns>
|
||||
public override TT GetCacheItem<TT>(string cacheKey, Func<TT> getCacheItem)
|
||||
public override object GetCacheItem(string cacheKey, Func<object> getCacheItem)
|
||||
{
|
||||
return GetCacheItem(cacheKey, CacheItemPriority.Normal, null, null, null, getCacheItem, Locker);
|
||||
return GetCacheItem(cacheKey, getCacheItem, null, dependentFiles: null);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets (and adds if necessary) an item from the cache with the specified absolute expiration date (from NOW)
|
||||
/// This overload is here for legacy purposes
|
||||
/// </summary>
|
||||
/// <typeparam name="TT"></typeparam>
|
||||
/// <param name="cacheKey"></param>
|
||||
/// <param name="timeout">This will set an absolute expiration from now until the timeout</param>
|
||||
/// <param name="getCacheItem"></param>
|
||||
/// <returns></returns>
|
||||
public override TT GetCacheItem<TT>(string cacheKey,
|
||||
TimeSpan? timeout, Func<TT> getCacheItem)
|
||||
{
|
||||
return GetCacheItem(cacheKey, null, timeout, getCacheItem);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets (and adds if necessary) an item from the cache with the specified absolute expiration date (from NOW)
|
||||
/// </summary>
|
||||
/// <typeparam name="TT"></typeparam>
|
||||
/// <param name="cacheKey"></param>
|
||||
/// <param name="refreshAction"></param>
|
||||
/// <param name="timeout">This will set an absolute expiration from now until the timeout</param>
|
||||
/// <param name="getCacheItem"></param>
|
||||
/// <returns></returns>
|
||||
public override TT GetCacheItem<TT>(string cacheKey,
|
||||
CacheItemRemovedCallback refreshAction, TimeSpan? timeout,
|
||||
Func<TT> getCacheItem)
|
||||
{
|
||||
return GetCacheItem(cacheKey, CacheItemPriority.Normal, refreshAction, timeout, getCacheItem);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets (and adds if necessary) an item from the cache with the specified absolute expiration date (from NOW)
|
||||
/// </summary>
|
||||
/// <typeparam name="TT"></typeparam>
|
||||
/// <param name="cacheKey"></param>
|
||||
/// <param name="timeout"></param>
|
||||
/// <param name="isSliding"></param>
|
||||
/// <param name="priority"></param>
|
||||
/// <param name="refreshAction"></param>
|
||||
/// <param name="timeout">This will set an absolute expiration from now until the timeout</param>
|
||||
/// <param name="getCacheItem"></param>
|
||||
/// <param name="removedCallback"></param>
|
||||
/// <param name="dependency"></param>
|
||||
/// <returns></returns>
|
||||
public override TT GetCacheItem<TT>(string cacheKey,
|
||||
CacheItemPriority priority, CacheItemRemovedCallback refreshAction, TimeSpan? timeout,
|
||||
Func<TT> getCacheItem)
|
||||
internal object GetCacheItem(string cacheKey, Func<object> getCacheItem, TimeSpan? timeout, bool isSliding = false, CacheItemPriority priority = CacheItemPriority.Normal, CacheItemRemovedCallback removedCallback = null, CacheDependency dependency = null)
|
||||
{
|
||||
return GetCacheItem(cacheKey, priority, refreshAction, null, timeout, getCacheItem);
|
||||
}
|
||||
cacheKey = GetCacheKey(cacheKey);
|
||||
|
||||
/// <summary>
|
||||
/// Gets (and adds if necessary) an item from the cache with the specified absolute expiration date (from NOW)
|
||||
/// </summary>
|
||||
/// <typeparam name="TT"></typeparam>
|
||||
/// <param name="cacheKey"></param>
|
||||
/// <param name="priority"></param>
|
||||
/// <param name="refreshAction"></param>
|
||||
/// <param name="cacheDependency"></param>
|
||||
/// <param name="timeout">This will set an absolute expiration from now until the timeout</param>
|
||||
/// <param name="getCacheItem"></param>
|
||||
/// <returns></returns>
|
||||
public override TT GetCacheItem<TT>(string cacheKey,
|
||||
CacheItemPriority priority,
|
||||
CacheItemRemovedCallback refreshAction,
|
||||
CacheDependency cacheDependency,
|
||||
TimeSpan? timeout,
|
||||
Func<TT> getCacheItem)
|
||||
{
|
||||
return GetCacheItem(cacheKey, priority, refreshAction, cacheDependency, timeout, getCacheItem, Locker);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets (and adds if necessary) an item from the cache with the specified absolute expiration date (from NOW)
|
||||
/// </summary>
|
||||
/// <typeparam name="TT"></typeparam>
|
||||
/// <param name="cacheKey"></param>
|
||||
/// <param name="priority"></param>
|
||||
/// <param name="refreshAction"></param>
|
||||
/// <param name="cacheDependency"></param>
|
||||
/// <param name="timeout">This will set an absolute expiration from now until the timeout</param>
|
||||
/// <param name="getCacheItem"></param>
|
||||
/// <param name="syncLock"></param>
|
||||
/// <returns></returns>
|
||||
private TT GetCacheItem<TT>(string cacheKey,
|
||||
CacheItemPriority priority, CacheItemRemovedCallback refreshAction,
|
||||
CacheDependency cacheDependency, TimeSpan? timeout, Func<TT> getCacheItem, object syncLock)
|
||||
{
|
||||
var result = _cache.Get(cacheKey);
|
||||
if (result == null)
|
||||
using (var lck = new UpgradeableReadLock(Locker))
|
||||
{
|
||||
lock (syncLock)
|
||||
var result = DictionaryCache.Get(cacheKey);
|
||||
if (result == null)
|
||||
{
|
||||
result = _cache.Get(cacheKey);
|
||||
if (result == null)
|
||||
{
|
||||
result = getCacheItem();
|
||||
if (result != null)
|
||||
{
|
||||
//we use Insert instead of add if for some crazy reason there is now a cache with the cache key in there, it will just overwrite it.
|
||||
_cache.Insert(cacheKey, result, cacheDependency,
|
||||
timeout == null ? System.Web.Caching.Cache.NoAbsoluteExpiration : DateTime.Now.Add(timeout.Value),
|
||||
TimeSpan.Zero, priority, refreshAction);
|
||||
}
|
||||
lck.UpgradeToWriteLock();
|
||||
|
||||
result = getCacheItem();
|
||||
if (result != null)
|
||||
{
|
||||
var absolute = isSliding ? System.Web.Caching.Cache.NoAbsoluteExpiration : (timeout == null ? System.Web.Caching.Cache.NoAbsoluteExpiration : DateTime.Now.Add(timeout.Value));
|
||||
var sliding = isSliding == false ? System.Web.Caching.Cache.NoSlidingExpiration : (timeout ?? System.Web.Caching.Cache.NoSlidingExpiration);
|
||||
|
||||
_cache.Insert(cacheKey, result, dependency, absolute, sliding, priority, removedCallback);
|
||||
}
|
||||
|
||||
}
|
||||
return result;
|
||||
}
|
||||
return result.TryConvertTo<TT>().Result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Inserts an item into the cache, if it already exists in the cache it will be replaced
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <param name="cacheKey"></param>
|
||||
/// <param name="priority"></param>
|
||||
/// <param name="getCacheItem"></param>
|
||||
public override void InsertCacheItem<T>(string cacheKey,
|
||||
CacheItemPriority priority,
|
||||
Func<T> getCacheItem)
|
||||
public object GetCacheItem(string cacheKey, Func<object> getCacheItem, TimeSpan? timeout, bool isSliding = false, CacheItemPriority priority = CacheItemPriority.Normal, CacheItemRemovedCallback removedCallback = null, string[] dependentFiles = null)
|
||||
{
|
||||
InsertCacheItem(cacheKey, priority, null, null, null, getCacheItem);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Inserts an item into the cache, if it already exists in the cache it will be replaced
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <param name="cacheKey"></param>
|
||||
/// <param name="priority"></param>
|
||||
/// <param name="timeout">This will set an absolute expiration from now until the timeout</param>
|
||||
/// <param name="getCacheItem"></param>
|
||||
public override void InsertCacheItem<T>(string cacheKey,
|
||||
CacheItemPriority priority,
|
||||
TimeSpan? timeout,
|
||||
Func<T> getCacheItem)
|
||||
{
|
||||
InsertCacheItem(cacheKey, priority, null, null, timeout, getCacheItem);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Inserts an item into the cache, if it already exists in the cache it will be replaced
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <param name="cacheKey"></param>
|
||||
/// <param name="priority"></param>
|
||||
/// <param name="cacheDependency"></param>
|
||||
/// <param name="timeout">This will set an absolute expiration from now until the timeout</param>
|
||||
/// <param name="getCacheItem"></param>
|
||||
public override void InsertCacheItem<T>(string cacheKey,
|
||||
CacheItemPriority priority,
|
||||
CacheDependency cacheDependency,
|
||||
TimeSpan? timeout,
|
||||
Func<T> getCacheItem)
|
||||
{
|
||||
InsertCacheItem(cacheKey, priority, null, cacheDependency, timeout, getCacheItem);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Inserts an item into the cache, if it already exists in the cache it will be replaced
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <param name="cacheKey"></param>
|
||||
/// <param name="priority"></param>
|
||||
/// <param name="refreshAction"></param>
|
||||
/// <param name="cacheDependency"></param>
|
||||
/// <param name="timeout">This will set an absolute expiration from now until the timeout</param>
|
||||
/// <param name="getCacheItem"></param>
|
||||
public override void InsertCacheItem<T>(string cacheKey,
|
||||
CacheItemPriority priority,
|
||||
CacheItemRemovedCallback refreshAction,
|
||||
CacheDependency cacheDependency,
|
||||
TimeSpan? timeout,
|
||||
Func<T> getCacheItem)
|
||||
{
|
||||
object result = getCacheItem();
|
||||
if (result != null)
|
||||
CacheDependency dependency = null;
|
||||
if (dependentFiles != null && dependentFiles.Any())
|
||||
{
|
||||
//we use Insert instead of add if for some crazy reason there is now a cache with the cache key in there, it will just overwrite it.
|
||||
_cache.Insert(cacheKey, result, cacheDependency,
|
||||
timeout == null ? System.Web.Caching.Cache.NoAbsoluteExpiration : DateTime.Now.Add(timeout.Value),
|
||||
TimeSpan.Zero, priority, refreshAction);
|
||||
dependency = new CacheDependency(dependentFiles);
|
||||
}
|
||||
return GetCacheItem(cacheKey, getCacheItem, timeout, isSliding, priority, removedCallback, dependency);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This overload is here for legacy purposes
|
||||
/// </summary>
|
||||
/// <param name="cacheKey"></param>
|
||||
/// <param name="getCacheItem"></param>
|
||||
/// <param name="timeout"></param>
|
||||
/// <param name="isSliding"></param>
|
||||
/// <param name="priority"></param>
|
||||
/// <param name="removedCallback"></param>
|
||||
/// <param name="dependency"></param>
|
||||
internal void InsertCacheItem(string cacheKey, Func<object> getCacheItem, TimeSpan? timeout = null, bool isSliding = false, CacheItemPriority priority = CacheItemPriority.Normal, CacheItemRemovedCallback removedCallback = null, CacheDependency dependency = null)
|
||||
{
|
||||
var result = getCacheItem();
|
||||
if (result == null) return;
|
||||
|
||||
cacheKey = GetCacheKey(cacheKey);
|
||||
|
||||
var absolute = isSliding ? System.Web.Caching.Cache.NoAbsoluteExpiration : (timeout == null ? System.Web.Caching.Cache.NoAbsoluteExpiration : DateTime.Now.Add(timeout.Value));
|
||||
var sliding = isSliding == false ? System.Web.Caching.Cache.NoSlidingExpiration : (timeout ?? System.Web.Caching.Cache.NoSlidingExpiration);
|
||||
|
||||
_cache.Insert(cacheKey, result, dependency, absolute, sliding, priority, removedCallback);
|
||||
}
|
||||
|
||||
public void InsertCacheItem(string cacheKey, Func<object> getCacheItem, TimeSpan? timeout = null, bool isSliding = false, CacheItemPriority priority = CacheItemPriority.Normal, CacheItemRemovedCallback removedCallback = null, string[] dependentFiles = null)
|
||||
{
|
||||
CacheDependency dependency = null;
|
||||
if (dependentFiles != null && dependentFiles.Any())
|
||||
{
|
||||
dependency = new CacheDependency(dependentFiles);
|
||||
}
|
||||
InsertCacheItem(cacheKey, getCacheItem, timeout, isSliding, priority, removedCallback, dependency);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Umbraco.Core.Cache
|
||||
{
|
||||
/// <summary>
|
||||
/// An abstract class for implementing a basic cache provider
|
||||
/// </summary>
|
||||
public interface ICacheProvider
|
||||
{
|
||||
void ClearAllCache();
|
||||
void ClearCacheItem(string key);
|
||||
void ClearCacheObjectTypes(string typeName);
|
||||
void ClearCacheObjectTypes<T>();
|
||||
void ClearCacheObjectTypes<T>(Func<string, T, bool> predicate);
|
||||
void ClearCacheByKeySearch(string keyStartsWith);
|
||||
void ClearCacheByKeyExpression(string regexString);
|
||||
IEnumerable<object> GetCacheItemsByKeySearch(string keyStartsWith);
|
||||
object GetCacheItem(string cacheKey);
|
||||
object GetCacheItem(string cacheKey, Func<object> getCacheItem);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
using System;
|
||||
using System.Runtime.Caching;
|
||||
using System.Text;
|
||||
using System.Web.Caching;
|
||||
using CacheItemPriority = System.Web.Caching.CacheItemPriority;
|
||||
|
||||
namespace Umbraco.Core.Cache
|
||||
{
|
||||
/// <summary>
|
||||
/// An abstract class for implementing a runtime cache provider
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// </remarks>
|
||||
public interface IRuntimeCacheProvider : ICacheProvider
|
||||
{
|
||||
object GetCacheItem(
|
||||
string cacheKey,
|
||||
Func<object> getCacheItem,
|
||||
TimeSpan? timeout,
|
||||
bool isSliding = false,
|
||||
CacheItemPriority priority = CacheItemPriority.Normal,
|
||||
CacheItemRemovedCallback removedCallback = null,
|
||||
string[] dependentFiles = null);
|
||||
|
||||
void InsertCacheItem(
|
||||
string cacheKey,
|
||||
Func<object> getCacheItem,
|
||||
TimeSpan? timeout = null,
|
||||
bool isSliding = false,
|
||||
CacheItemPriority priority = CacheItemPriority.Normal,
|
||||
CacheItemRemovedCallback removedCallback = null,
|
||||
string[] dependentFiles = null);
|
||||
|
||||
}
|
||||
}
|
||||
@@ -5,81 +5,62 @@ using System.Web.Caching;
|
||||
|
||||
namespace Umbraco.Core.Cache
|
||||
{
|
||||
internal class NullCacheProvider : RuntimeCacheProviderBase
|
||||
internal class NullCacheProvider : IRuntimeCacheProvider
|
||||
{
|
||||
public override void ClearAllCache()
|
||||
public virtual void ClearAllCache()
|
||||
{
|
||||
}
|
||||
|
||||
public override void ClearCacheItem(string key)
|
||||
public virtual void ClearCacheItem(string key)
|
||||
{
|
||||
}
|
||||
|
||||
public override void ClearCacheObjectTypes(string typeName)
|
||||
public virtual void ClearCacheObjectTypes(string typeName)
|
||||
{
|
||||
}
|
||||
|
||||
public override void ClearCacheObjectTypes<T>()
|
||||
public virtual void ClearCacheObjectTypes<T>()
|
||||
{
|
||||
}
|
||||
|
||||
public override void ClearCacheByKeySearch(string keyStartsWith)
|
||||
public virtual void ClearCacheObjectTypes<T>(Func<string, T, bool> predicate)
|
||||
{
|
||||
}
|
||||
|
||||
public override void ClearCacheByKeyExpression(string regexString)
|
||||
|
||||
|
||||
|
||||
public virtual void ClearCacheByKeySearch(string keyStartsWith)
|
||||
{
|
||||
}
|
||||
|
||||
public override IEnumerable<T> GetCacheItemsByKeySearch<T>(string keyStartsWith)
|
||||
public virtual void ClearCacheByKeyExpression(string regexString)
|
||||
{
|
||||
return Enumerable.Empty<T>();
|
||||
}
|
||||
|
||||
public override T GetCacheItem<T>(string cacheKey)
|
||||
public virtual IEnumerable<object> GetCacheItemsByKeySearch(string keyStartsWith)
|
||||
{
|
||||
return default(T);
|
||||
return Enumerable.Empty<object>();
|
||||
}
|
||||
|
||||
public override T GetCacheItem<T>(string cacheKey, Func<T> getCacheItem)
|
||||
public virtual object GetCacheItem(string cacheKey)
|
||||
{
|
||||
return default(object);
|
||||
}
|
||||
|
||||
public virtual object GetCacheItem(string cacheKey, Func<object> getCacheItem)
|
||||
{
|
||||
return getCacheItem();
|
||||
}
|
||||
|
||||
public override T GetCacheItem<T>(string cacheKey, TimeSpan? timeout, Func<T> getCacheItem)
|
||||
public object GetCacheItem(string cacheKey, Func<object> getCacheItem, TimeSpan? timeout, bool isSliding = false, CacheItemPriority priority = CacheItemPriority.Normal, CacheItemRemovedCallback removedCallback = null, string[] dependentFiles = null)
|
||||
{
|
||||
return getCacheItem();
|
||||
}
|
||||
|
||||
public override T GetCacheItem<T>(string cacheKey, CacheItemRemovedCallback refreshAction, TimeSpan? timeout, Func<T> getCacheItem)
|
||||
{
|
||||
return getCacheItem();
|
||||
}
|
||||
|
||||
public override T GetCacheItem<T>(string cacheKey, CacheItemPriority priority, CacheItemRemovedCallback refreshAction, TimeSpan? timeout, Func<T> getCacheItem)
|
||||
{
|
||||
return getCacheItem();
|
||||
}
|
||||
|
||||
public override T GetCacheItem<T>(string cacheKey, CacheItemPriority priority, CacheItemRemovedCallback refreshAction, CacheDependency cacheDependency, TimeSpan? timeout, Func<T> getCacheItem)
|
||||
{
|
||||
return getCacheItem();
|
||||
}
|
||||
|
||||
public override void InsertCacheItem<T>(string cacheKey, CacheItemPriority priority, Func<T> getCacheItem)
|
||||
{
|
||||
}
|
||||
|
||||
public override void InsertCacheItem<T>(string cacheKey, CacheItemPriority priority, TimeSpan? timeout, Func<T> getCacheItem)
|
||||
{
|
||||
}
|
||||
|
||||
public override void InsertCacheItem<T>(string cacheKey, CacheItemPriority priority, CacheDependency cacheDependency, TimeSpan? timeout, Func<T> getCacheItem)
|
||||
{
|
||||
}
|
||||
|
||||
public override void InsertCacheItem<T>(string cacheKey, CacheItemPriority priority, CacheItemRemovedCallback refreshAction, CacheDependency cacheDependency, TimeSpan? timeout, Func<T> getCacheItem)
|
||||
public void InsertCacheItem(string cacheKey, Func<object> getCacheItem, TimeSpan? timeout = null, bool isSliding = false, CacheItemPriority priority = CacheItemPriority.Normal, CacheItemRemovedCallback removedCallback = null, string[] dependentFiles = null)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,213 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Linq;
|
||||
using System.Runtime.Caching;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Threading;
|
||||
using System.Web.Caching;
|
||||
using Umbraco.Core.Logging;
|
||||
using CacheItemPriority = System.Web.Caching.CacheItemPriority;
|
||||
|
||||
namespace Umbraco.Core.Cache
|
||||
{
|
||||
/// <summary>
|
||||
/// A cache provider that wraps the logic of a System.Runtime.Caching.ObjectCache
|
||||
/// </summary>
|
||||
internal class ObjectCacheRuntimeCacheProvider : IRuntimeCacheProvider
|
||||
{
|
||||
private static readonly ReaderWriterLockSlim Locker = new ReaderWriterLockSlim(LockRecursionPolicy.SupportsRecursion);
|
||||
internal ObjectCache MemoryCache;
|
||||
|
||||
public ObjectCacheRuntimeCacheProvider()
|
||||
{
|
||||
MemoryCache = new MemoryCache("in-memory");
|
||||
}
|
||||
|
||||
public virtual void ClearAllCache()
|
||||
{
|
||||
using (new WriteLock(Locker))
|
||||
{
|
||||
MemoryCache.DisposeIfDisposable();
|
||||
MemoryCache = new MemoryCache("in-memory");
|
||||
}
|
||||
}
|
||||
|
||||
public virtual void ClearCacheItem(string key)
|
||||
{
|
||||
using (new WriteLock(Locker))
|
||||
{
|
||||
if (MemoryCache[key] == null) return;
|
||||
MemoryCache.Remove(key);
|
||||
}
|
||||
}
|
||||
|
||||
public virtual void ClearCacheObjectTypes(string typeName)
|
||||
{
|
||||
using (new WriteLock(Locker))
|
||||
{
|
||||
var keysToRemove = MemoryCache
|
||||
.Where(c => c.Value != null && c.Value.GetType().ToString().InvariantEquals(typeName))
|
||||
.Select(c => c.Key)
|
||||
.ToArray();
|
||||
foreach (var k in keysToRemove)
|
||||
MemoryCache.Remove(k);
|
||||
}
|
||||
}
|
||||
|
||||
public virtual void ClearCacheObjectTypes<T>()
|
||||
{
|
||||
using (new WriteLock(Locker))
|
||||
{
|
||||
var typeOfT = typeof (T);
|
||||
var keysToRemove = MemoryCache
|
||||
.Where(c => c.Value != null && c.Value.GetType() == typeOfT)
|
||||
.Select(c => c.Key)
|
||||
.ToArray();
|
||||
foreach (var k in keysToRemove)
|
||||
MemoryCache.Remove(k);
|
||||
}
|
||||
}
|
||||
|
||||
public virtual void ClearCacheObjectTypes<T>(Func<string, T, bool> predicate)
|
||||
{
|
||||
using (new WriteLock(Locker))
|
||||
{
|
||||
var typeOfT = typeof(T);
|
||||
var keysToRemove = MemoryCache
|
||||
.Where(c => c.Value != null && c.Value.GetType() == typeOfT && predicate(c.Key, (T)c.Value))
|
||||
.Select(c => c.Key)
|
||||
.ToArray();
|
||||
foreach (var k in keysToRemove)
|
||||
MemoryCache.Remove(k);
|
||||
}
|
||||
}
|
||||
|
||||
public virtual void ClearCacheByKeySearch(string keyStartsWith)
|
||||
{
|
||||
using (new WriteLock(Locker))
|
||||
{
|
||||
var keysToRemove = (from c in MemoryCache where c.Key.InvariantStartsWith(keyStartsWith) select c.Key).ToList();
|
||||
foreach (var k in keysToRemove)
|
||||
{
|
||||
MemoryCache.Remove(k);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public virtual void ClearCacheByKeyExpression(string regexString)
|
||||
{
|
||||
using (new WriteLock(Locker))
|
||||
{
|
||||
var keysToRemove = (from c in MemoryCache where Regex.IsMatch(c.Key, regexString) select c.Key).ToList();
|
||||
foreach (var k in keysToRemove)
|
||||
{
|
||||
MemoryCache.Remove(k);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public virtual IEnumerable<object> GetCacheItemsByKeySearch(string keyStartsWith)
|
||||
{
|
||||
return (from c in MemoryCache
|
||||
where c.Key.InvariantStartsWith(keyStartsWith)
|
||||
select c.Value).ToList();
|
||||
}
|
||||
|
||||
public virtual object GetCacheItem(string cacheKey)
|
||||
{
|
||||
var result = MemoryCache.Get(cacheKey);
|
||||
return result;
|
||||
}
|
||||
|
||||
public virtual object GetCacheItem(string cacheKey, Func<object> getCacheItem)
|
||||
{
|
||||
return GetCacheItem(cacheKey, getCacheItem, null);
|
||||
}
|
||||
|
||||
public object GetCacheItem(
|
||||
string cacheKey,
|
||||
Func<object> getCacheItem,
|
||||
TimeSpan? timeout,
|
||||
bool isSliding = false,
|
||||
CacheItemPriority priority = CacheItemPriority.Normal,
|
||||
CacheItemRemovedCallback removedCallback = null,
|
||||
string[] dependentFiles = null)
|
||||
{
|
||||
using (var lck = new UpgradeableReadLock(Locker))
|
||||
{
|
||||
var result = MemoryCache.Get(cacheKey);
|
||||
if (result == null)
|
||||
{
|
||||
lck.UpgradeToWriteLock();
|
||||
|
||||
result = getCacheItem();
|
||||
if (result != null)
|
||||
{
|
||||
var policy = GetPolicy(timeout, isSliding, removedCallback, dependentFiles);
|
||||
MemoryCache.Set(cacheKey, result, policy);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
public void InsertCacheItem(string cacheKey, Func<object> getCacheItem, TimeSpan? timeout = null, bool isSliding = false, CacheItemPriority priority = CacheItemPriority.Normal, CacheItemRemovedCallback removedCallback = null, string[] dependentFiles = null)
|
||||
{
|
||||
object result = getCacheItem();
|
||||
if (result != null)
|
||||
{
|
||||
var policy = GetPolicy(timeout, isSliding, removedCallback, dependentFiles);
|
||||
MemoryCache.Set(cacheKey, result, policy);
|
||||
}
|
||||
}
|
||||
|
||||
private static CacheItemPolicy GetPolicy(TimeSpan? timeout = null, bool isSliding = false, CacheItemRemovedCallback removedCallback = null, string[] dependentFiles = null)
|
||||
{
|
||||
var absolute = isSliding ? ObjectCache.InfiniteAbsoluteExpiration : (timeout == null ? ObjectCache.InfiniteAbsoluteExpiration : DateTime.Now.Add(timeout.Value));
|
||||
var sliding = isSliding == false ? ObjectCache.NoSlidingExpiration : (timeout ?? ObjectCache.NoSlidingExpiration);
|
||||
|
||||
var policy = new CacheItemPolicy
|
||||
{
|
||||
AbsoluteExpiration = absolute,
|
||||
SlidingExpiration = sliding
|
||||
};
|
||||
|
||||
if (dependentFiles != null && dependentFiles.Any())
|
||||
{
|
||||
policy.ChangeMonitors.Add(new HostFileChangeMonitor(dependentFiles.ToList()));
|
||||
}
|
||||
|
||||
if (removedCallback != null)
|
||||
{
|
||||
policy.RemovedCallback = arguments =>
|
||||
{
|
||||
//convert the reason
|
||||
var reason = CacheItemRemovedReason.Removed;
|
||||
switch (arguments.RemovedReason)
|
||||
{
|
||||
case CacheEntryRemovedReason.Removed:
|
||||
reason = CacheItemRemovedReason.Removed;
|
||||
break;
|
||||
case CacheEntryRemovedReason.Expired:
|
||||
reason = CacheItemRemovedReason.Expired;
|
||||
break;
|
||||
case CacheEntryRemovedReason.Evicted:
|
||||
reason = CacheItemRemovedReason.Underused;
|
||||
break;
|
||||
case CacheEntryRemovedReason.ChangeMonitorChanged:
|
||||
reason = CacheItemRemovedReason.Expired;
|
||||
break;
|
||||
case CacheEntryRemovedReason.CacheSpecificEviction:
|
||||
reason = CacheItemRemovedReason.Underused;
|
||||
break;
|
||||
}
|
||||
//call the callback
|
||||
removedCallback(arguments.CacheItem.Key, arguments.CacheItem.Value, reason);
|
||||
};
|
||||
}
|
||||
return policy;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,24 +0,0 @@
|
||||
using System;
|
||||
using System.Text;
|
||||
using System.Web.Caching;
|
||||
|
||||
namespace Umbraco.Core.Cache
|
||||
{
|
||||
/// <summary>
|
||||
/// An abstract class for implementing a runtime cache provider
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// THIS MUST REMAIN INTERNAL UNTIL WE STREAMLINE HOW ALL CACHE IS HANDLED, WE NEED TO SUPPORT HTTP RUNTIME CACHE, IN MEMORY CACHE, REQUEST CACHE, ETC...
|
||||
/// </remarks>
|
||||
internal abstract class RuntimeCacheProviderBase : CacheProviderBase
|
||||
{
|
||||
public abstract T GetCacheItem<T>(string cacheKey, TimeSpan? timeout, Func<T> getCacheItem);
|
||||
public abstract T GetCacheItem<T>(string cacheKey, CacheItemRemovedCallback refreshAction, TimeSpan? timeout, Func<T> getCacheItem);
|
||||
public abstract T GetCacheItem<T>(string cacheKey, CacheItemPriority priority, CacheItemRemovedCallback refreshAction, TimeSpan? timeout, Func<T> getCacheItem);
|
||||
public abstract T GetCacheItem<T>(string cacheKey, CacheItemPriority priority, CacheItemRemovedCallback refreshAction, CacheDependency cacheDependency, TimeSpan? timeout, Func<T> getCacheItem);
|
||||
public abstract void InsertCacheItem<T>(string cacheKey, CacheItemPriority priority, Func<T> getCacheItem);
|
||||
public abstract void InsertCacheItem<T>(string cacheKey, CacheItemPriority priority, TimeSpan? timeout, Func<T> getCacheItem);
|
||||
public abstract void InsertCacheItem<T>(string cacheKey, CacheItemPriority priority, CacheDependency cacheDependency, TimeSpan? timeout, Func<T> getCacheItem);
|
||||
public abstract void InsertCacheItem<T>(string cacheKey, CacheItemPriority priority, CacheItemRemovedCallback refreshAction, CacheDependency cacheDependency, TimeSpan? timeout, Func<T> getCacheItem);
|
||||
}
|
||||
}
|
||||
@@ -10,92 +10,64 @@ namespace Umbraco.Core.Cache
|
||||
/// <summary>
|
||||
/// A cache provider that statically caches everything in an in memory dictionary
|
||||
/// </summary>
|
||||
internal class StaticCacheProvider : CacheProviderBase
|
||||
internal class StaticCacheProvider : ICacheProvider
|
||||
{
|
||||
private readonly ConcurrentDictionary<string, object> _staticCache = new ConcurrentDictionary<string, object>();
|
||||
internal readonly ConcurrentDictionary<string, object> StaticCache = new ConcurrentDictionary<string, object>();
|
||||
|
||||
public override void ClearAllCache()
|
||||
public virtual void ClearAllCache()
|
||||
{
|
||||
_staticCache.Clear();
|
||||
StaticCache.Clear();
|
||||
}
|
||||
|
||||
public override void ClearCacheItem(string key)
|
||||
public virtual void ClearCacheItem(string key)
|
||||
{
|
||||
object val;
|
||||
_staticCache.TryRemove(key, out val);
|
||||
StaticCache.TryRemove(key, out val);
|
||||
}
|
||||
|
||||
public override void ClearCacheObjectTypes(string typeName)
|
||||
public virtual void ClearCacheObjectTypes(string typeName)
|
||||
{
|
||||
foreach (var key in _staticCache.Keys)
|
||||
{
|
||||
if (_staticCache[key] != null
|
||||
&& _staticCache[key].GetType().ToString().InvariantEquals(typeName))
|
||||
{
|
||||
object val;
|
||||
_staticCache.TryRemove(key, out val);
|
||||
}
|
||||
}
|
||||
StaticCache.RemoveAll(kvp => kvp.Value != null && kvp.Value.GetType().ToString().InvariantEquals(typeName));
|
||||
}
|
||||
|
||||
public override void ClearCacheObjectTypes<T>()
|
||||
public virtual void ClearCacheObjectTypes<T>()
|
||||
{
|
||||
foreach (var key in _staticCache.Keys)
|
||||
{
|
||||
if (_staticCache[key] != null
|
||||
&& _staticCache[key].GetType() == typeof(T))
|
||||
{
|
||||
object val;
|
||||
_staticCache.TryRemove(key, out val);
|
||||
}
|
||||
}
|
||||
var typeOfT = typeof(T);
|
||||
StaticCache.RemoveAll(kvp => kvp.Value != null && kvp.Value.GetType() == typeOfT);
|
||||
}
|
||||
|
||||
public override void ClearCacheByKeySearch(string keyStartsWith)
|
||||
public virtual void ClearCacheObjectTypes<T>(Func<string, T, bool> predicate)
|
||||
{
|
||||
foreach (var key in _staticCache.Keys)
|
||||
{
|
||||
if (key.InvariantStartsWith(keyStartsWith))
|
||||
{
|
||||
ClearCacheItem(key);
|
||||
}
|
||||
}
|
||||
var typeOfT = typeof(T);
|
||||
StaticCache.RemoveAll(kvp => kvp.Value != null && kvp.Value.GetType() == typeOfT && predicate(kvp.Key, (T)kvp.Value));
|
||||
}
|
||||
|
||||
public override void ClearCacheByKeyExpression(string regexString)
|
||||
public virtual void ClearCacheByKeySearch(string keyStartsWith)
|
||||
{
|
||||
foreach (var key in _staticCache.Keys)
|
||||
{
|
||||
if (Regex.IsMatch(key, regexString))
|
||||
{
|
||||
ClearCacheItem(key);
|
||||
}
|
||||
}
|
||||
StaticCache.RemoveAll(kvp => kvp.Key.InvariantStartsWith(keyStartsWith));
|
||||
}
|
||||
|
||||
public override IEnumerable<T> GetCacheItemsByKeySearch<T>(string keyStartsWith)
|
||||
public virtual void ClearCacheByKeyExpression(string regexString)
|
||||
{
|
||||
return (from KeyValuePair<string, object> c in _staticCache
|
||||
StaticCache.RemoveAll(kvp => Regex.IsMatch(kvp.Key, regexString));
|
||||
}
|
||||
|
||||
public virtual IEnumerable<object> GetCacheItemsByKeySearch(string keyStartsWith)
|
||||
{
|
||||
return (from KeyValuePair<string, object> c in StaticCache
|
||||
where c.Key.InvariantStartsWith(keyStartsWith)
|
||||
select c.Value.TryConvertTo<T>()
|
||||
into attempt
|
||||
where attempt.Success
|
||||
select attempt.Result).ToList();
|
||||
select c.Value).ToList();
|
||||
}
|
||||
|
||||
public override T GetCacheItem<T>(string cacheKey)
|
||||
public virtual object GetCacheItem(string cacheKey)
|
||||
{
|
||||
var result = _staticCache[cacheKey];
|
||||
if (result == null)
|
||||
{
|
||||
return default(T);
|
||||
}
|
||||
return result.TryConvertTo<T>().Result;
|
||||
var result = StaticCache[cacheKey];
|
||||
return result;
|
||||
}
|
||||
|
||||
public override T GetCacheItem<T>(string cacheKey, Func<T> getCacheItem)
|
||||
public virtual object GetCacheItem(string cacheKey, Func<object> getCacheItem)
|
||||
{
|
||||
return (T)_staticCache.GetOrAdd(cacheKey, getCacheItem);
|
||||
return StaticCache.GetOrAdd(cacheKey, key => getCacheItem());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+165
-193
@@ -12,195 +12,130 @@ using Umbraco.Core.Logging;
|
||||
namespace Umbraco.Core
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// Class that is exposed by the ApplicationContext for application wide caching purposes
|
||||
/// </summary>
|
||||
/// <summary>
|
||||
/// Class that is exposed by the ApplicationContext for application wide caching purposes
|
||||
/// </summary>
|
||||
public class CacheHelper
|
||||
{
|
||||
private readonly bool _enableCache;
|
||||
private readonly CacheProviderBase _staticCache;
|
||||
private readonly CacheProviderBase _nullStaticCache = new NullCacheProvider();
|
||||
private readonly RuntimeCacheProviderBase _httpCache;
|
||||
private readonly RuntimeCacheProviderBase _nullHttpCache = new NullCacheProvider();
|
||||
{
|
||||
private readonly bool _enableCache;
|
||||
private readonly ICacheProvider _requestCache;
|
||||
private readonly ICacheProvider _nullRequestCache = new NullCacheProvider();
|
||||
private readonly ICacheProvider _staticCache;
|
||||
private readonly ICacheProvider _nullStaticCache = new NullCacheProvider();
|
||||
private readonly IRuntimeCacheProvider _httpCache;
|
||||
private readonly IRuntimeCacheProvider _nullHttpCache = new NullCacheProvider();
|
||||
|
||||
public CacheHelper(System.Web.Caching.Cache cache)
|
||||
: this(cache, true)
|
||||
{
|
||||
}
|
||||
/// <summary>
|
||||
/// Creates a cache helper with disabled caches
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
/// <remarks>
|
||||
/// Good for unit testing
|
||||
/// </remarks>
|
||||
public static CacheHelper CreateDisabledCacheHelper()
|
||||
{
|
||||
return new CacheHelper(null, null, null, false);
|
||||
}
|
||||
|
||||
internal CacheHelper(System.Web.Caching.Cache cache, bool enableCache)
|
||||
: this(new HttpRuntimeCacheProvider(cache), enableCache)
|
||||
{
|
||||
}
|
||||
|
||||
internal CacheHelper(RuntimeCacheProviderBase httpCacheProvider, bool enableCache)
|
||||
: this(httpCacheProvider, new StaticCacheProvider(), enableCache)
|
||||
/// <summary>
|
||||
/// Initializes a new instance for use in the web
|
||||
/// </summary>
|
||||
public CacheHelper()
|
||||
: this(
|
||||
new HttpRuntimeCacheProvider(HttpRuntime.Cache),
|
||||
new StaticCacheProvider(),
|
||||
new HttpRequestCacheProvider(() => new HttpContextWrapper(HttpContext.Current)))
|
||||
{
|
||||
}
|
||||
|
||||
internal CacheHelper(RuntimeCacheProviderBase httpCacheProvider, CacheProviderBase staticCacheProvider, bool enableCache)
|
||||
/// <summary>
|
||||
/// Initializes a new instance for use in the web
|
||||
/// </summary>
|
||||
/// <param name="cache"></param>
|
||||
public CacheHelper(System.Web.Caching.Cache cache)
|
||||
: this(
|
||||
new HttpRuntimeCacheProvider(cache),
|
||||
new StaticCacheProvider(),
|
||||
new HttpRequestCacheProvider(() => new HttpContextWrapper(HttpContext.Current)))
|
||||
{
|
||||
_httpCache = httpCacheProvider;
|
||||
_staticCache = staticCacheProvider;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance based on the provided providers
|
||||
/// </summary>
|
||||
/// <param name="httpCacheProvider"></param>
|
||||
/// <param name="staticCacheProvider"></param>
|
||||
/// <param name="requestCacheProvider"></param>
|
||||
public CacheHelper(
|
||||
IRuntimeCacheProvider httpCacheProvider,
|
||||
ICacheProvider staticCacheProvider,
|
||||
ICacheProvider requestCacheProvider)
|
||||
: this(httpCacheProvider, staticCacheProvider, requestCacheProvider, true)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Private ctor used for creating a disabled cache helper
|
||||
/// </summary>
|
||||
/// <param name="httpCacheProvider"></param>
|
||||
/// <param name="staticCacheProvider"></param>
|
||||
/// <param name="requestCacheProvider"></param>
|
||||
/// <param name="enableCache"></param>
|
||||
private CacheHelper(
|
||||
IRuntimeCacheProvider httpCacheProvider,
|
||||
ICacheProvider staticCacheProvider,
|
||||
ICacheProvider requestCacheProvider,
|
||||
bool enableCache)
|
||||
{
|
||||
if (enableCache)
|
||||
{
|
||||
_httpCache = httpCacheProvider;
|
||||
_staticCache = staticCacheProvider;
|
||||
_requestCache = requestCacheProvider;
|
||||
}
|
||||
else
|
||||
{
|
||||
_httpCache = null;
|
||||
_staticCache = null;
|
||||
_requestCache = null;
|
||||
}
|
||||
|
||||
_enableCache = enableCache;
|
||||
}
|
||||
|
||||
#region Static cache
|
||||
|
||||
/// <summary>
|
||||
/// Clears the item in umbraco's static cache
|
||||
/// Returns the current Request cache
|
||||
/// </summary>
|
||||
internal void ClearAllStaticCache()
|
||||
public ICacheProvider RequestCache
|
||||
{
|
||||
if (!_enableCache)
|
||||
{
|
||||
_nullStaticCache.ClearAllCache();
|
||||
}
|
||||
else
|
||||
{
|
||||
_staticCache.ClearAllCache();
|
||||
}
|
||||
get { return _enableCache ? _requestCache : _nullRequestCache; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clears the item in umbraco's static cache with the given key
|
||||
/// Returns the current Runtime cache
|
||||
/// </summary>
|
||||
/// <param name="key">Key</param>
|
||||
internal void ClearStaticCacheItem(string key)
|
||||
public ICacheProvider StaticCache
|
||||
{
|
||||
if (!_enableCache)
|
||||
{
|
||||
_nullStaticCache.ClearCacheItem(key);
|
||||
}
|
||||
else
|
||||
{
|
||||
_staticCache.ClearCacheItem(key);
|
||||
}
|
||||
get { return _enableCache ? _staticCache : _nullStaticCache; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clears all objects in the static cache with the System.Type name as the
|
||||
/// input parameter. (using [object].GetType())
|
||||
/// Returns the current Runtime cache
|
||||
/// </summary>
|
||||
/// <param name="typeName">The name of the System.Type which should be cleared from cache ex "System.Xml.XmlDocument"</param>
|
||||
internal void ClearStaticCacheObjectTypes(string typeName)
|
||||
public IRuntimeCacheProvider RuntimeCache
|
||||
{
|
||||
if (!_enableCache)
|
||||
{
|
||||
_nullStaticCache.ClearCacheObjectTypes(typeName);
|
||||
}
|
||||
else
|
||||
{
|
||||
_staticCache.ClearCacheObjectTypes(typeName);
|
||||
}
|
||||
get { return _enableCache ? _httpCache : _nullHttpCache; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clears all objects in the static cache with the System.Type specified
|
||||
/// </summary>
|
||||
internal void ClearStaticCacheObjectTypes<T>()
|
||||
{
|
||||
if (!_enableCache)
|
||||
{
|
||||
_nullStaticCache.ClearCacheObjectTypes<T>();
|
||||
}
|
||||
else
|
||||
{
|
||||
_staticCache.ClearCacheObjectTypes<T>();
|
||||
}
|
||||
}
|
||||
#region Legacy Runtime/Http Cache accessors
|
||||
|
||||
/// <summary>
|
||||
/// Clears all static cache items that starts with the key passed.
|
||||
/// </summary>
|
||||
/// <param name="keyStartsWith">The start of the key</param>
|
||||
internal void ClearStaticCacheByKeySearch(string keyStartsWith)
|
||||
{
|
||||
if (!_enableCache)
|
||||
{
|
||||
_nullStaticCache.ClearCacheByKeySearch(keyStartsWith);
|
||||
}
|
||||
else
|
||||
{
|
||||
_staticCache.ClearCacheByKeySearch(keyStartsWith);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clears all cache items that have a key that matches the regular expression
|
||||
/// </summary>
|
||||
/// <param name="regexString"></param>
|
||||
internal void ClearStaticCacheByKeyExpression(string regexString)
|
||||
{
|
||||
if (!_enableCache)
|
||||
{
|
||||
_nullStaticCache.ClearCacheByKeyExpression(regexString);
|
||||
}
|
||||
else
|
||||
{
|
||||
_staticCache.ClearCacheByKeyExpression(regexString);
|
||||
}
|
||||
}
|
||||
|
||||
internal IEnumerable<T> GetStaticCacheItemsByKeySearch<T>(string keyStartsWith)
|
||||
{
|
||||
if (!_enableCache)
|
||||
{
|
||||
return _nullStaticCache.GetCacheItemsByKeySearch<T>(keyStartsWith);
|
||||
}
|
||||
else
|
||||
{
|
||||
return _staticCache.GetCacheItemsByKeySearch<T>(keyStartsWith);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a static cache item by key, does not update the cache if it isn't there.
|
||||
/// </summary>
|
||||
/// <typeparam name="TT"></typeparam>
|
||||
/// <param name="cacheKey"></param>
|
||||
/// <returns></returns>
|
||||
internal TT GetStaticCacheItem<TT>(string cacheKey)
|
||||
{
|
||||
if (!_enableCache)
|
||||
{
|
||||
return _nullStaticCache.GetCacheItem<TT>(cacheKey);
|
||||
}
|
||||
else
|
||||
{
|
||||
return _staticCache.GetCacheItem<TT>(cacheKey);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets (and adds if necessary) an item from the static cache with all of the default parameters
|
||||
/// </summary>
|
||||
/// <typeparam name="TT"></typeparam>
|
||||
/// <param name="cacheKey"></param>
|
||||
/// <param name="getCacheItem"></param>
|
||||
/// <returns></returns>
|
||||
internal TT GetStaticCacheItem<TT>(string cacheKey, Func<TT> getCacheItem)
|
||||
{
|
||||
if (!_enableCache)
|
||||
{
|
||||
return _nullStaticCache.GetCacheItem<TT>(cacheKey, getCacheItem);
|
||||
}
|
||||
else
|
||||
{
|
||||
return _staticCache.GetCacheItem<TT>(cacheKey, getCacheItem);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
#region Runtime/Http Cache
|
||||
/// <summary>
|
||||
/// Clears the item in umbraco's runtime cache
|
||||
/// </summary>
|
||||
[Obsolete("Do not use this method, access the runtime cache from the RuntimeCache property")]
|
||||
public void ClearAllCache()
|
||||
{
|
||||
if (!_enableCache)
|
||||
if (_enableCache == false)
|
||||
{
|
||||
_nullHttpCache.ClearAllCache();
|
||||
}
|
||||
@@ -214,9 +149,10 @@ namespace Umbraco.Core
|
||||
/// Clears the item in umbraco's runtime cache with the given key
|
||||
/// </summary>
|
||||
/// <param name="key">Key</param>
|
||||
[Obsolete("Do not use this method, access the runtime cache from the RuntimeCache property")]
|
||||
public void ClearCacheItem(string key)
|
||||
{
|
||||
if (!_enableCache)
|
||||
if (_enableCache == false)
|
||||
{
|
||||
_nullHttpCache.ClearCacheItem(key);
|
||||
}
|
||||
@@ -232,9 +168,10 @@ namespace Umbraco.Core
|
||||
/// input parameter. (using [object].GetType())
|
||||
/// </summary>
|
||||
/// <param name="typeName">The name of the System.Type which should be cleared from cache ex "System.Xml.XmlDocument"</param>
|
||||
[Obsolete("Do not use this method, access the runtime cache from the RuntimeCache property")]
|
||||
public void ClearCacheObjectTypes(string typeName)
|
||||
{
|
||||
if (!_enableCache)
|
||||
if (_enableCache == false)
|
||||
{
|
||||
_nullHttpCache.ClearCacheObjectTypes(typeName);
|
||||
}
|
||||
@@ -247,9 +184,10 @@ namespace Umbraco.Core
|
||||
/// <summary>
|
||||
/// Clears all objects in the System.Web.Cache with the System.Type specified
|
||||
/// </summary>
|
||||
[Obsolete("Do not use this method, access the runtime cache from the RuntimeCache property")]
|
||||
public void ClearCacheObjectTypes<T>()
|
||||
{
|
||||
if (!_enableCache)
|
||||
if (_enableCache == false)
|
||||
{
|
||||
_nullHttpCache.ClearCacheObjectTypes<T>();
|
||||
}
|
||||
@@ -263,9 +201,10 @@ namespace Umbraco.Core
|
||||
/// Clears all cache items that starts with the key passed.
|
||||
/// </summary>
|
||||
/// <param name="keyStartsWith">The start of the key</param>
|
||||
[Obsolete("Do not use this method, access the runtime cache from the RuntimeCache property")]
|
||||
public void ClearCacheByKeySearch(string keyStartsWith)
|
||||
{
|
||||
if (!_enableCache)
|
||||
if (_enableCache == false)
|
||||
{
|
||||
_nullHttpCache.ClearCacheByKeySearch(keyStartsWith);
|
||||
}
|
||||
@@ -279,9 +218,10 @@ namespace Umbraco.Core
|
||||
/// Clears all cache items that have a key that matches the regular expression
|
||||
/// </summary>
|
||||
/// <param name="regexString"></param>
|
||||
[Obsolete("Do not use this method, access the runtime cache from the RuntimeCache property")]
|
||||
public void ClearCacheByKeyExpression(string regexString)
|
||||
{
|
||||
if (!_enableCache)
|
||||
if (_enableCache == false)
|
||||
{
|
||||
_nullHttpCache.ClearCacheByKeyExpression(regexString);
|
||||
}
|
||||
@@ -291,9 +231,10 @@ namespace Umbraco.Core
|
||||
}
|
||||
}
|
||||
|
||||
[Obsolete("Do not use this method, access the runtime cache from the RuntimeCache property")]
|
||||
public IEnumerable<T> GetCacheItemsByKeySearch<T>(string keyStartsWith)
|
||||
{
|
||||
if (!_enableCache)
|
||||
if (_enableCache == false)
|
||||
{
|
||||
return _nullHttpCache.GetCacheItemsByKeySearch<T>(keyStartsWith);
|
||||
}
|
||||
@@ -309,9 +250,10 @@ namespace Umbraco.Core
|
||||
/// <typeparam name="TT"></typeparam>
|
||||
/// <param name="cacheKey"></param>
|
||||
/// <returns></returns>
|
||||
[Obsolete("Do not use this method, access the runtime cache from the RuntimeCache property")]
|
||||
public TT GetCacheItem<TT>(string cacheKey)
|
||||
{
|
||||
if (!_enableCache)
|
||||
if (_enableCache == false)
|
||||
{
|
||||
return _nullHttpCache.GetCacheItem<TT>(cacheKey);
|
||||
}
|
||||
@@ -328,9 +270,10 @@ namespace Umbraco.Core
|
||||
/// <param name="cacheKey"></param>
|
||||
/// <param name="getCacheItem"></param>
|
||||
/// <returns></returns>
|
||||
[Obsolete("Do not use this method, access the runtime cache from the RuntimeCache property")]
|
||||
public TT GetCacheItem<TT>(string cacheKey, Func<TT> getCacheItem)
|
||||
{
|
||||
if (!_enableCache)
|
||||
if (_enableCache == false)
|
||||
{
|
||||
return _nullHttpCache.GetCacheItem<TT>(cacheKey, getCacheItem);
|
||||
}
|
||||
@@ -348,16 +291,17 @@ namespace Umbraco.Core
|
||||
/// <param name="timeout">This will set an absolute expiration from now until the timeout</param>
|
||||
/// <param name="getCacheItem"></param>
|
||||
/// <returns></returns>
|
||||
[Obsolete("Do not use this method, access the runtime cache from the RuntimeCache property")]
|
||||
public TT GetCacheItem<TT>(string cacheKey,
|
||||
TimeSpan timeout, Func<TT> getCacheItem)
|
||||
{
|
||||
if (!_enableCache)
|
||||
if (_enableCache == false)
|
||||
{
|
||||
return _nullHttpCache.GetCacheItem<TT>(cacheKey, timeout, getCacheItem);
|
||||
return _nullHttpCache.GetCacheItem<TT>(cacheKey, getCacheItem, timeout);
|
||||
}
|
||||
else
|
||||
{
|
||||
return _httpCache.GetCacheItem<TT>(cacheKey, timeout, getCacheItem);
|
||||
return _httpCache.GetCacheItem<TT>(cacheKey, getCacheItem, timeout);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -370,17 +314,18 @@ namespace Umbraco.Core
|
||||
/// <param name="timeout">This will set an absolute expiration from now until the timeout</param>
|
||||
/// <param name="getCacheItem"></param>
|
||||
/// <returns></returns>
|
||||
[Obsolete("Do not use this method, access the runtime cache from the RuntimeCache property")]
|
||||
public TT GetCacheItem<TT>(string cacheKey,
|
||||
CacheItemRemovedCallback refreshAction, TimeSpan timeout,
|
||||
Func<TT> getCacheItem)
|
||||
{
|
||||
if (!_enableCache)
|
||||
{
|
||||
return _nullHttpCache.GetCacheItem<TT>(cacheKey, refreshAction, timeout, getCacheItem);
|
||||
return _nullHttpCache.GetCacheItem<TT>(cacheKey, getCacheItem, timeout, removedCallback: refreshAction);
|
||||
}
|
||||
else
|
||||
{
|
||||
return _httpCache.GetCacheItem<TT>(cacheKey, refreshAction, timeout, getCacheItem);
|
||||
return _httpCache.GetCacheItem<TT>(cacheKey, getCacheItem, timeout, removedCallback: refreshAction);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -394,17 +339,18 @@ namespace Umbraco.Core
|
||||
/// <param name="timeout">This will set an absolute expiration from now until the timeout</param>
|
||||
/// <param name="getCacheItem"></param>
|
||||
/// <returns></returns>
|
||||
[Obsolete("Do not use this method, access the runtime cache from the RuntimeCache property")]
|
||||
public TT GetCacheItem<TT>(string cacheKey,
|
||||
CacheItemPriority priority, CacheItemRemovedCallback refreshAction, TimeSpan timeout,
|
||||
Func<TT> getCacheItem)
|
||||
{
|
||||
if (!_enableCache)
|
||||
if (_enableCache == false)
|
||||
{
|
||||
return _nullHttpCache.GetCacheItem<TT>(cacheKey, priority, refreshAction, timeout, getCacheItem);
|
||||
return _nullHttpCache.GetCacheItem<TT>(cacheKey, getCacheItem, timeout, false, priority, refreshAction);
|
||||
}
|
||||
else
|
||||
{
|
||||
return _httpCache.GetCacheItem<TT>(cacheKey, priority, refreshAction, timeout, getCacheItem);
|
||||
return _httpCache.GetCacheItem<TT>(cacheKey, getCacheItem, timeout, false, priority, refreshAction);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -419,6 +365,7 @@ namespace Umbraco.Core
|
||||
/// <param name="timeout">This will set an absolute expiration from now until the timeout</param>
|
||||
/// <param name="getCacheItem"></param>
|
||||
/// <returns></returns>
|
||||
[Obsolete("Do not use this method, we no longer support the caching overloads with references to CacheDependency, use the overloads specifying a file collection instead")]
|
||||
public TT GetCacheItem<TT>(string cacheKey,
|
||||
CacheItemPriority priority,
|
||||
CacheItemRemovedCallback refreshAction,
|
||||
@@ -426,13 +373,19 @@ namespace Umbraco.Core
|
||||
TimeSpan timeout,
|
||||
Func<TT> getCacheItem)
|
||||
{
|
||||
if (!_enableCache)
|
||||
if (_enableCache == false)
|
||||
{
|
||||
return _nullHttpCache.GetCacheItem<TT>(cacheKey, priority, refreshAction, cacheDependency, timeout, getCacheItem);
|
||||
return _nullHttpCache.GetCacheItem<TT>(cacheKey, getCacheItem, timeout, false, priority, refreshAction, null);
|
||||
}
|
||||
else
|
||||
{
|
||||
return _httpCache.GetCacheItem<TT>(cacheKey, priority, refreshAction, cacheDependency, timeout, getCacheItem);
|
||||
var cache = _httpCache as HttpRuntimeCacheProvider;
|
||||
if (cache != null)
|
||||
{
|
||||
var result = cache.GetCacheItem(cacheKey, () => getCacheItem(), timeout, false, priority, refreshAction, cacheDependency);
|
||||
return result == null ? default(TT) : result.TryConvertTo<TT>().Result;
|
||||
}
|
||||
throw new InvalidOperationException("Cannot use this obsoleted overload when the current provider is not of type " + typeof(HttpRuntimeCacheProvider));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -445,6 +398,7 @@ namespace Umbraco.Core
|
||||
/// <param name="cacheDependency"></param>
|
||||
/// <param name="getCacheItem"></param>
|
||||
/// <returns></returns>
|
||||
[Obsolete("Do not use this method, we no longer support the caching overloads with references to CacheDependency, use the overloads specifying a file collection instead")]
|
||||
public TT GetCacheItem<TT>(string cacheKey,
|
||||
CacheItemPriority priority,
|
||||
CacheDependency cacheDependency,
|
||||
@@ -452,11 +406,17 @@ namespace Umbraco.Core
|
||||
{
|
||||
if (!_enableCache)
|
||||
{
|
||||
return _nullHttpCache.GetCacheItem<TT>(cacheKey, priority, null, cacheDependency, null, getCacheItem);
|
||||
return _nullHttpCache.GetCacheItem<TT>(cacheKey, getCacheItem, null, false, priority, null, null);
|
||||
}
|
||||
else
|
||||
{
|
||||
return _httpCache.GetCacheItem<TT>(cacheKey, priority, null, cacheDependency, null, getCacheItem);
|
||||
var cache = _httpCache as HttpRuntimeCacheProvider;
|
||||
if (cache != null)
|
||||
{
|
||||
var result = cache.GetCacheItem(cacheKey, () => getCacheItem(), null, false, priority, null, cacheDependency);
|
||||
return result == null ? default(TT) : result.TryConvertTo<TT>().Result;
|
||||
}
|
||||
throw new InvalidOperationException("Cannot use this obsoleted overload when the current provider is not of type " + typeof(HttpRuntimeCacheProvider));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -471,13 +431,13 @@ namespace Umbraco.Core
|
||||
CacheItemPriority priority,
|
||||
Func<T> getCacheItem)
|
||||
{
|
||||
if (!_enableCache)
|
||||
if (_enableCache == false)
|
||||
{
|
||||
_nullHttpCache.InsertCacheItem<T>(cacheKey, priority, getCacheItem);
|
||||
_nullHttpCache.InsertCacheItem<T>(cacheKey, getCacheItem, priority: priority);
|
||||
}
|
||||
else
|
||||
{
|
||||
_httpCache.InsertCacheItem<T>(cacheKey, priority, getCacheItem);
|
||||
_httpCache.InsertCacheItem<T>(cacheKey, getCacheItem, priority: priority);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -494,13 +454,13 @@ namespace Umbraco.Core
|
||||
TimeSpan timeout,
|
||||
Func<T> getCacheItem)
|
||||
{
|
||||
if (!_enableCache)
|
||||
if (_enableCache == false)
|
||||
{
|
||||
_nullHttpCache.InsertCacheItem<T>(cacheKey, priority, timeout, getCacheItem);
|
||||
_nullHttpCache.InsertCacheItem<T>(cacheKey, getCacheItem, timeout, priority: priority);
|
||||
}
|
||||
else
|
||||
{
|
||||
_httpCache.InsertCacheItem<T>(cacheKey, priority, timeout, getCacheItem);
|
||||
_httpCache.InsertCacheItem<T>(cacheKey, getCacheItem, timeout, priority: priority);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -513,19 +473,25 @@ namespace Umbraco.Core
|
||||
/// <param name="cacheDependency"></param>
|
||||
/// <param name="timeout">This will set an absolute expiration from now until the timeout</param>
|
||||
/// <param name="getCacheItem"></param>
|
||||
[Obsolete("Do not use this method, we no longer support the caching overloads with references to CacheDependency, use the overloads specifying a file collection instead")]
|
||||
public void InsertCacheItem<T>(string cacheKey,
|
||||
CacheItemPriority priority,
|
||||
CacheDependency cacheDependency,
|
||||
TimeSpan timeout,
|
||||
Func<T> getCacheItem)
|
||||
{
|
||||
if (!_enableCache)
|
||||
if (_enableCache == false)
|
||||
{
|
||||
_nullHttpCache.InsertCacheItem<T>(cacheKey, priority, cacheDependency, timeout, getCacheItem);
|
||||
_nullHttpCache.InsertCacheItem<T>(cacheKey, getCacheItem, timeout, priority: priority, dependentFiles: null);
|
||||
}
|
||||
else
|
||||
{
|
||||
_httpCache.InsertCacheItem<T>(cacheKey, priority, cacheDependency, timeout, getCacheItem);
|
||||
var cache = _httpCache as HttpRuntimeCacheProvider;
|
||||
if (cache != null)
|
||||
{
|
||||
cache.InsertCacheItem(cacheKey, () => getCacheItem(), timeout, false, priority, null, cacheDependency);
|
||||
}
|
||||
throw new InvalidOperationException("Cannot use this obsoleted overload when the current provider is not of type " + typeof(HttpRuntimeCacheProvider));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -539,6 +505,7 @@ namespace Umbraco.Core
|
||||
/// <param name="cacheDependency"></param>
|
||||
/// <param name="timeout">This will set an absolute expiration from now until the timeout</param>
|
||||
/// <param name="getCacheItem"></param>
|
||||
[Obsolete("Do not use this method, we no longer support the caching overloads with references to CacheDependency, use the overloads specifying a file collection instead")]
|
||||
public void InsertCacheItem<T>(string cacheKey,
|
||||
CacheItemPriority priority,
|
||||
CacheItemRemovedCallback refreshAction,
|
||||
@@ -546,17 +513,22 @@ namespace Umbraco.Core
|
||||
TimeSpan? timeout,
|
||||
Func<T> getCacheItem)
|
||||
{
|
||||
if (!_enableCache)
|
||||
if (_enableCache == false)
|
||||
{
|
||||
_nullHttpCache.InsertCacheItem<T>(cacheKey, priority, refreshAction, cacheDependency, timeout, getCacheItem);
|
||||
_nullHttpCache.InsertCacheItem<T>(cacheKey, getCacheItem, timeout, false, priority, refreshAction, null);
|
||||
}
|
||||
else
|
||||
{
|
||||
_httpCache.InsertCacheItem<T>(cacheKey, priority, refreshAction, cacheDependency, timeout, getCacheItem);
|
||||
var cache = _httpCache as HttpRuntimeCacheProvider;
|
||||
if (cache != null)
|
||||
{
|
||||
cache.InsertCacheItem(cacheKey, () => getCacheItem(), timeout, false, priority, refreshAction, cacheDependency);
|
||||
}
|
||||
throw new InvalidOperationException("Cannot use this obsoleted overload when the current provider is not of type " + typeof(HttpRuntimeCacheProvider));
|
||||
}
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -27,7 +27,7 @@ namespace Umbraco.Core
|
||||
/// <summary>
|
||||
/// Gets the <see cref="ICacheRefresher"/> implementations.
|
||||
/// </summary>
|
||||
public IEnumerable<ICacheRefresher> CacheResolvers
|
||||
public IEnumerable<ICacheRefresher> CacheRefreshers
|
||||
{
|
||||
get
|
||||
{
|
||||
|
||||
@@ -16,7 +16,7 @@ namespace Umbraco.Core.Configuration
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Increases the version number in ClientDependency.config by 1
|
||||
/// Changes the version number in ClientDependency.config to a random value to avoid stale caches
|
||||
/// </summary>
|
||||
internal bool IncreaseVersionNumber()
|
||||
{
|
||||
@@ -28,9 +28,9 @@ namespace Umbraco.Core.Configuration
|
||||
|
||||
var versionAttribute = clientDependencyConfigXml.Root.Attribute("version");
|
||||
|
||||
int oldVersion;
|
||||
int.TryParse(versionAttribute.Value, out oldVersion);
|
||||
var newVersion = oldVersion + 1;
|
||||
//Set the new version to the hashcode of now
|
||||
var oldVersion = versionAttribute.Value;
|
||||
var newVersion = Math.Abs(DateTime.UtcNow.GetHashCode());
|
||||
|
||||
versionAttribute.SetValue(newVersion);
|
||||
clientDependencyConfigXml.Save(_fileName, SaveOptions.DisableFormatting);
|
||||
|
||||
@@ -7,6 +7,7 @@ using System.Web.Configuration;
|
||||
using System.Web.Routing;
|
||||
using System.Xml;
|
||||
using System.Xml.Linq;
|
||||
using System.Xml.XPath;
|
||||
using Umbraco.Core.IO;
|
||||
using Umbraco.Core.Logging;
|
||||
|
||||
@@ -175,7 +176,10 @@ namespace Umbraco.Core.Configuration
|
||||
{
|
||||
throw new InvalidOperationException("Cannot create an MVC Area path without the umbracoPath specified");
|
||||
}
|
||||
return Path.TrimStart(SystemDirectories.Root).TrimStart('~').TrimStart('/').Replace('/', '-').Trim().ToLower();
|
||||
var path = Path;
|
||||
if (path.StartsWith(SystemDirectories.Root)) // beware of TrimStart, see U4-2518
|
||||
path = path.Substring(SystemDirectories.Root.Length);
|
||||
return path.TrimStart('~').TrimStart('/').Replace('/', '-').Trim().ToLower();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -197,7 +201,7 @@ namespace Umbraco.Core.Configuration
|
||||
/// Gets the database connection string
|
||||
/// </summary>
|
||||
/// <value>The database connection string.</value>
|
||||
[Obsolete("Use System.ConfigurationManager.ConnectionStrings to get the connection with the key Umbraco.Core.Configuration.GlobalSettings.UmbracoConnectionName instead")]
|
||||
[Obsolete("Use System.Configuration.ConfigurationManager.ConnectionStrings[\"umbracoDbDSN\"] instead")]
|
||||
public static string DbDsn
|
||||
{
|
||||
get
|
||||
@@ -209,7 +213,7 @@ namespace Umbraco.Core.Configuration
|
||||
{
|
||||
connectionString = settings.ConnectionString;
|
||||
|
||||
// The SqlCe connectionString is formatted slightly differently, so we need to updat it
|
||||
// The SqlCe connectionString is formatted slightly differently, so we need to update it
|
||||
if (settings.ProviderName.Contains("SqlServerCe"))
|
||||
connectionString = string.Format("datalayer=SQLCE4Umbraco.SqlCEHelper,SQLCE4Umbraco;{0}", connectionString);
|
||||
}
|
||||
@@ -252,7 +256,38 @@ namespace Umbraco.Core.Configuration
|
||||
SaveSetting("umbracoConfigurationStatus", value);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the Umbraco members membership providers' useLegacyEncoding state. This will return a boolean
|
||||
/// </summary>
|
||||
/// <value>The useLegacyEncoding status.</value>
|
||||
public static bool UmbracoMembershipProviderLegacyEncoding
|
||||
{
|
||||
get
|
||||
{
|
||||
return ConfiguredMembershipProvidersLegacyEncoding(Constants.Conventions.Member.UmbracoMemberProviderName);
|
||||
}
|
||||
set
|
||||
{
|
||||
SetMembershipProvidersLegacyEncoding(Constants.Conventions.Member.UmbracoMemberProviderName, value);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the Umbraco users membership providers' useLegacyEncoding state. This will return a boolean
|
||||
/// </summary>
|
||||
/// <value>The useLegacyEncoding status.</value>
|
||||
public static bool UmbracoUsersMembershipProviderLegacyEncoding
|
||||
{
|
||||
get
|
||||
{
|
||||
return ConfiguredMembershipProvidersLegacyEncoding(Constants.Conventions.User.UmbracoUsersProviderName);
|
||||
}
|
||||
set
|
||||
{
|
||||
SetMembershipProvidersLegacyEncoding(Constants.Conventions.User.UmbracoUsersProviderName, value);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Saves a setting into the configuration file.
|
||||
@@ -312,6 +347,43 @@ namespace Umbraco.Core.Configuration
|
||||
return fileName;
|
||||
}
|
||||
|
||||
private static void SetMembershipProvidersLegacyEncoding(string providerName, bool useLegacyEncoding)
|
||||
{
|
||||
var webConfigFilename = GetFullWebConfigFileName();
|
||||
var webConfigXml = XDocument.Load(webConfigFilename, LoadOptions.PreserveWhitespace);
|
||||
|
||||
var membershipConfigs = webConfigXml.XPathSelectElements("configuration/system.web/membership/providers/add").ToList();
|
||||
|
||||
if (membershipConfigs.Any() == false)
|
||||
return;
|
||||
|
||||
var provider = membershipConfigs.SingleOrDefault(c => c.Attribute("name") != null && c.Attribute("name").Value == providerName);
|
||||
|
||||
if (provider == null)
|
||||
return;
|
||||
|
||||
provider.SetAttributeValue("useLegacyEncoding", useLegacyEncoding);
|
||||
|
||||
webConfigXml.Save(webConfigFilename, SaveOptions.DisableFormatting);
|
||||
}
|
||||
|
||||
private static bool ConfiguredMembershipProvidersLegacyEncoding(string providerName)
|
||||
{
|
||||
var webConfigFilename = GetFullWebConfigFileName();
|
||||
var webConfigXml = XDocument.Load(webConfigFilename, LoadOptions.PreserveWhitespace);
|
||||
|
||||
var membershipConfigs = webConfigXml.XPathSelectElements("configuration/system.web/membership/providers/add").ToList();
|
||||
|
||||
var provider = membershipConfigs.SingleOrDefault(c => c.Attribute("name") != null && c.Attribute("name").Value == providerName);
|
||||
|
||||
var useLegacyEncodingAttribute = provider.Attribute("useLegacyEncoding");
|
||||
|
||||
bool useLegacyEncoding;
|
||||
bool.TryParse(useLegacyEncodingAttribute.Value, out useLegacyEncoding);
|
||||
|
||||
return useLegacyEncoding;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the full path to root.
|
||||
/// </summary>
|
||||
@@ -701,8 +773,12 @@ namespace Umbraco.Core.Configuration
|
||||
StartsWithContainer _newReservedList = new StartsWithContainer();
|
||||
foreach (string reservedUrl in _reservedUrlsCache.Split(new[] { "," }, StringSplitOptions.RemoveEmptyEntries))
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(reservedUrl))
|
||||
continue;
|
||||
|
||||
|
||||
//resolves the url to support tilde chars
|
||||
string reservedUrlTrimmed = IOHelper.ResolveUrl(reservedUrl).Trim().ToLower();
|
||||
string reservedUrlTrimmed = IOHelper.ResolveUrl(reservedUrl.Trim()).Trim().ToLower();
|
||||
if (reservedUrlTrimmed.Length > 0)
|
||||
_newReservedList.Add(reservedUrlTrimmed);
|
||||
}
|
||||
@@ -710,8 +786,11 @@ namespace Umbraco.Core.Configuration
|
||||
foreach (string reservedPath in _reservedPathsCache.Split(new[] { "," }, StringSplitOptions.RemoveEmptyEntries))
|
||||
{
|
||||
bool trimEnd = !reservedPath.EndsWith("/");
|
||||
if (string.IsNullOrWhiteSpace(reservedPath))
|
||||
continue;
|
||||
|
||||
//resolves the url to support tilde chars
|
||||
string reservedPathTrimmed = IOHelper.ResolveUrl(reservedPath).Trim().ToLower();
|
||||
string reservedPathTrimmed = IOHelper.ResolveUrl(reservedPath.Trim()).Trim().ToLower();
|
||||
|
||||
if (reservedPathTrimmed.Length > 0)
|
||||
_newReservedList.Add(reservedPathTrimmed + (reservedPathTrimmed.EndsWith("/") ? "" : "/"));
|
||||
|
||||
@@ -309,7 +309,7 @@ namespace Umbraco.Core.Configuration
|
||||
{
|
||||
return value;
|
||||
}
|
||||
return "UMB_UCONTEXT";
|
||||
return Constants.Web.AuthCookieName;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -502,8 +502,12 @@ namespace Umbraco.Core.Configuration
|
||||
}
|
||||
}
|
||||
|
||||
//TODO: I"m not sure why we need this, need to ask Gareth what the deal is, pretty sure we can remove it or change it, seems like
|
||||
// massive overkill.
|
||||
// we have that one because we auto-discover when a property is "xml" and should be returned by Razor as
|
||||
// dynamic xml. But, stuff such as "<p>hello</p>" can be parsed into xml and would be returned as xml,
|
||||
// unless <p> has been defined in the exclusion list... this is dirty and not-efficient, we should at least
|
||||
// cache the list somewhere!
|
||||
//
|
||||
// TODO get rid of that whole dynamic xml mess
|
||||
|
||||
/// <summary>
|
||||
/// razor DynamicNode typecasting detects XML and returns DynamicXml - Root elements that won't convert to DynamicXml
|
||||
@@ -514,12 +518,10 @@ namespace Umbraco.Core.Configuration
|
||||
{
|
||||
try
|
||||
{
|
||||
List<string> items = new List<string>();
|
||||
XmlNode root = GetKeyAsNode("/settings/scripting/razor/notDynamicXmlDocumentElements");
|
||||
foreach (XmlNode element in root.SelectNodes(".//element"))
|
||||
{
|
||||
items.Add(element.InnerText);
|
||||
}
|
||||
var items = new List<string>();
|
||||
var root = GetKeyAsNode("/settings/scripting/razor/notDynamicXmlDocumentElements");
|
||||
if (root != null)
|
||||
items.AddRange(root.SelectNodes(".//element").Cast<XmlNode>().Select(n => n.InnerText));
|
||||
return items;
|
||||
}
|
||||
catch
|
||||
@@ -656,7 +658,7 @@ namespace Umbraco.Core.Configuration
|
||||
{
|
||||
string defaultProvider = GetKey("/settings/providers/users/DefaultBackofficeProvider");
|
||||
if (String.IsNullOrEmpty(defaultProvider))
|
||||
defaultProvider = "UsersMembershipProvider";
|
||||
defaultProvider = Constants.Conventions.User.UmbracoUsersProviderName;
|
||||
|
||||
return defaultProvider;
|
||||
}
|
||||
@@ -853,6 +855,8 @@ namespace Umbraco.Core.Configuration
|
||||
/// <summary>
|
||||
/// Whether to replace double dashes from url (ie my--story----from--dash.aspx caused by multiple url replacement chars
|
||||
/// </summary>
|
||||
// was used by the legacy short string helper, is not used anymore by the new default short string helper
|
||||
// should update documentation
|
||||
internal static bool RemoveDoubleDashesFromUrlReplacing
|
||||
{
|
||||
get
|
||||
|
||||
@@ -5,7 +5,7 @@ namespace Umbraco.Core.Configuration
|
||||
{
|
||||
public class UmbracoVersion
|
||||
{
|
||||
private static readonly Version Version = new Version("6.1.6");
|
||||
private static readonly Version Version = new Version("6.2.0");
|
||||
|
||||
/// <summary>
|
||||
/// Gets the current version of Umbraco.
|
||||
@@ -23,7 +23,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 ""; } }
|
||||
public static string CurrentComment { get { return "beta"; } }
|
||||
|
||||
// Get the version of the umbraco.dll by looking at a class in that dll
|
||||
// Had to do it like this due to medium trust issues, see: http://haacked.com/archive/2010/11/04/assembly-location-and-medium-trust.aspx
|
||||
|
||||
@@ -1,4 +1,8 @@
|
||||
namespace Umbraco.Core
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Umbraco.Core.Models;
|
||||
|
||||
namespace Umbraco.Core
|
||||
{
|
||||
public static partial class Constants
|
||||
{
|
||||
@@ -81,7 +85,7 @@
|
||||
|
||||
/// <summary>
|
||||
/// MediaType alias for a folder.
|
||||
/// </summary>
|
||||
/// </summary>
|
||||
public const string Folder = "Folder";
|
||||
|
||||
/// <summary>
|
||||
@@ -90,6 +94,186 @@
|
||||
public const string Image = "Image";
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Constants for Umbraco user
|
||||
/// </summary>
|
||||
public static class User
|
||||
{
|
||||
public static readonly string UmbracoUsersProviderName = "UsersMembershipProvider";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constants for Umbraco Member property aliases.
|
||||
/// </summary>
|
||||
|
||||
public static class Member
|
||||
{
|
||||
public static readonly string UmbracoMemberProviderName = "UmbracoMembershipProvider";
|
||||
|
||||
public static readonly string UmbracoRoleProviderName = "UmbracoRoleProvider";
|
||||
|
||||
/// <summary>
|
||||
/// Property alias for a Members Password Question
|
||||
/// </summary>
|
||||
public const string PasswordQuestion = "umbracoPasswordRetrievalQuestionPropertyTypeAlias";
|
||||
|
||||
public const string PasswordQuestionLabel = "Password Question";
|
||||
|
||||
/// <summary>
|
||||
/// Property alias for Members Password Answer
|
||||
/// </summary>
|
||||
public const string PasswordAnswer = "umbracoPasswordRetrievalAnswerPropertyTypeAlias";
|
||||
|
||||
public const string PasswordAnswerLabel = "Password Answer";
|
||||
|
||||
/// <summary>
|
||||
/// Property alias for the Comments on a Member
|
||||
/// </summary>
|
||||
public const string Comments = "umbracoCommentPropertyTypeAlias";
|
||||
|
||||
public const string CommentsLabel = "Comments";
|
||||
|
||||
/// <summary>
|
||||
/// Property alias for the Approved boolean of a Member
|
||||
/// </summary>
|
||||
public const string IsApproved = "umbracoApprovePropertyTypeAlias";
|
||||
|
||||
public const string IsApprovedLabel = "Is Approved";
|
||||
|
||||
/// <summary>
|
||||
/// Property alias for the Locked out boolean of a Member
|
||||
/// </summary>
|
||||
public const string IsLockedOut = "umbracoLockPropertyTypeAlias";
|
||||
|
||||
public const string IsLockedOutLabel = "Is Locked Out";
|
||||
|
||||
/// <summary>
|
||||
/// Property alias for the last date the Member logged in
|
||||
/// </summary>
|
||||
public const string LastLoginDate = "umbracoLastLoginPropertyTypeAlias";
|
||||
|
||||
public const string LastLoginDateLabel = "Last Login Date";
|
||||
|
||||
/// <summary>
|
||||
/// Property alias for the last date a Member changed its password
|
||||
/// </summary>
|
||||
public const string LastPasswordChangeDate = "umbracoMemberLastPasswordChange";
|
||||
|
||||
public const string LastPasswordChangeDateLabel = "Last Password Change Date";
|
||||
|
||||
/// <summary>
|
||||
/// Property alias for the last date a Member was locked out
|
||||
/// </summary>
|
||||
public const string LastLockoutDate = "umbracoMemberLastLockout";
|
||||
|
||||
public const string LastLockoutDateLabel = "Last Lockout Date";
|
||||
|
||||
/// <summary>
|
||||
/// Property alias for the number of failed login attemps
|
||||
/// </summary>
|
||||
public const string FailedPasswordAttempts = "umbracoFailedPasswordAttemptsPropertyTypeAlias";
|
||||
|
||||
public const string FailedPasswordAttemptsLabel = "Failed Password Attempts";
|
||||
|
||||
/// <summary>
|
||||
/// Group name to put the membership properties on
|
||||
/// </summary>
|
||||
internal const string StandardPropertiesGroupName = "Membership";
|
||||
|
||||
internal static Dictionary<string, PropertyType> GetStandardPropertyTypeStubs()
|
||||
{
|
||||
return new Dictionary<string, PropertyType>
|
||||
{
|
||||
{
|
||||
Comments,
|
||||
new PropertyType(new Guid(PropertyEditors.TextboxMultiple), DataTypeDatabaseType.Ntext, true)
|
||||
{
|
||||
Alias = Comments,
|
||||
Name = CommentsLabel
|
||||
}
|
||||
},
|
||||
{
|
||||
FailedPasswordAttempts,
|
||||
new PropertyType(new Guid(PropertyEditors.NoEdit), DataTypeDatabaseType.Integer, true)
|
||||
{
|
||||
Alias = FailedPasswordAttempts,
|
||||
Name = FailedPasswordAttemptsLabel
|
||||
}
|
||||
},
|
||||
{
|
||||
IsApproved,
|
||||
new PropertyType(new Guid(PropertyEditors.TrueFalse), DataTypeDatabaseType.Integer, true)
|
||||
{
|
||||
Alias = IsApproved,
|
||||
Name = IsApprovedLabel
|
||||
}
|
||||
},
|
||||
{
|
||||
IsLockedOut,
|
||||
new PropertyType(new Guid(PropertyEditors.TrueFalse), DataTypeDatabaseType.Integer, true)
|
||||
{
|
||||
Alias = IsLockedOut,
|
||||
Name = IsLockedOutLabel
|
||||
}
|
||||
},
|
||||
{
|
||||
LastLockoutDate,
|
||||
new PropertyType(new Guid(PropertyEditors.NoEdit), DataTypeDatabaseType.Date, true)
|
||||
{
|
||||
Alias = LastLockoutDate,
|
||||
Name = LastLockoutDateLabel
|
||||
}
|
||||
},
|
||||
{
|
||||
LastLoginDate,
|
||||
new PropertyType(new Guid(PropertyEditors.NoEdit), DataTypeDatabaseType.Date, true)
|
||||
{
|
||||
Alias = LastLoginDate,
|
||||
Name = LastLoginDateLabel
|
||||
}
|
||||
},
|
||||
{
|
||||
LastPasswordChangeDate,
|
||||
new PropertyType(new Guid(PropertyEditors.NoEdit), DataTypeDatabaseType.Date, true)
|
||||
{
|
||||
Alias = LastPasswordChangeDate,
|
||||
Name = LastPasswordChangeDateLabel
|
||||
}
|
||||
},
|
||||
{
|
||||
PasswordAnswer,
|
||||
new PropertyType(new Guid(PropertyEditors.NoEdit), DataTypeDatabaseType.Nvarchar, true)
|
||||
{
|
||||
Alias = PasswordAnswer,
|
||||
Name = PasswordAnswerLabel
|
||||
}
|
||||
},
|
||||
{
|
||||
PasswordQuestion,
|
||||
new PropertyType(new Guid(PropertyEditors.NoEdit), DataTypeDatabaseType.Nvarchar, true)
|
||||
{
|
||||
Alias = PasswordQuestion,
|
||||
Name = PasswordQuestionLabel
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Defines the alias identifiers for Umbraco member types.
|
||||
/// </summary>
|
||||
public static class MemberTypes
|
||||
{
|
||||
/// <summary>
|
||||
/// MemberType alias for default member type.
|
||||
/// </summary>
|
||||
public const string Member = "Member";
|
||||
|
||||
public const string SystemDefaultProtectType = "_umbracoSystemDefaultProtectType";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constants for Umbraco URLs/Querystrings.
|
||||
/// </summary>
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
namespace Umbraco.Core
|
||||
{
|
||||
public static partial class Constants
|
||||
{
|
||||
/// <summary>
|
||||
/// Defines the identifiers for Umbraco system nodes.
|
||||
/// </summary>
|
||||
public static class Web
|
||||
{
|
||||
/// <summary>
|
||||
/// The preview cookie name
|
||||
/// </summary>
|
||||
public const string PreviewCookieName = "UMB_PREVIEW";
|
||||
|
||||
/// <summary>
|
||||
/// The auth cookie name
|
||||
/// </summary>
|
||||
public const string AuthCookieName = "UMB_UCONTEXT";
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Web.UI;
|
||||
|
||||
namespace Umbraco.Core
|
||||
{
|
||||
internal static class ControlExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Recursively finds a control with the specified identifier.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">
|
||||
/// The type of control to be found.
|
||||
/// </typeparam>
|
||||
/// <param name="parent">
|
||||
/// The parent control from which the search will start.
|
||||
/// </param>
|
||||
/// <param name="id">
|
||||
/// The identifier of the control to be found.
|
||||
/// </param>
|
||||
/// <returns>
|
||||
/// The control with the specified identifier, otherwise <see langword="null"/> if the control
|
||||
/// is not found.
|
||||
/// </returns>
|
||||
public static T FindControlRecursive<T>(this Control parent, string id) where T : Control
|
||||
{
|
||||
if ((parent is T) && (parent.ID == id))
|
||||
{
|
||||
return (T)parent;
|
||||
}
|
||||
|
||||
foreach (Control control in parent.Controls)
|
||||
{
|
||||
var foundControl = FindControlRecursive<T>(control, id);
|
||||
if (foundControl != null)
|
||||
{
|
||||
return foundControl;
|
||||
}
|
||||
}
|
||||
return default(T);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,9 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Umbraco.Core.Cache;
|
||||
using Umbraco.Core.Configuration;
|
||||
using Umbraco.Core.Logging;
|
||||
using Umbraco.Core.Models.PublishedContent;
|
||||
using Umbraco.Core.ObjectResolution;
|
||||
using Umbraco.Core.Persistence;
|
||||
using Umbraco.Core.Persistence.Mappers;
|
||||
@@ -12,6 +14,7 @@ using Umbraco.Core.Persistence.SqlSyntax;
|
||||
using Umbraco.Core.Persistence.UnitOfWork;
|
||||
using Umbraco.Core.Profiling;
|
||||
using Umbraco.Core.PropertyEditors;
|
||||
using Umbraco.Core.PropertyEditors.ValueConverters;
|
||||
using Umbraco.Core.Publishing;
|
||||
using Umbraco.Core.Macros;
|
||||
using Umbraco.Core.Services;
|
||||
@@ -36,6 +39,7 @@ namespace Umbraco.Core
|
||||
private bool _isComplete = false;
|
||||
private readonly UmbracoApplicationBase _umbracoApplication;
|
||||
protected ApplicationContext ApplicationContext { get; private set; }
|
||||
protected CacheHelper ApplicationCache { get; set; }
|
||||
|
||||
protected UmbracoApplicationBase UmbracoApplication
|
||||
{
|
||||
@@ -55,16 +59,19 @@ namespace Umbraco.Core
|
||||
|
||||
InitializeProfilerResolver();
|
||||
|
||||
CreateApplicationCache();
|
||||
|
||||
_timer = DisposableTimer.DebugDuration<CoreBootManager>("Umbraco application starting", "Umbraco application startup complete");
|
||||
|
||||
//create database and service contexts for the app context
|
||||
var dbFactory = new DefaultDatabaseFactory(GlobalSettings.UmbracoConnectionName);
|
||||
Database.Mapper = new PetaPocoMapper();
|
||||
var dbContext = new DatabaseContext(dbFactory);
|
||||
var serviceContext = new ServiceContext(
|
||||
new PetaPocoUnitOfWorkProvider(dbFactory),
|
||||
new FileUnitOfWorkProvider(),
|
||||
new PublishingStrategy());
|
||||
var serviceContext = new ServiceContext(
|
||||
new PetaPocoUnitOfWorkProvider(dbFactory),
|
||||
new FileUnitOfWorkProvider(),
|
||||
new PublishingStrategy(),
|
||||
ApplicationCache);
|
||||
|
||||
CreateApplicationContext(dbContext, serviceContext);
|
||||
|
||||
@@ -92,7 +99,21 @@ namespace Umbraco.Core
|
||||
protected virtual void CreateApplicationContext(DatabaseContext dbContext, ServiceContext serviceContext)
|
||||
{
|
||||
//create the ApplicationContext
|
||||
ApplicationContext = ApplicationContext.Current = new ApplicationContext(dbContext, serviceContext);
|
||||
ApplicationContext = ApplicationContext.Current = new ApplicationContext(dbContext, serviceContext, ApplicationCache);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates and assigns the ApplicationCache based on a new instance of System.Web.Caching.Cache
|
||||
/// </summary>
|
||||
protected virtual void CreateApplicationCache()
|
||||
{
|
||||
var cacheHelper = new CacheHelper(
|
||||
new ObjectCacheRuntimeCacheProvider(),
|
||||
new StaticCacheProvider(),
|
||||
//we have no request based cache when not running in web-based context
|
||||
new NullCacheProvider());
|
||||
|
||||
ApplicationCache = cacheHelper;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -125,8 +146,9 @@ namespace Umbraco.Core
|
||||
{
|
||||
CanResolveBeforeFrozen = true
|
||||
};
|
||||
//add custom types here that are internal
|
||||
ApplicationEventsResolver.Current.AddType<PublishedContentHelper>();
|
||||
|
||||
// add custom types here that are internal, if needed
|
||||
//ApplicationEventsResolver.Current.AddType<>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -228,7 +250,7 @@ namespace Umbraco.Core
|
||||
() => PluginManager.Current.ResolveAssignedMapperTypes());
|
||||
|
||||
RepositoryResolver.Current = new RepositoryResolver(
|
||||
new RepositoryFactory());
|
||||
new RepositoryFactory(ApplicationCache));
|
||||
|
||||
SqlSyntaxProvidersResolver.Current = new SqlSyntaxProvidersResolver(
|
||||
new[] { typeof(MySqlSyntaxProvider), typeof(SqlCeSyntaxProvider), typeof(SqlServerSyntaxProvider) })
|
||||
@@ -254,27 +276,26 @@ namespace Umbraco.Core
|
||||
//the database migration objects
|
||||
MigrationResolver.Current = new MigrationResolver(
|
||||
() => PluginManager.Current.ResolveMigrationTypes());
|
||||
|
||||
|
||||
|
||||
PropertyEditorValueConvertersResolver.Current = new PropertyEditorValueConvertersResolver(
|
||||
// todo: remove once we drop IPropertyEditorValueConverter support.
|
||||
PropertyEditorValueConvertersResolver.Current = new PropertyEditorValueConvertersResolver(
|
||||
PluginManager.Current.ResolvePropertyEditorValueConverters());
|
||||
//add the internal ones, these are not public currently so need to add them manually
|
||||
PropertyEditorValueConvertersResolver.Current.AddType<DatePickerPropertyEditorValueConverter>();
|
||||
PropertyEditorValueConvertersResolver.Current.AddType<TinyMcePropertyEditorValueConverter>();
|
||||
PropertyEditorValueConvertersResolver.Current.AddType<YesNoPropertyEditorValueConverter>();
|
||||
|
||||
// this is how we'd switch over to DefaultShortStringHelper _and_ still use
|
||||
// UmbracoSettings UrlReplaceCharacters...
|
||||
//ShortStringHelperResolver.Current = new ShortStringHelperResolver(
|
||||
// new DefaultShortStringHelper().WithConfig(DefaultShortStringHelper.ApplyUrlReplaceCharacters));
|
||||
// initialize the new property value converters by discovering IPropertyValueConverter
|
||||
PropertyValueConvertersResolver.Current = new PropertyValueConvertersResolver(
|
||||
PluginManager.Current.ResolveTypes<IPropertyValueConverter>());
|
||||
|
||||
// use the Legacy one for now
|
||||
// use the new DefaultShortStringHelper
|
||||
ShortStringHelperResolver.Current = new ShortStringHelperResolver(
|
||||
new LegacyShortStringHelper());
|
||||
//new LegacyShortStringHelper());
|
||||
new DefaultShortStringHelper().WithDefaultConfig());
|
||||
|
||||
UrlSegmentProviderResolver.Current = new UrlSegmentProviderResolver(
|
||||
typeof (DefaultUrlSegmentProvider));
|
||||
|
||||
// keep it internal for now
|
||||
PublishedContentModelFactoryResolver.Current = new PublishedContentModelFactoryResolver();
|
||||
// new PublishedContentModelFactoryImpl());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,8 +22,8 @@ namespace Umbraco.Core
|
||||
if (value is string)
|
||||
{
|
||||
var str = (string)value;
|
||||
if (str == null || str.Length == 0 || str == "0") return false;
|
||||
if (str == "1") return true;
|
||||
if (str == "0" || str == "") return false;
|
||||
}
|
||||
|
||||
return base.ConvertFrom(context, culture, value);
|
||||
|
||||
@@ -28,7 +28,7 @@ namespace Umbraco.Core
|
||||
private string _providerName;
|
||||
private DatabaseSchemaResult _result;
|
||||
|
||||
internal DatabaseContext(IDatabaseFactory factory)
|
||||
public DatabaseContext(IDatabaseFactory factory)
|
||||
{
|
||||
_factory = factory;
|
||||
}
|
||||
@@ -433,24 +433,19 @@ namespace Umbraco.Core
|
||||
return _result;
|
||||
}
|
||||
|
||||
internal Result CreateDatabaseSchemaAndDataOrUpgrade()
|
||||
internal Result CreateDatabaseSchemaAndData()
|
||||
{
|
||||
if (_configured == false || (string.IsNullOrEmpty(_connectionString) || string.IsNullOrEmpty(ProviderName)))
|
||||
var readyForInstall = CheckReadyForInstall();
|
||||
if (readyForInstall.Success == false)
|
||||
{
|
||||
return new Result
|
||||
{
|
||||
Message =
|
||||
"Database configuration is invalid. Please check that the entered database exists and that the provided username and password has write access to the database.",
|
||||
Success = false,
|
||||
Percentage = "10"
|
||||
};
|
||||
return readyForInstall.Result;
|
||||
}
|
||||
|
||||
|
||||
try
|
||||
{
|
||||
LogHelper.Info<DatabaseContext>("Database configuration status: Started");
|
||||
|
||||
var message = string.Empty;
|
||||
string message;
|
||||
|
||||
var database = new UmbracoDatabase(_connectionString, ProviderName);
|
||||
var supportsCaseInsensitiveQueries = SqlSyntaxContext.SqlSyntaxProvider.SupportsCaseInsensitiveQueries(database);
|
||||
@@ -465,50 +460,73 @@ namespace Umbraco.Core
|
||||
|
||||
return new Result { Message = message, Success = false, Percentage = "15" };
|
||||
}
|
||||
else if (supportsCaseInsensitiveQueries == null)
|
||||
{
|
||||
message = "<p> </p><p>Warning! Could not check if your database type supports case insensitive queries. <br />We currently do not support these databases that do not support case insensitive queries.</p>" +
|
||||
"<p>You can check this by looking for the following setting in your my.ini file in your MySQL installation directory:</p>" +
|
||||
"<pre>lower_case_table_names=1</pre><br />" +
|
||||
"<p>Note: Make sure to check with your hosting provider if they support case insensitive queries as well.</p>" +
|
||||
"<p>For more technical information on case sensitivity in MySQL, have a look at " +
|
||||
"<a href='http://dev.mysql.com/doc/refman/5.0/en/identifier-case-sensitivity.html'>the documentation on the subject</a></p>";
|
||||
}
|
||||
else
|
||||
{
|
||||
if (SqlSyntaxContext.SqlSyntaxProvider.GetType() == typeof(MySqlSyntaxProvider))
|
||||
{
|
||||
message = "<p> </p><p>Congratulations, the database step ran successfully!</p>" +
|
||||
"<p>Note: You're using MySQL and the database instance you're connecting to seems to support case insensitive queries.</p>" +
|
||||
"<p>However, your hosting provider may not support this option. Umbraco does not currently support MySQL installs that do not support case insensitive queries</p>" +
|
||||
"<p>Make sure to check with your hosting provider if they support case insensitive queries as well.</p>" +
|
||||
"<p>They can check this by looking for the following setting in the my.ini file in their MySQL installation directory:</p>" +
|
||||
"<pre>lower_case_table_names=1</pre><br />" +
|
||||
"<p>For more technical information on case sensitivity in MySQL, have a look at " +
|
||||
"<a href='http://dev.mysql.com/doc/refman/5.0/en/identifier-case-sensitivity.html'>the documentation on the subject</a></p>";
|
||||
}
|
||||
}
|
||||
|
||||
message = GetResultMessageForMySql(supportsCaseInsensitiveQueries);
|
||||
|
||||
var schemaResult = ValidateDatabaseSchema();
|
||||
var installedVersion = schemaResult.DetermineInstalledVersion();
|
||||
|
||||
|
||||
//If Configuration Status is empty and the determined version is "empty" its a new install - otherwise upgrade the existing
|
||||
if (string.IsNullOrEmpty(GlobalSettings.ConfigurationStatus) && installedVersion.Equals(new Version(0, 0, 0)))
|
||||
{
|
||||
database.CreateDatabaseSchema();
|
||||
message = message + "<p>Installation completed!</p>";
|
||||
|
||||
//now that everything is done, we need to determine the version of SQL server that is executing
|
||||
LogHelper.Info<DatabaseContext>("Database configuration status: " + message);
|
||||
return new Result { Message = message, Success = true, Percentage = "100" };
|
||||
}
|
||||
else
|
||||
{
|
||||
var configuredVersion = string.IsNullOrEmpty(GlobalSettings.ConfigurationStatus)
|
||||
|
||||
//we need to do an upgrade so return a new status message and it will need to be done during the next step
|
||||
LogHelper.Info<DatabaseContext>("Database requires upgrade");
|
||||
message = "<p>Upgrading database, this may take some time...</p>";
|
||||
return new Result
|
||||
{
|
||||
RequiresUpgrade = true,
|
||||
Message = message,
|
||||
Success = true,
|
||||
Percentage = "30"
|
||||
};
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return HandleInstallException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This assumes all of the previous checks are done!
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
internal Result UpgradeSchemaAndData()
|
||||
{
|
||||
var readyForInstall = CheckReadyForInstall();
|
||||
if (readyForInstall.Success == false)
|
||||
{
|
||||
return readyForInstall.Result;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
LogHelper.Info<DatabaseContext>("Database upgrade started");
|
||||
|
||||
var database = new UmbracoDatabase(_connectionString, ProviderName);
|
||||
var supportsCaseInsensitiveQueries = SqlSyntaxContext.SqlSyntaxProvider.SupportsCaseInsensitiveQueries(database);
|
||||
|
||||
var message = GetResultMessageForMySql(supportsCaseInsensitiveQueries);
|
||||
|
||||
var schemaResult = ValidateDatabaseSchema();
|
||||
var installedVersion = schemaResult.DetermineInstalledVersion();
|
||||
|
||||
//DO the upgrade!
|
||||
|
||||
var configuredVersion = string.IsNullOrEmpty(GlobalSettings.ConfigurationStatus)
|
||||
? installedVersion
|
||||
: new Version(GlobalSettings.ConfigurationStatus);
|
||||
var targetVersion = UmbracoVersion.Current;
|
||||
var runner = new MigrationRunner(configuredVersion, targetVersion, GlobalSettings.UmbracoMigrationName);
|
||||
var upgraded = runner.Execute(database, true);
|
||||
message = message + "<p>Upgrade completed!</p>";
|
||||
}
|
||||
var targetVersion = UmbracoVersion.Current;
|
||||
var runner = new MigrationRunner(configuredVersion, targetVersion, GlobalSettings.UmbracoMigrationName);
|
||||
var upgraded = runner.Execute(database, true);
|
||||
message = message + "<p>Upgrade completed!</p>";
|
||||
|
||||
//now that everything is done, we need to determine the version of SQL server that is executing
|
||||
|
||||
@@ -518,26 +536,72 @@ namespace Umbraco.Core
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogHelper.Info<DatabaseContext>("Database configuration failed with the following error and stack trace: " + ex.Message + "\n" + ex.StackTrace);
|
||||
|
||||
if (_result != null)
|
||||
{
|
||||
LogHelper.Info<DatabaseContext>("The database schema validation produced the following summary: \n" + _result.GetSummary());
|
||||
}
|
||||
|
||||
return new Result
|
||||
{
|
||||
Message =
|
||||
"The database configuration failed with the following message: " + ex.Message +
|
||||
"\n Please check log file for additional information (can be found in '/App_Data/Logs/UmbracoTraceLog.txt')",
|
||||
Success = false,
|
||||
Percentage = "90"
|
||||
};
|
||||
return HandleInstallException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
private string GetResultMessageForMySql(bool? supportsCaseInsensitiveQueries)
|
||||
{
|
||||
if (supportsCaseInsensitiveQueries == null)
|
||||
{
|
||||
return "<p> </p><p>Warning! Could not check if your database type supports case insensitive queries. <br />We currently do not support these databases that do not support case insensitive queries.</p>" +
|
||||
"<p>You can check this by looking for the following setting in your my.ini file in your MySQL installation directory:</p>" +
|
||||
"<pre>lower_case_table_names=1</pre><br />" +
|
||||
"<p>Note: Make sure to check with your hosting provider if they support case insensitive queries as well.</p>" +
|
||||
"<p>For more technical information on case sensitivity in MySQL, have a look at " +
|
||||
"<a href='http://dev.mysql.com/doc/refman/5.0/en/identifier-case-sensitivity.html'>the documentation on the subject</a></p>";
|
||||
}
|
||||
if (SqlSyntaxContext.SqlSyntaxProvider.GetType() == typeof(MySqlSyntaxProvider))
|
||||
{
|
||||
return "<p> </p><p>Congratulations, the database step ran successfully!</p>" +
|
||||
"<p>Note: You're using MySQL and the database instance you're connecting to seems to support case insensitive queries.</p>" +
|
||||
"<p>However, your hosting provider may not support this option. Umbraco does not currently support MySQL installs that do not support case insensitive queries</p>" +
|
||||
"<p>Make sure to check with your hosting provider if they support case insensitive queries as well.</p>" +
|
||||
"<p>They can check this by looking for the following setting in the my.ini file in their MySQL installation directory:</p>" +
|
||||
"<pre>lower_case_table_names=1</pre><br />" +
|
||||
"<p>For more technical information on case sensitivity in MySQL, have a look at " +
|
||||
"<a href='http://dev.mysql.com/doc/refman/5.0/en/identifier-case-sensitivity.html'>the documentation on the subject</a></p>";
|
||||
}
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
private Attempt<Result> CheckReadyForInstall()
|
||||
{
|
||||
if (_configured == false || (string.IsNullOrEmpty(_connectionString) || string.IsNullOrEmpty(ProviderName)))
|
||||
{
|
||||
return Attempt.Fail(new Result
|
||||
{
|
||||
Message =
|
||||
"Database configuration is invalid. Please check that the entered database exists and that the provided username and password has write access to the database.",
|
||||
Success = false,
|
||||
Percentage = "10"
|
||||
});
|
||||
}
|
||||
return Attempt<Result>.Succeed();
|
||||
}
|
||||
|
||||
private Result HandleInstallException(Exception ex)
|
||||
{
|
||||
LogHelper.Info<DatabaseContext>("Database configuration failed with the following error and stack trace: " + ex.Message + "\n" + ex.StackTrace);
|
||||
|
||||
if (_result != null)
|
||||
{
|
||||
LogHelper.Info<DatabaseContext>("The database schema validation produced the following summary: \n" + _result.GetSummary());
|
||||
}
|
||||
|
||||
return new Result
|
||||
{
|
||||
Message =
|
||||
"The database configuration failed with the following message: " + ex.Message +
|
||||
"\n Please check log file for additional information (can be found in '/App_Data/Logs/UmbracoTraceLog.txt')",
|
||||
Success = false,
|
||||
Percentage = "90"
|
||||
};
|
||||
}
|
||||
|
||||
internal class Result
|
||||
{
|
||||
public bool RequiresUpgrade { get; set; }
|
||||
public string Message { get; set; }
|
||||
public bool Success { get; set; }
|
||||
public string Percentage { get; set; }
|
||||
|
||||
@@ -18,5 +18,30 @@ namespace Umbraco.Core
|
||||
return dt.ToString("yyyy-MM-dd HH:mm:ss");
|
||||
}
|
||||
|
||||
public static DateTime TruncateTo(this DateTime dt, DateTruncate truncateTo)
|
||||
{
|
||||
if (truncateTo == DateTruncate.Year)
|
||||
return new DateTime(dt.Year, 0, 0);
|
||||
if (truncateTo == DateTruncate.Month)
|
||||
return new DateTime(dt.Year, dt.Month, 0);
|
||||
if (truncateTo == DateTruncate.Day)
|
||||
return new DateTime(dt.Year, dt.Month, dt.Day);
|
||||
if (truncateTo == DateTruncate.Hour)
|
||||
return new DateTime(dt.Year, dt.Month, dt.Day, dt.Hour, 0, 0);
|
||||
if (truncateTo == DateTruncate.Minute)
|
||||
return new DateTime(dt.Year, dt.Month, dt.Day, dt.Hour, dt.Minute, 0);
|
||||
return new DateTime(dt.Year, dt.Month, dt.Day, dt.Hour, dt.Minute, dt.Second);
|
||||
}
|
||||
|
||||
public enum DateTruncate
|
||||
{
|
||||
Year,
|
||||
Month,
|
||||
Day,
|
||||
Hour,
|
||||
Minute,
|
||||
Second
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -131,30 +131,48 @@ namespace Umbraco.Core
|
||||
return n;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a new dictionary of this ... others merged leftward.
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <typeparam name="TK"></typeparam>
|
||||
/// <typeparam name="TV"></typeparam>
|
||||
/// <param name="me"></param>
|
||||
/// <param name="others"></param>
|
||||
/// <returns></returns>
|
||||
/// <remarks>
|
||||
/// Reference: http://stackoverflow.com/questions/294138/merging-dictionaries-in-c
|
||||
/// </remarks>
|
||||
public static T MergeLeft<T, TK, TV>(this T me, params IDictionary<TK, TV>[] others)
|
||||
where T : IDictionary<TK, TV>, new()
|
||||
{
|
||||
var newMap = new T();
|
||||
foreach (var p in (new List<IDictionary<TK, TV>> { me }).Concat(others).SelectMany(src => src))
|
||||
{
|
||||
newMap[p.Key] = p.Value;
|
||||
}
|
||||
return newMap;
|
||||
}
|
||||
/// <summary>
|
||||
/// Merges all key/values from the sources dictionaries into the destination dictionary
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <typeparam name="TK"></typeparam>
|
||||
/// <typeparam name="TV"></typeparam>
|
||||
/// <param name="destination">The source dictionary to merge other dictionaries into</param>
|
||||
/// <param name="overwrite">
|
||||
/// By default all values will be retained in the destination if the same keys exist in the sources but
|
||||
/// this can changed if overwrite = true, then any key/value found in any of the sources will overwritten in the destination. Note that
|
||||
/// it will just use the last found key/value if this is true.
|
||||
/// </param>
|
||||
/// <param name="sources">The other dictionaries to merge values from</param>
|
||||
public static void MergeLeft<T, TK, TV>(this T destination, IEnumerable<IDictionary<TK, TV>> sources, bool overwrite = false)
|
||||
where T : IDictionary<TK, TV>
|
||||
{
|
||||
foreach (var p in sources.SelectMany(src => src).Where(p => overwrite || destination.ContainsKey(p.Key) == false))
|
||||
{
|
||||
destination[p.Key] = p.Value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// <summary>
|
||||
/// Merges all key/values from the sources dictionaries into the destination dictionary
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <typeparam name="TK"></typeparam>
|
||||
/// <typeparam name="TV"></typeparam>
|
||||
/// <param name="destination">The source dictionary to merge other dictionaries into</param>
|
||||
/// <param name="overwrite">
|
||||
/// By default all values will be retained in the destination if the same keys exist in the sources but
|
||||
/// this can changed if overwrite = true, then any key/value found in any of the sources will overwritten in the destination. Note that
|
||||
/// it will just use the last found key/value if this is true.
|
||||
/// </param>
|
||||
/// <param name="source">The other dictionary to merge values from</param>
|
||||
public static void MergeLeft<T, TK, TV>(this T destination, IDictionary<TK, TV> source, bool overwrite = false)
|
||||
where T : IDictionary<TK, TV>
|
||||
{
|
||||
destination.MergeLeft(new[] {source}, overwrite);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the value of the key value based on the key, if the key is not found, a null value is returned
|
||||
/// </summary>
|
||||
/// <typeparam name="TKey">The type of the key.</typeparam>
|
||||
@@ -187,6 +205,25 @@ namespace Umbraco.Core
|
||||
return String.Empty;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the value of the key value based on the key as it's string value, if the key is not found or is an empty string, then the provided default value is returned
|
||||
/// </summary>
|
||||
/// <param name="d"></param>
|
||||
/// <param name="key"></param>
|
||||
/// <param name="defaultValue"></param>
|
||||
/// <returns></returns>
|
||||
public static string GetValueAsString<TKey, TVal>(this IDictionary<TKey, TVal> d, TKey key, string defaultValue)
|
||||
{
|
||||
if (d.ContainsKey(key))
|
||||
{
|
||||
var value = d[key].ToString();
|
||||
if (value != string.Empty)
|
||||
return value;
|
||||
}
|
||||
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
/// <summary>contains key ignore case.</summary>
|
||||
/// <param name="dictionary">The dictionary.</param>
|
||||
/// <param name="key">The key.</param>
|
||||
|
||||
@@ -82,7 +82,7 @@ namespace Umbraco.Core.Dynamics
|
||||
null,
|
||||
thisObject,
|
||||
args);
|
||||
return new Attempt<TryInvokeMemberResult>(true, new TryInvokeMemberResult(result, TryInvokeMemberSuccessReason.FoundProperty));
|
||||
return Attempt.Succeed(new TryInvokeMemberResult(result, TryInvokeMemberSuccessReason.FoundProperty));
|
||||
}
|
||||
catch (MissingMethodException)
|
||||
{
|
||||
@@ -97,7 +97,7 @@ namespace Umbraco.Core.Dynamics
|
||||
null,
|
||||
thisObject,
|
||||
args);
|
||||
return new Attempt<TryInvokeMemberResult>(true, new TryInvokeMemberResult(result, TryInvokeMemberSuccessReason.FoundMethod));
|
||||
return Attempt.Succeed(new TryInvokeMemberResult(result, TryInvokeMemberSuccessReason.FoundMethod));
|
||||
}
|
||||
catch (MissingMethodException)
|
||||
{
|
||||
@@ -106,13 +106,14 @@ namespace Umbraco.Core.Dynamics
|
||||
try
|
||||
{
|
||||
result = FindAndExecuteExtensionMethod(thisObject, args, binder.Name, findExtensionMethodsOnTypes);
|
||||
return new Attempt<TryInvokeMemberResult>(true, new TryInvokeMemberResult(result, TryInvokeMemberSuccessReason.FoundExtensionMethod));
|
||||
return Attempt.Succeed(new TryInvokeMemberResult(result, TryInvokeMemberSuccessReason.FoundExtensionMethod));
|
||||
}
|
||||
catch (TargetInvocationException ext)
|
||||
{
|
||||
//don't log here, we return this exception because the caller may need to do something specific when
|
||||
//this exception occurs.
|
||||
return new Attempt<TryInvokeMemberResult>(ext);
|
||||
var mresult = new TryInvokeMemberResult(null, TryInvokeMemberSuccessReason.FoundExtensionMethod);
|
||||
return Attempt<TryInvokeMemberResult>.Fail(mresult, ext);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
@@ -124,16 +125,17 @@ namespace Umbraco.Core.Dynamics
|
||||
sb.Append(t + ",");
|
||||
}
|
||||
LogHelper.Error<DynamicInstanceHelper>(sb.ToString(), ex);
|
||||
return new Attempt<TryInvokeMemberResult>(ex);
|
||||
var mresult = new TryInvokeMemberResult(null, TryInvokeMemberSuccessReason.FoundExtensionMethod);
|
||||
return Attempt<TryInvokeMemberResult>.Fail(mresult, ex);
|
||||
}
|
||||
}
|
||||
return Attempt<TryInvokeMemberResult>.False;
|
||||
return Attempt<TryInvokeMemberResult>.Fail();
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogHelper.Error<DynamicInstanceHelper>("An unhandled exception occurred in method TryInvokeMember", ex);
|
||||
return new Attempt<TryInvokeMemberResult>(ex);
|
||||
return Attempt<TryInvokeMemberResult>.Fail(ex);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -11,78 +11,101 @@ namespace Umbraco.Core.Dynamics
|
||||
//Because it's IEnumerable, if the user is actually trying @Model.TextPages or similar
|
||||
//it will still return an enumerable object (assuming the call actually failed because there were no children of that type)
|
||||
//but in .Where, if they use a property that doesn't exist, the lambda will bypass this and return false
|
||||
|
||||
// returned when TryGetMember fails on a DynamicPublishedContent
|
||||
//
|
||||
// so if user does @CurrentPage.TextPages it will get something that is enumerable (but empty)
|
||||
// note - not sure I understand the stuff about .Where, though
|
||||
|
||||
public class DynamicNull : DynamicObject, IEnumerable, IHtmlString
|
||||
{
|
||||
public static readonly DynamicNull Null = new DynamicNull();
|
||||
|
||||
private DynamicNull() {}
|
||||
|
||||
public IEnumerator GetEnumerator()
|
||||
{
|
||||
return (new List<DynamicNull>()).GetEnumerator();
|
||||
}
|
||||
|
||||
public DynamicNull Where(string predicate, params object[] values)
|
||||
{
|
||||
return this;
|
||||
}
|
||||
|
||||
public DynamicNull OrderBy(string orderBy)
|
||||
{
|
||||
return this;
|
||||
}
|
||||
|
||||
public DynamicNull ToContentSet()
|
||||
{
|
||||
return this;
|
||||
}
|
||||
|
||||
public int Count()
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
public override bool TryGetMember(GetMemberBinder binder, out object result)
|
||||
{
|
||||
result = this;
|
||||
return true;
|
||||
}
|
||||
|
||||
public override bool TryGetIndex(GetIndexBinder binder, object[] indexes, out object result)
|
||||
{
|
||||
result = this;
|
||||
return true;
|
||||
}
|
||||
|
||||
public override bool TryInvoke(InvokeBinder binder, object[] args, out object result)
|
||||
{
|
||||
result = this;
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool IsNull()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool HasValue()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
public string Name
|
||||
{
|
||||
get
|
||||
{
|
||||
return string.Empty;
|
||||
}
|
||||
get { return string.Empty; }
|
||||
}
|
||||
|
||||
public int Id
|
||||
{
|
||||
get
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
get { return 0; }
|
||||
}
|
||||
|
||||
public static implicit operator bool(DynamicNull n)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
public static implicit operator DateTime(DynamicNull n)
|
||||
{
|
||||
return DateTime.MinValue;
|
||||
}
|
||||
|
||||
public static implicit operator int(DynamicNull n)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
public static implicit operator string(DynamicNull n)
|
||||
{
|
||||
return string.Empty;
|
||||
@@ -92,6 +115,5 @@ namespace Umbraco.Core.Dynamics
|
||||
{
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -201,9 +201,9 @@ namespace Umbraco.Core.Dynamics
|
||||
|
||||
//this is the result of an extension method execution gone wrong so we return dynamic null
|
||||
if (attempt.Result.Reason == DynamicInstanceHelper.TryInvokeMemberSuccessReason.FoundExtensionMethod
|
||||
&& attempt.Error != null && attempt.Error is TargetInvocationException)
|
||||
&& attempt.Exception != null && attempt.Exception is TargetInvocationException)
|
||||
{
|
||||
result = new DynamicNull();
|
||||
result = DynamicNull.Null;
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -261,7 +261,7 @@ namespace Umbraco.Core.Dynamics
|
||||
var attributes = xmlElement.Attributes(name).Select(attr => attr.Value).ToArray();
|
||||
if (attributes.Any())
|
||||
{
|
||||
return new Attempt<IEnumerable<string>>(true, attributes);
|
||||
return Attempt<IEnumerable<string>>.Succeed(attributes);
|
||||
}
|
||||
|
||||
if (!attributes.Any() && xmlElement.Name == "root" && xmlElement.Elements().Count() == 1)
|
||||
@@ -271,12 +271,12 @@ namespace Umbraco.Core.Dynamics
|
||||
if (childElements.Any())
|
||||
{
|
||||
//we've found a match by the first child of an element called 'root' (strange, but sure)
|
||||
return new Attempt<IEnumerable<string>>(true, childElements);
|
||||
return Attempt<IEnumerable<string>>.Succeed(childElements);
|
||||
}
|
||||
}
|
||||
|
||||
//no deal
|
||||
return Attempt<IEnumerable<string>>.False;
|
||||
return Attempt<IEnumerable<string>>.Fail();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -293,7 +293,7 @@ namespace Umbraco.Core.Dynamics
|
||||
//Check if we've got any matches, if so then return true
|
||||
if (elements.Any())
|
||||
{
|
||||
return new Attempt<IEnumerable<XElement>>(true, elements);
|
||||
return Attempt<IEnumerable<XElement>>.Succeed(elements);
|
||||
}
|
||||
|
||||
if (!elements.Any() && xmlElement.Name == "root" && xmlElement.Elements().Count() == 1)
|
||||
@@ -303,12 +303,12 @@ namespace Umbraco.Core.Dynamics
|
||||
if (childElements.Any())
|
||||
{
|
||||
//we've found a match by the first child of an element called 'root' (strange, but sure)
|
||||
return new Attempt<IEnumerable<XElement>>(true, childElements);
|
||||
return Attempt<IEnumerable<XElement>>.Succeed(childElements);
|
||||
}
|
||||
}
|
||||
|
||||
//no deal
|
||||
return Attempt<IEnumerable<XElement>>.False;
|
||||
return Attempt<IEnumerable<XElement>>.Fail();
|
||||
}
|
||||
|
||||
private bool HandleIEnumerableXElement(IEnumerable<XElement> elements, out object result)
|
||||
|
||||
@@ -13,7 +13,96 @@ namespace Umbraco.Core.Dynamics
|
||||
/// Utility class for finding extension methods on a type to execute
|
||||
/// </summary>
|
||||
internal static class ExtensionMethodFinder
|
||||
{
|
||||
{
|
||||
private static readonly MethodInfo[] AllExtensionMethods;
|
||||
|
||||
static ExtensionMethodFinder()
|
||||
{
|
||||
AllExtensionMethods = TypeFinder.GetAssembliesWithKnownExclusions()
|
||||
// assemblies that contain extension methods
|
||||
.Where(a => a.IsDefined(typeof(ExtensionAttribute), false))
|
||||
// types that contain extension methods
|
||||
.SelectMany(a => a.GetTypes()
|
||||
.Where(t => t.IsDefined(typeof(ExtensionAttribute), false) && t.IsSealed && t.IsGenericType == false && t.IsNested == false))
|
||||
// actual extension methods
|
||||
.SelectMany(t => t.GetMethods(BindingFlags.Static | BindingFlags.Public)
|
||||
.Where(m => m.IsDefined(typeof(ExtensionAttribute), false)))
|
||||
// and also IEnumerable<T> extension methods - because the assembly is excluded
|
||||
.Concat(typeof(Enumerable).GetMethods(BindingFlags.Static | BindingFlags.Public))
|
||||
.ToArray();
|
||||
}
|
||||
|
||||
// ORIGINAL CODE IS NOT COMPLETE, DOES NOT HANDLE GENERICS, ETC...
|
||||
|
||||
// so this is an attempt at fixing things, but it's not done yet
|
||||
// and do we really want to do this? extension methods are not supported on dynamics, period
|
||||
// we should use strongly typed content instead of dynamics.
|
||||
|
||||
/*
|
||||
|
||||
// get all extension methods for type thisType, with name name,
|
||||
// accepting argsCount arguments (not counting the instance of thisType).
|
||||
private static IEnumerable<MethodInfo> GetExtensionMethods(Type thisType, string name, int argsCount)
|
||||
{
|
||||
var key = string.Format("{0}.{1}::{2}", thisType.FullName, name, argsCount);
|
||||
|
||||
var types = thisType.GetBaseTypes(true); // either do this OR have MatchFirstParameter handle the stuff... F*XME
|
||||
|
||||
var methods = AllExtensionMethods
|
||||
.Where(m => m.Name == name)
|
||||
.Where(m => m.GetParameters().Length == argsCount)
|
||||
.Where(m => MatchFirstParameter(thisType, m.GetParameters()[0].ParameterType));
|
||||
|
||||
// f*xme - is this what we should cache?
|
||||
return methods;
|
||||
}
|
||||
|
||||
// find out whether the first parameter is a match for thisType
|
||||
private static bool MatchFirstParameter(Type thisType, Type firstParameterType)
|
||||
{
|
||||
return MethodArgZeroHasCorrectTargetType(null, firstParameterType, thisType);
|
||||
}
|
||||
|
||||
// get the single extension method for type thisType, with name name,
|
||||
// that accepts the arguments in args (which does not contain the instance of thisType).
|
||||
public static MethodInfo GetExtensionMethod(Type thisType, string name, object[] args)
|
||||
{
|
||||
MethodInfo result = null;
|
||||
foreach (var method in GetExtensionMethods(thisType, name, args.Length).Where(m => MatchParameters(m, args)))
|
||||
{
|
||||
if (result == null)
|
||||
result = method;
|
||||
else
|
||||
throw new AmbiguousMatchException("More than one matching extension method was found.");
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// find out whether the method can accept the arguments
|
||||
private static bool MatchParameters(MethodInfo method, IList<object> args)
|
||||
{
|
||||
var parameters = method.GetParameters();
|
||||
|
||||
var i = 0;
|
||||
for (; i < parameters.Length; ++i)
|
||||
{
|
||||
if (MatchParameter(parameters[i].ParameterType, args[i].GetType()) == false)
|
||||
break;
|
||||
}
|
||||
return (i == parameters.Length);
|
||||
}
|
||||
|
||||
internal static bool MatchParameter(Type parameterType, Type argumentType)
|
||||
{
|
||||
// public static int DoSomething<T>(Foo foo, T t1, T t2)
|
||||
// DoSomething(foo, t1, t2) => how can we match?!
|
||||
return parameterType == argumentType; // f*xme of course!
|
||||
}
|
||||
*
|
||||
*/
|
||||
|
||||
// BELOW IS THE ORIGINAL CODE...
|
||||
|
||||
/// <summary>
|
||||
/// Returns all extension methods found matching the definition
|
||||
/// </summary>
|
||||
@@ -27,6 +116,10 @@ namespace Umbraco.Core.Dynamics
|
||||
/// </remarks>
|
||||
private static IEnumerable<MethodInfo> GetAllExtensionMethods(Type thisType, string name, int argumentCount, bool argsContainsThis)
|
||||
{
|
||||
// at *least* we can cache the extension methods discovery
|
||||
var candidates = AllExtensionMethods;
|
||||
|
||||
/*
|
||||
//only scan assemblies we know to contain extension methods (user assemblies)
|
||||
var assembliesToScan = TypeFinder.GetAssembliesWithKnownExclusions();
|
||||
|
||||
@@ -45,6 +138,7 @@ namespace Umbraco.Core.Dynamics
|
||||
|
||||
//add the extension methods defined in IEnumerable
|
||||
candidates = candidates.Concat(typeof(Enumerable).GetMethods(BindingFlags.Static | BindingFlags.Public));
|
||||
*/
|
||||
|
||||
//filter by name
|
||||
var methodsByName = candidates.Where(m => m.Name == name);
|
||||
|
||||
@@ -1,65 +1,47 @@
|
||||
using System;
|
||||
using Umbraco.Core.Models;
|
||||
using umbraco.interfaces;
|
||||
using System.Web;
|
||||
|
||||
namespace Umbraco.Core.Dynamics
|
||||
{
|
||||
internal class PropertyResult : IPublishedContentProperty, IHtmlString
|
||||
{
|
||||
internal PropertyResult(IPublishedContentProperty source, PropertyResultType type)
|
||||
internal class PropertyResult : IPublishedProperty, IHtmlString
|
||||
{
|
||||
private readonly IPublishedProperty _source;
|
||||
private readonly string _alias;
|
||||
private readonly object _value;
|
||||
private readonly PropertyResultType _type;
|
||||
|
||||
internal PropertyResult(IPublishedProperty source, PropertyResultType type)
|
||||
{
|
||||
if (source == null) throw new ArgumentNullException("source");
|
||||
|
||||
Alias = source.Alias;
|
||||
Value = source.Value;
|
||||
Version = source.Version;
|
||||
PropertyType = type;
|
||||
|
||||
_type = type;
|
||||
_source = source;
|
||||
}
|
||||
internal PropertyResult(string alias, object value, Guid version, PropertyResultType type)
|
||||
|
||||
internal PropertyResult(string alias, object value, Guid version, PropertyResultType type)
|
||||
{
|
||||
if (alias == null) throw new ArgumentNullException("alias");
|
||||
if (value == null) throw new ArgumentNullException("value");
|
||||
|
||||
Alias = alias;
|
||||
Value = value;
|
||||
Version = version;
|
||||
PropertyType = type;
|
||||
_type = type;
|
||||
_alias = alias;
|
||||
_value = value;
|
||||
}
|
||||
|
||||
internal PropertyResultType PropertyType { get; private set; }
|
||||
|
||||
public string Alias { get; private set; }
|
||||
internal PropertyResultType PropertyType { get { return _type; } }
|
||||
|
||||
public object Value { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Returns the value as a string output, this is used in the final rendering process of a property
|
||||
/// </summary>
|
||||
internal string ValueAsString
|
||||
{
|
||||
get
|
||||
{
|
||||
return Value == null ? "" : Convert.ToString(Value);
|
||||
}
|
||||
}
|
||||
|
||||
public Guid Version { get; private set; }
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// The Id of the document for which this property belongs to
|
||||
/// </summary>
|
||||
public int DocumentId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The alias of the document type alias for which this property belongs to
|
||||
/// </summary>
|
||||
public string DocumentTypeAlias { get; set; }
|
||||
public string PropertyTypeAlias { get { return _source == null ? _alias : _source.PropertyTypeAlias; } }
|
||||
public object DataValue { get { return _source == null ? _value : _source.DataValue; } }
|
||||
public bool HasValue { get { return _source == null || _source.HasValue; } }
|
||||
public object Value { get { return _source == null ? _value : _source.Value; } }
|
||||
public object XPathValue { get { return Value == null ? null : Value.ToString(); } }
|
||||
|
||||
// implements IHtmlString.ToHtmlString
|
||||
public string ToHtmlString()
|
||||
{
|
||||
return ValueAsString;
|
||||
var value = Value;
|
||||
return value == null ? string.Empty : value.ToString();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,16 +13,45 @@ namespace Umbraco.Core
|
||||
{
|
||||
public static IEnumerable<IEnumerable<T>> InGroupsOf<T>(this IEnumerable<T> source, int groupSize)
|
||||
{
|
||||
var i = 0;
|
||||
var length = source.Count();
|
||||
if (source == null)
|
||||
throw new NullReferenceException("source");
|
||||
|
||||
while ((i * groupSize) < length)
|
||||
{
|
||||
yield return source.Skip(i * groupSize).Take(groupSize);
|
||||
i++;
|
||||
}
|
||||
// enumerate the source only once!
|
||||
return new InGroupsEnumerator<T>(source, groupSize).Groups();
|
||||
}
|
||||
|
||||
// this class makes sure that the source is enumerated only ONCE
|
||||
// which means that when it is enumerated, the actual groups content
|
||||
// has to be evaluated at the same time, and stored in an array.
|
||||
private class InGroupsEnumerator<T>
|
||||
{
|
||||
private readonly IEnumerator<T> _source;
|
||||
private readonly int _count;
|
||||
private bool _mightHaveNext;
|
||||
|
||||
public InGroupsEnumerator(IEnumerable<T> source, int count)
|
||||
{
|
||||
_source = source.GetEnumerator();
|
||||
_count = count;
|
||||
_mightHaveNext = true;
|
||||
}
|
||||
|
||||
public IEnumerable<IEnumerable<T>> Groups()
|
||||
{
|
||||
while (_mightHaveNext && _source.MoveNext())
|
||||
yield return Group().ToArray(); // see note above
|
||||
}
|
||||
|
||||
private IEnumerable<T> Group()
|
||||
{
|
||||
var c = 0;
|
||||
do
|
||||
{
|
||||
yield return _source.Current;
|
||||
} while (++c < _count && _source.MoveNext());
|
||||
_mightHaveNext = c == _count;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>The distinct by.</summary>
|
||||
/// <param name="source">The source.</param>
|
||||
@@ -243,23 +272,45 @@ namespace Umbraco.Core
|
||||
});
|
||||
}
|
||||
|
||||
///<summary>Finds the index of the first item matching an expression in an enumerable.</summary>
|
||||
///<param name="items">The enumerable to search.</param>
|
||||
///<param name="predicate">The expression to test the items against.</param>
|
||||
///<returns>The index of the first matching item, or -1 if no items match.</returns>
|
||||
/// <summary>
|
||||
/// Finds the index of the first item matching an expression in an enumerable.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of the enumerated objects.</typeparam>
|
||||
/// <param name="items">The enumerable to search.</param>
|
||||
/// <param name="predicate">The expression to test the items against.</param>
|
||||
/// <returns>The index of the first matching item, or -1.</returns>
|
||||
public static int FindIndex<T>(this IEnumerable<T> items, Func<T, bool> predicate)
|
||||
{
|
||||
return FindIndex(items, 0, predicate);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Finds the index of the first item matching an expression in an enumerable.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of the enumerated objects.</typeparam>
|
||||
/// <param name="items">The enumerable to search.</param>
|
||||
/// <param name="startIndex">The index to start at.</param>
|
||||
/// <param name="predicate">The expression to test the items against.</param>
|
||||
/// <returns>The index of the first matching item, or -1.</returns>
|
||||
public static int FindIndex<T>(this IEnumerable<T> items, int startIndex, Func<T, bool> predicate)
|
||||
{
|
||||
if (items == null) throw new ArgumentNullException("items");
|
||||
if (predicate == null) throw new ArgumentNullException("predicate");
|
||||
if (startIndex < 0) throw new ArgumentOutOfRangeException("startIndex");
|
||||
|
||||
var index = startIndex;
|
||||
if (index > 0)
|
||||
items = items.Skip(index);
|
||||
|
||||
var retVal = 0;
|
||||
foreach (var item in items)
|
||||
{
|
||||
if (predicate(item)) return retVal;
|
||||
retVal++;
|
||||
if (predicate(item)) return index;
|
||||
index++;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
///<summary>Finds the index of the first occurence of an item in an enumerable.</summary>
|
||||
///<param name="items">The enumerable to search.</param>
|
||||
///<param name="item">The item to find.</param>
|
||||
|
||||
@@ -1,37 +1,47 @@
|
||||
using System.IO;
|
||||
using System;
|
||||
using System.IO;
|
||||
using Umbraco.Core.CodeAnnotations;
|
||||
|
||||
namespace Umbraco.Core.IO
|
||||
{
|
||||
[UmbracoExperimentalFeature("http://issues.umbraco.org/issue/U4-1156", "Will be declared public after 4.10")]
|
||||
{
|
||||
public static class FileSystemExtensions
|
||||
{
|
||||
[UmbracoExperimentalFeature("http://issues.umbraco.org/issue/U4-1156", "Will be declared public after 4.10")]
|
||||
internal static long GetSize(this IFileSystem fs, string path)
|
||||
public static long GetSize(this IFileSystem fs, string path)
|
||||
{
|
||||
var s = fs.OpenFile(path);
|
||||
var size = s.Length;
|
||||
s.Close();
|
||||
using (var s = fs.OpenFile(path))
|
||||
{
|
||||
var size = s.Length;
|
||||
s.Close();
|
||||
|
||||
return size;
|
||||
return size;
|
||||
}
|
||||
}
|
||||
|
||||
[UmbracoExperimentalFeature("http://issues.umbraco.org/issue/U4-1156", "Will be declared public after 4.10")]
|
||||
internal static void CopyFile(this IFileSystem fs, string path, string newPath)
|
||||
public static void CopyFile(this IFileSystem fs, string path, string newPath)
|
||||
{
|
||||
fs.AddFile(newPath, fs.OpenFile(path));
|
||||
}
|
||||
|
||||
[UmbracoExperimentalFeature("http://issues.umbraco.org/issue/U4-1156", "Will be declared public after 4.10")]
|
||||
internal static string GetExtension(this IFileSystem fs, string path)
|
||||
public static string GetExtension(this IFileSystem fs, string path)
|
||||
{
|
||||
return Path.GetExtension(fs.GetFullPath(path));
|
||||
}
|
||||
|
||||
[UmbracoExperimentalFeature("http://issues.umbraco.org/issue/U4-1156", "Will be declared public after 4.10")]
|
||||
internal static string GetFileName(this IFileSystem fs, string path)
|
||||
public static string GetFileName(this IFileSystem fs, string path)
|
||||
{
|
||||
return Path.GetFileName(fs.GetFullPath(path));
|
||||
}
|
||||
|
||||
//TODO: Currently this is the only way to do this
|
||||
internal static void CreateFolder(this IFileSystem fs, string folderPath)
|
||||
{
|
||||
var path = fs.GetRelativePath(folderPath);
|
||||
var tempFile = Path.Combine(path, Guid.NewGuid().ToString("N") + ".tmp");
|
||||
using (var s = new MemoryStream())
|
||||
{
|
||||
fs.AddFile(tempFile, s);
|
||||
}
|
||||
fs.DeleteFile(tempFile);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,6 +5,8 @@ using Umbraco.Core.CodeAnnotations;
|
||||
|
||||
namespace Umbraco.Core.IO
|
||||
{
|
||||
//TODO: There is no way to create a directory here without creating a file in a directory and then deleting it
|
||||
|
||||
public interface IFileSystem
|
||||
{
|
||||
IEnumerable<string> GetDirectories(string path);
|
||||
|
||||
@@ -43,7 +43,7 @@ namespace Umbraco.Core.IO
|
||||
//Replaces tildes with the root dir
|
||||
public static string ResolveUrl(string virtualPath)
|
||||
{
|
||||
if (virtualPath.StartsWith("~"))
|
||||
if (virtualPath.StartsWith("~"))
|
||||
return virtualPath.Replace("~", SystemDirectories.Root).Replace("//", "/");
|
||||
else if (Uri.IsWellFormedUriString(virtualPath, UriKind.Absolute))
|
||||
return virtualPath;
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Runtime.Serialization;
|
||||
using Umbraco.Core.Persistence.Mappers;
|
||||
|
||||
namespace Umbraco.Core.Models
|
||||
{
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Specialized;
|
||||
using System.ComponentModel;
|
||||
using System.Diagnostics;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
@@ -50,6 +51,7 @@ namespace Umbraco.Core.Models
|
||||
_contentTypeId = int.Parse(contentType.Id.ToString(CultureInfo.InvariantCulture));
|
||||
_properties = properties;
|
||||
_properties.EnsurePropertyTypes(PropertyTypes);
|
||||
_additionalData = new Dictionary<string, object>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -73,6 +75,7 @@ namespace Umbraco.Core.Models
|
||||
_contentTypeId = int.Parse(contentType.Id.ToString(CultureInfo.InvariantCulture));
|
||||
_properties = properties;
|
||||
_properties.EnsurePropertyTypes(PropertyTypes);
|
||||
_additionalData = new Dictionary<string, object>();
|
||||
}
|
||||
|
||||
private static readonly PropertyInfo NameSelector = ExpressionHelper.GetPropertyInfo<ContentBase, string>(x => x.Name);
|
||||
@@ -253,6 +256,16 @@ namespace Umbraco.Core.Models
|
||||
}
|
||||
}
|
||||
|
||||
private readonly IDictionary<string, object> _additionalData;
|
||||
/// <summary>
|
||||
/// Some entities may expose additional data that other's might not, this custom data will be available in this collection
|
||||
/// </summary>
|
||||
[EditorBrowsable(EditorBrowsableState.Never)]
|
||||
IDictionary<string, object> IUmbracoEntity.AdditionalData
|
||||
{
|
||||
get { return _additionalData; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// List of PropertyGroups available on this Content object
|
||||
/// </summary>
|
||||
@@ -293,10 +306,8 @@ namespace Umbraco.Core.Models
|
||||
/// <returns><see cref="Property"/> Value as a <see cref="TPassType"/></returns>
|
||||
public virtual TPassType GetValue<TPassType>(string propertyTypeAlias)
|
||||
{
|
||||
if (Properties[propertyTypeAlias].Value is TPassType)
|
||||
return (TPassType)Properties[propertyTypeAlias].Value;
|
||||
|
||||
return (TPassType)Convert.ChangeType(Properties[propertyTypeAlias].Value, typeof(TPassType));
|
||||
var convertAttempt = Properties[propertyTypeAlias].Value.TryConvertTo<TPassType>();
|
||||
return convertAttempt.Success ? convertAttempt.Result : default(TPassType);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -25,6 +25,20 @@ namespace Umbraco.Core.Models
|
||||
{
|
||||
#region IContent
|
||||
|
||||
/// <summary>
|
||||
/// Returns true if this entity was just published as part of a recent save operation (i.e. it wasn't previously published)
|
||||
/// </summary>
|
||||
/// <param name="entity"></param>
|
||||
/// <returns></returns>
|
||||
/// <remarks>
|
||||
/// This is helpful for determining if the published event will execute during the saved event for a content item.
|
||||
/// </remarks>
|
||||
internal static bool JustPublished(this IContent entity)
|
||||
{
|
||||
var dirty = (IRememberBeingDirty)entity;
|
||||
return dirty.WasPropertyDirty("Published") && entity.Published;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines if a new version should be created
|
||||
/// </summary>
|
||||
@@ -363,8 +377,7 @@ namespace Umbraco.Core.Models
|
||||
/// <param name="propertyTypeAlias">Alias of the property to save the value on</param>
|
||||
/// <param name="fileName">Name of the file</param>
|
||||
/// <param name="fileStream"><see cref="Stream"/> to save to disk</param>
|
||||
public static void SetValue(this IContentBase content, string propertyTypeAlias, string fileName,
|
||||
Stream fileStream)
|
||||
public static void SetValue(this IContentBase content, string propertyTypeAlias, string fileName, Stream fileStream)
|
||||
{
|
||||
var name = IOHelper.SafeFileName(fileName);
|
||||
|
||||
@@ -378,75 +391,82 @@ namespace Umbraco.Core.Models
|
||||
if (property == null)
|
||||
return;
|
||||
|
||||
bool supportsResizing = false;
|
||||
var numberedFolder = MediaSubfolderCounter.Current.Increment();
|
||||
string fileName = UmbracoSettings.UploadAllowDirectories
|
||||
var fileName = UmbracoSettings.UploadAllowDirectories
|
||||
? Path.Combine(numberedFolder.ToString(CultureInfo.InvariantCulture), name)
|
||||
: numberedFolder + "-" + name;
|
||||
|
||||
string extension = Path.GetExtension(name).Substring(1).ToLowerInvariant();
|
||||
var extension = Path.GetExtension(name).Substring(1).ToLowerInvariant();
|
||||
|
||||
//the file size is the length of the stream in bytes
|
||||
var fileSize = fileStream.Length;
|
||||
|
||||
var fs = FileSystemProviderManager.Current.GetFileSystemProvider<MediaFileSystem>();
|
||||
fs.AddFile(fileName, fileStream);
|
||||
|
||||
|
||||
//Check if file supports resizing and create thumbnails
|
||||
if (("," + UmbracoSettings.ImageFileTypes + ",").Contains(string.Format(",{0},", extension)))
|
||||
{
|
||||
supportsResizing = true;
|
||||
var supportsResizing = ("," + UmbracoSettings.ImageFileTypes + ",").Contains(string.Format(",{0},", extension));
|
||||
|
||||
// Make default thumbnail
|
||||
var thumbUrl = Resize(fs, fileName, extension, 100, "thumb");
|
||||
|
||||
//Look up Prevalues for this upload datatype - if it is an upload datatype
|
||||
var uploadFieldId = new Guid(Constants.PropertyEditors.UploadField);
|
||||
if (property.PropertyType.DataTypeId == uploadFieldId)
|
||||
{
|
||||
//Get Prevalues by the DataType's Id: property.PropertyType.DataTypeId
|
||||
var values = ApplicationContext.Current.Services.DataTypeService.GetPreValuesByDataTypeId(property.PropertyType.DataTypeDefinitionId);
|
||||
var thumbnailSizes = values.FirstOrDefault();
|
||||
//Additional thumbnails configured as prevalues on the DataType
|
||||
if (thumbnailSizes != null)
|
||||
{
|
||||
char sep = (!thumbnailSizes.Contains("") && thumbnailSizes.Contains(",")) ? ',' : ';';
|
||||
|
||||
foreach (string thumb in thumbnailSizes.Split(sep))
|
||||
{
|
||||
int thumbSize;
|
||||
if (thumb != "" && int.TryParse(thumb, out thumbSize))
|
||||
{
|
||||
Resize(fs, fileName, extension, thumbSize, string.Format("thumb_{0}", thumbSize));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
//the config section used to auto-fill properties
|
||||
XmlNode uploadFieldConfigNode = null;
|
||||
|
||||
//Check for auto fill of additional properties
|
||||
if (UmbracoSettings.ImageAutoFillImageProperties != null)
|
||||
{
|
||||
XmlNode uploadFieldConfigNode =
|
||||
uploadFieldConfigNode =
|
||||
UmbracoSettings.ImageAutoFillImageProperties.SelectSingleNode(
|
||||
string.Format("uploadField [@alias = \"{0}\"]", propertyTypeAlias));
|
||||
}
|
||||
|
||||
if (uploadFieldConfigNode != null)
|
||||
{
|
||||
//Only add dimensions to web images
|
||||
if (supportsResizing)
|
||||
if (supportsResizing)
|
||||
{
|
||||
//get the original image from the original stream
|
||||
if (fileStream.CanSeek) fileStream.Seek(0, 0);
|
||||
using (var originalImage = Image.FromStream(fileStream))
|
||||
{
|
||||
// Make default thumbnail
|
||||
Resize(fs, fileName, extension, 100, "thumb", originalImage);
|
||||
|
||||
//Look up Prevalues for this upload datatype - if it is an upload datatype
|
||||
var uploadFieldId = new Guid(Constants.PropertyEditors.UploadField);
|
||||
if (property.PropertyType.DataTypeId == uploadFieldId)
|
||||
{
|
||||
SetPropertyValue(content, uploadFieldConfigNode, "widthFieldAlias", GetDimensions(fs, fileName).Item1.ToString(CultureInfo.InvariantCulture));
|
||||
SetPropertyValue(content, uploadFieldConfigNode, "heightFieldAlias", GetDimensions(fs, fileName).Item2.ToString(CultureInfo.InvariantCulture));
|
||||
}
|
||||
else
|
||||
{
|
||||
SetPropertyValue(content, uploadFieldConfigNode, "widthFieldAlias", string.Empty);
|
||||
SetPropertyValue(content, uploadFieldConfigNode, "heightFieldAlias", string.Empty);
|
||||
//Get Prevalues by the DataType's Id: property.PropertyType.DataTypeId
|
||||
var values = ApplicationContext.Current.Services.DataTypeService.GetPreValuesByDataTypeId(property.PropertyType.DataTypeDefinitionId);
|
||||
var thumbnailSizes = values.FirstOrDefault();
|
||||
//Additional thumbnails configured as prevalues on the DataType
|
||||
if (thumbnailSizes != null)
|
||||
{
|
||||
char sep = (!thumbnailSizes.Contains("") && thumbnailSizes.Contains(",")) ? ',' : ';';
|
||||
|
||||
foreach (var thumb in thumbnailSizes.Split(sep))
|
||||
{
|
||||
int thumbSize;
|
||||
if (thumb != "" && int.TryParse(thumb, out thumbSize))
|
||||
{
|
||||
Resize(fs, fileName, extension, thumbSize, string.Format("thumb_{0}", thumbSize), originalImage);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SetPropertyValue(content, uploadFieldConfigNode, "lengthFieldAlias", fs.GetSize(fileName).ToString(CultureInfo.InvariantCulture));
|
||||
SetPropertyValue(content, uploadFieldConfigNode, "extensionFieldAlias", extension);
|
||||
//while the image is still open, we'll check if we need to auto-populate the image properties
|
||||
if (uploadFieldConfigNode != null)
|
||||
{
|
||||
SetPropertyValue(content, uploadFieldConfigNode, "widthFieldAlias", originalImage.Width.ToString(CultureInfo.InvariantCulture));
|
||||
SetPropertyValue(content, uploadFieldConfigNode, "heightFieldAlias", originalImage.Height.ToString(CultureInfo.InvariantCulture));
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
//if auto-fill is true, then fill the remaining, non-image properties
|
||||
if (uploadFieldConfigNode != null)
|
||||
{
|
||||
SetPropertyValue(content, uploadFieldConfigNode, "lengthFieldAlias", fileSize.ToString(CultureInfo.InvariantCulture));
|
||||
SetPropertyValue(content, uploadFieldConfigNode, "extensionFieldAlias", extension);
|
||||
}
|
||||
|
||||
//Set the value of the property to that of the uploaded file's url
|
||||
property.Value = fs.GetUrl(fileName);
|
||||
}
|
||||
@@ -460,68 +480,37 @@ namespace Umbraco.Core.Models
|
||||
}
|
||||
}
|
||||
|
||||
private static string Resize(MediaFileSystem fileSystem, string path, string extension, int maxWidthHeight, string fileNameAddition)
|
||||
private static ResizedImage Resize(MediaFileSystem fileSystem, string path, string extension, int maxWidthHeight, string fileNameAddition, Image originalImage)
|
||||
{
|
||||
var fileNameThumb = DoResize(fileSystem, path, extension, GetDimensions(fileSystem, path).Item1, GetDimensions(fileSystem, path).Item2, maxWidthHeight, fileNameAddition);
|
||||
|
||||
return fileSystem.GetUrl(fileNameThumb);
|
||||
}
|
||||
|
||||
private static Tuple<int, int> GetDimensions(MediaFileSystem fileSystem, string path)
|
||||
{
|
||||
var fs = fileSystem.OpenFile(path);
|
||||
var image = Image.FromStream(fs);
|
||||
var fileWidth = image.Width;
|
||||
var fileHeight = image.Height;
|
||||
fs.Close();
|
||||
image.Dispose();
|
||||
|
||||
return new Tuple<int, int>(fileWidth, fileHeight);
|
||||
}
|
||||
|
||||
private static string DoResize(MediaFileSystem fileSystem, string path, string extension, int width, int height, int maxWidthHeight, string fileNameAddition)
|
||||
{
|
||||
var fs = fileSystem.OpenFile(path);
|
||||
var image = Image.FromStream(fs);
|
||||
fs.Close();
|
||||
|
||||
string fileNameThumb = String.IsNullOrEmpty(fileNameAddition) ?
|
||||
string.Format("{0}_UMBRACOSYSTHUMBNAIL.jpg", path.Substring(0, path.LastIndexOf("."))) :
|
||||
string.Format("{0}_{1}.jpg", path.Substring(0, path.LastIndexOf(".")), fileNameAddition);
|
||||
var fileNameThumb = String.IsNullOrEmpty(fileNameAddition)
|
||||
? string.Format("{0}_UMBRACOSYSTHUMBNAIL.jpg", path.Substring(0, path.LastIndexOf(".")))
|
||||
: string.Format("{0}_{1}.jpg", path.Substring(0, path.LastIndexOf(".")), fileNameAddition);
|
||||
|
||||
var thumb = GenerateThumbnail(fileSystem,
|
||||
image,
|
||||
originalImage,
|
||||
maxWidthHeight,
|
||||
width,
|
||||
height,
|
||||
path,
|
||||
extension,
|
||||
fileNameThumb,
|
||||
maxWidthHeight == 0);
|
||||
|
||||
fileNameThumb = thumb.Item3;
|
||||
|
||||
image.Dispose();
|
||||
|
||||
return fileNameThumb;
|
||||
return thumb;
|
||||
}
|
||||
|
||||
private static Tuple<int, int, string> GenerateThumbnail(MediaFileSystem fileSystem, Image image, int maxWidthHeight, int fileWidth,
|
||||
int fileHeight, string fullFilePath, string extension,
|
||||
string thumbnailFileName, bool useFixedDimensions)
|
||||
|
||||
private static ResizedImage GenerateThumbnail(MediaFileSystem fileSystem, Image image, int maxWidthHeight, string extension, string thumbnailFileName, bool useFixedDimensions)
|
||||
{
|
||||
// Generate thumbnail
|
||||
float f = 1;
|
||||
if (!useFixedDimensions)
|
||||
{
|
||||
var fx = (float)image.Size.Width / (float)maxWidthHeight;
|
||||
var fy = (float)image.Size.Height / (float)maxWidthHeight;
|
||||
var fx = image.Width / (float)maxWidthHeight;
|
||||
var fy = image.Height / (float)maxWidthHeight;
|
||||
|
||||
// must fit in thumbnail size
|
||||
f = Math.Max(fx, fy); //if (f < 1) f = 1;
|
||||
}
|
||||
|
||||
var widthTh = (int)Math.Round((float)fileWidth / f); int heightTh = (int)Math.Round((float)fileHeight / f);
|
||||
var widthTh = (int)Math.Round(image.Width / f);
|
||||
var heightTh = (int)Math.Round(image.Height / f);
|
||||
|
||||
// fixes for empty width or height
|
||||
if (widthTh == 0)
|
||||
@@ -530,44 +519,45 @@ namespace Umbraco.Core.Models
|
||||
heightTh = 1;
|
||||
|
||||
// Create new image with best quality settings
|
||||
var bp = new Bitmap(widthTh, heightTh);
|
||||
var g = Graphics.FromImage(bp);
|
||||
g.SmoothingMode = SmoothingMode.HighQuality;
|
||||
g.InterpolationMode = InterpolationMode.HighQualityBicubic;
|
||||
g.PixelOffsetMode = PixelOffsetMode.HighQuality;
|
||||
g.CompositingQuality = CompositingQuality.HighQuality;
|
||||
using (var bp = new Bitmap(widthTh, heightTh))
|
||||
{
|
||||
using (var g = Graphics.FromImage(bp))
|
||||
{
|
||||
g.SmoothingMode = SmoothingMode.HighQuality;
|
||||
g.InterpolationMode = InterpolationMode.HighQualityBicubic;
|
||||
g.PixelOffsetMode = PixelOffsetMode.HighQuality;
|
||||
g.CompositingQuality = CompositingQuality.HighQuality;
|
||||
|
||||
// Copy the old image to the new and resized
|
||||
var rect = new Rectangle(0, 0, widthTh, heightTh);
|
||||
g.DrawImage(image, rect, 0, 0, image.Width, image.Height, GraphicsUnit.Pixel);
|
||||
// Copy the old image to the new and resized
|
||||
var rect = new Rectangle(0, 0, widthTh, heightTh);
|
||||
g.DrawImage(image, rect, 0, 0, image.Width, image.Height, GraphicsUnit.Pixel);
|
||||
|
||||
// Copy metadata
|
||||
var imageEncoders = ImageCodecInfo.GetImageEncoders();
|
||||
ImageCodecInfo codec = null;
|
||||
if (extension.ToLower() == "png" || extension.ToLower() == "gif")
|
||||
codec = imageEncoders.Single(t => t.MimeType.Equals("image/png"));
|
||||
else
|
||||
codec = imageEncoders.Single(t => t.MimeType.Equals("image/jpeg"));
|
||||
// Copy metadata
|
||||
var imageEncoders = ImageCodecInfo.GetImageEncoders();
|
||||
ImageCodecInfo codec;
|
||||
if (extension.ToLower() == "png" || extension.ToLower() == "gif")
|
||||
codec = imageEncoders.Single(t => t.MimeType.Equals("image/png"));
|
||||
else
|
||||
codec = imageEncoders.Single(t => t.MimeType.Equals("image/jpeg"));
|
||||
|
||||
// Set compresion ratio to 90%
|
||||
var ep = new EncoderParameters();
|
||||
ep.Param[0] = new EncoderParameter(Encoder.Quality, 90L);
|
||||
|
||||
// Save the new image using the dimensions of the image
|
||||
var newFileName = thumbnailFileName.Replace("UMBRACOSYSTHUMBNAIL",
|
||||
string.Format("{0}x{1}", widthTh, heightTh));
|
||||
using (var ms = new MemoryStream())
|
||||
{
|
||||
bp.Save(ms, codec, ep);
|
||||
ms.Seek(0, 0);
|
||||
|
||||
// Set compresion ratio to 90%
|
||||
var ep = new EncoderParameters();
|
||||
ep.Param[0] = new EncoderParameter(Encoder.Quality, 90L);
|
||||
fileSystem.AddFile(newFileName, ms);
|
||||
}
|
||||
|
||||
// Save the new image using the dimensions of the image
|
||||
string newFileName = thumbnailFileName.Replace("UMBRACOSYSTHUMBNAIL",
|
||||
string.Format("{0}x{1}", widthTh, heightTh));
|
||||
var ms = new MemoryStream();
|
||||
bp.Save(ms, codec, ep);
|
||||
ms.Seek(0, 0);
|
||||
|
||||
fileSystem.AddFile(newFileName, ms);
|
||||
|
||||
ms.Close();
|
||||
bp.Dispose();
|
||||
g.Dispose();
|
||||
|
||||
return new Tuple<int, int, string>(widthTh, heightTh, newFileName);
|
||||
return new ResizedImage(widthTh, heightTh, newFileName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -637,11 +627,16 @@ namespace Umbraco.Core.Models
|
||||
return ApplicationContext.Current.Services.PackagingService.Export(media);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates the full xml representation for the <see cref="IMedia"/> object and all of it's descendants
|
||||
/// </summary>
|
||||
/// <param name="media"><see cref="IMedia"/> to generate xml for</param>
|
||||
/// <returns>Xml representation of the passed in <see cref="IMedia"/></returns>
|
||||
internal static XElement ToDeepXml(this IMedia media)
|
||||
{
|
||||
return ApplicationContext.Current.Services.PackagingService.Export(media, true);
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Creates the xml representation for the <see cref="IContent"/> object
|
||||
/// </summary>
|
||||
@@ -654,5 +649,15 @@ namespace Umbraco.Core.Models
|
||||
//If current IContent is published we should get latest unpublished version
|
||||
return content.ToXml();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates the xml representation for the <see cref="IMember"/> object
|
||||
/// </summary>
|
||||
/// <param name="member"><see cref="IMember"/> to generate xml for</param>
|
||||
/// <returns>Xml representation of the passed in <see cref="IContent"/></returns>
|
||||
public static XElement ToXml(this IMember member)
|
||||
{
|
||||
return ApplicationContext.Current.Services.PackagingService.Export(member);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -120,67 +120,7 @@ namespace Umbraco.Core.Models
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Indicates whether a specific property on the current <see cref="IContent"/> entity is dirty.
|
||||
/// </summary>
|
||||
/// <param name="propertyName">Name of the property to check</param>
|
||||
/// <returns>True if Property is dirty, otherwise False</returns>
|
||||
public override bool IsPropertyDirty(string propertyName)
|
||||
{
|
||||
bool existsInEntity = base.IsPropertyDirty(propertyName);
|
||||
|
||||
bool anyDirtyGroups = PropertyGroups.Any(x => x.IsPropertyDirty(propertyName));
|
||||
bool anyDirtyTypes = PropertyTypes.Any(x => x.IsPropertyDirty(propertyName));
|
||||
|
||||
return existsInEntity || anyDirtyGroups || anyDirtyTypes;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Indicates whether the current entity is dirty.
|
||||
/// </summary>
|
||||
/// <returns>True if entity is dirty, otherwise False</returns>
|
||||
public override bool IsDirty()
|
||||
{
|
||||
bool dirtyEntity = base.IsDirty();
|
||||
|
||||
bool dirtyGroups = PropertyGroups.Any(x => x.IsDirty());
|
||||
bool dirtyTypes = PropertyTypes.Any(x => x.IsDirty());
|
||||
|
||||
return dirtyEntity || dirtyGroups || dirtyTypes;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Resets dirty properties by clearing the dictionary used to track changes.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Please note that resetting the dirty properties could potentially
|
||||
/// obstruct the saving of a new or updated entity.
|
||||
/// </remarks>
|
||||
public override void ResetDirtyProperties()
|
||||
{
|
||||
base.ResetDirtyProperties();
|
||||
|
||||
//loop through each property group to reset the property types
|
||||
var propertiesReset = new List<int>();
|
||||
|
||||
foreach (var propertyGroup in PropertyGroups)
|
||||
{
|
||||
propertyGroup.ResetDirtyProperties();
|
||||
foreach (var propertyType in propertyGroup.PropertyTypes)
|
||||
{
|
||||
propertyType.ResetDirtyProperties();
|
||||
propertiesReset.Add(propertyType.Id);
|
||||
}
|
||||
}
|
||||
//then loop through our property type collection since some might not exist on a property group
|
||||
//but don't re-reset ones we've already done.
|
||||
foreach (var propertyType in PropertyTypes.Where(x => propertiesReset.Contains(x.Id) == false))
|
||||
{
|
||||
propertyType.ResetDirtyProperties();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Creates a clone of the current entity
|
||||
/// </summary>
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Specialized;
|
||||
using System.ComponentModel;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Runtime.Serialization;
|
||||
@@ -42,6 +43,8 @@ namespace Umbraco.Core.Models
|
||||
_allowedContentTypes = new List<ContentTypeSort>();
|
||||
_propertyGroups = new PropertyGroupCollection();
|
||||
_propertyTypes = new PropertyTypeCollection();
|
||||
_propertyTypes.CollectionChanged += PropertyTypesChanged;
|
||||
_additionalData = new Dictionary<string, object>();
|
||||
}
|
||||
|
||||
protected ContentTypeBase(IContentTypeBase parent)
|
||||
@@ -52,6 +55,8 @@ namespace Umbraco.Core.Models
|
||||
_allowedContentTypes = new List<ContentTypeSort>();
|
||||
_propertyGroups = new PropertyGroupCollection();
|
||||
_propertyTypes = new PropertyTypeCollection();
|
||||
_propertyTypes.CollectionChanged += PropertyTypesChanged;
|
||||
_additionalData = new Dictionary<string, object>();
|
||||
}
|
||||
|
||||
private static readonly PropertyInfo NameSelector = ExpressionHelper.GetPropertyInfo<ContentTypeBase, string>(x => x.Name);
|
||||
@@ -314,6 +319,16 @@ namespace Umbraco.Core.Models
|
||||
}
|
||||
}
|
||||
|
||||
private readonly IDictionary<string, object> _additionalData;
|
||||
/// <summary>
|
||||
/// Some entities may expose additional data that other's might not, this custom data will be available in this collection
|
||||
/// </summary>
|
||||
[EditorBrowsable(EditorBrowsableState.Never)]
|
||||
IDictionary<string, object> IUmbracoEntity.AdditionalData
|
||||
{
|
||||
get { return _additionalData; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a list of integer Ids for allowed ContentTypes
|
||||
/// </summary>
|
||||
@@ -415,8 +430,7 @@ namespace Umbraco.Core.Models
|
||||
{
|
||||
if (PropertyTypeExists(propertyType.Alias) == false)
|
||||
{
|
||||
_propertyTypes.Add(propertyType);
|
||||
_propertyTypes.CollectionChanged += PropertyTypesChanged;
|
||||
_propertyTypes.Add(propertyType);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -503,5 +517,65 @@ namespace Umbraco.Core.Models
|
||||
{
|
||||
get { return _propertyTypes; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Indicates whether a specific property on the current <see cref="IContent"/> entity is dirty.
|
||||
/// </summary>
|
||||
/// <param name="propertyName">Name of the property to check</param>
|
||||
/// <returns>True if Property is dirty, otherwise False</returns>
|
||||
public override bool IsPropertyDirty(string propertyName)
|
||||
{
|
||||
bool existsInEntity = base.IsPropertyDirty(propertyName);
|
||||
|
||||
bool anyDirtyGroups = PropertyGroups.Any(x => x.IsPropertyDirty(propertyName));
|
||||
bool anyDirtyTypes = PropertyTypes.Any(x => x.IsPropertyDirty(propertyName));
|
||||
|
||||
return existsInEntity || anyDirtyGroups || anyDirtyTypes;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Indicates whether the current entity is dirty.
|
||||
/// </summary>
|
||||
/// <returns>True if entity is dirty, otherwise False</returns>
|
||||
public override bool IsDirty()
|
||||
{
|
||||
bool dirtyEntity = base.IsDirty();
|
||||
|
||||
bool dirtyGroups = PropertyGroups.Any(x => x.IsDirty());
|
||||
bool dirtyTypes = PropertyTypes.Any(x => x.IsDirty());
|
||||
|
||||
return dirtyEntity || dirtyGroups || dirtyTypes;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Resets dirty properties by clearing the dictionary used to track changes.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Please note that resetting the dirty properties could potentially
|
||||
/// obstruct the saving of a new or updated entity.
|
||||
/// </remarks>
|
||||
public override void ResetDirtyProperties()
|
||||
{
|
||||
base.ResetDirtyProperties();
|
||||
|
||||
//loop through each property group to reset the property types
|
||||
var propertiesReset = new List<int>();
|
||||
|
||||
foreach (var propertyGroup in PropertyGroups)
|
||||
{
|
||||
propertyGroup.ResetDirtyProperties();
|
||||
foreach (var propertyType in propertyGroup.PropertyTypes)
|
||||
{
|
||||
propertyType.ResetDirtyProperties();
|
||||
propertiesReset.Add(propertyType.Id);
|
||||
}
|
||||
}
|
||||
//then loop through our property type collection since some might not exist on a property group
|
||||
//but don't re-reset ones we've already done.
|
||||
foreach (var propertyType in PropertyTypes.Where(x => propertiesReset.Contains(x.Id) == false))
|
||||
{
|
||||
propertyType.ResetDirtyProperties();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,6 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Reflection;
|
||||
using System.Runtime.Serialization;
|
||||
using Umbraco.Core.Models.EntityBase;
|
||||
@@ -31,6 +33,7 @@ namespace Umbraco.Core.Models
|
||||
{
|
||||
_parentId = parentId;
|
||||
_controlId = controlId;
|
||||
_additionalData = new Dictionary<string, object>();
|
||||
}
|
||||
|
||||
private static readonly PropertyInfo NameSelector = ExpressionHelper.GetPropertyInfo<DataTypeDefinition, string>(x => x.Name);
|
||||
@@ -160,9 +163,11 @@ namespace Umbraco.Core.Models
|
||||
_trashed = value;
|
||||
return _trashed;
|
||||
}, _trashed, TrashedSelector);
|
||||
//This is a custom property that is not exposed in IUmbracoEntity so add it to the additional data
|
||||
_additionalData["Trashed"] = value;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Id of the DataType control
|
||||
/// </summary>
|
||||
@@ -177,6 +182,8 @@ namespace Umbraco.Core.Models
|
||||
_controlId = value;
|
||||
return _controlId;
|
||||
}, _controlId, ControlIdSelector);
|
||||
//This is a custom property that is not exposed in IUmbracoEntity so add it to the additional data
|
||||
_additionalData["ControlId"] = value;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -194,9 +201,22 @@ namespace Umbraco.Core.Models
|
||||
_databaseType = value;
|
||||
return _databaseType;
|
||||
}, _databaseType, DatabaseTypeSelector);
|
||||
|
||||
//This is a custom property that is not exposed in IUmbracoEntity so add it to the additional data
|
||||
_additionalData["DatabaseType"] = value;
|
||||
}
|
||||
}
|
||||
|
||||
private readonly IDictionary<string, object> _additionalData;
|
||||
/// <summary>
|
||||
/// Some entities may expose additional data that other's might not, this custom data will be available in this collection
|
||||
/// </summary>
|
||||
[EditorBrowsable(EditorBrowsableState.Never)]
|
||||
IDictionary<string, object> IUmbracoEntity.AdditionalData
|
||||
{
|
||||
get { return _additionalData; }
|
||||
}
|
||||
|
||||
internal override void AddingEntity()
|
||||
{
|
||||
base.AddingEntity();
|
||||
|
||||
@@ -3,7 +3,6 @@ using System.Collections.Generic;
|
||||
using System.Reflection;
|
||||
using System.Runtime.Serialization;
|
||||
using Umbraco.Core.Models.EntityBase;
|
||||
using Umbraco.Core.Persistence.Mappers;
|
||||
|
||||
namespace Umbraco.Core.Models
|
||||
{
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
namespace Umbraco.Core.Models.EntityBase
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Umbraco.Core.Models.EntityBase
|
||||
{
|
||||
public interface IUmbracoEntity : IAggregateRoot
|
||||
{
|
||||
@@ -41,5 +43,10 @@
|
||||
/// Not all entities support being trashed, they'll always return false.
|
||||
/// </remarks>
|
||||
bool Trashed { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Some entities may expose additional data that other's might not, this custom data will be available in this collection
|
||||
/// </summary>
|
||||
IDictionary<string, object> AdditionalData { get; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using Umbraco.Core.Models.EntityBase;
|
||||
|
||||
namespace Umbraco.Core.Models
|
||||
{
|
||||
public static class EntityExtensions
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// Returns true if this entity has just been created and persisted to the data store
|
||||
/// </summary>
|
||||
/// <param name="entity"></param>
|
||||
/// <returns></returns>
|
||||
/// <remarks>
|
||||
/// This is useful when handling events to determine if an entity is a brand new entity or was
|
||||
/// already existing.
|
||||
/// </remarks>
|
||||
public static bool IsNewEntity(this IEntity entity)
|
||||
{
|
||||
var dirty = (IRememberBeingDirty)entity;
|
||||
return dirty.WasPropertyDirty("Id");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -6,6 +6,16 @@ using Umbraco.Core.Models.EntityBase;
|
||||
|
||||
namespace Umbraco.Core.Models
|
||||
{
|
||||
internal sealed class Folder : Entity
|
||||
{
|
||||
public Folder(string folderPath)
|
||||
{
|
||||
Path = folderPath;
|
||||
}
|
||||
|
||||
public string Path { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Represents an abstract file which provides basic functionality for a File with an Alias and Name
|
||||
/// </summary>
|
||||
@@ -23,6 +33,8 @@ namespace Umbraco.Core.Models
|
||||
|
||||
private static readonly PropertyInfo ContentSelector = ExpressionHelper.GetPropertyInfo<File, string>(x => x.Content);
|
||||
private static readonly PropertyInfo PathSelector = ExpressionHelper.GetPropertyInfo<File, string>(x => x.Path);
|
||||
private string _alias;
|
||||
private string _name;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the Name of the File including extension
|
||||
@@ -32,7 +44,11 @@ namespace Umbraco.Core.Models
|
||||
{
|
||||
get
|
||||
{
|
||||
return new FileInfo(Path).Name;
|
||||
if (_name == null)
|
||||
{
|
||||
_name = System.IO.Path.GetFileName(Path);
|
||||
}
|
||||
return _name;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -44,10 +60,14 @@ namespace Umbraco.Core.Models
|
||||
{
|
||||
get
|
||||
{
|
||||
var fileInfo = new FileInfo(Path);
|
||||
var name = fileInfo.Name;
|
||||
int lastIndexOf = name.LastIndexOf(".", StringComparison.InvariantCultureIgnoreCase);
|
||||
return name.Substring(0, lastIndexOf);
|
||||
if (_alias == null)
|
||||
{
|
||||
var name = System.IO.Path.GetFileName(Path);
|
||||
if (name == null) return string.Empty;
|
||||
var lastIndexOf = name.LastIndexOf(".", StringComparison.InvariantCultureIgnoreCase);
|
||||
_alias = name.Substring(0, lastIndexOf);
|
||||
}
|
||||
return _alias;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -60,6 +80,10 @@ namespace Umbraco.Core.Models
|
||||
get { return _path; }
|
||||
set
|
||||
{
|
||||
//reset
|
||||
_alias = null;
|
||||
_name = null;
|
||||
|
||||
SetPropertyValueAndDetectChanges(o =>
|
||||
{
|
||||
_path = value;
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
using System.Collections.Generic;
|
||||
using Umbraco.Core.Persistence.Mappers;
|
||||
|
||||
namespace Umbraco.Core.Models
|
||||
{
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
using System;
|
||||
using Umbraco.Core.Models.Membership;
|
||||
|
||||
namespace Umbraco.Core.Models
|
||||
{
|
||||
public interface IMember : IContentBase, IMembershipUser
|
||||
{
|
||||
/// <summary>
|
||||
/// String alias of the default ContentType
|
||||
/// </summary>
|
||||
string ContentTypeAlias { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the ContentType used by this content object
|
||||
/// </summary>
|
||||
IMemberType ContentType { get; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
using System.Collections.Generic;
|
||||
using Umbraco.Core.Models.EntityBase;
|
||||
|
||||
namespace Umbraco.Core.Models
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a member type
|
||||
/// </summary>
|
||||
public interface IMemberGroup : IAggregateRoot
|
||||
{
|
||||
/// <summary>
|
||||
/// The name of the member group
|
||||
/// </summary>
|
||||
string Name { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Profile of the user who created this Entity
|
||||
/// </summary>
|
||||
int CreatorId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Some entities may expose additional data that other's might not, this custom data will be available in this collection
|
||||
/// </summary>
|
||||
IDictionary<string, object> AdditionalData { get; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
namespace Umbraco.Core.Models
|
||||
{
|
||||
/// <summary>
|
||||
/// Defines a MemberType, which Member is based on
|
||||
/// </summary>
|
||||
public interface IMemberType : IContentTypeComposition
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets a boolean indicating whether a Property is editable by the Member.
|
||||
/// </summary>
|
||||
/// <param name="propertyTypeAlias">PropertyType Alias of the Property to check</param>
|
||||
/// <returns></returns>
|
||||
bool MemberCanEditProperty(string propertyTypeAlias);
|
||||
|
||||
/// <summary>
|
||||
/// Gets a boolean indicating whether a Property is visible on the Members profile.
|
||||
/// </summary>
|
||||
/// <param name="propertyTypeAlias">PropertyType Alias of the Property to check</param>
|
||||
/// <returns></returns>
|
||||
bool MemberCanViewProperty(string propertyTypeAlias);
|
||||
|
||||
/// <summary>
|
||||
/// Sets a boolean indicating whether a Property is editable by the Member.
|
||||
/// </summary>
|
||||
/// <param name="propertyTypeAlias">PropertyType Alias of the Property to set</param>
|
||||
/// <param name="value">Boolean value, true or false</param>
|
||||
void SetMemberCanEditProperty(string propertyTypeAlias, bool value);
|
||||
|
||||
/// <summary>
|
||||
/// Sets a boolean indicating whether a Property is visible on the Members profile.
|
||||
/// </summary>
|
||||
/// <param name="propertyTypeAlias">PropertyType Alias of the Property to set</param>
|
||||
/// <param name="value">Boolean value, true or false</param>
|
||||
void SetMemberCanViewProperty(string propertyTypeAlias, bool value);
|
||||
}
|
||||
}
|
||||
@@ -1,20 +1,48 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Diagnostics;
|
||||
using Umbraco.Core.Models.PublishedContent;
|
||||
|
||||
namespace Umbraco.Core.Models
|
||||
{
|
||||
/// <summary>
|
||||
/// Defines a published item in Umbraco
|
||||
/// Represents a cached content.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// A replacement for INode which needs to occur since INode doesn't contain the document type alias
|
||||
/// and INode is poorly formatted with mutable properties (i.e. Lists instead of IEnumerable)
|
||||
/// <para>SD: A replacement for INode which needs to occur since INode doesn't contain the document type alias
|
||||
/// and INode is poorly formatted with mutable properties (i.e. Lists instead of IEnumerable).</para>
|
||||
/// <para>Stephan: initially, that was for cached published content only. Now, we're using it also for
|
||||
/// cached preview (so, maybe unpublished) content. A better name would therefore be ICachedContent, as
|
||||
/// has been suggested. However, can't change now. Maybe in v7?</para>
|
||||
/// </remarks>
|
||||
public interface IPublishedContent
|
||||
{
|
||||
int Id { get; }
|
||||
public interface IPublishedContent
|
||||
{
|
||||
#region ContentSet
|
||||
|
||||
// Because of http://issues.umbraco.org/issue/U4-1797 and in order to implement
|
||||
// Index() and methods that derive from it such as IsFirst(), IsLast(), etc... all
|
||||
// content items must know about their containing content set.
|
||||
|
||||
/// <summary>
|
||||
/// Gets the content set to which the content belongs.
|
||||
/// </summary>
|
||||
/// <remarks>The default set consists in the siblings of the content (including the content
|
||||
/// itself) ordered by <c>sortOrder</c>.</remarks>
|
||||
IEnumerable<IPublishedContent> ContentSet { get; }
|
||||
|
||||
#endregion
|
||||
|
||||
#region ContentType
|
||||
|
||||
/// <summary>
|
||||
/// Gets the content type.
|
||||
/// </summary>
|
||||
PublishedContentType ContentType { get; }
|
||||
|
||||
#endregion
|
||||
|
||||
#region Content
|
||||
|
||||
int Id { get; }
|
||||
int TemplateId { get; }
|
||||
int SortOrder { get; }
|
||||
string Name { get; }
|
||||
@@ -31,32 +59,97 @@ namespace Umbraco.Core.Models
|
||||
Guid Version { get; }
|
||||
int Level { get; }
|
||||
string Url { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether the content is a content (aka a document) or a media.
|
||||
/// </summary>
|
||||
PublishedItemType ItemType { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether the content is draft.
|
||||
/// </summary>
|
||||
/// <remarks>A content is draft when it is the unpublished version of a content, which may
|
||||
/// have a published version, or not.</remarks>
|
||||
bool IsDraft { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the index of the published content within its current owning content set.
|
||||
/// </summary>
|
||||
/// <returns>The index of the published content within its current owning content set.</returns>
|
||||
int GetIndex();
|
||||
|
||||
#endregion
|
||||
|
||||
#region Tree
|
||||
|
||||
/// <summary>
|
||||
/// Gets the parent of the content.
|
||||
/// </summary>
|
||||
/// <remarks>The parent of root content is <c>null</c>.</remarks>
|
||||
IPublishedContent Parent { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the children of the content.
|
||||
/// </summary>
|
||||
/// <remarks>Children are sorted by their sortOrder.</remarks>
|
||||
IEnumerable<IPublishedContent> Children { get; }
|
||||
|
||||
ICollection<IPublishedContentProperty> Properties { get; }
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// Returns the property value for the property alias specified
|
||||
/// </summary>
|
||||
/// <param name="propertyAlias"></param>
|
||||
/// <returns></returns>
|
||||
object this[string propertyAlias] { get; }
|
||||
#region Properties
|
||||
|
||||
/// <summary>
|
||||
/// Returns a property on the object based on an alias
|
||||
/// <summary>
|
||||
/// Gets the properties of the content.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>Contains one <c>IPublishedProperty</c> for each property defined for the content type, including
|
||||
/// inherited properties. Some properties may have no value.</para>
|
||||
/// <para>The properties collection of an IPublishedContent instance should be read-only ie it is illegal
|
||||
/// to add properties to the collection.</para>
|
||||
/// </remarks>
|
||||
ICollection<IPublishedProperty> Properties { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a property identified by its alias.
|
||||
/// </summary>
|
||||
/// <param name="alias">The property alias.</param>
|
||||
/// <returns>The property identified by the alias.</returns>
|
||||
/// <remarks>
|
||||
/// <para>If the content type has no property with that alias, including inherited properties, returns <c>null</c>,</para>
|
||||
/// <para>otherwise return a property -- that may have no value (ie <c>HasValue</c> is <c>false</c>).</para>
|
||||
/// <para>The alias is case-insensitive.</para>
|
||||
/// </remarks>
|
||||
IPublishedProperty GetProperty(string alias);
|
||||
|
||||
/// <summary>
|
||||
/// Gets a property identified by its alias.
|
||||
/// </summary>
|
||||
/// <param name="alias">The property alias.</param>
|
||||
/// <param name="recurse">A value indicating whether to navigate the tree upwards until a property with a value is found.</param>
|
||||
/// <returns>The property identified by the alias.</returns>
|
||||
/// <remarks>
|
||||
/// <para>Navigate the tree upwards and look for a property with that alias and with a value (ie <c>HasValue</c> is <c>true</c>).
|
||||
/// If found, return the property. If no property with that alias is found, having a value or not, return <c>null</c>. Otherwise
|
||||
/// return the first property that was found with the alias but had no value (ie <c>HasValue</c> is <c>false</c>).</para>
|
||||
/// <para>The alias is case-insensitive.</para>
|
||||
/// </remarks>
|
||||
IPublishedProperty GetProperty(string alias, bool recurse);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the value of a property identified by its alias.
|
||||
/// </summary>
|
||||
/// <param name="alias"></param>
|
||||
/// <returns></returns>
|
||||
/// <remarks>
|
||||
/// Although we do have a a property to return Properties of the object, in some cases a custom implementation may not know
|
||||
/// about all properties until specifically asked for one by alias.
|
||||
///
|
||||
/// This method is mostly used in places such as DynamicPublishedContent when trying to resolve a property based on an alias.
|
||||
/// In some cases Pulish Stores, a property value may exist in multiple places and we need to fallback to different cached locations
|
||||
/// therefore sometimes the 'Properties' collection may not be sufficient.
|
||||
/// </remarks>
|
||||
IPublishedContentProperty GetProperty(string alias);
|
||||
}
|
||||
/// <param name="alias">The property alias.</param>
|
||||
/// <returns>The value of the property identified by the alias.</returns>
|
||||
/// <remarks>
|
||||
/// <para>If <c>GetProperty(alias)</c> is <c>null</c> then returns <c>null</c> else return <c>GetProperty(alias).Value</c>.</para>
|
||||
/// <para>So if the property has no value, returns the default value for that property type.</para>
|
||||
/// <para>This one is defined here really because we cannot define index extension methods, but all it should do is:
|
||||
/// <code>var p = GetProperty(alias); return p == null ? null : p.Value;</code> and nothing else.</para>
|
||||
/// <para>The recursive syntax (eg "_title") is _not_ supported here.</para>
|
||||
/// <para>The alias is case-insensitive.</para>
|
||||
/// </remarks>
|
||||
object this[string alias] { get; } // todo - should obsolete this[alias] (when?)
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
using System;
|
||||
|
||||
namespace Umbraco.Core.Models
|
||||
{
|
||||
public interface IPublishedContentProperty
|
||||
{
|
||||
string Alias { get; }
|
||||
object Value { get; }
|
||||
Guid Version { get; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
namespace Umbraco.Core.Models
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a property of an <c>IPublishedContent</c>.
|
||||
/// </summary>
|
||||
public interface IPublishedProperty
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the alias of the property.
|
||||
/// </summary>
|
||||
string PropertyTypeAlias { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether the property has a value.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>This is somewhat implementation-dependent -- depending on whatever IPublishedCache considers
|
||||
/// a missing value.</para>
|
||||
/// <para>The XmlPublishedCache raw values are strings, and it will consider missing, null or empty (and
|
||||
/// that includes whitespace-only) strings as "no value".</para>
|
||||
/// <para>Other caches that get their raw value from the database would consider that a property has "no
|
||||
/// value" if it is missing, null, or an empty string (including whitespace-only).</para>
|
||||
/// </remarks>
|
||||
bool HasValue { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the data value of the property.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>The data value is whatever was passed to the property when it was instanciated, and it is
|
||||
/// somewhat implementation-dependent -- depending on how the IPublishedCache is implemented.</para>
|
||||
/// <para>The XmlPublishedCache raw values are strings exclusively since they come from the Xml cache.</para>
|
||||
/// <para>For other caches that get their raw value from the database, it would be either a string,
|
||||
/// an integer (Int32), or a date and time (DateTime).</para>
|
||||
/// <para>If you're using that value, you're probably wrong, unless you're doing some internal
|
||||
/// Umbraco stuff.</para>
|
||||
/// </remarks>
|
||||
object DataValue { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the object value of the property.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>The value is what you want to use when rendering content in an MVC view ie in C#.</para>
|
||||
/// <para>It can be null, or any type of CLR object.</para>
|
||||
/// <para>It has been fully prepared and processed by the appropriate converter.</para>
|
||||
/// </remarks>
|
||||
object Value { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the XPath value of the property.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>The XPath value is what you want to use when navigating content via XPath eg in the XSLT engine.</para>
|
||||
/// <para>It must be either null, or a string, or an XPathNavigator.</para>
|
||||
/// <para>It has been fully prepared and processed by the appropriate converter.</para>
|
||||
/// </remarks>
|
||||
object XPathValue { get; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
using System.Runtime.Serialization;
|
||||
using Umbraco.Core.Models.EntityBase;
|
||||
|
||||
namespace Umbraco.Core.Models
|
||||
{
|
||||
public interface IRelation : IAggregateRoot
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the Parent Id of the Relation (Source)
|
||||
/// </summary>
|
||||
[DataMember]
|
||||
int ParentId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the Child Id of the Relation (Destination)
|
||||
/// </summary>
|
||||
[DataMember]
|
||||
int ChildId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the <see cref="RelationType"/> for the Relation
|
||||
/// </summary>
|
||||
[DataMember]
|
||||
IRelationType RelationType { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a comment for the Relation
|
||||
/// </summary>
|
||||
[DataMember]
|
||||
string Comment { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the Id of the <see cref="RelationType"/> that this Relation is based on.
|
||||
/// </summary>
|
||||
[IgnoreDataMember]
|
||||
int RelationTypeId { get; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
using System;
|
||||
using System.Runtime.Serialization;
|
||||
using Umbraco.Core.Models.EntityBase;
|
||||
|
||||
namespace Umbraco.Core.Models
|
||||
{
|
||||
public interface IRelationType : IAggregateRoot
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the Name of the RelationType
|
||||
/// </summary>
|
||||
[DataMember]
|
||||
string Name { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the Alias of the RelationType
|
||||
/// </summary>
|
||||
[DataMember]
|
||||
string Alias { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a boolean indicating whether the RelationType is Bidirectional (true) or Parent to Child (false)
|
||||
/// </summary>
|
||||
[DataMember]
|
||||
bool IsBidirectional { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the Parents object type id
|
||||
/// </summary>
|
||||
/// <remarks>Corresponds to the NodeObjectType in the umbracoNode table</remarks>
|
||||
[DataMember]
|
||||
Guid ParentObjectType { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the Childs object type id
|
||||
/// </summary>
|
||||
/// <remarks>Corresponds to the NodeObjectType in the umbracoNode table</remarks>
|
||||
[DataMember]
|
||||
Guid ChildObjectType { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -10,5 +10,11 @@
|
||||
/// </summary>
|
||||
/// <returns><see cref="RenderingEngine"/></returns>
|
||||
RenderingEngine GetTypeOfRenderingEngine();
|
||||
|
||||
/// <summary>
|
||||
/// Set the mastertemplate
|
||||
/// </summary>
|
||||
/// <param name="masterTemplate"></param>
|
||||
void SetMasterTemplate(ITemplate masterTemplate);
|
||||
}
|
||||
}
|
||||
@@ -3,7 +3,6 @@ using System.Globalization;
|
||||
using System.Reflection;
|
||||
using System.Runtime.Serialization;
|
||||
using Umbraco.Core.Models.EntityBase;
|
||||
using Umbraco.Core.Persistence.Mappers;
|
||||
|
||||
namespace Umbraco.Core.Models
|
||||
{
|
||||
|
||||
@@ -0,0 +1,599 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Runtime.Serialization;
|
||||
using Umbraco.Core.Logging;
|
||||
|
||||
namespace Umbraco.Core.Models
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a Member object
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
[DataContract(IsReference = true)]
|
||||
public class Member : ContentBase, IMember
|
||||
{
|
||||
private readonly IMemberType _contentType;
|
||||
private string _contentTypeAlias;
|
||||
private string _username;
|
||||
private string _email;
|
||||
private string _password;
|
||||
private object _providerUserKey;
|
||||
private Type _userTypeKey;
|
||||
|
||||
/// <summary>
|
||||
/// Constructor for creating a Member object
|
||||
/// </summary>
|
||||
/// <param name="name">Name of the content</param>
|
||||
/// <param name="contentType">ContentType for the current Content object</param>
|
||||
public Member(string name, IMemberType contentType)
|
||||
: base(name, -1, contentType, new PropertyCollection())
|
||||
{
|
||||
_contentType = contentType;
|
||||
IsApproved = true;
|
||||
}
|
||||
|
||||
//TODO: Should we just get rid of this one? no reason to have a level set.
|
||||
internal Member(string name, string email, string username, string password, int parentId, IMemberType contentType)
|
||||
: base(name, parentId, contentType, new PropertyCollection())
|
||||
{
|
||||
Mandate.ParameterNotNull(contentType, "contentType");
|
||||
|
||||
_contentType = contentType;
|
||||
_email = email;
|
||||
_username = username;
|
||||
_password = password;
|
||||
IsApproved = true;
|
||||
}
|
||||
|
||||
public Member(string name, string email, string username, string password, IMemberType contentType)
|
||||
: this(name, email, username, password, -1, contentType)
|
||||
{
|
||||
Mandate.ParameterNotNull(contentType, "contentType");
|
||||
|
||||
_contentType = contentType;
|
||||
_email = email;
|
||||
_username = username;
|
||||
_password = password;
|
||||
IsApproved = true;
|
||||
}
|
||||
|
||||
//public Member(string name, string email, string username, string password, IContentBase parent, IMemberType contentType)
|
||||
// : base(name, parent, contentType, new PropertyCollection())
|
||||
//{
|
||||
// Mandate.ParameterNotNull(contentType, "contentType");
|
||||
|
||||
// _contentType = contentType;
|
||||
// _email = email;
|
||||
// _username = username;
|
||||
// _password = password;
|
||||
//}
|
||||
|
||||
private static readonly PropertyInfo DefaultContentTypeAliasSelector = ExpressionHelper.GetPropertyInfo<Member, string>(x => x.ContentTypeAlias);
|
||||
private static readonly PropertyInfo UsernameSelector = ExpressionHelper.GetPropertyInfo<Member, string>(x => x.Username);
|
||||
private static readonly PropertyInfo EmailSelector = ExpressionHelper.GetPropertyInfo<Member, string>(x => x.Email);
|
||||
private static readonly PropertyInfo PasswordSelector = ExpressionHelper.GetPropertyInfo<Member, string>(x => x.Password);
|
||||
private static readonly PropertyInfo ProviderUserKeySelector = ExpressionHelper.GetPropertyInfo<Member, object>(x => x.ProviderUserKey);
|
||||
private static readonly PropertyInfo UserTypeKeySelector = ExpressionHelper.GetPropertyInfo<Member, Type>(x => x.ProviderUserKeyType);
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the Username
|
||||
/// </summary>
|
||||
[DataMember]
|
||||
public string Username
|
||||
{
|
||||
get { return _username; }
|
||||
set
|
||||
{
|
||||
SetPropertyValueAndDetectChanges(o =>
|
||||
{
|
||||
_username = value;
|
||||
return _username;
|
||||
}, _username, UsernameSelector);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the Email
|
||||
/// </summary>
|
||||
[DataMember]
|
||||
public string Email
|
||||
{
|
||||
get { return _email; }
|
||||
set
|
||||
{
|
||||
SetPropertyValueAndDetectChanges(o =>
|
||||
{
|
||||
_email = value;
|
||||
return _email;
|
||||
}, _email, EmailSelector);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the Password
|
||||
/// </summary>
|
||||
[DataMember]
|
||||
public string Password
|
||||
{
|
||||
get { return _password; }
|
||||
set
|
||||
{
|
||||
SetPropertyValueAndDetectChanges(o =>
|
||||
{
|
||||
_password = value;
|
||||
return _password;
|
||||
}, _password, PasswordSelector);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the Groups that Member is part of
|
||||
/// </summary>
|
||||
[DataMember]
|
||||
public IEnumerable<string> Groups { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the Password Question
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Alias: umbracoPasswordRetrievalQuestionPropertyTypeAlias
|
||||
/// Part of the standard properties collection.
|
||||
/// </remarks>
|
||||
[IgnoreDataMember]
|
||||
public string PasswordQuestion
|
||||
{
|
||||
get
|
||||
{
|
||||
var a = WarnIfPropertyTypeNotFoundOnGet(Constants.Conventions.Member.PasswordQuestion, "PasswordQuestion", default(string));
|
||||
if (a.Success == false) return a.Result;
|
||||
|
||||
return Properties[Constants.Conventions.Member.PasswordQuestion].Value == null
|
||||
? string.Empty
|
||||
: Properties[Constants.Conventions.Member.PasswordQuestion].Value.ToString();
|
||||
}
|
||||
set
|
||||
{
|
||||
if (WarnIfPropertyTypeNotFoundOnSet(
|
||||
Constants.Conventions.Member.PasswordQuestion,
|
||||
"PasswordQuestion") == false) return;
|
||||
|
||||
Properties[Constants.Conventions.Member.PasswordQuestion].Value = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the Password Answer
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Alias: umbracoPasswordRetrievalAnswerPropertyTypeAlias
|
||||
/// Part of the standard properties collection.
|
||||
/// </remarks>
|
||||
[IgnoreDataMember]
|
||||
public string PasswordAnswer
|
||||
{
|
||||
get
|
||||
{
|
||||
var a = WarnIfPropertyTypeNotFoundOnGet(Constants.Conventions.Member.PasswordAnswer, "PasswordAnswer", default(string));
|
||||
if (a.Success == false) return a.Result;
|
||||
|
||||
return Properties[Constants.Conventions.Member.PasswordAnswer].Value == null
|
||||
? string.Empty
|
||||
: Properties[Constants.Conventions.Member.PasswordAnswer].Value.ToString();
|
||||
}
|
||||
set
|
||||
{
|
||||
if (WarnIfPropertyTypeNotFoundOnSet(
|
||||
Constants.Conventions.Member.PasswordAnswer,
|
||||
"PasswordAnswer") == false) return;
|
||||
|
||||
Properties[Constants.Conventions.Member.PasswordAnswer].Value = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or set the comments for the member
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Alias: umbracoCommentPropertyTypeAlias
|
||||
/// Part of the standard properties collection.
|
||||
/// </remarks>
|
||||
[IgnoreDataMember]
|
||||
public string Comments
|
||||
{
|
||||
get
|
||||
{
|
||||
var a = WarnIfPropertyTypeNotFoundOnGet(Constants.Conventions.Member.Comments, "Comments", default(string));
|
||||
if (a.Success == false) return a.Result;
|
||||
|
||||
return Properties[Constants.Conventions.Member.Comments].Value == null
|
||||
? string.Empty
|
||||
: Properties[Constants.Conventions.Member.Comments].Value.ToString();
|
||||
}
|
||||
set
|
||||
{
|
||||
if (WarnIfPropertyTypeNotFoundOnSet(
|
||||
Constants.Conventions.Member.Comments,
|
||||
"Comments") == false) return;
|
||||
|
||||
Properties[Constants.Conventions.Member.Comments].Value = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a boolean indicating whether the Member is approved
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Alias: umbracoApprovePropertyTypeAlias
|
||||
/// Part of the standard properties collection.
|
||||
/// </remarks>
|
||||
[IgnoreDataMember]
|
||||
public bool IsApproved
|
||||
{
|
||||
get
|
||||
{
|
||||
var a = WarnIfPropertyTypeNotFoundOnGet(Constants.Conventions.Member.IsApproved, "IsApproved",
|
||||
//This is the default value if the prop is not found
|
||||
true);
|
||||
if (a.Success == false) return a.Result;
|
||||
|
||||
var tryConvert = Properties[Constants.Conventions.Member.IsApproved].Value.TryConvertTo<bool>();
|
||||
if (tryConvert.Success)
|
||||
{
|
||||
return tryConvert.Result;
|
||||
}
|
||||
//if the property exists but it cannot be converted, we will assume true
|
||||
return true;
|
||||
}
|
||||
set
|
||||
{
|
||||
if (WarnIfPropertyTypeNotFoundOnSet(
|
||||
Constants.Conventions.Member.IsApproved,
|
||||
"IsApproved") == false) return;
|
||||
|
||||
Properties[Constants.Conventions.Member.IsApproved].Value = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a boolean indicating whether the Member is locked out
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Alias: umbracoLockPropertyTypeAlias
|
||||
/// Part of the standard properties collection.
|
||||
/// </remarks>
|
||||
[IgnoreDataMember]
|
||||
public bool IsLockedOut
|
||||
{
|
||||
get
|
||||
{
|
||||
var a = WarnIfPropertyTypeNotFoundOnGet(Constants.Conventions.Member.IsLockedOut, "IsLockedOut", false);
|
||||
if (a.Success == false) return a.Result;
|
||||
|
||||
var tryConvert = Properties[Constants.Conventions.Member.IsLockedOut].Value.TryConvertTo<bool>();
|
||||
if (tryConvert.Success)
|
||||
{
|
||||
return tryConvert.Result;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
set
|
||||
{
|
||||
if (WarnIfPropertyTypeNotFoundOnSet(
|
||||
Constants.Conventions.Member.IsLockedOut,
|
||||
"IsLockedOut") == false) return;
|
||||
|
||||
Properties[Constants.Conventions.Member.IsLockedOut].Value = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the date for last login
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Alias: umbracoLastLoginPropertyTypeAlias
|
||||
/// Part of the standard properties collection.
|
||||
/// </remarks>
|
||||
[IgnoreDataMember]
|
||||
public DateTime LastLoginDate
|
||||
{
|
||||
get
|
||||
{
|
||||
var a = WarnIfPropertyTypeNotFoundOnGet(Constants.Conventions.Member.LastLoginDate, "LastLoginDate", default(DateTime));
|
||||
if (a.Success == false) return a.Result;
|
||||
|
||||
var tryConvert = Properties[Constants.Conventions.Member.LastLoginDate].Value.TryConvertTo<DateTime>();
|
||||
if (tryConvert.Success)
|
||||
{
|
||||
return tryConvert.Result;
|
||||
}
|
||||
return default(DateTime);
|
||||
}
|
||||
set
|
||||
{
|
||||
if (WarnIfPropertyTypeNotFoundOnSet(
|
||||
Constants.Conventions.Member.LastLoginDate,
|
||||
"LastLoginDate") == false) return;
|
||||
|
||||
Properties[Constants.Conventions.Member.LastLoginDate].Value = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gest or sets the date for last password change
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Alias: umbracoMemberLastPasswordChange
|
||||
/// Part of the standard properties collection.
|
||||
/// </remarks>
|
||||
[IgnoreDataMember]
|
||||
public DateTime LastPasswordChangeDate
|
||||
{
|
||||
get
|
||||
{
|
||||
var a = WarnIfPropertyTypeNotFoundOnGet(Constants.Conventions.Member.LastPasswordChangeDate, "LastPasswordChangeDate", default(DateTime));
|
||||
if (a.Success == false) return a.Result;
|
||||
|
||||
var tryConvert = Properties[Constants.Conventions.Member.LastPasswordChangeDate].Value.TryConvertTo<DateTime>();
|
||||
if (tryConvert.Success)
|
||||
{
|
||||
return tryConvert.Result;
|
||||
}
|
||||
return default(DateTime);
|
||||
}
|
||||
set
|
||||
{
|
||||
if (WarnIfPropertyTypeNotFoundOnSet(
|
||||
Constants.Conventions.Member.LastPasswordChangeDate,
|
||||
"LastPasswordChangeDate") == false) return;
|
||||
|
||||
Properties[Constants.Conventions.Member.LastPasswordChangeDate].Value = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the date for when Member was locked out
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Alias: umbracoMemberLastLockout
|
||||
/// Part of the standard properties collection.
|
||||
/// </remarks>
|
||||
[IgnoreDataMember]
|
||||
public DateTime LastLockoutDate
|
||||
{
|
||||
get
|
||||
{
|
||||
var a = WarnIfPropertyTypeNotFoundOnGet(Constants.Conventions.Member.LastLockoutDate, "LastLockoutDate", default(DateTime));
|
||||
if (a.Success == false) return a.Result;
|
||||
|
||||
var tryConvert = Properties[Constants.Conventions.Member.LastLockoutDate].Value.TryConvertTo<DateTime>();
|
||||
if (tryConvert.Success)
|
||||
{
|
||||
return tryConvert.Result;
|
||||
}
|
||||
return default(DateTime);
|
||||
}
|
||||
set
|
||||
{
|
||||
if (WarnIfPropertyTypeNotFoundOnSet(
|
||||
Constants.Conventions.Member.LastLockoutDate,
|
||||
"LastLockoutDate") == false) return;
|
||||
|
||||
Properties[Constants.Conventions.Member.LastLockoutDate].Value = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the number of failed password attempts.
|
||||
/// This is the number of times the password was entered incorrectly upon login.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Alias: umbracoFailedPasswordAttemptsPropertyTypeAlias
|
||||
/// Part of the standard properties collection.
|
||||
/// </remarks>
|
||||
[IgnoreDataMember]
|
||||
public int FailedPasswordAttempts
|
||||
{
|
||||
get
|
||||
{
|
||||
var a = WarnIfPropertyTypeNotFoundOnGet(Constants.Conventions.Member.FailedPasswordAttempts, "FailedPasswordAttempts", 0);
|
||||
if (a.Success == false) return a.Result;
|
||||
|
||||
var tryConvert = Properties[Constants.Conventions.Member.FailedPasswordAttempts].Value.TryConvertTo<int>();
|
||||
if (tryConvert.Success)
|
||||
{
|
||||
return tryConvert.Result;
|
||||
}
|
||||
return default(int);
|
||||
}
|
||||
set
|
||||
{
|
||||
if (WarnIfPropertyTypeNotFoundOnSet(
|
||||
Constants.Conventions.Member.FailedPasswordAttempts,
|
||||
"FailedPasswordAttempts") == false) return;
|
||||
|
||||
Properties[Constants.Conventions.Member.FailedPasswordAttempts].Value = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// String alias of the default ContentType
|
||||
/// </summary>
|
||||
[DataMember]
|
||||
public virtual string ContentTypeAlias
|
||||
{
|
||||
get { return _contentTypeAlias; }
|
||||
internal set
|
||||
{
|
||||
SetPropertyValueAndDetectChanges(o =>
|
||||
{
|
||||
_contentTypeAlias = value;
|
||||
return _contentTypeAlias;
|
||||
}, _contentTypeAlias, DefaultContentTypeAliasSelector);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// User key from the Provider.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// When using standard umbraco provider this key will
|
||||
/// correspond to the guid UniqueId/Key.
|
||||
/// Otherwise it will the one available from the asp.net
|
||||
/// membership provider.
|
||||
/// </remarks>
|
||||
[DataMember]
|
||||
public virtual object ProviderUserKey
|
||||
{
|
||||
get
|
||||
{
|
||||
return _providerUserKey;
|
||||
}
|
||||
set
|
||||
{
|
||||
SetPropertyValueAndDetectChanges(o =>
|
||||
{
|
||||
_providerUserKey = value;
|
||||
return _providerUserKey;
|
||||
}, _providerUserKey, ProviderUserKeySelector);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the type of the provider user key.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The type of the provider user key.
|
||||
/// </value>
|
||||
[IgnoreDataMember]
|
||||
internal Type ProviderUserKeyType
|
||||
{
|
||||
get
|
||||
{
|
||||
return _userTypeKey;
|
||||
}
|
||||
private set
|
||||
{
|
||||
SetPropertyValueAndDetectChanges(o =>
|
||||
{
|
||||
_userTypeKey = value;
|
||||
return _userTypeKey;
|
||||
}, _userTypeKey, UserTypeKeySelector);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the type of the provider user key.
|
||||
/// </summary>
|
||||
/// <param name="type">The type.</param>
|
||||
internal void SetProviderUserKeyType(Type type)
|
||||
{
|
||||
ProviderUserKeyType = type;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Method to call when Entity is being saved
|
||||
/// </summary>
|
||||
/// <remarks>Created date is set and a Unique key is assigned</remarks>
|
||||
internal override void AddingEntity()
|
||||
{
|
||||
base.AddingEntity();
|
||||
|
||||
if (Key == Guid.Empty)
|
||||
Key = Guid.NewGuid();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the ContentType used by this content object
|
||||
/// </summary>
|
||||
[IgnoreDataMember]
|
||||
public IMemberType ContentType
|
||||
{
|
||||
get { return _contentType; }
|
||||
}
|
||||
|
||||
public override void ChangeTrashedState(bool isTrashed, int parentId = -20)
|
||||
{
|
||||
throw new NotSupportedException("Members can't be trashed as no Recycle Bin exists, so use of this method is invalid");
|
||||
}
|
||||
|
||||
/* Internal experiment - only used for mapping queries.
|
||||
* Adding these to have first level properties instead of the Properties collection.
|
||||
*/
|
||||
internal string LongStringPropertyValue { get; set; }
|
||||
internal string ShortStringPropertyValue { get; set; }
|
||||
internal int IntegerropertyValue { get; set; }
|
||||
internal bool BoolPropertyValue { get; set; }
|
||||
internal DateTime DateTimePropertyValue { get; set; }
|
||||
internal string PropertyTypeAlias { get; set; }
|
||||
|
||||
private Attempt<T> WarnIfPropertyTypeNotFoundOnGet<T>(string propertyAlias, string propertyName, T defaultVal)
|
||||
{
|
||||
Action doLog = () => LogHelper.Warn<Member>(
|
||||
"Trying to access the '"
|
||||
+ propertyName
|
||||
+ "' property on "
|
||||
+ typeof(Member)
|
||||
+ " but the "
|
||||
+ propertyAlias
|
||||
+ " property does not exist on the member type so a default value is returned. Ensure that you have a property type with alias: "
|
||||
+ propertyAlias
|
||||
+ " configured on your member type in order to use the '"
|
||||
+ propertyName
|
||||
+ "' property on the model correctly.");
|
||||
|
||||
//if the property doesn't exist, then do the logging and return a failure
|
||||
if (Properties.Contains(propertyAlias) == false)
|
||||
{
|
||||
//we'll put a warn in the log if this entity has been persisted
|
||||
if (HasIdentity)
|
||||
{
|
||||
doLog();
|
||||
}
|
||||
return Attempt<T>.Fail(defaultVal);
|
||||
}
|
||||
|
||||
//if the property doesn't have an identity but we do, then do logging and return failure
|
||||
var prop = Properties.Single(x => x.Alias == propertyAlias);
|
||||
if (prop.HasIdentity == false && HasIdentity)
|
||||
{
|
||||
doLog();
|
||||
return Attempt<T>.Fail(defaultVal);
|
||||
}
|
||||
|
||||
return Attempt<T>.Succeed();
|
||||
}
|
||||
|
||||
private bool WarnIfPropertyTypeNotFoundOnSet(string propertyAlias, string propertyname)
|
||||
{
|
||||
Action doLog = () => LogHelper.Warn<Member>("An attempt was made to set a value on the property '"
|
||||
+ propertyname
|
||||
+ "' on type "
|
||||
+ typeof(Member)
|
||||
+ " but the property type "
|
||||
+ propertyAlias
|
||||
+ " does not exist on the member type, ensure that this property type exists so that setting this property works correctly.");
|
||||
|
||||
//if the property doesn't exist, then do the logging and return a failure
|
||||
if (Properties.Contains(propertyAlias) == false)
|
||||
{
|
||||
if (HasIdentity)
|
||||
{
|
||||
doLog();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
//if the property doesn't have an identity but we do, then do logging and return failure
|
||||
var prop = Properties.Single(x => x.Alias == propertyAlias);
|
||||
if (prop.HasIdentity == false && HasIdentity)
|
||||
{
|
||||
doLog();
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Reflection;
|
||||
using System.Runtime.Serialization;
|
||||
using Umbraco.Core.Models.EntityBase;
|
||||
|
||||
namespace Umbraco.Core.Models
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a member type
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
[DataContract(IsReference = true)]
|
||||
public class MemberGroup : Entity, IMemberGroup
|
||||
{
|
||||
public MemberGroup()
|
||||
{
|
||||
AdditionalData = new Dictionary<string, object>();
|
||||
}
|
||||
|
||||
private string _name;
|
||||
private int _creatorId;
|
||||
|
||||
private static readonly PropertyInfo NameSelector = ExpressionHelper.GetPropertyInfo<MemberGroup, string>(x => x.Name);
|
||||
private static readonly PropertyInfo CreatorIdSelector = ExpressionHelper.GetPropertyInfo<MemberGroup, int>(x => x.CreatorId);
|
||||
|
||||
[DataMember]
|
||||
public string Name
|
||||
{
|
||||
get { return _name; }
|
||||
set
|
||||
{
|
||||
SetPropertyValueAndDetectChanges(o =>
|
||||
{
|
||||
if (_name != value)
|
||||
{
|
||||
//if the name has changed, add the value to the additional data,
|
||||
//this is required purely for event handlers to know the previous name of the group
|
||||
//so we can keep the public access up to date.
|
||||
AdditionalData["previousName"] = _name;
|
||||
}
|
||||
|
||||
_name = value;
|
||||
return _name;
|
||||
}, _name, NameSelector);
|
||||
}
|
||||
}
|
||||
|
||||
public int CreatorId
|
||||
{
|
||||
get { return _creatorId; }
|
||||
set
|
||||
{
|
||||
SetPropertyValueAndDetectChanges(o =>
|
||||
{
|
||||
_creatorId = value;
|
||||
return _creatorId;
|
||||
}, _creatorId, CreatorIdSelector);
|
||||
}
|
||||
}
|
||||
|
||||
public IDictionary<string, object> AdditionalData { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Method to call when Entity is being saved
|
||||
/// </summary>
|
||||
/// <remarks>Created date is set and a Unique key is assigned</remarks>
|
||||
internal override void AddingEntity()
|
||||
{
|
||||
base.AddingEntity();
|
||||
|
||||
if (Key == Guid.Empty)
|
||||
Key = Guid.NewGuid();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,146 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Runtime.Serialization;
|
||||
|
||||
namespace Umbraco.Core.Models
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents the content type that a <see cref="Member"/> object is based on
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
[DataContract(IsReference = true)]
|
||||
public class MemberType : ContentTypeCompositionBase, IMemberType
|
||||
{
|
||||
//Dictionary is divided into string: PropertyTypeAlias, Tuple: MemberCanEdit, VisibleOnProfile, PropertyTypeId
|
||||
private string _alias;
|
||||
|
||||
public MemberType(int parentId) : base(parentId)
|
||||
{
|
||||
MemberTypePropertyTypes = new Dictionary<string, MemberTypePropertyProfileAccess>();
|
||||
}
|
||||
|
||||
public MemberType(IContentTypeComposition parent) : base(parent)
|
||||
{
|
||||
MemberTypePropertyTypes = new Dictionary<string, MemberTypePropertyProfileAccess>();
|
||||
}
|
||||
|
||||
private static readonly PropertyInfo AliasSelector = ExpressionHelper.GetPropertyInfo<MemberType, string>(x => x.Alias);
|
||||
|
||||
/// <summary>
|
||||
/// The Alias of the ContentType
|
||||
/// </summary>
|
||||
[DataMember]
|
||||
public override string Alias
|
||||
{
|
||||
get { return _alias; }
|
||||
set
|
||||
{
|
||||
//NOTE: WE are overriding this because we don't want to do a ToSafeAlias when the alias is the special case of
|
||||
// "_umbracoSystemDefaultProtectType" which is used internally, currently there is an issue with the safe alias as it strips
|
||||
// leading underscores which we don't want in this case.
|
||||
// see : http://issues.umbraco.org/issue/U4-3968
|
||||
|
||||
SetPropertyValueAndDetectChanges(o =>
|
||||
{
|
||||
_alias = value == "_umbracoSystemDefaultProtectType" ? value : value.ToSafeAlias();
|
||||
return _alias;
|
||||
}, _alias, AliasSelector);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or Sets a Dictionary of Tuples (MemberCanEdit, VisibleOnProfile) by the PropertyTypes' alias.
|
||||
/// </summary>
|
||||
[DataMember]
|
||||
internal IDictionary<string, MemberTypePropertyProfileAccess> MemberTypePropertyTypes { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a boolean indicating whether a Property is editable by the Member.
|
||||
/// </summary>
|
||||
/// <param name="propertyTypeAlias">PropertyType Alias of the Property to check</param>
|
||||
/// <returns></returns>
|
||||
public bool MemberCanEditProperty(string propertyTypeAlias)
|
||||
{
|
||||
if (MemberTypePropertyTypes.ContainsKey(propertyTypeAlias))
|
||||
{
|
||||
return MemberTypePropertyTypes[propertyTypeAlias].IsEditable;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a boolean indicating whether a Property is visible on the Members profile.
|
||||
/// </summary>
|
||||
/// <param name="propertyTypeAlias">PropertyType Alias of the Property to check</param>
|
||||
/// <returns></returns>
|
||||
public bool MemberCanViewProperty(string propertyTypeAlias)
|
||||
{
|
||||
if (MemberTypePropertyTypes.ContainsKey(propertyTypeAlias))
|
||||
{
|
||||
return MemberTypePropertyTypes[propertyTypeAlias].IsVisible;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets a boolean indicating whether a Property is editable by the Member.
|
||||
/// </summary>
|
||||
/// <param name="propertyTypeAlias">PropertyType Alias of the Property to set</param>
|
||||
/// <param name="value">Boolean value, true or false</param>
|
||||
public void SetMemberCanEditProperty(string propertyTypeAlias, bool value)
|
||||
{
|
||||
if (MemberTypePropertyTypes.ContainsKey(propertyTypeAlias))
|
||||
{
|
||||
MemberTypePropertyTypes[propertyTypeAlias].IsEditable = value;
|
||||
}
|
||||
else
|
||||
{
|
||||
var tuple = new MemberTypePropertyProfileAccess(false, value);
|
||||
MemberTypePropertyTypes.Add(propertyTypeAlias, tuple);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets a boolean indicating whether a Property is visible on the Members profile.
|
||||
/// </summary>
|
||||
/// <param name="propertyTypeAlias">PropertyType Alias of the Property to set</param>
|
||||
/// <param name="value">Boolean value, true or false</param>
|
||||
public void SetMemberCanViewProperty(string propertyTypeAlias, bool value)
|
||||
{
|
||||
if (MemberTypePropertyTypes.ContainsKey(propertyTypeAlias))
|
||||
{
|
||||
MemberTypePropertyTypes[propertyTypeAlias].IsVisible = value;
|
||||
}
|
||||
else
|
||||
{
|
||||
var tuple = new MemberTypePropertyProfileAccess(value, false);
|
||||
MemberTypePropertyTypes.Add(propertyTypeAlias, tuple);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Method to call when Entity is being saved
|
||||
/// </summary>
|
||||
/// <remarks>Created date is set and a Unique key is assigned</remarks>
|
||||
internal override void AddingEntity()
|
||||
{
|
||||
base.AddingEntity();
|
||||
|
||||
if (Key == Guid.Empty)
|
||||
Key = Guid.NewGuid();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Method to call when Entity is being updated
|
||||
/// </summary>
|
||||
/// <remarks>Modified Date is set and a new Version guid is set</remarks>
|
||||
internal override void UpdatingEntity()
|
||||
{
|
||||
base.UpdatingEntity();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
namespace Umbraco.Core.Models
|
||||
{
|
||||
/// <summary>
|
||||
/// Used to track the property types that are visible/editable on member profiles
|
||||
/// </summary>
|
||||
internal class MemberTypePropertyProfileAccess
|
||||
{
|
||||
public MemberTypePropertyProfileAccess(bool isVisible, bool isEditable)
|
||||
{
|
||||
IsVisible = isVisible;
|
||||
IsEditable = isEditable;
|
||||
}
|
||||
|
||||
public bool IsVisible { get; set; }
|
||||
public bool IsEditable { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
namespace Umbraco.Core.Models.Membership
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a user -> entity permission
|
||||
/// </summary>
|
||||
public class EntityPermission
|
||||
{
|
||||
public EntityPermission(int userId, int entityId, string[] assignedPermissions)
|
||||
{
|
||||
UserId = userId;
|
||||
EntityId = entityId;
|
||||
AssignedPermissions = assignedPermissions;
|
||||
}
|
||||
|
||||
public int UserId { get; private set; }
|
||||
public int EntityId { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// The assigned permissions for the user/entity combo
|
||||
/// </summary>
|
||||
public string[] AssignedPermissions { get; private set; }
|
||||
}
|
||||
|
||||
}
|
||||
@@ -4,9 +4,9 @@ using Umbraco.Core.Models.EntityBase;
|
||||
|
||||
namespace Umbraco.Core.Models.Membership
|
||||
{
|
||||
internal interface IMembershipUser : IMembershipUserId, IAggregateRoot
|
||||
{
|
||||
new object Id { get; set; }
|
||||
public interface IMembershipUser : IAggregateRoot
|
||||
{
|
||||
object ProviderUserKey { get; set; }
|
||||
string Username { get; set; }
|
||||
string Email { get; set; }
|
||||
string Password { get; set; }
|
||||
@@ -14,17 +14,22 @@ namespace Umbraco.Core.Models.Membership
|
||||
string PasswordAnswer { get; set; }
|
||||
string Comments { get; set; }
|
||||
bool IsApproved { get; set; }
|
||||
bool IsOnline { get; set; }
|
||||
bool IsLockedOut { get; set; }
|
||||
//Was CreationDate
|
||||
//DateTime CreateDate { get; set; }
|
||||
//LastActivityDate
|
||||
//DateTime UpdateDate { get; set; }
|
||||
DateTime LastLoginDate { get; set; }
|
||||
DateTime LastPasswordChangeDate { get; set; }
|
||||
DateTime LastLockoutDate { get; set; }
|
||||
|
||||
object ProfileId { get; set; }
|
||||
IEnumerable<object> Groups { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets the number of failed password attempts.
|
||||
/// This is the number of times the password was entered incorrectly upon login.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Alias: umbracoFailedPasswordAttemptsPropertyTypeAlias
|
||||
/// Part of the standard properties collection.
|
||||
/// </remarks>
|
||||
int FailedPasswordAttempts { get; set; }
|
||||
|
||||
//object ProfileId { get; set; }
|
||||
//IEnumerable<object> Groups { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
namespace Umbraco.Core.Models.Membership
|
||||
{
|
||||
internal interface IMembershipUserId
|
||||
{
|
||||
object ProviderUserKey { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -3,6 +3,10 @@
|
||||
/// <summary>
|
||||
/// Defines the the Profile interface
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This interface is pretty useless but has been exposed publicly from 6.x so we're stuck with it. It would make more sense
|
||||
/// if the Id was an int but since it's not people have to cast it to int all of the time!
|
||||
/// </remarks>
|
||||
public interface IProfile
|
||||
{
|
||||
object Id { get; set; }
|
||||
|
||||
@@ -7,25 +7,34 @@ namespace Umbraco.Core.Models.Membership
|
||||
/// Defines the interface for a <see cref="User"/>
|
||||
/// </summary>
|
||||
/// <remarks>Will be left internal until a proper Membership implementation is part of the roadmap</remarks>
|
||||
internal interface IUser : IMembershipUser, IUserProfile
|
||||
public interface IUser : IMembershipUser
|
||||
{
|
||||
new object Id { get; set; }
|
||||
//string Name { get; set; }
|
||||
string Name { get; set; }
|
||||
int SessionTimeout { get; set; }
|
||||
int StartContentId { get; set; }
|
||||
int StartMediaId { get; set; }
|
||||
string Language { get; set; }
|
||||
bool DefaultToLiveEditing { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets/sets the user type for the user
|
||||
/// </summary>
|
||||
IUserType UserType { get; set; }
|
||||
|
||||
bool NoConsole { get; set; }
|
||||
IUserType UserType { get; }
|
||||
string DefaultPermissions { get; set; }
|
||||
}
|
||||
/// <summary>
|
||||
/// The default permission set for the user
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Currently in umbraco each permission is a single char but with an Enumerable{string} collection this allows for flexible changes to this in the future
|
||||
/// </remarks>
|
||||
IEnumerable<string> DefaultPermissions { get; set; }
|
||||
|
||||
internal interface IUserProfile : IProfile
|
||||
{
|
||||
IEnumerable<string> AllowedSections { get; }
|
||||
void RemoveAllowedSection(string sectionAlias);
|
||||
void AddAllowedSection(string sectionAlias);
|
||||
|
||||
/// <summary>
|
||||
/// Exposes the basic profile data
|
||||
/// </summary>
|
||||
IProfile ProfileData { get; }
|
||||
}
|
||||
}
|
||||
@@ -1,13 +1,28 @@
|
||||
using System.Collections.Generic;
|
||||
using Umbraco.Core.Models.EntityBase;
|
||||
using Umbraco.Core.Persistence.Mappers;
|
||||
|
||||
namespace Umbraco.Core.Models.Membership
|
||||
{
|
||||
|
||||
internal interface IUserType : IAggregateRoot
|
||||
public interface IUserType : IAggregateRoot
|
||||
{
|
||||
/// <summary>
|
||||
/// The user type alias
|
||||
/// </summary>
|
||||
string Alias { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The user type name
|
||||
/// </summary>
|
||||
string Name { get; set; }
|
||||
string Permissions { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The set of default permissions for the user type
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// By default each permission is simply a single char but we've made this an enumerable{string} to support a more flexible permissions structure in the future.
|
||||
/// </remarks>
|
||||
IEnumerable<string> Permissions { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -1,38 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.Serialization;
|
||||
|
||||
namespace Umbraco.Core.Models.Membership
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents an Umbraco Member
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Should be internal until a proper user/membership implementation
|
||||
/// is part of the roadmap.
|
||||
/// </remarks>
|
||||
[Serializable]
|
||||
[DataContract(IsReference = true)]
|
||||
internal class Member : MemberProfile, IMembershipUser
|
||||
{
|
||||
public int Id { get; set; }
|
||||
public string Username { get; set; }
|
||||
public string Email { get; set; }
|
||||
public string Password { get; set; }
|
||||
public string PasswordQuestion { get; set; }
|
||||
public string PasswordAnswer { get; set; }
|
||||
public string Comments { get; set; }
|
||||
public bool IsApproved { get; set; }
|
||||
public bool IsOnline { get; set; }
|
||||
public bool IsLockedOut { get; set; }
|
||||
public DateTime LastLoginDate { get; set; }
|
||||
public DateTime LastPasswordChangeDate { get; set; }
|
||||
public DateTime LastLockoutDate { get; set; }
|
||||
public object ProfileId { get; set; }
|
||||
public IEnumerable<object> Groups { get; set; }
|
||||
public Guid Key { get; set; }
|
||||
public DateTime CreateDate { get; set; }
|
||||
public DateTime UpdateDate { get; set; }
|
||||
public bool HasIdentity { get; private set; }
|
||||
}
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
using System;
|
||||
using System.Runtime.Serialization;
|
||||
using Umbraco.Core.Models.EntityBase;
|
||||
|
||||
namespace Umbraco.Core.Models.Membership
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a Group for a Backoffice User
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Should be internal until a proper user/membership implementation
|
||||
/// is part of the roadmap.
|
||||
/// </remarks>
|
||||
[Serializable]
|
||||
[DataContract(IsReference = true)]
|
||||
internal class MemberGroup : Entity
|
||||
{
|
||||
//Add MemberCollection ?
|
||||
}
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
namespace Umbraco.Core.Models.Membership
|
||||
{
|
||||
internal class MemberProfile : Profile
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
using System;
|
||||
using System.Runtime.Serialization;
|
||||
using Umbraco.Core.Models.EntityBase;
|
||||
|
||||
namespace Umbraco.Core.Models.Membership
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents the Type for an Umbraco Member
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Should be internal until a proper user/membership implementation
|
||||
/// is part of the roadmap.
|
||||
/// </remarks>
|
||||
[Serializable]
|
||||
[DataContract(IsReference = true)]
|
||||
internal class MemberType : Entity
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
using System;
|
||||
using System.Web.Security;
|
||||
|
||||
namespace Umbraco.Core.Models.Membership
|
||||
{
|
||||
internal static class MembershipExtensions
|
||||
{
|
||||
internal static UmbracoMembershipMember AsConcreteMembershipUser(this IMembershipUser member, string providerName)
|
||||
{
|
||||
var membershipMember = new UmbracoMembershipMember(member, providerName);
|
||||
return membershipMember;
|
||||
}
|
||||
|
||||
internal static IMembershipUser AsIMember(this UmbracoMembershipMember membershipMember)
|
||||
{
|
||||
var member = membershipMember;
|
||||
if (member != null)
|
||||
{
|
||||
return member.Member;
|
||||
}
|
||||
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,111 +0,0 @@
|
||||
using System;
|
||||
using System.Reflection;
|
||||
using System.Runtime.Serialization;
|
||||
using Umbraco.Core.Models.EntityBase;
|
||||
|
||||
namespace Umbraco.Core.Models.Membership
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a Profile which is shared between Members and Users
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
[DataContract(IsReference = true)]
|
||||
internal class Profile : TracksChangesEntityBase, IProfile, IRememberBeingDirty
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="Profile"/> class.
|
||||
/// </summary>
|
||||
public Profile()
|
||||
{
|
||||
ProviderUserKeyType = typeof(int);
|
||||
}
|
||||
|
||||
public Profile(object id, string name)
|
||||
{
|
||||
ProviderUserKeyType = typeof(int);
|
||||
Id = id;
|
||||
Name = name;
|
||||
}
|
||||
|
||||
private object _id;
|
||||
private string _name;
|
||||
private Type _userTypeKey;
|
||||
|
||||
private static readonly PropertyInfo IdSelector = ExpressionHelper.GetPropertyInfo<Profile, object>(x => x.Id);
|
||||
private static readonly PropertyInfo NameSelector = ExpressionHelper.GetPropertyInfo<Profile, string>(x => x.Name);
|
||||
private static readonly PropertyInfo UserTypeKeySelector = ExpressionHelper.GetPropertyInfo<Profile, Type>(x => x.ProviderUserKeyType);
|
||||
|
||||
[DataMember]
|
||||
public object Id
|
||||
{
|
||||
get
|
||||
{
|
||||
return _id;
|
||||
}
|
||||
set
|
||||
{
|
||||
SetPropertyValueAndDetectChanges(o =>
|
||||
{
|
||||
_id = value;
|
||||
return _id;
|
||||
}, _id, IdSelector);
|
||||
}
|
||||
}
|
||||
|
||||
[DataMember]
|
||||
public string Name
|
||||
{
|
||||
get
|
||||
{
|
||||
return _name;
|
||||
}
|
||||
set
|
||||
{
|
||||
SetPropertyValueAndDetectChanges(o =>
|
||||
{
|
||||
_name = value;
|
||||
return _name;
|
||||
}, _name, NameSelector);
|
||||
}
|
||||
}
|
||||
|
||||
[IgnoreDataMember]
|
||||
public virtual object ProviderUserKey
|
||||
{
|
||||
get { throw new System.NotImplementedException(); }
|
||||
set { throw new System.NotImplementedException(); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the type of the provider user key.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The type of the provider user key.
|
||||
/// </value>
|
||||
[IgnoreDataMember]
|
||||
internal Type ProviderUserKeyType
|
||||
{
|
||||
get
|
||||
{
|
||||
return _userTypeKey;
|
||||
}
|
||||
private set
|
||||
{
|
||||
SetPropertyValueAndDetectChanges(o =>
|
||||
{
|
||||
_userTypeKey = value;
|
||||
return _userTypeKey;
|
||||
}, _userTypeKey, UserTypeKeySelector);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the type of the provider user key.
|
||||
/// </summary>
|
||||
/// <param name="type">The type.</param>
|
||||
internal void SetProviderUserKeyType(Type type)
|
||||
{
|
||||
ProviderUserKeyType = type;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,127 @@
|
||||
using System;
|
||||
using System.Web.Security;
|
||||
|
||||
namespace Umbraco.Core.Models.Membership
|
||||
{
|
||||
|
||||
internal class UmbracoMembershipMember : MembershipUser
|
||||
{
|
||||
private readonly IMembershipUser _member;
|
||||
private readonly string _userName;
|
||||
private readonly object _providerUserKey;
|
||||
private readonly string _passwordQuestion;
|
||||
private readonly bool _isLockedOut;
|
||||
private readonly DateTime _lastLockoutDate;
|
||||
private readonly DateTime _creationDate;
|
||||
private DateTime _lastLoginDate;
|
||||
private readonly DateTime _lastPasswordChangedDate;
|
||||
private readonly string _providerName;
|
||||
private string _email;
|
||||
private string _comment;
|
||||
private bool _isApproved;
|
||||
private DateTime _lastActivityDate;
|
||||
|
||||
//NOTE: We are only overriding the properties that matter, we don't override things like IsOnline since that is handled with the sub-class and the membership providers.
|
||||
|
||||
//NOTE: We are not calling the base constructor which will validate that a provider with the specified name exists which causes issues with unit tests. The ctor
|
||||
// validation for that doesn't need to be there anyways (have checked the source).
|
||||
public UmbracoMembershipMember(IMembershipUser member, string providerName)
|
||||
{
|
||||
_member = member;
|
||||
//NOTE: We are copying the values here so that everything is consistent with how the underlying built-in ASP.Net membership user
|
||||
// handles data! We don't want to do anything differently there but since we cannot use their ctor we'll need to handle this logic ourselves.
|
||||
if (member.Username != null)
|
||||
_userName = member.Username.Trim();
|
||||
if (member.Email != null)
|
||||
_email = member.Email.Trim();
|
||||
if (member.PasswordQuestion != null)
|
||||
_passwordQuestion = member.PasswordQuestion.Trim();
|
||||
_providerName = providerName;
|
||||
_providerUserKey = member.ProviderUserKey;
|
||||
_comment = member.Comments;
|
||||
_isApproved = member.IsApproved;
|
||||
_isLockedOut = member.IsLockedOut;
|
||||
_creationDate = member.CreateDate.ToUniversalTime();
|
||||
_lastLoginDate = member.LastLoginDate.ToUniversalTime();
|
||||
//TODO: We currently don't really have any place to store this data!!
|
||||
_lastActivityDate = member.LastLoginDate.ToUniversalTime();
|
||||
_lastPasswordChangedDate = member.LastPasswordChangeDate.ToUniversalTime();
|
||||
_lastLockoutDate = member.LastLockoutDate.ToUniversalTime();
|
||||
}
|
||||
|
||||
internal IMembershipUser Member
|
||||
{
|
||||
get { return _member; }
|
||||
}
|
||||
|
||||
public override string UserName
|
||||
{
|
||||
get { return _userName; }
|
||||
}
|
||||
|
||||
public override object ProviderUserKey
|
||||
{
|
||||
get { return _providerUserKey; }
|
||||
}
|
||||
|
||||
public override string Email
|
||||
{
|
||||
get { return _email; }
|
||||
set { _email = value; }
|
||||
}
|
||||
|
||||
public override string PasswordQuestion
|
||||
{
|
||||
get { return _passwordQuestion; }
|
||||
}
|
||||
|
||||
public override string Comment
|
||||
{
|
||||
get { return _comment; }
|
||||
set { _comment = value; }
|
||||
}
|
||||
|
||||
public override bool IsApproved
|
||||
{
|
||||
get { return _isApproved; }
|
||||
set { _isApproved = value; }
|
||||
}
|
||||
|
||||
public override bool IsLockedOut
|
||||
{
|
||||
get { return _isLockedOut; }
|
||||
}
|
||||
|
||||
public override DateTime LastLockoutDate
|
||||
{
|
||||
get { return _lastLockoutDate; }
|
||||
}
|
||||
|
||||
public override DateTime CreationDate
|
||||
{
|
||||
get { return _creationDate; }
|
||||
}
|
||||
|
||||
public override DateTime LastLoginDate
|
||||
{
|
||||
get { return _lastLoginDate; }
|
||||
set { _lastLoginDate = value; }
|
||||
}
|
||||
|
||||
public override DateTime LastActivityDate
|
||||
{
|
||||
get { return _lastActivityDate; }
|
||||
set { _lastActivityDate = value; }
|
||||
}
|
||||
|
||||
public override DateTime LastPasswordChangedDate
|
||||
{
|
||||
get { return _lastPasswordChangedDate; }
|
||||
}
|
||||
|
||||
public override string ProviderName
|
||||
{
|
||||
get { return _providerName; }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,11 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Collections.Specialized;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Runtime.Serialization;
|
||||
using Umbraco.Core.Configuration;
|
||||
using Umbraco.Core.Models.EntityBase;
|
||||
using Umbraco.Core.Persistence.Mappers;
|
||||
|
||||
@@ -16,98 +20,498 @@ namespace Umbraco.Core.Models.Membership
|
||||
/// </remarks>
|
||||
[Serializable]
|
||||
[DataContract(IsReference = true)]
|
||||
internal class User : UserProfile, IUser
|
||||
public class User : TracksChangesEntityBase, IUser
|
||||
{
|
||||
private bool _hasIdentity;
|
||||
|
||||
public User(IUserType userType)
|
||||
{
|
||||
Groups = new List<object> { userType };
|
||||
if (userType == null) throw new ArgumentNullException("userType");
|
||||
|
||||
_userType = userType;
|
||||
//Groups = new List<object> { userType };
|
||||
SessionTimeout = 60;
|
||||
_sectionCollection = new ObservableCollection<string>();
|
||||
_addedSections = new List<string>();
|
||||
_removedSections = new List<string>();
|
||||
_language = GlobalSettings.DefaultUILanguage;
|
||||
_sectionCollection.CollectionChanged += SectionCollectionChanged;
|
||||
_isApproved = true;
|
||||
_isLockedOut = false;
|
||||
_startContentId = -1;
|
||||
_startMediaId = -1;
|
||||
}
|
||||
|
||||
public User(string name, string email, string username, string password, IUserType userType)
|
||||
: this(userType)
|
||||
{
|
||||
_name = name;
|
||||
_email = email;
|
||||
_username = username;
|
||||
_password = password;
|
||||
_isApproved = true;
|
||||
_isLockedOut = false;
|
||||
_startContentId = -1;
|
||||
_startMediaId = -1;
|
||||
}
|
||||
|
||||
private IUserType _userType;
|
||||
private bool _hasIdentity;
|
||||
private int _id;
|
||||
private string _name;
|
||||
private Type _userTypeKey;
|
||||
private readonly List<string> _addedSections;
|
||||
private readonly List<string> _removedSections;
|
||||
private readonly ObservableCollection<string> _sectionCollection;
|
||||
private int _sessionTimeout;
|
||||
private int _startContentId;
|
||||
private int _startMediaId;
|
||||
|
||||
private string _username;
|
||||
private string _email;
|
||||
private string _password;
|
||||
private bool _isApproved;
|
||||
private bool _isLockedOut;
|
||||
private string _language;
|
||||
private IEnumerable<string> _defaultPermissions;
|
||||
private bool _defaultToLiveEditing;
|
||||
|
||||
private static readonly PropertyInfo SessionTimeoutSelector = ExpressionHelper.GetPropertyInfo<User, int>(x => x.SessionTimeout);
|
||||
private static readonly PropertyInfo StartContentIdSelector = ExpressionHelper.GetPropertyInfo<User, int>(x => x.StartContentId);
|
||||
private static readonly PropertyInfo StartMediaIdSelector = ExpressionHelper.GetPropertyInfo<User, int>(x => x.StartMediaId);
|
||||
private static readonly PropertyInfo AllowedSectionsSelector = ExpressionHelper.GetPropertyInfo<User, IEnumerable<string>>(x => x.AllowedSections);
|
||||
private static readonly PropertyInfo IdSelector = ExpressionHelper.GetPropertyInfo<User, object>(x => x.Id);
|
||||
private static readonly PropertyInfo NameSelector = ExpressionHelper.GetPropertyInfo<User, string>(x => x.Name);
|
||||
private static readonly PropertyInfo UserTypeKeySelector = ExpressionHelper.GetPropertyInfo<User, Type>(x => x.ProviderUserKeyType);
|
||||
|
||||
private static readonly PropertyInfo UsernameSelector = ExpressionHelper.GetPropertyInfo<User, string>(x => x.Username);
|
||||
private static readonly PropertyInfo EmailSelector = ExpressionHelper.GetPropertyInfo<User, string>(x => x.Email);
|
||||
private static readonly PropertyInfo PasswordSelector = ExpressionHelper.GetPropertyInfo<User, string>(x => x.Password);
|
||||
private static readonly PropertyInfo IsLockedOutSelector = ExpressionHelper.GetPropertyInfo<User, bool>(x => x.IsLockedOut);
|
||||
private static readonly PropertyInfo IsApprovedSelector = ExpressionHelper.GetPropertyInfo<User, bool>(x => x.IsApproved);
|
||||
private static readonly PropertyInfo LanguageSelector = ExpressionHelper.GetPropertyInfo<User, string>(x => x.Language);
|
||||
private static readonly PropertyInfo DefaultPermissionsSelector = ExpressionHelper.GetPropertyInfo<User, IEnumerable<string>>(x => x.DefaultPermissions);
|
||||
private static readonly PropertyInfo DefaultToLiveEditingSelector = ExpressionHelper.GetPropertyInfo<User, bool>(x => x.DefaultToLiveEditing);
|
||||
private static readonly PropertyInfo HasIdentitySelector = ExpressionHelper.GetPropertyInfo<User, bool>(x => x.HasIdentity);
|
||||
private static readonly PropertyInfo UserTypeSelector = ExpressionHelper.GetPropertyInfo<User, IUserType>(x => x.UserType);
|
||||
|
||||
#region Implementation of IEntity
|
||||
|
||||
[IgnoreDataMember]
|
||||
public bool HasIdentity { get { return Id != null || _hasIdentity; } }
|
||||
|
||||
[IgnoreDataMember]
|
||||
int IEntity.Id
|
||||
public bool HasIdentity
|
||||
{
|
||||
get
|
||||
{
|
||||
return int.Parse(base.Id.ToString());
|
||||
return _hasIdentity;
|
||||
}
|
||||
set
|
||||
protected set
|
||||
{
|
||||
base.Id = value;
|
||||
_hasIdentity = true;
|
||||
SetPropertyValueAndDetectChanges(o =>
|
||||
{
|
||||
_hasIdentity = value;
|
||||
return _hasIdentity;
|
||||
}, _hasIdentity, HasIdentitySelector);
|
||||
}
|
||||
}
|
||||
|
||||
[DataMember]
|
||||
public int Id
|
||||
{
|
||||
get
|
||||
{
|
||||
return _id;
|
||||
}
|
||||
set
|
||||
{
|
||||
SetPropertyValueAndDetectChanges(o =>
|
||||
{
|
||||
_id = value;
|
||||
HasIdentity = true; //set the has Identity
|
||||
return _id;
|
||||
}, _id, IdSelector);
|
||||
}
|
||||
}
|
||||
|
||||
//this doesn't get used
|
||||
[IgnoreDataMember]
|
||||
public Guid Key { get; set; }
|
||||
|
||||
#endregion
|
||||
|
||||
#region Implementation of IMembershipUser
|
||||
|
||||
[IgnoreDataMember]
|
||||
public object ProviderUserKey
|
||||
{
|
||||
get { return Id; }
|
||||
set { throw new NotSupportedException("Cannot set the provider user key for a user"); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the type of the provider user key.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The type of the provider user key.
|
||||
/// </value>
|
||||
[IgnoreDataMember]
|
||||
internal Type ProviderUserKeyType
|
||||
{
|
||||
get
|
||||
{
|
||||
return _userTypeKey;
|
||||
}
|
||||
private set
|
||||
{
|
||||
SetPropertyValueAndDetectChanges(o =>
|
||||
{
|
||||
_userTypeKey = value;
|
||||
return _userTypeKey;
|
||||
}, _userTypeKey, UserTypeKeySelector);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the type of the provider user key.
|
||||
/// </summary>
|
||||
/// <param name="type">The type.</param>
|
||||
internal void SetProviderUserKeyType(Type type)
|
||||
{
|
||||
ProviderUserKeyType = type;
|
||||
}
|
||||
|
||||
[DataMember]
|
||||
public string Username { get; set; }
|
||||
public string Username
|
||||
{
|
||||
get { return _username; }
|
||||
set
|
||||
{
|
||||
SetPropertyValueAndDetectChanges(o =>
|
||||
{
|
||||
_username = value;
|
||||
return _username;
|
||||
}, _username, UsernameSelector);
|
||||
}
|
||||
}
|
||||
[DataMember]
|
||||
public string Email { get; set; }
|
||||
public string Email
|
||||
{
|
||||
get { return _email; }
|
||||
set
|
||||
{
|
||||
SetPropertyValueAndDetectChanges(o =>
|
||||
{
|
||||
_email = value;
|
||||
return _email;
|
||||
}, _email, EmailSelector);
|
||||
}
|
||||
}
|
||||
[DataMember]
|
||||
public string Password { get; set; }
|
||||
public string Password
|
||||
{
|
||||
get { return _password; }
|
||||
set
|
||||
{
|
||||
SetPropertyValueAndDetectChanges(o =>
|
||||
{
|
||||
_password = value;
|
||||
return _password;
|
||||
}, _password, PasswordSelector);
|
||||
}
|
||||
}
|
||||
|
||||
[DataMember]
|
||||
public bool IsApproved
|
||||
{
|
||||
get { return _isApproved; }
|
||||
set
|
||||
{
|
||||
SetPropertyValueAndDetectChanges(o =>
|
||||
{
|
||||
_isApproved = value;
|
||||
return _isApproved;
|
||||
}, _isApproved, IsApprovedSelector);
|
||||
}
|
||||
}
|
||||
|
||||
[DataMember]
|
||||
public bool IsLockedOut
|
||||
{
|
||||
get { return _isLockedOut; }
|
||||
set
|
||||
{
|
||||
SetPropertyValueAndDetectChanges(o =>
|
||||
{
|
||||
_isLockedOut = value;
|
||||
return _isLockedOut;
|
||||
}, _isLockedOut, IsLockedOutSelector);
|
||||
}
|
||||
}
|
||||
|
||||
//TODO: Figure out how to support all of this! - we cannot have NotImplementedExceptions because these get used by the IMembershipMemberService<IUser> service so
|
||||
// we'll just have them as generic get/set which don't interact with the db.
|
||||
|
||||
[IgnoreDataMember]
|
||||
public string PasswordQuestion { get; set; }
|
||||
[DataMember]
|
||||
[IgnoreDataMember]
|
||||
public string PasswordAnswer { get; set; }
|
||||
[DataMember]
|
||||
[IgnoreDataMember]
|
||||
public string Comments { get; set; }
|
||||
[DataMember]
|
||||
public bool IsApproved { get; set; }
|
||||
[DataMember]
|
||||
public bool IsOnline { get; set; }
|
||||
[DataMember]
|
||||
public bool IsLockedOut { get; set; }
|
||||
[DataMember]
|
||||
[IgnoreDataMember]
|
||||
public DateTime CreateDate { get; set; }
|
||||
[DataMember]
|
||||
[IgnoreDataMember]
|
||||
public DateTime UpdateDate { get; set; }
|
||||
[DataMember]
|
||||
[IgnoreDataMember]
|
||||
public DateTime LastLoginDate { get; set; }
|
||||
[DataMember]
|
||||
[IgnoreDataMember]
|
||||
public DateTime LastPasswordChangeDate { get; set; }
|
||||
[DataMember]
|
||||
[IgnoreDataMember]
|
||||
public DateTime LastLockoutDate { get; set; }
|
||||
|
||||
[DataMember]
|
||||
public object ProfileId { get; set; }
|
||||
[DataMember]
|
||||
public IEnumerable<object> Groups { get; set; }
|
||||
|
||||
[IgnoreDataMember]
|
||||
public int FailedPasswordAttempts { get; set; }
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
#region Implementation of IUser
|
||||
|
||||
[DataMember]
|
||||
public string Language { get; set; }
|
||||
public string Name
|
||||
{
|
||||
get { return _name; }
|
||||
set
|
||||
{
|
||||
SetPropertyValueAndDetectChanges(o =>
|
||||
{
|
||||
_name = value;
|
||||
return _name;
|
||||
}, _name, NameSelector);
|
||||
}
|
||||
}
|
||||
|
||||
public IEnumerable<string> AllowedSections
|
||||
{
|
||||
get { return _sectionCollection; }
|
||||
}
|
||||
|
||||
public void RemoveAllowedSection(string sectionAlias)
|
||||
{
|
||||
_sectionCollection.Remove(sectionAlias);
|
||||
}
|
||||
|
||||
public void AddAllowedSection(string sectionAlias)
|
||||
{
|
||||
if (_sectionCollection.Contains(sectionAlias) == false)
|
||||
{
|
||||
_sectionCollection.Add(sectionAlias);
|
||||
}
|
||||
}
|
||||
|
||||
public IProfile ProfileData
|
||||
{
|
||||
get { return new UserProfile(this); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Used internally to check if we need to add a section in the repository to the db
|
||||
/// </summary>
|
||||
internal IEnumerable<string> AddedSections
|
||||
{
|
||||
get { return _addedSections; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Used internally to check if we need to remove a section in the repository to the db
|
||||
/// </summary>
|
||||
internal IEnumerable<string> RemovedSections
|
||||
{
|
||||
get { return _removedSections; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the session timeout.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The session timeout.
|
||||
/// </value>
|
||||
[DataMember]
|
||||
public string DefaultPermissions { get; set; }
|
||||
public int SessionTimeout
|
||||
{
|
||||
get { return _sessionTimeout; }
|
||||
set
|
||||
{
|
||||
SetPropertyValueAndDetectChanges(o =>
|
||||
{
|
||||
_sessionTimeout = value;
|
||||
return _sessionTimeout;
|
||||
}, _sessionTimeout, SessionTimeoutSelector);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the start content id.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The start content id.
|
||||
/// </value>
|
||||
[DataMember]
|
||||
public int StartContentId
|
||||
{
|
||||
get { return _startContentId; }
|
||||
set
|
||||
{
|
||||
SetPropertyValueAndDetectChanges(o =>
|
||||
{
|
||||
_startContentId = value;
|
||||
return _startContentId;
|
||||
}, _startContentId, StartContentIdSelector);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the start media id.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The start media id.
|
||||
/// </value>
|
||||
[DataMember]
|
||||
public int StartMediaId
|
||||
{
|
||||
get { return _startMediaId; }
|
||||
set
|
||||
{
|
||||
SetPropertyValueAndDetectChanges(o =>
|
||||
{
|
||||
_startMediaId = value;
|
||||
return _startMediaId;
|
||||
}, _startMediaId, StartMediaIdSelector);
|
||||
}
|
||||
}
|
||||
|
||||
[DataMember]
|
||||
public bool DefaultToLiveEditing { get; set; }
|
||||
public string Language
|
||||
{
|
||||
get { return _language; }
|
||||
set
|
||||
{
|
||||
SetPropertyValueAndDetectChanges(o =>
|
||||
{
|
||||
_language = value;
|
||||
return _language;
|
||||
}, _language, LanguageSelector);
|
||||
}
|
||||
}
|
||||
|
||||
[DataMember]
|
||||
public bool NoConsole { get; set; }
|
||||
public IEnumerable<string> DefaultPermissions
|
||||
{
|
||||
get { return _defaultPermissions; }
|
||||
set
|
||||
{
|
||||
SetPropertyValueAndDetectChanges(o =>
|
||||
{
|
||||
_defaultPermissions = value;
|
||||
return _defaultPermissions;
|
||||
}, _defaultPermissions, DefaultPermissionsSelector);
|
||||
}
|
||||
}
|
||||
|
||||
[IgnoreDataMember]
|
||||
internal bool DefaultToLiveEditing
|
||||
{
|
||||
get { return _defaultToLiveEditing; }
|
||||
set
|
||||
{
|
||||
SetPropertyValueAndDetectChanges(o =>
|
||||
{
|
||||
_defaultToLiveEditing = value;
|
||||
return _defaultToLiveEditing;
|
||||
}, _defaultToLiveEditing, DefaultToLiveEditingSelector);
|
||||
}
|
||||
}
|
||||
|
||||
[IgnoreDataMember]
|
||||
public IUserType UserType
|
||||
{
|
||||
get
|
||||
get { return _userType; }
|
||||
set
|
||||
{
|
||||
var type = Groups.FirstOrDefault();
|
||||
return type as IUserType;
|
||||
if (value.HasIdentity == false)
|
||||
{
|
||||
throw new InvalidOperationException("Cannot assign a User Type that has not been persisted");
|
||||
}
|
||||
|
||||
SetPropertyValueAndDetectChanges(o =>
|
||||
{
|
||||
_userType = value;
|
||||
return _userType;
|
||||
}, _userType, UserTypeSelector);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// Whenever resetting occurs, clear the remembered add/removed collections, even if
|
||||
/// rememberPreviouslyChangedProperties is true, the AllowedSections property will still
|
||||
/// be flagged as dirty.
|
||||
/// </summary>
|
||||
/// <param name="rememberPreviouslyChangedProperties"></param>
|
||||
internal override void ResetDirtyProperties(bool rememberPreviouslyChangedProperties)
|
||||
{
|
||||
_addedSections.Clear();
|
||||
_removedSections.Clear();
|
||||
base.ResetDirtyProperties(rememberPreviouslyChangedProperties);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handles the collection changed event in order for us to flag the AllowedSections property as changed
|
||||
/// </summary>
|
||||
/// <param name="sender"></param>
|
||||
/// <param name="e"></param>
|
||||
void SectionCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
|
||||
{
|
||||
OnPropertyChanged(AllowedSectionsSelector);
|
||||
|
||||
if (e.Action == NotifyCollectionChangedAction.Add)
|
||||
{
|
||||
//remove from the removed/added sections (since people could add/remove all they want in one request)
|
||||
_removedSections.RemoveAll(s => s == e.NewItems.Cast<string>().First());
|
||||
_addedSections.RemoveAll(s => s == e.NewItems.Cast<string>().First());
|
||||
|
||||
//add to the added sections
|
||||
_addedSections.Add(e.NewItems.Cast<string>().First());
|
||||
}
|
||||
else if (e.Action == NotifyCollectionChangedAction.Remove)
|
||||
{
|
||||
//remove from the removed/added sections (since people could add/remove all they want in one request)
|
||||
_removedSections.RemoveAll(s => s == e.OldItems.Cast<string>().First());
|
||||
_addedSections.RemoveAll(s => s == e.OldItems.Cast<string>().First());
|
||||
|
||||
//add to the added sections
|
||||
_removedSections.Add(e.OldItems.Cast<string>().First());
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Internal class used to wrap the user in a profile
|
||||
/// </summary>
|
||||
private class UserProfile : IProfile
|
||||
{
|
||||
private readonly IUser _user;
|
||||
|
||||
public UserProfile(IUser user)
|
||||
{
|
||||
_user = user;
|
||||
}
|
||||
|
||||
public object Id
|
||||
{
|
||||
get { return _user.Id; }
|
||||
set { _user.Id = (int)value; }
|
||||
}
|
||||
|
||||
public string Name
|
||||
{
|
||||
get { return _user.Name; }
|
||||
set { _user.Name = value; }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,188 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Collections.Specialized;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Runtime.Serialization;
|
||||
|
||||
namespace Umbraco.Core.Models.Membership
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents the Profile implementation for a backoffice User
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Should be internal until a proper user/membership implementation
|
||||
/// is part of the roadmap.
|
||||
/// </remarks>
|
||||
[Serializable]
|
||||
[DataContract(IsReference = true)]
|
||||
internal class UserProfile : Profile, IUserProfile
|
||||
{
|
||||
public UserProfile()
|
||||
{
|
||||
SessionTimeout = 60;
|
||||
_sectionCollection = new ObservableCollection<string>();
|
||||
_addedSections = new List<string>();
|
||||
_removedSections = new List<string>();
|
||||
_sectionCollection.CollectionChanged += SectionCollectionChanged;
|
||||
}
|
||||
|
||||
private readonly List<string> _addedSections;
|
||||
private readonly List<string> _removedSections;
|
||||
private readonly ObservableCollection<string> _sectionCollection;
|
||||
private int _sessionTimeout;
|
||||
private int _startContentId;
|
||||
private int _startMediaId;
|
||||
|
||||
private static readonly PropertyInfo SessionTimeoutSelector = ExpressionHelper.GetPropertyInfo<UserProfile, int>(x => x.SessionTimeout);
|
||||
private static readonly PropertyInfo StartContentIdSelector = ExpressionHelper.GetPropertyInfo<UserProfile, int>(x => x.StartContentId);
|
||||
private static readonly PropertyInfo StartMediaIdSelector = ExpressionHelper.GetPropertyInfo<UserProfile, int>(x => x.StartMediaId);
|
||||
private static readonly PropertyInfo AllowedSectionsSelector = ExpressionHelper.GetPropertyInfo<UserProfile, IEnumerable<string>>(x => x.AllowedSections);
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the session timeout.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The session timeout.
|
||||
/// </value>
|
||||
[DataMember]
|
||||
public int SessionTimeout
|
||||
{
|
||||
get
|
||||
{
|
||||
return _sessionTimeout;
|
||||
}
|
||||
set
|
||||
{
|
||||
SetPropertyValueAndDetectChanges(o =>
|
||||
{
|
||||
_sessionTimeout = value;
|
||||
return _sessionTimeout;
|
||||
}, _sessionTimeout, SessionTimeoutSelector);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the start content id.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The start content id.
|
||||
/// </value>
|
||||
[DataMember]
|
||||
public int StartContentId
|
||||
{
|
||||
get
|
||||
{
|
||||
return _startContentId;
|
||||
}
|
||||
set
|
||||
{
|
||||
SetPropertyValueAndDetectChanges(o =>
|
||||
{
|
||||
_startContentId = value;
|
||||
return _startContentId;
|
||||
}, _startContentId, StartContentIdSelector);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the start media id.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The start media id.
|
||||
/// </value>
|
||||
[DataMember]
|
||||
public int StartMediaId
|
||||
{
|
||||
get
|
||||
{
|
||||
return _startMediaId;
|
||||
}
|
||||
set
|
||||
{
|
||||
SetPropertyValueAndDetectChanges(o =>
|
||||
{
|
||||
_startMediaId = value;
|
||||
return _startMediaId;
|
||||
}, _startMediaId, StartMediaIdSelector);
|
||||
}
|
||||
}
|
||||
|
||||
public IEnumerable<string> AllowedSections
|
||||
{
|
||||
get { return _sectionCollection; }
|
||||
}
|
||||
|
||||
public void RemoveAllowedSection(string sectionAlias)
|
||||
{
|
||||
_sectionCollection.Remove(sectionAlias);
|
||||
}
|
||||
|
||||
public void AddAllowedSection(string sectionAlias)
|
||||
{
|
||||
if (!_sectionCollection.Contains(sectionAlias))
|
||||
{
|
||||
_sectionCollection.Add(sectionAlias);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Whenever resetting occurs, clear the remembered add/removed collections, even if
|
||||
/// rememberPreviouslyChangedProperties is true, the AllowedSections property will still
|
||||
/// be flagged as dirty.
|
||||
/// </summary>
|
||||
/// <param name="rememberPreviouslyChangedProperties"></param>
|
||||
internal override void ResetDirtyProperties(bool rememberPreviouslyChangedProperties)
|
||||
{
|
||||
_addedSections.Clear();
|
||||
_removedSections.Clear();
|
||||
base.ResetDirtyProperties(rememberPreviouslyChangedProperties);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Used internally to check if we need to add a section in the repository to the db
|
||||
/// </summary>
|
||||
internal IEnumerable<string> AddedSections
|
||||
{
|
||||
get { return _addedSections; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Used internally to check if we need to remove a section in the repository to the db
|
||||
/// </summary>
|
||||
internal IEnumerable<string> RemovedSections
|
||||
{
|
||||
get { return _removedSections; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handles the collection changed event in order for us to flag the AllowedSections property as changed
|
||||
/// </summary>
|
||||
/// <param name="sender"></param>
|
||||
/// <param name="e"></param>
|
||||
void SectionCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
|
||||
{
|
||||
OnPropertyChanged(AllowedSectionsSelector);
|
||||
|
||||
if (e.Action == NotifyCollectionChangedAction.Add)
|
||||
{
|
||||
//remove from the removed/added sections (since people could add/remove all they want in one request)
|
||||
_removedSections.RemoveAll(s => s == e.NewItems.Cast<string>().First());
|
||||
_addedSections.RemoveAll(s => s == e.NewItems.Cast<string>().First());
|
||||
|
||||
//add to the added sections
|
||||
_addedSections.Add(e.NewItems.Cast<string>().First());
|
||||
}
|
||||
else if (e.Action == NotifyCollectionChangedAction.Remove)
|
||||
{
|
||||
//remove from the removed/added sections (since people could add/remove all they want in one request)
|
||||
_removedSections.RemoveAll(s => s == e.OldItems.Cast<string>().First());
|
||||
_addedSections.RemoveAll(s => s == e.OldItems.Cast<string>().First());
|
||||
|
||||
//add to the added sections
|
||||
_removedSections.Add(e.OldItems.Cast<string>().First());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.Serialization;
|
||||
using Umbraco.Core.Models.EntityBase;
|
||||
using Umbraco.Core.Persistence.Mappers;
|
||||
@@ -22,7 +23,13 @@ namespace Umbraco.Core.Models.Membership
|
||||
[DataMember]
|
||||
public string Name { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The set of default permissions for the user type
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// By default each permission is simply a single char but we've made this an enumerable{string} to support a more flexible permissions structure in the future.
|
||||
/// </remarks>
|
||||
[DataMember]
|
||||
public string Permissions { get; set; }
|
||||
public IEnumerable<string> Permissions { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace Umbraco.Core.Models
|
||||
{
|
||||
internal class Notification
|
||||
{
|
||||
public Notification(int entityId, int userId, string action, Guid entityType)
|
||||
{
|
||||
EntityId = entityId;
|
||||
UserId = userId;
|
||||
Action = action;
|
||||
EntityType = entityType;
|
||||
}
|
||||
|
||||
public int EntityId { get; private set; }
|
||||
public int UserId { get; private set; }
|
||||
public string Action { get; private set; }
|
||||
public Guid EntityType { get; private set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.Serialization;
|
||||
|
||||
namespace Umbraco.Core.Models
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a paged result for a model collection
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
[DataContract(Name = "pagedCollection", Namespace = "")]
|
||||
public class PagedResult<T>
|
||||
{
|
||||
public PagedResult(long totalItems, long pageNumber, long pageSize)
|
||||
{
|
||||
TotalItems = totalItems;
|
||||
PageNumber = pageNumber;
|
||||
PageSize = pageSize;
|
||||
|
||||
if (pageSize > 0)
|
||||
{
|
||||
TotalPages = (long) Math.Ceiling(totalItems/(Decimal) pageSize);
|
||||
}
|
||||
else
|
||||
{
|
||||
TotalPages = 1;
|
||||
}
|
||||
}
|
||||
|
||||
[DataMember(Name = "pageNumber")]
|
||||
public long PageNumber { get; private set; }
|
||||
|
||||
[DataMember(Name = "pageSize")]
|
||||
public long PageSize { get; private set; }
|
||||
|
||||
[DataMember(Name = "totalPages")]
|
||||
public long TotalPages { get; private set; }
|
||||
|
||||
[DataMember(Name = "totalItems")]
|
||||
public long TotalItems { get; private set; }
|
||||
|
||||
[DataMember(Name = "items")]
|
||||
public IEnumerable<T> Items { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Calculates the skip size based on the paged parameters specified
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Returns 0 if the page number or page size is zero
|
||||
/// </remarks>
|
||||
internal int SkipSize
|
||||
{
|
||||
get
|
||||
{
|
||||
if (PageNumber > 0 && PageSize > 0)
|
||||
{
|
||||
return Convert.ToInt32((PageNumber - 1)*PageSize);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
namespace Umbraco.Core.Models
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a stored pre-value field value
|
||||
/// </summary>
|
||||
public class PreValue
|
||||
{
|
||||
public PreValue(int id, string value)
|
||||
{
|
||||
Value = value;
|
||||
Id = id;
|
||||
}
|
||||
|
||||
public PreValue(string value)
|
||||
{
|
||||
Value = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The value stored for the pre-value field
|
||||
/// </summary>
|
||||
public string Value { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// The database id for the pre-value field value
|
||||
/// </summary>
|
||||
public int Id { get; private set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,86 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace Umbraco.Core.Models
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents the pre-value data for a DataType
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Due to the legacy nature of the data that can be stored for pre-values, we have this class which encapsulates the 2 different
|
||||
/// ways that pre-values are stored: A string array or a Dictionary.
|
||||
///
|
||||
/// Most legacy property editors won't support the dictionary format but new property editors should always use the dictionary format.
|
||||
/// In order to get overrideable pre-values working we need a dictionary since we'll have to reference a pre-value by a key.
|
||||
/// </remarks>
|
||||
public class PreValueCollection
|
||||
{
|
||||
private IDictionary<string, PreValue> _preValuesAsDictionary;
|
||||
private IEnumerable<PreValue> _preValuesAsArray;
|
||||
public IEnumerable<PreValue> PreValuesAsArray
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_preValuesAsArray == null)
|
||||
{
|
||||
throw new InvalidOperationException("The current pre-value collection is dictionary based, use the PreValuesAsDictionary property instead");
|
||||
}
|
||||
return _preValuesAsArray;
|
||||
}
|
||||
set { _preValuesAsArray = value; }
|
||||
}
|
||||
|
||||
public IDictionary<string, PreValue> PreValuesAsDictionary
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_preValuesAsDictionary == null)
|
||||
{
|
||||
throw new InvalidOperationException("The current pre-value collection is array based, use the PreValuesAsArray property instead");
|
||||
}
|
||||
return _preValuesAsDictionary;
|
||||
}
|
||||
set { _preValuesAsDictionary = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Check if it is a dictionary based collection
|
||||
/// </summary>
|
||||
public bool IsDictionaryBased
|
||||
{
|
||||
get { return _preValuesAsDictionary != null; }
|
||||
}
|
||||
|
||||
public PreValueCollection(IEnumerable<PreValue> preVals)
|
||||
{
|
||||
_preValuesAsArray = preVals;
|
||||
}
|
||||
|
||||
public PreValueCollection(IDictionary<string, PreValue> preVals)
|
||||
{
|
||||
_preValuesAsDictionary = preVals;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Regardless of how the pre-values are stored this will return as a dictionary, it will convert an array based to a dictionary
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public IDictionary<string, PreValue> FormatAsDictionary()
|
||||
{
|
||||
if (IsDictionaryBased)
|
||||
{
|
||||
return PreValuesAsDictionary;
|
||||
}
|
||||
|
||||
//it's an array so need to format it, the alias will just be an iteration
|
||||
var result = new Dictionary<string, PreValue>();
|
||||
var asArray = PreValuesAsArray.ToArray();
|
||||
for (var i = 0; i < asArray.Length; i++)
|
||||
{
|
||||
result.Add(i.ToInvariantString(), asArray[i]);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -43,7 +43,7 @@ namespace Umbraco.Core.Models
|
||||
|
||||
private static readonly PropertyInfo ValueSelector = ExpressionHelper.GetPropertyInfo<Property, object>(x => x.Value);
|
||||
private static readonly PropertyInfo VersionSelector = ExpressionHelper.GetPropertyInfo<Property, Guid>(x => x.Version);
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Returns the Alias of the PropertyType, which this Property is based on
|
||||
/// </summary>
|
||||
@@ -59,9 +59,14 @@ namespace Umbraco.Core.Models
|
||||
/// <summary>
|
||||
/// Returns the DatabaseType that the underlaying DataType is using to store its values
|
||||
/// </summary>
|
||||
/// <remarks>Only used internally when saving the property value</remarks>
|
||||
/// <remarks>
|
||||
/// Only used internally when saving the property value.
|
||||
/// </remarks>
|
||||
[IgnoreDataMember]
|
||||
internal DataTypeDatabaseType DataTypeDatabaseType { get { return _propertyType.DataTypeDatabaseType; } }
|
||||
internal DataTypeDatabaseType DataTypeDatabaseType
|
||||
{
|
||||
get { return _propertyType.DataTypeDatabaseType; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the PropertyType, which this Property is based on
|
||||
|
||||
@@ -3,7 +3,6 @@ using System.Collections.Specialized;
|
||||
using System.Reflection;
|
||||
using System.Runtime.Serialization;
|
||||
using Umbraco.Core.Models.EntityBase;
|
||||
using Umbraco.Core.Persistence.Mappers;
|
||||
|
||||
namespace Umbraco.Core.Models
|
||||
{
|
||||
|
||||
@@ -14,6 +14,7 @@ namespace Umbraco.Core.Models
|
||||
[DataContract(IsReference = true)]
|
||||
public class PropertyType : Entity, IEquatable<PropertyType>
|
||||
{
|
||||
private readonly bool _isExplicitDbType;
|
||||
private string _name;
|
||||
private string _alias;
|
||||
private string _description;
|
||||
@@ -29,16 +30,28 @@ namespace Umbraco.Core.Models
|
||||
public PropertyType(IDataTypeDefinition dataTypeDefinition)
|
||||
{
|
||||
if(dataTypeDefinition.HasIdentity)
|
||||
DataTypeDefinitionId = dataTypeDefinition.Id;
|
||||
_dataTypeDefinitionId = dataTypeDefinition.Id;
|
||||
|
||||
DataTypeId = dataTypeDefinition.ControlId;
|
||||
DataTypeDatabaseType = dataTypeDefinition.DatabaseType;
|
||||
_dataTypeId = dataTypeDefinition.ControlId;
|
||||
_dataTypeDatabaseType = dataTypeDefinition.DatabaseType;
|
||||
}
|
||||
|
||||
internal PropertyType(Guid dataTypeControlId, DataTypeDatabaseType dataTypeDatabaseType)
|
||||
: this(dataTypeControlId, dataTypeDatabaseType, false)
|
||||
{
|
||||
DataTypeId = dataTypeControlId;
|
||||
DataTypeDatabaseType = dataTypeDatabaseType;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Used internally to assign an explicity database type for this property type regardless of what the underlying data type/property editor is.
|
||||
/// </summary>
|
||||
/// <param name="dataTypeControlId"></param>
|
||||
/// <param name="dataTypeDatabaseType"></param>
|
||||
/// <param name="isExplicitDbType"></param>
|
||||
internal PropertyType(Guid dataTypeControlId, DataTypeDatabaseType dataTypeDatabaseType, bool isExplicitDbType)
|
||||
{
|
||||
_isExplicitDbType = isExplicitDbType;
|
||||
_dataTypeId = dataTypeControlId;
|
||||
_dataTypeDatabaseType = dataTypeDatabaseType;
|
||||
}
|
||||
|
||||
private static readonly PropertyInfo NameSelector = ExpressionHelper.GetPropertyInfo<PropertyType, string>(x => x.Name);
|
||||
@@ -149,6 +162,9 @@ namespace Umbraco.Core.Models
|
||||
get { return _dataTypeDatabaseType; }
|
||||
set
|
||||
{
|
||||
//don't allow setting this if an explicit declaration has been made in the ctor
|
||||
if (_isExplicitDbType) return;
|
||||
|
||||
SetPropertyValueAndDetectChanges(o =>
|
||||
{
|
||||
_dataTypeDatabaseType = value;
|
||||
@@ -361,8 +377,16 @@ namespace Umbraco.Core.Models
|
||||
//Check against Regular Expression for Legacy DataTypes - Validation exists and value is not null:
|
||||
if(string.IsNullOrEmpty(ValidationRegExp) == false && (value != null && string.IsNullOrEmpty(value.ToString()) == false))
|
||||
{
|
||||
var regexPattern = new Regex(ValidationRegExp);
|
||||
return regexPattern.IsMatch(value.ToString());
|
||||
try
|
||||
{
|
||||
var regexPattern = new Regex(ValidationRegExp);
|
||||
return regexPattern.IsMatch(value.ToString());
|
||||
}
|
||||
catch
|
||||
{
|
||||
throw new Exception(string .Format("Invalid validation expression on property {0}",this.Alias));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//TODO Add PropertyEditor validation when its relevant to introduce
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace Umbraco.Core.Models.PublishedContent
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides methods to handle extended content.
|
||||
/// </summary>
|
||||
internal interface IPublishedContentExtended : IPublishedContent
|
||||
{
|
||||
/// <summary>
|
||||
/// Adds a property to the extended content.
|
||||
/// </summary>
|
||||
/// <param name="property">The property to add.</param>
|
||||
void AddProperty(IPublishedProperty property);
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether properties were added to the extended content.
|
||||
/// </summary>
|
||||
bool HasAddedProperties { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Sets the content set of the extended content.
|
||||
/// </summary>
|
||||
/// <param name="contentSet"></param>
|
||||
void SetContentSet(IEnumerable<IPublishedContent> contentSet);
|
||||
|
||||
/// <summary>
|
||||
/// Resets the content set of the extended content.
|
||||
/// </summary>
|
||||
void ClearContentSet();
|
||||
|
||||
/// <summary>
|
||||
/// Sets the index of the extended content.
|
||||
/// </summary>
|
||||
/// <param name="value">The index value.</param>
|
||||
void SetIndex(int value);
|
||||
|
||||
/// <summary>
|
||||
/// Resets the index of the extended content.
|
||||
/// </summary>
|
||||
void ClearIndex();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
namespace Umbraco.Core.Models.PublishedContent
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides the model creation service.
|
||||
/// </summary>
|
||||
internal interface IPublishedContentModelFactory
|
||||
{
|
||||
/// <summary>
|
||||
/// Creates a strongly-typed model representing a published content.
|
||||
/// </summary>
|
||||
/// <param name="content">The original published content.</param>
|
||||
/// <returns>The strongly-typed model representing the published content, or the published content
|
||||
/// itself it the factory has no model for that content type.</returns>
|
||||
IPublishedContent CreateModel(IPublishedContent content);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,151 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Linq;
|
||||
|
||||
namespace Umbraco.Core.Models.PublishedContent
|
||||
{
|
||||
|
||||
public class PublishedContentExtended : PublishedContentWrapped, IPublishedContentExtended
|
||||
{
|
||||
#region Constructor
|
||||
|
||||
// protected for models, private for our static Extend method
|
||||
protected PublishedContentExtended(IPublishedContent content)
|
||||
: base(content)
|
||||
{ }
|
||||
|
||||
#endregion
|
||||
|
||||
#region Index
|
||||
|
||||
private int? _index;
|
||||
|
||||
public override int GetIndex()
|
||||
{
|
||||
// fast
|
||||
if (_index.HasValue) return _index.Value;
|
||||
|
||||
// slow -- and don't cache, not in a set
|
||||
if (_contentSet == null) return Content.GetIndex();
|
||||
|
||||
// slow -- but cache for next time
|
||||
var index = _contentSet.FindIndex(x => x.Id == Id);
|
||||
if (index < 0)
|
||||
throw new IndexOutOfRangeException("Could not find content in the content set.");
|
||||
_index = index;
|
||||
return index;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Extend
|
||||
|
||||
internal static IPublishedContentExtended Extend(IPublishedContent content, IEnumerable<IPublishedContent> contentSet)
|
||||
{
|
||||
var wrapped = content as PublishedContentExtended;
|
||||
while (wrapped != null && ((IPublishedContentExtended)wrapped).HasAddedProperties == false)
|
||||
wrapped = (content = wrapped.Unwrap()) as PublishedContentExtended;
|
||||
|
||||
// if the factory returns something else than content it means it has created
|
||||
// a model, and then that model has to inherit from PublishedContentExtended,
|
||||
// => implements the internal IPublishedContentExtended.
|
||||
|
||||
var model = PublishedContentModelFactory.CreateModel(content);
|
||||
var extended = model == content // == means the factory did not create a model
|
||||
? new PublishedContentExtended(content) // so we have to extend
|
||||
: model; // else we can use what the factory returned
|
||||
|
||||
var extended2 = extended as IPublishedContentExtended;
|
||||
if (extended2 != null) // always true, but keeps Resharper happy
|
||||
extended2.SetContentSet(contentSet);
|
||||
return extended2;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region IPublishedContentExtended
|
||||
|
||||
void IPublishedContentExtended.AddProperty(IPublishedProperty property)
|
||||
{
|
||||
if (_properties == null)
|
||||
_properties = new Collection<IPublishedProperty>();
|
||||
_properties.Add(property);
|
||||
}
|
||||
|
||||
bool IPublishedContentExtended.HasAddedProperties
|
||||
{
|
||||
get { return _properties != null; }
|
||||
}
|
||||
|
||||
void IPublishedContentExtended.SetContentSet(IEnumerable<IPublishedContent> contentSet)
|
||||
{
|
||||
_contentSet = contentSet;
|
||||
}
|
||||
|
||||
void IPublishedContentExtended.ClearContentSet()
|
||||
{
|
||||
_contentSet = null;
|
||||
}
|
||||
|
||||
void IPublishedContentExtended.SetIndex(int value)
|
||||
{
|
||||
_index = value;
|
||||
}
|
||||
|
||||
void IPublishedContentExtended.ClearIndex()
|
||||
{
|
||||
_index = null;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Content set
|
||||
|
||||
private IEnumerable<IPublishedContent> _contentSet;
|
||||
|
||||
public override IEnumerable<IPublishedContent> ContentSet
|
||||
{
|
||||
get { return _contentSet ?? Content.ContentSet; }
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Properties
|
||||
|
||||
private ICollection<IPublishedProperty> _properties;
|
||||
|
||||
public override ICollection<IPublishedProperty> Properties
|
||||
{
|
||||
get
|
||||
{
|
||||
return _properties == null
|
||||
? Content.Properties
|
||||
: Content.Properties.Union(_properties).ToList();
|
||||
}
|
||||
}
|
||||
|
||||
public override object this[string alias]
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_properties != null)
|
||||
{
|
||||
var property = _properties.FirstOrDefault(prop => prop.PropertyTypeAlias.InvariantEquals(alias));
|
||||
if (property != null) return property.HasValue ? property.Value : null;
|
||||
}
|
||||
return Content[alias];
|
||||
}
|
||||
}
|
||||
|
||||
public override IPublishedProperty GetProperty(string alias)
|
||||
{
|
||||
return _properties == null
|
||||
? Content.GetProperty(alias)
|
||||
: _properties.FirstOrDefault(prop => prop.PropertyTypeAlias.InvariantEquals(alias)) ?? Content.GetProperty(alias);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace Umbraco.Core.Models.PublishedContent
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a strongly-typed published content.
|
||||
/// </summary>
|
||||
/// <remarks>Every strongly-typed published content class should inherit from <c>PublishedContentModel</c>
|
||||
/// (or inherit from a class that inherits from... etc.) so they are picked by the factory.</remarks>
|
||||
internal abstract class PublishedContentModel : PublishedContentExtended
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="PublishedContentModel"/> class with
|
||||
/// an original <see cref="IPublishedContent"/> instance.
|
||||
/// </summary>
|
||||
/// <param name="content">The original content.</param>
|
||||
protected PublishedContentModel(IPublishedContent content)
|
||||
: base(content)
|
||||
{ }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
using System;
|
||||
|
||||
namespace Umbraco.Core.Models.PublishedContent
|
||||
{
|
||||
/// <summary>
|
||||
/// Indicates that the class is a published content model for a specified content type.
|
||||
/// </summary>
|
||||
/// <remarks>By default, the name of the class is assumed to be the content type alias. The
|
||||
/// <c>PublishedContentModelAttribute</c> can be used to indicate a different alias.</remarks>
|
||||
[AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = false)]
|
||||
internal sealed class PublishedContentModelAttribute : Attribute
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="PublishedContentModelAttribute"/> class with a content type alias.
|
||||
/// </summary>
|
||||
/// <param name="contentTypeAlias">The content type alias.</param>
|
||||
public PublishedContentModelAttribute(string contentTypeAlias)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(contentTypeAlias))
|
||||
throw new ArgumentException("Argument cannot be null nor empty.", "contentTypeAlias");
|
||||
ContentTypeAlias = contentTypeAlias;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the content type alias.
|
||||
/// </summary>
|
||||
public string ContentTypeAlias { get; private set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
namespace Umbraco.Core.Models.PublishedContent
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides strongly typed published content models services.
|
||||
/// </summary>
|
||||
internal static class PublishedContentModelFactory
|
||||
{
|
||||
/// <summary>
|
||||
/// Creates a strongly typed published content model for an internal published content.
|
||||
/// </summary>
|
||||
/// <param name="content">The internal published content.</param>
|
||||
/// <returns>The strongly typed published content model.</returns>
|
||||
public static IPublishedContent CreateModel(IPublishedContent content)
|
||||
{
|
||||
return PublishedContentModelFactoryResolver.Current.HasValue
|
||||
? PublishedContentModelFactoryResolver.Current.Factory.CreateModel(content)
|
||||
: content;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq.Expressions;
|
||||
|
||||
namespace Umbraco.Core.Models.PublishedContent
|
||||
{
|
||||
/// <summary>
|
||||
/// Implements a strongly typed content model factory
|
||||
/// </summary>
|
||||
internal class PublishedContentModelFactoryImpl : IPublishedContentModelFactory
|
||||
{
|
||||
//private readonly Dictionary<string, ConstructorInfo> _constructors
|
||||
// = new Dictionary<string, ConstructorInfo>();
|
||||
|
||||
private readonly Dictionary<string, Func<IPublishedContent, IPublishedContent>> _constructors
|
||||
= new Dictionary<string, Func<IPublishedContent, IPublishedContent>>();
|
||||
|
||||
public PublishedContentModelFactoryImpl()
|
||||
{
|
||||
var types = PluginManager.Current.ResolveTypes<PublishedContentModel>();
|
||||
var ctorArgTypes = new[] { typeof(IPublishedContent) };
|
||||
|
||||
foreach (var type in types)
|
||||
{
|
||||
var constructor = type.GetConstructor(ctorArgTypes);
|
||||
if (constructor == null)
|
||||
throw new InvalidOperationException(string.Format("Type {0} is missing a public constructor with one argument of type IPublishedContent.", type.FullName));
|
||||
var attribute = type.GetCustomAttribute<PublishedContentModelAttribute>(false);
|
||||
var typeName = attribute == null ? type.Name : attribute.ContentTypeAlias;
|
||||
typeName = typeName.ToLowerInvariant();
|
||||
|
||||
if (_constructors.ContainsKey(typeName))
|
||||
throw new InvalidOperationException(string.Format("More that one type want to be a model for content type {0}.", typeName));
|
||||
|
||||
// should work everywhere, but slow
|
||||
//_constructors[typeName] = constructor;
|
||||
|
||||
// much faster with a dynamic method but potential MediumTrust issues
|
||||
// here http://stackoverflow.com/questions/16363838/how-do-you-call-a-constructor-via-an-expression-tree-on-an-existing-object
|
||||
|
||||
// fast enough and works in MediumTrust
|
||||
// read http://boxbinary.com/2011/10/how-to-run-a-unit-test-in-medium-trust-with-nunitpart-three-umbraco-framework-testing/
|
||||
var exprArg = Expression.Parameter(typeof(IPublishedContent), "content");
|
||||
var exprNew = Expression.New(constructor, exprArg);
|
||||
var expr = Expression.Lambda<Func<IPublishedContent, IPublishedContent>>(exprNew, exprArg);
|
||||
var func = expr.Compile();
|
||||
_constructors[typeName] = func;
|
||||
}
|
||||
}
|
||||
|
||||
public IPublishedContent CreateModel(IPublishedContent content)
|
||||
{
|
||||
// be case-insensitive
|
||||
var contentTypeAlias = content.DocumentTypeAlias.ToLowerInvariant();
|
||||
|
||||
//ConstructorInfo constructor;
|
||||
//return _constructors.TryGetValue(contentTypeAlias, out constructor)
|
||||
// ? (IPublishedContent) constructor.Invoke(new object[] { content })
|
||||
// : content;
|
||||
|
||||
Func<IPublishedContent, IPublishedContent> constructor;
|
||||
return _constructors.TryGetValue(contentTypeAlias, out constructor)
|
||||
? constructor(content)
|
||||
: content;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
using Umbraco.Core.ObjectResolution;
|
||||
|
||||
namespace Umbraco.Core.Models.PublishedContent
|
||||
{
|
||||
/// <summary>
|
||||
/// Resolves the IPublishedContentModelFactory object.
|
||||
/// </summary>
|
||||
internal class PublishedContentModelFactoryResolver : SingleObjectResolverBase<PublishedContentModelFactoryResolver, IPublishedContentModelFactory>
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="PublishedContentModelFactoryResolver"/>.
|
||||
/// </summary>
|
||||
/// <remarks>The resolver is created by the <c>WebBootManager</c> and thus the constructor remains internal.</remarks>
|
||||
internal PublishedContentModelFactoryResolver()
|
||||
: base()
|
||||
{ }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="PublishedContentModelFactoryResolver"/> with a factory.
|
||||
/// </summary>
|
||||
/// <param name="factory">The factory.</param>
|
||||
/// <remarks>The resolver is created by the <c>WebBootManager</c> and thus the constructor remains internal.</remarks>
|
||||
internal PublishedContentModelFactoryResolver(IPublishedContentModelFactory factory)
|
||||
: base(factory)
|
||||
{ }
|
||||
|
||||
/// <summary>
|
||||
/// Sets the factory.
|
||||
/// </summary>
|
||||
/// <param name="factory">The factory.</param>
|
||||
/// <remarks>For developers, at application startup.</remarks>
|
||||
public void SetFactory(IPublishedContentModelFactory factory)
|
||||
{
|
||||
Value = factory;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the factory.
|
||||
/// </summary>
|
||||
public IPublishedContentModelFactory Factory
|
||||
{
|
||||
get { return Value; }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace Umbraco.Core.Models.PublishedContent
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents an ordered set of <see cref="IPublishedContent"/>.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of content.</typeparam>
|
||||
public class PublishedContentOrderedSet<T> : PublishedContentSet<T>, IOrderedEnumerable<T>
|
||||
where T : class, IPublishedContent
|
||||
{
|
||||
// ReSharper disable ParameterTypeCanBeEnumerable.Local
|
||||
internal PublishedContentOrderedSet(IOrderedEnumerable<T> content)
|
||||
// ReSharper restore ParameterTypeCanBeEnumerable.Local
|
||||
: base(content)
|
||||
{ }
|
||||
|
||||
// note: because we implement IOrderedEnumerable, we don't need to implement the ThenBy nor
|
||||
// ThenByDescending methods here, only CreateOrderedEnumerable and that does it.
|
||||
|
||||
#region IOrderedEnumerable<T>
|
||||
|
||||
public IOrderedEnumerable<T> CreateOrderedEnumerable<TKey>(Func<T, TKey> keySelector, IComparer<TKey> comparer, bool descending)
|
||||
{
|
||||
return new PublishedContentOrderedSet<T>(((IOrderedEnumerable<T>)Source).CreateOrderedEnumerable(keySelector, comparer, descending));
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,234 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace Umbraco.Core.Models.PublishedContent
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a set of <see cref="IPublishedContent"/>.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of content.</typeparam>
|
||||
/// <remarks>
|
||||
/// <para>A <c>ContentSet{T}</c> is created from an <c>IEnumerable{T}</c> using the <c>ToContentSet</c>
|
||||
/// extension method.</para>
|
||||
/// <para>The content set source is enumerated only once. Same as what you get
|
||||
/// when you call ToList on an IEnumerable. Only, ToList enumerates its source when
|
||||
/// created, whereas a content set enumerates its source only when the content set itself
|
||||
/// is enumerated.</para>
|
||||
/// </remarks>
|
||||
public class PublishedContentSet<T> : IEnumerable<T>
|
||||
where T : class, IPublishedContent
|
||||
{
|
||||
// used by <c>ToContentSet</c> extension method to initialize a new set from an IEnumerable.
|
||||
internal PublishedContentSet(IEnumerable<T> source)
|
||||
{
|
||||
if (source == null)
|
||||
throw new ArgumentNullException("source");
|
||||
Source = source;
|
||||
}
|
||||
|
||||
#region Source
|
||||
|
||||
protected readonly IEnumerable<T> Source;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Enumerated
|
||||
|
||||
// cache the enumeration so we don't enumerate more than once. Same as what you get
|
||||
// when you call ToList on an IEnumerable. Only, ToList enumerates its source when
|
||||
// created, whereas a content set enumerates its source only when the content set itself
|
||||
// is enumerated.
|
||||
|
||||
// cache the wrapped items so if we reset the enumeration, we do not re-wrap everything (only new items).
|
||||
|
||||
private T[] _enumerated;
|
||||
private readonly Dictionary<T, IPublishedContentExtended> _xContent = new Dictionary<T, IPublishedContentExtended>();
|
||||
|
||||
// wrap an item, ie create the actual clone for this set
|
||||
private T MapContentAsT(T t)
|
||||
{
|
||||
return MapContent(t) as T;
|
||||
}
|
||||
|
||||
internal IPublishedContentExtended MapContent(T t)
|
||||
{
|
||||
IPublishedContentExtended extend;
|
||||
if (_xContent.TryGetValue(t, out extend)) return extend;
|
||||
|
||||
extend = PublishedContentExtended.Extend(t, this);
|
||||
var asT = extend as T;
|
||||
if (asT == null)
|
||||
throw new InvalidOperationException(string.Format("Failed extend a published content of type {0}."
|
||||
+ "Got {1} when expecting {2}.", t.GetType().FullName, extend.GetType().FullName, typeof(T).FullName));
|
||||
_xContent[t] = extend;
|
||||
return extend;
|
||||
}
|
||||
|
||||
private T[] Enumerated
|
||||
{
|
||||
get
|
||||
{
|
||||
// enumerate the source and cache the result
|
||||
// tell clones about their index within the set (for perfs purposes)
|
||||
var index = 0;
|
||||
return _enumerated ?? (_enumerated = Source.Select(t =>
|
||||
{
|
||||
var extend = MapContent(t);
|
||||
extend.SetIndex(index++);
|
||||
return extend as T;
|
||||
}).ToArray());
|
||||
}
|
||||
}
|
||||
|
||||
// indicates that the source has changed
|
||||
// so the set can clear its inner caches
|
||||
// should only be used by DynamicPublishedContentList
|
||||
internal void SourceChanged()
|
||||
{
|
||||
// reset the cached enumeration so it's enumerated again
|
||||
if (_enumerated == null) return;
|
||||
_enumerated = null;
|
||||
|
||||
foreach (var item in _xContent.Values)
|
||||
item.ClearIndex();
|
||||
|
||||
var removed = _xContent.Keys.Except(Source);
|
||||
foreach (var content in removed)
|
||||
{
|
||||
_xContent[content].ClearContentSet();
|
||||
_xContent.Remove(content);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the number of items in the set.
|
||||
/// </summary>
|
||||
/// <returns>The number of items in the set.</returns>
|
||||
/// <remarks>Will cause the set to be enumerated if it hasn't been already.</remarks>
|
||||
public virtual int Count
|
||||
{
|
||||
get { return Enumerated.Length; }
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region IEnumerable<T>
|
||||
|
||||
public IEnumerator<T> GetEnumerator()
|
||||
{
|
||||
return ((IEnumerable<T>)Enumerated).GetEnumerator();
|
||||
}
|
||||
|
||||
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
|
||||
{
|
||||
return GetEnumerator();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Wrap methods returning T
|
||||
|
||||
public T ElementAt(int index)
|
||||
{
|
||||
return MapContentAsT(Source.ElementAt(index));
|
||||
}
|
||||
|
||||
public T ElementAtOrDefault(int index)
|
||||
{
|
||||
var element = Source.ElementAtOrDefault(index);
|
||||
return element == null ? null : MapContentAsT(element);
|
||||
}
|
||||
|
||||
public T First()
|
||||
{
|
||||
return MapContentAsT(Source.First());
|
||||
}
|
||||
|
||||
public T First(Func<T, bool> predicate)
|
||||
{
|
||||
return MapContentAsT(Source.First(predicate));
|
||||
}
|
||||
|
||||
public T FirstOrDefault()
|
||||
{
|
||||
var first = Source.FirstOrDefault();
|
||||
return first == null ? null : MapContentAsT(first);
|
||||
}
|
||||
|
||||
public T FirstOrDefault(Func<T, bool> predicate)
|
||||
{
|
||||
var first = Source.FirstOrDefault(predicate);
|
||||
return first == null ? null : MapContentAsT(first);
|
||||
}
|
||||
|
||||
public T Last()
|
||||
{
|
||||
return MapContentAsT(Source.Last());
|
||||
}
|
||||
|
||||
public T Last(Func<T, bool> predicate)
|
||||
{
|
||||
return MapContentAsT(Source.Last(predicate));
|
||||
}
|
||||
|
||||
public T LastOrDefault()
|
||||
{
|
||||
var last = Source.LastOrDefault();
|
||||
return last == null ? null : MapContentAsT(last);
|
||||
}
|
||||
|
||||
public T LastOrDefault(Func<T, bool> predicate)
|
||||
{
|
||||
var last = Source.LastOrDefault(predicate);
|
||||
return last == null ? null : MapContentAsT(last);
|
||||
}
|
||||
|
||||
public T Single()
|
||||
{
|
||||
return MapContentAsT(Source.Single());
|
||||
}
|
||||
|
||||
public T Single(Func<T, bool> predicate)
|
||||
{
|
||||
return MapContentAsT(Source.Single(predicate));
|
||||
}
|
||||
|
||||
public T SingleOrDefault()
|
||||
{
|
||||
var single = Source.SingleOrDefault();
|
||||
return single == null ? null : MapContentAsT(single);
|
||||
}
|
||||
|
||||
public T SingleOrDefault(Func<T, bool> predicate)
|
||||
{
|
||||
var single = Source.SingleOrDefault(predicate);
|
||||
return single == null ? null : MapContentAsT(single);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Wrap methods returning IOrderedEnumerable<T>
|
||||
|
||||
public PublishedContentOrderedSet<T> OrderBy<TKey>(Func<T, TKey> keySelector)
|
||||
{
|
||||
return new PublishedContentOrderedSet<T>(Source.OrderBy(keySelector));
|
||||
}
|
||||
|
||||
public PublishedContentOrderedSet<T> OrderBy<TKey>(Func<T, TKey> keySelector, IComparer<TKey> comparer)
|
||||
{
|
||||
return new PublishedContentOrderedSet<T>(Source.OrderBy(keySelector, comparer));
|
||||
}
|
||||
|
||||
public PublishedContentOrderedSet<T> OrderByDescending<TKey>(Func<T, TKey> keySelector)
|
||||
{
|
||||
return new PublishedContentOrderedSet<T>(Source.OrderByDescending(keySelector));
|
||||
}
|
||||
|
||||
public PublishedContentOrderedSet<T> OrderByDescending<TKey>(Func<T, TKey> keySelector, IComparer<TKey> comparer)
|
||||
{
|
||||
return new PublishedContentOrderedSet<T>(Source.OrderByDescending(keySelector, comparer));
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user